esm-analyzer
v0.3.6
Published
The scanner and analyzer of ESM.
Downloads
4
Maintainers
Readme
esm-analyzer
The scanner and analyzer of ESM.
Installation
pnpm i esm-analyzer
Scanner
The scanner uses @babel/parser
to parse the source code and find the import
and export
statements.
import { scan } from 'esm-analyzer'
const { imports, exports } = scan(sourceCode, lang)
lang
The lang
parameter is used to specify the language of the source code.
It can be one of the following values:
js
jsx
ts
tsx
imports
scanner
cases
- ✅ import default, e.g.
import foo from 'bar'
- ✅ import namespace, e.g.
import * as foo from 'bar'
- ✅ import named, e.g.
import { foo } from 'bar'
- ✅ import named with alias, e.g.
import { foo as bar } from 'bar'
- ✅ import type named, e.g.
import type { foo } from 'bar'
orimport { type foo } from 'bar'
type definition
The imports
type is defined as follows:
interface ScanResultBase {
source: string
loc: ASTNodeLocation
}
// import a from 'a'
interface ScanImportResultDefault extends ScanResultBase {
type: 'default'
local: string // a
}
// import * as a from 'a'
interface ScanImportResultNamespace extends ScanResultBase {
type: 'namespace'
local: string // a
}
// import { a as b } from 'a'
interface ScanImportResultImport extends ScanResultBase {
type: 'import'
subType: 'id' | 'string' // id: `import { a } from 'a'`; string: `import { 'a' } from 'a'`
isType: boolean // `import type { a } from 'a'` or `import { type a } from 'a'`
local: string // b
imported: string // a
}
type ScanImportResultItem = ScanImportResultDefault | ScanImportResultNamespace | ScanImportResultImport
The imports
is an array of ScanImportResultItem
.
examples
The basic example:
const code = 'import foo from "bar"'
scan(code, 'js').imports
will be:
[
{
loc: {
end: {
column: 10,
index: 10,
line: 1,
},
start: {
column: 7,
index: 7,
line: 1,
},
},
local: 'foo',
source: 'bar',
type: 'default',
},
]
the standalone API
Also, you can use the standalone import
scanner API(with loadScanner
helper):
import { loadScanner } from 'esm-analyzer'
const importResults = loadScanner(sourceCode, lang, node => scanImport(node))
config
The scanImport
function accepts a config object as the second parameter:
interface ScanImportConfig {
includeSource?: string[] // the source list to be included
excludeSource?: string[] // the source list to be excluded
skipType?: boolean // whether to skip the type import
}
const defaultConfig: Required<ScanImportConfig> = {
includeSource: [],
excludeSource: [],
skipType: false,
}
variable declarations
scanner
The variable declarations
is an array of ScanVariableDeclarationResult
.
cases
- ❌ deferred init
- primitive declaration
- ✅
StringLiteral
- ✅
NumericLiteral
- ✅
BooleanLiteral
- ✅
NullLiteral
- ✅
- ✅ reference declaration
- complex declaration
- ✅
ObjectExpression
- ✅
ArrayExpression
- ✅
CallExpression
- ❗ Others are not supported yet
- ✅
type definition
The ScanVariableDeclarationResult
is defined as follows:
export interface ScanVariableDeclarationResult {
loc: ASTNodeLocation
kind: t.VariableDeclaration['kind']
name: string
init: ResolveVariableDeclaration
}
examples
The basic example:
const code = 'const foo = "bar"'
scan(code, 'js').variables
The output will be:
[
{
init: {
type: 'StringLiteral',
value: 'bar',
},
kind: 'const',
loc: {
end: {
column: 17,
index: 17,
line: 1,
},
start: {
column: 6,
index: 6,
line: 1,
},
},
name: 'foo',
},
]
the standalone API
Also, you can use the standalone variable
scanner API(with loadScanner
helper):
import { loadScanner } from 'esm-analyzer'
const importResults = loadScanner(sourceCode, lang, node => scanVariableDeclaration(node))
config
The scanVariableDeclaration
function accepts a config object as the second parameter:
export type VariableType =
| 'StringLiteral'
| 'NumericLiteral'
| 'BooleanLiteral'
| 'NullLiteral'
| 'ObjectExpression'
| 'ArrayExpression'
| 'CallExpression'
interface ScanVariableDeclarationConfig {
includeType?: VariableType[]
excludeType?: VariableType[]
}
export
scanner
cases
- ✅ export default, e.g.
export default foo
- ✅ export named, e.g.
export { foo }
- only support
Identifier
and primitive - functions are not supported yet
- only support
- ✅ export named with alias, e.g.
export { foo as bar }
- ✅ export all, e.g.
export * from 'foo'
- ❌ export type, e.g.
export type { foo } from 'bar'
- ❌ export type named, e.g.
export { type foo } from 'bar'
type definition
The exports
type is defined as follows, will return ScanExportResult[]
export interface ScanExportNamedDeclarationResult {
type: 'ExportNamedDeclaration'
subType: 'VariableDeclaration'
kind: t.VariableDeclaration['kind']
declarations: {
name: string
init: ResolveVariableDeclaration
}[]
}
export interface ScanExportNamedSpecifiersResult {
type: 'ExportNamedDeclaration'
subType: 'Specifiers'
specifiers: {
local: string
exported: string
}[]
source: string | null
}
export interface ScanExportAllResult {
type: 'ExportAllDeclaration'
source: string
}
export interface ScanExportDefaultIdentifierResult {
type: 'ExportDefaultDeclaration'
subType: 'Identifier'
id: string
}
export interface ScanExportDefaultObjectResult {
type: 'ExportDefaultDeclaration'
subType: 'ObjectExpression'
properties: {
key: string
value: ResolveVariableDeclaration
}[]
}
export type ScanExportResult = (
| ScanExportNamedDeclarationResult
| ScanExportNamedSpecifiersResult
| ScanExportAllResult
| ScanExportDefaultIdentifierResult
| ScanExportDefaultObjectResult
) & {
loc: ASTNodeLocation
}
examples
The basic example:
const code = 'export default { a: 1, b: 2 }'
scan(code, 'js').exports
The result will be:
[
{
loc: {
end: {
column: 7,
index: 58,
line: 5,
},
start: {
column: 6,
index: 7,
line: 2,
},
},
properties: [
{
key: 'a',
value: {
type: 'NumericLiteral',
value: 1,
},
},
{
key: 'b',
value: {
type: 'NumericLiteral',
value: 2,
},
},
],
subType: 'ObjectExpression',
type: 'ExportDefaultDeclaration',
},
]
the standalone API
Also, you can use the standalone export
scanner API(with loadScanner
helper):
import { loadScanner } from 'esm-analyzer'
const importResults = loadScanner(sourceCode, lang, node => scanExport(node))
config
The scanExport
function accepts a config object as the second parameter:
export type ScanExportType =
| 'ExportNamedDeclaration'
| 'ExportAllDeclaration'
| 'ExportDefaultDeclaration'
interface ScanExportConfig {
includeType?: ScanExportType[]
excludeType?: ScanExportType[]
}
Analyzer
use analyze
you can find all the variable declarations
and their import statement
and export statement
const code1 = {
filename: '/src/bar.js',
code: `
export const bar = 'bar'
`,
}
const code2 = {
filename: '/src/foo.js',
code: `
import { bar, ref } from './bar'
export const foo = bar
const foo2 = ref(1)
`,
}
const p = new Project('test')
p.addFile(code1.filename, code1.code)
p.addFile(code2.filename, code2.code)
await p.prepare()
const c = p.findAnalyzeResults(code2.filename)
expect(c).toMatchSnapshot()
The result is map
, and it's entries is:
[
[
{
init: {
id: 'bar',
type: 'Identifier',
},
kind: 'const',
loc: {
end: {
column: 28,
index: 68,
line: 3,
},
start: {
column: 19,
index: 59,
line: 3,
},
},
name: 'foo',
},
{
fromExport: {
declarations: [
{
init: {
type: 'StringLiteral',
value: 'bar',
},
name: 'bar',
},
],
kind: 'const',
loc: {
end: {
column: 30,
index: 31,
line: 2,
},
start: {
column: 6,
index: 7,
line: 2,
},
},
subType: 'VariableDeclaration',
type: 'ExportNamedDeclaration',
},
fromImport: {
imported: 'bar',
isType: false,
loc: {
end: {
column: 18,
index: 19,
line: 2,
},
start: {
column: 15,
index: 16,
line: 2,
},
},
local: 'bar',
source: './bar',
subType: 'id',
type: 'import',
},
id: 'bar',
importFile: '/src/bar.js',
type: 'Identifier',
},
],
[
{
init: {
arguments: [
{
type: 'NumericLiteral',
value: 1,
},
],
callee: 'ref',
type: 'CallExpression',
},
kind: 'const',
loc: {
end: {
column: 25,
index: 94,
line: 4,
},
start: {
column: 12,
index: 81,
line: 4,
},
},
name: 'foo2',
},
{
arguments: [
{
type: 'NumericLiteral',
value: 1,
},
],
callee: 'ref',
calleeFrom: {
imported: 'ref',
isType: false,
loc: {
end: {
column: 23,
index: 24,
line: 2,
},
start: {
column: 20,
index: 21,
line: 2,
},
},
local: 'ref',
source: './bar',
subType: 'id',
type: 'import',
},
type: 'CallExpression',
},
],
]
License
MIT