You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
401 lines
12 KiB
401 lines
12 KiB
"use strict"; |
|
|
|
Object.defineProperty(exports, "__esModule", { |
|
value: true |
|
}); |
|
exports.default = normalizeModuleAndLoadMetadata; |
|
exports.hasExports = hasExports; |
|
exports.isSideEffectImport = isSideEffectImport; |
|
exports.validateImportInteropOption = validateImportInteropOption; |
|
|
|
var _path = require("path"); |
|
|
|
var _helperValidatorIdentifier = require("@babel/helper-validator-identifier"); |
|
|
|
var _helperSplitExportDeclaration = require("@babel/helper-split-export-declaration"); |
|
|
|
function hasExports(metadata) { |
|
return metadata.hasExports; |
|
} |
|
|
|
function isSideEffectImport(source) { |
|
return source.imports.size === 0 && source.importsNamespace.size === 0 && source.reexports.size === 0 && source.reexportNamespace.size === 0 && !source.reexportAll; |
|
} |
|
|
|
function validateImportInteropOption(importInterop) { |
|
if (typeof importInterop !== "function" && importInterop !== "none" && importInterop !== "babel" && importInterop !== "node") { |
|
throw new Error(`.importInterop must be one of "none", "babel", "node", or a function returning one of those values (received ${importInterop}).`); |
|
} |
|
|
|
return importInterop; |
|
} |
|
|
|
function resolveImportInterop(importInterop, source, filename) { |
|
if (typeof importInterop === "function") { |
|
return validateImportInteropOption(importInterop(source, filename)); |
|
} |
|
|
|
return importInterop; |
|
} |
|
|
|
function normalizeModuleAndLoadMetadata(programPath, exportName, { |
|
importInterop, |
|
initializeReexports = false, |
|
lazy = false, |
|
esNamespaceOnly = false, |
|
filename |
|
}) { |
|
if (!exportName) { |
|
exportName = programPath.scope.generateUidIdentifier("exports").name; |
|
} |
|
|
|
const stringSpecifiers = new Set(); |
|
nameAnonymousExports(programPath); |
|
const { |
|
local, |
|
source, |
|
hasExports |
|
} = getModuleMetadata(programPath, { |
|
initializeReexports, |
|
lazy |
|
}, stringSpecifiers); |
|
removeModuleDeclarations(programPath); |
|
|
|
for (const [, metadata] of source) { |
|
if (metadata.importsNamespace.size > 0) { |
|
metadata.name = metadata.importsNamespace.values().next().value; |
|
} |
|
|
|
const resolvedInterop = resolveImportInterop(importInterop, metadata.source, filename); |
|
|
|
if (resolvedInterop === "none") { |
|
metadata.interop = "none"; |
|
} else if (resolvedInterop === "node" && metadata.interop === "namespace") { |
|
metadata.interop = "node-namespace"; |
|
} else if (resolvedInterop === "node" && metadata.interop === "default") { |
|
metadata.interop = "node-default"; |
|
} else if (esNamespaceOnly && metadata.interop === "namespace") { |
|
metadata.interop = "default"; |
|
} |
|
} |
|
|
|
return { |
|
exportName, |
|
exportNameListName: null, |
|
hasExports, |
|
local, |
|
source, |
|
stringSpecifiers |
|
}; |
|
} |
|
|
|
function getExportSpecifierName(path, stringSpecifiers) { |
|
if (path.isIdentifier()) { |
|
return path.node.name; |
|
} else if (path.isStringLiteral()) { |
|
const stringValue = path.node.value; |
|
|
|
if (!(0, _helperValidatorIdentifier.isIdentifierName)(stringValue)) { |
|
stringSpecifiers.add(stringValue); |
|
} |
|
|
|
return stringValue; |
|
} else { |
|
throw new Error(`Expected export specifier to be either Identifier or StringLiteral, got ${path.node.type}`); |
|
} |
|
} |
|
|
|
function assertExportSpecifier(path) { |
|
if (path.isExportSpecifier()) { |
|
return; |
|
} else if (path.isExportNamespaceSpecifier()) { |
|
throw path.buildCodeFrameError("Export namespace should be first transformed by `@babel/plugin-proposal-export-namespace-from`."); |
|
} else { |
|
throw path.buildCodeFrameError("Unexpected export specifier type"); |
|
} |
|
} |
|
|
|
function getModuleMetadata(programPath, { |
|
lazy, |
|
initializeReexports |
|
}, stringSpecifiers) { |
|
const localData = getLocalExportMetadata(programPath, initializeReexports, stringSpecifiers); |
|
const sourceData = new Map(); |
|
|
|
const getData = sourceNode => { |
|
const source = sourceNode.value; |
|
let data = sourceData.get(source); |
|
|
|
if (!data) { |
|
data = { |
|
name: programPath.scope.generateUidIdentifier((0, _path.basename)(source, (0, _path.extname)(source))).name, |
|
interop: "none", |
|
loc: null, |
|
imports: new Map(), |
|
importsNamespace: new Set(), |
|
reexports: new Map(), |
|
reexportNamespace: new Set(), |
|
reexportAll: null, |
|
lazy: false, |
|
source |
|
}; |
|
sourceData.set(source, data); |
|
} |
|
|
|
return data; |
|
}; |
|
|
|
let hasExports = false; |
|
programPath.get("body").forEach(child => { |
|
if (child.isImportDeclaration()) { |
|
const data = getData(child.node.source); |
|
if (!data.loc) data.loc = child.node.loc; |
|
child.get("specifiers").forEach(spec => { |
|
if (spec.isImportDefaultSpecifier()) { |
|
const localName = spec.get("local").node.name; |
|
data.imports.set(localName, "default"); |
|
const reexport = localData.get(localName); |
|
|
|
if (reexport) { |
|
localData.delete(localName); |
|
reexport.names.forEach(name => { |
|
data.reexports.set(name, "default"); |
|
}); |
|
} |
|
} else if (spec.isImportNamespaceSpecifier()) { |
|
const localName = spec.get("local").node.name; |
|
data.importsNamespace.add(localName); |
|
const reexport = localData.get(localName); |
|
|
|
if (reexport) { |
|
localData.delete(localName); |
|
reexport.names.forEach(name => { |
|
data.reexportNamespace.add(name); |
|
}); |
|
} |
|
} else if (spec.isImportSpecifier()) { |
|
const importName = getExportSpecifierName(spec.get("imported"), stringSpecifiers); |
|
const localName = spec.get("local").node.name; |
|
data.imports.set(localName, importName); |
|
const reexport = localData.get(localName); |
|
|
|
if (reexport) { |
|
localData.delete(localName); |
|
reexport.names.forEach(name => { |
|
data.reexports.set(name, importName); |
|
}); |
|
} |
|
} |
|
}); |
|
} else if (child.isExportAllDeclaration()) { |
|
hasExports = true; |
|
const data = getData(child.node.source); |
|
if (!data.loc) data.loc = child.node.loc; |
|
data.reexportAll = { |
|
loc: child.node.loc |
|
}; |
|
} else if (child.isExportNamedDeclaration() && child.node.source) { |
|
hasExports = true; |
|
const data = getData(child.node.source); |
|
if (!data.loc) data.loc = child.node.loc; |
|
child.get("specifiers").forEach(spec => { |
|
assertExportSpecifier(spec); |
|
const importName = getExportSpecifierName(spec.get("local"), stringSpecifiers); |
|
const exportName = getExportSpecifierName(spec.get("exported"), stringSpecifiers); |
|
data.reexports.set(exportName, importName); |
|
|
|
if (exportName === "__esModule") { |
|
throw spec.get("exported").buildCodeFrameError('Illegal export "__esModule".'); |
|
} |
|
}); |
|
} else if (child.isExportNamedDeclaration() || child.isExportDefaultDeclaration()) { |
|
hasExports = true; |
|
} |
|
}); |
|
|
|
for (const metadata of sourceData.values()) { |
|
let needsDefault = false; |
|
let needsNamed = false; |
|
|
|
if (metadata.importsNamespace.size > 0) { |
|
needsDefault = true; |
|
needsNamed = true; |
|
} |
|
|
|
if (metadata.reexportAll) { |
|
needsNamed = true; |
|
} |
|
|
|
for (const importName of metadata.imports.values()) { |
|
if (importName === "default") needsDefault = true;else needsNamed = true; |
|
} |
|
|
|
for (const importName of metadata.reexports.values()) { |
|
if (importName === "default") needsDefault = true;else needsNamed = true; |
|
} |
|
|
|
if (needsDefault && needsNamed) { |
|
metadata.interop = "namespace"; |
|
} else if (needsDefault) { |
|
metadata.interop = "default"; |
|
} |
|
} |
|
|
|
for (const [source, metadata] of sourceData) { |
|
if (lazy !== false && !(isSideEffectImport(metadata) || metadata.reexportAll)) { |
|
if (lazy === true) { |
|
metadata.lazy = !/\./.test(source); |
|
} else if (Array.isArray(lazy)) { |
|
metadata.lazy = lazy.indexOf(source) !== -1; |
|
} else if (typeof lazy === "function") { |
|
metadata.lazy = lazy(source); |
|
} else { |
|
throw new Error(`.lazy must be a boolean, string array, or function`); |
|
} |
|
} |
|
} |
|
|
|
return { |
|
hasExports, |
|
local: localData, |
|
source: sourceData |
|
}; |
|
} |
|
|
|
function getLocalExportMetadata(programPath, initializeReexports, stringSpecifiers) { |
|
const bindingKindLookup = new Map(); |
|
programPath.get("body").forEach(child => { |
|
let kind; |
|
|
|
if (child.isImportDeclaration()) { |
|
kind = "import"; |
|
} else { |
|
if (child.isExportDefaultDeclaration()) { |
|
child = child.get("declaration"); |
|
} |
|
|
|
if (child.isExportNamedDeclaration()) { |
|
if (child.node.declaration) { |
|
child = child.get("declaration"); |
|
} else if (initializeReexports && child.node.source && child.get("source").isStringLiteral()) { |
|
child.get("specifiers").forEach(spec => { |
|
assertExportSpecifier(spec); |
|
bindingKindLookup.set(spec.get("local").node.name, "block"); |
|
}); |
|
return; |
|
} |
|
} |
|
|
|
if (child.isFunctionDeclaration()) { |
|
kind = "hoisted"; |
|
} else if (child.isClassDeclaration()) { |
|
kind = "block"; |
|
} else if (child.isVariableDeclaration({ |
|
kind: "var" |
|
})) { |
|
kind = "var"; |
|
} else if (child.isVariableDeclaration()) { |
|
kind = "block"; |
|
} else { |
|
return; |
|
} |
|
} |
|
|
|
Object.keys(child.getOuterBindingIdentifiers()).forEach(name => { |
|
bindingKindLookup.set(name, kind); |
|
}); |
|
}); |
|
const localMetadata = new Map(); |
|
|
|
const getLocalMetadata = idPath => { |
|
const localName = idPath.node.name; |
|
let metadata = localMetadata.get(localName); |
|
|
|
if (!metadata) { |
|
const kind = bindingKindLookup.get(localName); |
|
|
|
if (kind === undefined) { |
|
throw idPath.buildCodeFrameError(`Exporting local "${localName}", which is not declared.`); |
|
} |
|
|
|
metadata = { |
|
names: [], |
|
kind |
|
}; |
|
localMetadata.set(localName, metadata); |
|
} |
|
|
|
return metadata; |
|
}; |
|
|
|
programPath.get("body").forEach(child => { |
|
if (child.isExportNamedDeclaration() && (initializeReexports || !child.node.source)) { |
|
if (child.node.declaration) { |
|
const declaration = child.get("declaration"); |
|
const ids = declaration.getOuterBindingIdentifierPaths(); |
|
Object.keys(ids).forEach(name => { |
|
if (name === "__esModule") { |
|
throw declaration.buildCodeFrameError('Illegal export "__esModule".'); |
|
} |
|
|
|
getLocalMetadata(ids[name]).names.push(name); |
|
}); |
|
} else { |
|
child.get("specifiers").forEach(spec => { |
|
const local = spec.get("local"); |
|
const exported = spec.get("exported"); |
|
const localMetadata = getLocalMetadata(local); |
|
const exportName = getExportSpecifierName(exported, stringSpecifiers); |
|
|
|
if (exportName === "__esModule") { |
|
throw exported.buildCodeFrameError('Illegal export "__esModule".'); |
|
} |
|
|
|
localMetadata.names.push(exportName); |
|
}); |
|
} |
|
} else if (child.isExportDefaultDeclaration()) { |
|
const declaration = child.get("declaration"); |
|
|
|
if (declaration.isFunctionDeclaration() || declaration.isClassDeclaration()) { |
|
getLocalMetadata(declaration.get("id")).names.push("default"); |
|
} else { |
|
throw declaration.buildCodeFrameError("Unexpected default expression export."); |
|
} |
|
} |
|
}); |
|
return localMetadata; |
|
} |
|
|
|
function nameAnonymousExports(programPath) { |
|
programPath.get("body").forEach(child => { |
|
if (!child.isExportDefaultDeclaration()) return; |
|
(0, _helperSplitExportDeclaration.default)(child); |
|
}); |
|
} |
|
|
|
function removeModuleDeclarations(programPath) { |
|
programPath.get("body").forEach(child => { |
|
if (child.isImportDeclaration()) { |
|
child.remove(); |
|
} else if (child.isExportNamedDeclaration()) { |
|
if (child.node.declaration) { |
|
child.node.declaration._blockHoist = child.node._blockHoist; |
|
child.replaceWith(child.node.declaration); |
|
} else { |
|
child.remove(); |
|
} |
|
} else if (child.isExportDefaultDeclaration()) { |
|
const declaration = child.get("declaration"); |
|
|
|
if (declaration.isFunctionDeclaration() || declaration.isClassDeclaration()) { |
|
declaration._blockHoist = child.node._blockHoist; |
|
child.replaceWith(declaration); |
|
} else { |
|
throw declaration.buildCodeFrameError("Unexpected default expression export."); |
|
} |
|
} else if (child.isExportAllDeclaration()) { |
|
child.remove(); |
|
} |
|
}); |
|
} |