Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parse imports work #133

Merged
merged 3 commits into from
Dec 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 22 additions & 18 deletions src/resolve-exports.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,24 +30,7 @@ const proxy = new Proxy(base, {
},
})

function resolveExports(source) {
let AST
try {
debug('parsing source as a script')
AST = babel.parse(source, {
plugins,
sourceType: 'script',
}).program
debug('success!')
} catch (e) {
debug('parsing source as a module')
AST = babel.parse(source, {
plugins,
sourceType: 'module',
}).program
debug('success!')
}

function resolveExportsInAst(AST, proxy) {
const exportedValues = {}

walk.ancestor(
Expand Down Expand Up @@ -83,6 +66,27 @@ function resolveExports(source) {
return exportedValues
}

function resolveExports(source) {
let AST
try {
debug('parsing source as a script for exports')
AST = babel.parse(source, {
plugins,
sourceType: 'script',
}).program
debug('success!')
} catch (e) {
debug('parsing source as a module for exports')
AST = babel.parse(source, {
plugins,
sourceType: 'module',
}).program
debug('success!')
}

return resolveExportsInAst(AST, proxy)
}

module.exports = {
resolveExports,
}
101 changes: 101 additions & 0 deletions src/resolve-imports.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
const babel = require('@babel/parser')
const walk = require('acorn-walk')
const debug = require('debug')('find-test-names')
const { resolveExports } = require('./resolve-exports')

const base = walk.make({})

const plugins = [
'jsx',
'estree', // To generate estree compatible AST
'typescript',
]

function ignore(_node, _st, _c) {}

/**
* The proxy ignores all AST nodes for which acorn has no base visitor.
* This includes TypeScript specific nodes like TSInterfaceDeclaration,
* but also babel-specific nodes like ClassPrivateProperty.
*
* Since describe / it are CallExpressions, ignoring nodes should not affect
* the test name extraction.
*/
const proxy = new Proxy(base, {
get: function (target, prop) {
if (target[prop]) {
return Reflect.get(...arguments)
}

return ignore
},
})

function resolveImportsInAst(AST, fileProvider) {
const importedValues = {}

walk.ancestor(
AST,
{
ImportDeclaration(node) {
// console.log(node)
// from where...
const fromWhere = node.source?.value
if (!fromWhere) {
return
}
debug('importing from "%s"', fromWhere)
const source = fileProvider(fromWhere)
if (!source) {
debug('could not find source for "%s"', fromWhere)
return
}
const exportedValues = resolveExports(source)
if (!exportedValues) {
debug('could not find any exports in "%s"', fromWhere)
return
}

node.specifiers.forEach((specifier) => {
const importedName = specifier.imported.name
const localName = specifier.local.name
debug('importing "%s" as "%s"', importedName, localName)
if (!exportedValues[importedName]) {
debug('could not find export "%s" in "%s"', importedName, fromWhere)
return
}
importedValues[localName] = exportedValues[importedName]
})
},
},
proxy,
)

return importedValues
}

function resolveImports(source, fileProvider) {
let AST
try {
debug('parsing source as a script for imports')
AST = babel.parse(source, {
plugins,
sourceType: 'script',
}).program
debug('success!')
} catch (e) {
debug('parsing source as a module for imports')
AST = babel.parse(source, {
plugins,
sourceType: 'module',
}).program
debug('success!')
}

return resolveImportsInAst(AST, fileProvider)
}

module.exports = {
resolveImports,
resolveImportsInAst,
}
71 changes: 71 additions & 0 deletions test/resolve-imports.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
const { resolveImports } = require('../src/resolve-imports')
const { stripIndent } = require('common-tags')
const test = require('ava')

const fileProvider = (relativePath) => {
if (relativePath === './file-a') {
return stripIndent`
export const foo = 'foo'
export const bar = 'bar'
`
}

if (relativePath === './file-b') {
return stripIndent`
export const TAGS = {
user: '@user',
sanity: '@sanity',
}
`
}
}

test('finds the imports', (t) => {
t.plan(1)
const source = stripIndent`
import { foo } from './file-a'
`

const result = resolveImports(source, fileProvider)
t.deepEqual(result, { foo: 'foo' })
})

test('renames the import', (t) => {
t.plan(1)
const source = stripIndent`
import { foo as FOOBAR } from './file-a'
`

const result = resolveImports(source, fileProvider)
t.deepEqual(result, { FOOBAR: 'foo' })
})

test('two imports', (t) => {
t.plan(1)
const source = stripIndent`
import { foo, bar } from './file-a'
`

const result = resolveImports(source, fileProvider)
t.deepEqual(result, { foo: 'foo', bar: 'bar' })
})

test('non-existent import', (t) => {
t.plan(1)
const source = stripIndent`
import { quux } from './file-a'
`

const result = resolveImports(source, fileProvider)
t.deepEqual(result, {})
})

test('finds the exported object', (t) => {
t.plan(1)
const source = stripIndent`
import { TAGS } from './file-b'
`

const result = resolveImports(source, fileProvider)
t.deepEqual(result, { TAGS: { user: '@user', sanity: '@sanity' } })
})