UNPKG

js-slang

Version:

Javascript-based implementations of Source, written in Typescript

81 lines 3.96 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const lodash_1 = require("lodash"); const create = require("../../../utils/ast/astCreator"); const helpers_1 = require("../../../utils/ast/helpers"); const typeGuards_1 = require("../../../utils/ast/typeGuards"); const dict_1 = require("../../../utils/dict"); const utils_1 = require("../../utils"); /** * Collates import declarations from each module and creates corresponding combined * import declarations. Will also filter out non-Source module imports */ function hoistAndMergeImports(program) { const [importDeclarations, nonImportDeclarations] = (0, lodash_1.partition)(program.body, typeGuards_1.isImportDeclaration); const importRecords = new dict_1.default(); importDeclarations.forEach(decl => { const source = (0, helpers_1.getModuleDeclarationSource)(decl); if (!(0, utils_1.isSourceModule)(source)) return; // Non-Source module imports should have already been dealt with at this point // so we only need to be concerned with Source module imports const { namespaces, regularSpecifiers, defaultSpecifiers } = importRecords.setdefault(source, { regularSpecifiers: new dict_1.default(), defaultSpecifiers: new Set(), namespaces: new Set() }); decl.specifiers.forEach(spec => { const declaredName = spec.local.name; switch (spec.type) { case 'ImportNamespaceSpecifier': { namespaces.add(declaredName); break; } case 'ImportDefaultSpecifier': { defaultSpecifiers.add(declaredName); break; } case 'ImportSpecifier': { const importedName = spec.imported.name; regularSpecifiers.setdefault(importedName, new Set()).add(declaredName); break; } } }); }); const combinedImports = importRecords.flatMap((source, { regularSpecifiers, defaultSpecifiers, namespaces }) => { const declarations = []; namespaces.forEach(name => { declarations.push(create.importDeclaration(source, [create.importNamespaceSpecifier(name)])); }); const specifiers = []; regularSpecifiers.forEach((importedName, localNames) => { localNames.forEach(name => { specifiers.push(create.importSpecifier(importedName, name)); }); }); if (defaultSpecifiers.size > 0) { const [first, ...others] = defaultSpecifiers; // We can combine only one default specifier with regular import specifiers // Insert it at the front of the array because when acorn parses the AST // its usually the first specifier. When we compare ASTs in tests // the specifier will then be in the right place specifiers.unshift(create.importDefaultSpecifier(first)); // If there is more than 1 default specifier, // then we need to create a separate declaration for each // since one ImportDeclaration cannot have multiple ImportDefaultSpecifiers others.forEach(localName => { declarations.push(create.importDeclaration(source, [create.importDefaultSpecifier(localName)])); }); } // Ensures that every module will at least have one ImportDeclaration // associated with it, preserving side effect imports if (specifiers.length > 0 || declarations.length === 0) { declarations.push(create.importDeclaration(source, specifiers)); } return declarations; }); program.body = [...combinedImports, ...nonImportDeclarations]; } exports.default = hoistAndMergeImports; //# sourceMappingURL=hoistAndMergeImports.js.map