js-slang
Version:
Javascript-based implementations of Source, written in Typescript
128 lines • 6.37 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.defaultAnalysisOptions = void 0;
const lodash_1 = require("lodash");
const assert_1 = require("../../utils/assert");
const helpers_1 = require("../../utils/ast/helpers");
const typeGuards_1 = require("../../utils/ast/typeGuards");
const dict_1 = require("../../utils/dict");
const errors_1 = require("../errors");
const utils_1 = require("../utils");
exports.defaultAnalysisOptions = {
allowUndefinedImports: false,
throwOnDuplicateNames: true
};
/**
* Import and Export analyzer:
* - Checks for undefined imports
* - Checks for different imports being given the same local name
*/
function analyzeImportsAndExports(programs, entrypointFilePath, topoOrder, { nativeStorage: { loadedModules } }, options = {}) {
const declaredNames = new dict_1.default();
const moduleDocs = Object.fromEntries(Object.entries(loadedModules).map(([name, obj]) => [name, new Set(Object.keys(obj))]));
for (const sourceModule of [...topoOrder, entrypointFilePath]) {
const program = programs[sourceModule];
moduleDocs[sourceModule] = new Set();
for (const node of program.body) {
if (node.type === 'ExportDefaultDeclaration') {
if (!options.allowUndefinedImports) {
(0, assert_1.default)(!moduleDocs[sourceModule].has('default'), "Multiple default exports should've been caught by the parser");
moduleDocs[sourceModule].add('default');
}
continue;
}
if (node.type === 'ExportNamedDeclaration') {
if (node.declaration) {
if (!options.allowUndefinedImports) {
const ids = (0, helpers_1.getIdsFromDeclaration)(node.declaration);
ids.forEach(id => {
moduleDocs[sourceModule].add(id.name);
});
}
continue;
}
for (const spec of node.specifiers) {
moduleDocs[sourceModule].add(spec.exported.name);
}
if (!node.source)
continue;
}
else if (!(0, typeGuards_1.isModuleDeclaration)(node)) {
continue;
}
const dstModule = (0, helpers_1.getModuleDeclarationSource)(node);
const dstModuleDocs = moduleDocs[dstModule];
if (node.type === 'ExportAllDeclaration') {
if (!options.allowUndefinedImports) {
if (dstModuleDocs.size === 0)
throw new errors_1.UndefinedNamespaceImportError(dstModule, node);
if (node.exported) {
moduleDocs[sourceModule].add(node.exported.name);
}
else {
for (const each of dstModuleDocs) {
if (each === 'default') {
// ExportAllDeclarations do not implicitly reexport default exports
continue;
}
moduleDocs[sourceModule].add(each);
}
}
}
continue;
}
for (const spec of node.specifiers) {
if (options.throwOnDuplicateNames &&
spec.type !== 'ExportSpecifier' &&
(0, utils_1.isSourceModule)(dstModule)) {
const declaredName = spec.local.name;
declaredNames.setdefault(declaredName, new dict_1.ArrayMap()).add(dstModule, spec);
}
if (options.allowUndefinedImports)
continue;
if (spec.type === 'ImportNamespaceSpecifier') {
if (dstModuleDocs.size === 0)
throw new errors_1.UndefinedNamespaceImportError(dstModule, spec);
continue;
}
const importedName = (0, helpers_1.getImportedName)(spec);
if (!dstModuleDocs.has(importedName)) {
if (importedName === 'default')
throw new errors_1.UndefinedDefaultImportError(dstModule, spec);
throw new errors_1.UndefinedImportError(importedName, dstModule, spec);
}
}
}
}
if (!options.throwOnDuplicateNames)
return;
// Because of the way the preprocessor works, different imports with the same declared name
// will cause errors
// There are two conditions we need to check:
// 1. Two different symbols from the same module are declared with the same name:
// import { a as x } from 'one_module'; AND import { b as x } from 'one_module';
// 2. Two different symbols from different modules are declared with the same name:
// import { a } from 'one_module'; AND import { b as a } from 'another_module';
for (const [localName, moduleToSpecifierMap] of declaredNames) {
if (moduleToSpecifierMap.size > 1) {
// This means that two imports from different modules have the same
// declared name
const nodes = moduleToSpecifierMap.flatMap((_, v) => v);
throw new errors_1.DuplicateImportNameError(localName, nodes);
}
const [[, specifiers]] = moduleToSpecifierMap;
const [namespaceSpecifiers, regularSpecifiers] = (0, lodash_1.partition)(specifiers, typeGuards_1.isNamespaceSpecifier);
// For the given local name, it can only represent one imported name from
// the module. Collect specifiers referring to the same export.
const importedNames = new Set(regularSpecifiers.map(helpers_1.getImportedName));
if ((namespaceSpecifiers.length > 0 && regularSpecifiers.length > 0) ||
importedNames.size > 1) {
// This means that there is more than one unique export being given the same
// local name
const specs = [...regularSpecifiers, ...namespaceSpecifiers];
throw new errors_1.DuplicateImportNameError(localName, specs);
}
}
}
exports.default = analyzeImportsAndExports;
//# sourceMappingURL=analyzer.js.map
;