
Security News
Axios Supply Chain Attack Reaches OpenAI macOS Signing Pipeline, Forces Certificate Rotation
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.
esm-analyzer
Advanced tools
The scanner and analyzer of ESM.
pnpm i esm-analyzer
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)
langThe lang parameter is used to specify the language of the source code.
It can be one of the following values:
jsjsxtstsximports scannerimport foo from 'bar'import * as foo from 'bar'import { foo } from 'bar'import { foo as bar } from 'bar'import type { foo } from 'bar' or import { type foo } from 'bar'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.
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',
},
]
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))
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 scannerThe variable declarations is an array of ScanVariableDeclarationResult.
StringLiteralNumericLiteralBooleanLiteralNullLiteralObjectExpressionArrayExpressionCallExpressionThe ScanVariableDeclarationResult is defined as follows:
export interface ScanVariableDeclarationResult {
loc: ASTNodeLocation
kind: t.VariableDeclaration['kind']
name: string
init: ResolveVariableDeclaration
}
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',
},
]
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))
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 scannerexport default fooexport { foo }
Identifier and primitiveexport { foo as bar }export * from 'foo'export type { foo } from 'bar'export { type foo } from 'bar'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
}
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',
},
]
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))
The scanExport function accepts a config object as the second parameter:
export type ScanExportType =
| 'ExportNamedDeclaration'
| 'ExportAllDeclaration'
| 'ExportDefaultDeclaration'
interface ScanExportConfig {
includeType?: ScanExportType[]
excludeType?: ScanExportType[]
}
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',
},
],
]
MIT
FAQs
The scanner and analyzer of ESM.
We found that esm-analyzer demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.

Security News
Open source is under attack because of how much value it creates. It has been the foundation of every major software innovation for the last three decades. This is not the time to walk away from it.

Security News
Socket CEO Feross Aboukhadijeh breaks down how North Korea hijacked Axios and what it means for the future of software supply chain security.