tasq/node_modules/import-in-the-middle/lib/get-esm-exports.js

119 lines
3.4 KiB
JavaScript

'use strict'
const { Parser } = require('acorn')
const { importAttributesOrAssertions } = require('acorn-import-attributes')
const acornOpts = {
ecmaVersion: 'latest',
sourceType: 'module'
}
const parser = Parser.extend(importAttributesOrAssertions)
function warn (txt) {
process.emitWarning(txt, 'get-esm-exports')
}
/**
* Utilizes an AST parser to interpret ESM source code and build a list of
* exported identifiers. In the baseline case, the list of identifiers will be
* the simple identifier names as written in the source code of the module.
* However, there is a special case:
*
* When an `export * from './foo.js'` line is encountered it is rewritten
* as `* from ./foo.js`. This allows the interpreting code to recognize a
* transitive export and recursively parse the indicated module. The returned
* identifier list will have "* from ./foo.js" as an item.
*
* @param {object} params
* @param {string} params.moduleSource The source code of the module to parse
* and interpret.
*
* @returns {Set<string>} The identifiers exported by the module along with any
* custom directives.
*/
function getEsmExports (moduleSource) {
const exportedNames = new Set()
const tree = parser.parse(moduleSource, acornOpts)
for (const node of tree.body) {
if (!node.type.startsWith('Export')) continue
switch (node.type) {
case 'ExportNamedDeclaration':
if (node.declaration) {
parseDeclaration(node, exportedNames)
} else {
parseSpecifiers(node, exportedNames)
}
break
case 'ExportDefaultDeclaration': {
exportedNames.add('default')
break
}
case 'ExportAllDeclaration':
if (node.exported) {
exportedNames.add(node.exported.name)
} else {
exportedNames.add(`* from ${node.source.value}`)
}
break
default:
warn('unrecognized export type: ' + node.type)
}
}
return exportedNames
}
function parseDeclaration (node, exportedNames) {
switch (node.declaration.type) {
case 'FunctionDeclaration':
exportedNames.add(node.declaration.id.name)
break
case 'VariableDeclaration':
for (const varDecl of node.declaration.declarations) {
parseVariableDeclaration(varDecl, exportedNames)
}
break
case 'ClassDeclaration':
exportedNames.add(node.declaration.id.name)
break
default:
warn('unknown declaration type: ' + node.delcaration.type)
}
}
function parseVariableDeclaration (node, exportedNames) {
switch (node.id.type) {
case 'Identifier':
exportedNames.add(node.id.name)
break
case 'ObjectPattern':
for (const prop of node.id.properties) {
exportedNames.add(prop.value.name)
}
break
case 'ArrayPattern':
for (const elem of node.id.elements) {
exportedNames.add(elem.name)
}
break
default:
warn('unknown variable declaration type: ' + node.id.type)
}
}
function parseSpecifiers (node, exportedNames) {
for (const specifier of node.specifiers) {
if (specifier.exported.type === 'Identifier') {
exportedNames.add(specifier.exported.name)
} else if (specifier.exported.type === 'Literal') {
exportedNames.add(specifier.exported.value)
} else {
warn('unrecognized specifier type: ' + specifier.exported.type)
}
}
}
module.exports = getEsmExports