summaryrefslogtreecommitdiff
path: root/node_modules/sucrase/dist/esm/CJSImportProcessor.js
diff options
context:
space:
mode:
authorPhilipp Tanlak <philipp.tanlak@gmail.com>2025-11-24 20:54:57 +0100
committerPhilipp Tanlak <philipp.tanlak@gmail.com>2025-11-24 20:57:48 +0100
commitb1e2c8fd5cb5dfa46bc440a12eafaf56cd844b1c (patch)
tree49d360fd6cbc6a2754efe93524ac47ff0fbe0f7d /node_modules/sucrase/dist/esm/CJSImportProcessor.js
Docs
Diffstat (limited to 'node_modules/sucrase/dist/esm/CJSImportProcessor.js')
-rw-r--r--node_modules/sucrase/dist/esm/CJSImportProcessor.js456
1 files changed, 456 insertions, 0 deletions
diff --git a/node_modules/sucrase/dist/esm/CJSImportProcessor.js b/node_modules/sucrase/dist/esm/CJSImportProcessor.js
new file mode 100644
index 0000000..d8b7803
--- /dev/null
+++ b/node_modules/sucrase/dist/esm/CJSImportProcessor.js
@@ -0,0 +1,456 @@
+
+
+
+import {isDeclaration} from "./parser/tokenizer";
+import {ContextualKeyword} from "./parser/tokenizer/keywords";
+import {TokenType as tt} from "./parser/tokenizer/types";
+
+import getImportExportSpecifierInfo from "./util/getImportExportSpecifierInfo";
+import {getNonTypeIdentifiers} from "./util/getNonTypeIdentifiers";
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/**
+ * Class responsible for preprocessing and bookkeeping import and export declarations within the
+ * file.
+ *
+ * TypeScript uses a simpler mechanism that does not use functions like interopRequireDefault and
+ * interopRequireWildcard, so we also allow that mode for compatibility.
+ */
+export default class CJSImportProcessor {
+ __init() {this.nonTypeIdentifiers = new Set()}
+ __init2() {this.importInfoByPath = new Map()}
+ __init3() {this.importsToReplace = new Map()}
+ __init4() {this.identifierReplacements = new Map()}
+ __init5() {this.exportBindingsByLocalName = new Map()}
+
+ constructor(
+ nameManager,
+ tokens,
+ enableLegacyTypeScriptModuleInterop,
+ options,
+ isTypeScriptTransformEnabled,
+ keepUnusedImports,
+ helperManager,
+ ) {;this.nameManager = nameManager;this.tokens = tokens;this.enableLegacyTypeScriptModuleInterop = enableLegacyTypeScriptModuleInterop;this.options = options;this.isTypeScriptTransformEnabled = isTypeScriptTransformEnabled;this.keepUnusedImports = keepUnusedImports;this.helperManager = helperManager;CJSImportProcessor.prototype.__init.call(this);CJSImportProcessor.prototype.__init2.call(this);CJSImportProcessor.prototype.__init3.call(this);CJSImportProcessor.prototype.__init4.call(this);CJSImportProcessor.prototype.__init5.call(this);}
+
+ preprocessTokens() {
+ for (let i = 0; i < this.tokens.tokens.length; i++) {
+ if (
+ this.tokens.matches1AtIndex(i, tt._import) &&
+ !this.tokens.matches3AtIndex(i, tt._import, tt.name, tt.eq)
+ ) {
+ this.preprocessImportAtIndex(i);
+ }
+ if (
+ this.tokens.matches1AtIndex(i, tt._export) &&
+ !this.tokens.matches2AtIndex(i, tt._export, tt.eq)
+ ) {
+ this.preprocessExportAtIndex(i);
+ }
+ }
+ this.generateImportReplacements();
+ }
+
+ /**
+ * In TypeScript, import statements that only import types should be removed.
+ * This includes `import {} from 'foo';`, but not `import 'foo';`.
+ */
+ pruneTypeOnlyImports() {
+ this.nonTypeIdentifiers = getNonTypeIdentifiers(this.tokens, this.options);
+ for (const [path, importInfo] of this.importInfoByPath.entries()) {
+ if (
+ importInfo.hasBareImport ||
+ importInfo.hasStarExport ||
+ importInfo.exportStarNames.length > 0 ||
+ importInfo.namedExports.length > 0
+ ) {
+ continue;
+ }
+ const names = [
+ ...importInfo.defaultNames,
+ ...importInfo.wildcardNames,
+ ...importInfo.namedImports.map(({localName}) => localName),
+ ];
+ if (names.every((name) => this.shouldAutomaticallyElideImportedName(name))) {
+ this.importsToReplace.set(path, "");
+ }
+ }
+ }
+
+ shouldAutomaticallyElideImportedName(name) {
+ return (
+ this.isTypeScriptTransformEnabled &&
+ !this.keepUnusedImports &&
+ !this.nonTypeIdentifiers.has(name)
+ );
+ }
+
+ generateImportReplacements() {
+ for (const [path, importInfo] of this.importInfoByPath.entries()) {
+ const {
+ defaultNames,
+ wildcardNames,
+ namedImports,
+ namedExports,
+ exportStarNames,
+ hasStarExport,
+ } = importInfo;
+
+ if (
+ defaultNames.length === 0 &&
+ wildcardNames.length === 0 &&
+ namedImports.length === 0 &&
+ namedExports.length === 0 &&
+ exportStarNames.length === 0 &&
+ !hasStarExport
+ ) {
+ // Import is never used, so don't even assign a name.
+ this.importsToReplace.set(path, `require('${path}');`);
+ continue;
+ }
+
+ const primaryImportName = this.getFreeIdentifierForPath(path);
+ let secondaryImportName;
+ if (this.enableLegacyTypeScriptModuleInterop) {
+ secondaryImportName = primaryImportName;
+ } else {
+ secondaryImportName =
+ wildcardNames.length > 0 ? wildcardNames[0] : this.getFreeIdentifierForPath(path);
+ }
+ let requireCode = `var ${primaryImportName} = require('${path}');`;
+ if (wildcardNames.length > 0) {
+ for (const wildcardName of wildcardNames) {
+ const moduleExpr = this.enableLegacyTypeScriptModuleInterop
+ ? primaryImportName
+ : `${this.helperManager.getHelperName("interopRequireWildcard")}(${primaryImportName})`;
+ requireCode += ` var ${wildcardName} = ${moduleExpr};`;
+ }
+ } else if (exportStarNames.length > 0 && secondaryImportName !== primaryImportName) {
+ requireCode += ` var ${secondaryImportName} = ${this.helperManager.getHelperName(
+ "interopRequireWildcard",
+ )}(${primaryImportName});`;
+ } else if (defaultNames.length > 0 && secondaryImportName !== primaryImportName) {
+ requireCode += ` var ${secondaryImportName} = ${this.helperManager.getHelperName(
+ "interopRequireDefault",
+ )}(${primaryImportName});`;
+ }
+
+ for (const {importedName, localName} of namedExports) {
+ requireCode += ` ${this.helperManager.getHelperName(
+ "createNamedExportFrom",
+ )}(${primaryImportName}, '${localName}', '${importedName}');`;
+ }
+ for (const exportStarName of exportStarNames) {
+ requireCode += ` exports.${exportStarName} = ${secondaryImportName};`;
+ }
+ if (hasStarExport) {
+ requireCode += ` ${this.helperManager.getHelperName(
+ "createStarExport",
+ )}(${primaryImportName});`;
+ }
+
+ this.importsToReplace.set(path, requireCode);
+
+ for (const defaultName of defaultNames) {
+ this.identifierReplacements.set(defaultName, `${secondaryImportName}.default`);
+ }
+ for (const {importedName, localName} of namedImports) {
+ this.identifierReplacements.set(localName, `${primaryImportName}.${importedName}`);
+ }
+ }
+ }
+
+ getFreeIdentifierForPath(path) {
+ const components = path.split("/");
+ const lastComponent = components[components.length - 1];
+ const baseName = lastComponent.replace(/\W/g, "");
+ return this.nameManager.claimFreeName(`_${baseName}`);
+ }
+
+ preprocessImportAtIndex(index) {
+ const defaultNames = [];
+ const wildcardNames = [];
+ const namedImports = [];
+
+ index++;
+ if (
+ (this.tokens.matchesContextualAtIndex(index, ContextualKeyword._type) ||
+ this.tokens.matches1AtIndex(index, tt._typeof)) &&
+ !this.tokens.matches1AtIndex(index + 1, tt.comma) &&
+ !this.tokens.matchesContextualAtIndex(index + 1, ContextualKeyword._from)
+ ) {
+ // import type declaration, so no need to process anything.
+ return;
+ }
+
+ if (this.tokens.matches1AtIndex(index, tt.parenL)) {
+ // Dynamic import, so nothing to do
+ return;
+ }
+
+ if (this.tokens.matches1AtIndex(index, tt.name)) {
+ defaultNames.push(this.tokens.identifierNameAtIndex(index));
+ index++;
+ if (this.tokens.matches1AtIndex(index, tt.comma)) {
+ index++;
+ }
+ }
+
+ if (this.tokens.matches1AtIndex(index, tt.star)) {
+ // * as
+ index += 2;
+ wildcardNames.push(this.tokens.identifierNameAtIndex(index));
+ index++;
+ }
+
+ if (this.tokens.matches1AtIndex(index, tt.braceL)) {
+ const result = this.getNamedImports(index + 1);
+ index = result.newIndex;
+
+ for (const namedImport of result.namedImports) {
+ // Treat {default as X} as a default import to ensure usage of require interop helper
+ if (namedImport.importedName === "default") {
+ defaultNames.push(namedImport.localName);
+ } else {
+ namedImports.push(namedImport);
+ }
+ }
+ }
+
+ if (this.tokens.matchesContextualAtIndex(index, ContextualKeyword._from)) {
+ index++;
+ }
+
+ if (!this.tokens.matches1AtIndex(index, tt.string)) {
+ throw new Error("Expected string token at the end of import statement.");
+ }
+ const path = this.tokens.stringValueAtIndex(index);
+ const importInfo = this.getImportInfo(path);
+ importInfo.defaultNames.push(...defaultNames);
+ importInfo.wildcardNames.push(...wildcardNames);
+ importInfo.namedImports.push(...namedImports);
+ if (defaultNames.length === 0 && wildcardNames.length === 0 && namedImports.length === 0) {
+ importInfo.hasBareImport = true;
+ }
+ }
+
+ preprocessExportAtIndex(index) {
+ if (
+ this.tokens.matches2AtIndex(index, tt._export, tt._var) ||
+ this.tokens.matches2AtIndex(index, tt._export, tt._let) ||
+ this.tokens.matches2AtIndex(index, tt._export, tt._const)
+ ) {
+ this.preprocessVarExportAtIndex(index);
+ } else if (
+ this.tokens.matches2AtIndex(index, tt._export, tt._function) ||
+ this.tokens.matches2AtIndex(index, tt._export, tt._class)
+ ) {
+ const exportName = this.tokens.identifierNameAtIndex(index + 2);
+ this.addExportBinding(exportName, exportName);
+ } else if (this.tokens.matches3AtIndex(index, tt._export, tt.name, tt._function)) {
+ const exportName = this.tokens.identifierNameAtIndex(index + 3);
+ this.addExportBinding(exportName, exportName);
+ } else if (this.tokens.matches2AtIndex(index, tt._export, tt.braceL)) {
+ this.preprocessNamedExportAtIndex(index);
+ } else if (this.tokens.matches2AtIndex(index, tt._export, tt.star)) {
+ this.preprocessExportStarAtIndex(index);
+ }
+ }
+
+ preprocessVarExportAtIndex(index) {
+ let depth = 0;
+ // Handle cases like `export let {x} = y;`, starting at the open-brace in that case.
+ for (let i = index + 2; ; i++) {
+ if (
+ this.tokens.matches1AtIndex(i, tt.braceL) ||
+ this.tokens.matches1AtIndex(i, tt.dollarBraceL) ||
+ this.tokens.matches1AtIndex(i, tt.bracketL)
+ ) {
+ depth++;
+ } else if (
+ this.tokens.matches1AtIndex(i, tt.braceR) ||
+ this.tokens.matches1AtIndex(i, tt.bracketR)
+ ) {
+ depth--;
+ } else if (depth === 0 && !this.tokens.matches1AtIndex(i, tt.name)) {
+ break;
+ } else if (this.tokens.matches1AtIndex(1, tt.eq)) {
+ const endIndex = this.tokens.currentToken().rhsEndIndex;
+ if (endIndex == null) {
+ throw new Error("Expected = token with an end index.");
+ }
+ i = endIndex - 1;
+ } else {
+ const token = this.tokens.tokens[i];
+ if (isDeclaration(token)) {
+ const exportName = this.tokens.identifierNameAtIndex(i);
+ this.identifierReplacements.set(exportName, `exports.${exportName}`);
+ }
+ }
+ }
+ }
+
+ /**
+ * Walk this export statement just in case it's an export...from statement.
+ * If it is, combine it into the import info for that path. Otherwise, just
+ * bail out; it'll be handled later.
+ */
+ preprocessNamedExportAtIndex(index) {
+ // export {
+ index += 2;
+ const {newIndex, namedImports} = this.getNamedImports(index);
+ index = newIndex;
+
+ if (this.tokens.matchesContextualAtIndex(index, ContextualKeyword._from)) {
+ index++;
+ } else {
+ // Reinterpret "a as b" to be local/exported rather than imported/local.
+ for (const {importedName: localName, localName: exportedName} of namedImports) {
+ this.addExportBinding(localName, exportedName);
+ }
+ return;
+ }
+
+ if (!this.tokens.matches1AtIndex(index, tt.string)) {
+ throw new Error("Expected string token at the end of import statement.");
+ }
+ const path = this.tokens.stringValueAtIndex(index);
+ const importInfo = this.getImportInfo(path);
+ importInfo.namedExports.push(...namedImports);
+ }
+
+ preprocessExportStarAtIndex(index) {
+ let exportedName = null;
+ if (this.tokens.matches3AtIndex(index, tt._export, tt.star, tt._as)) {
+ // export * as
+ index += 3;
+ exportedName = this.tokens.identifierNameAtIndex(index);
+ // foo from
+ index += 2;
+ } else {
+ // export * from
+ index += 3;
+ }
+ if (!this.tokens.matches1AtIndex(index, tt.string)) {
+ throw new Error("Expected string token at the end of star export statement.");
+ }
+ const path = this.tokens.stringValueAtIndex(index);
+ const importInfo = this.getImportInfo(path);
+ if (exportedName !== null) {
+ importInfo.exportStarNames.push(exportedName);
+ } else {
+ importInfo.hasStarExport = true;
+ }
+ }
+
+ getNamedImports(index) {
+ const namedImports = [];
+ while (true) {
+ if (this.tokens.matches1AtIndex(index, tt.braceR)) {
+ index++;
+ break;
+ }
+
+ const specifierInfo = getImportExportSpecifierInfo(this.tokens, index);
+ index = specifierInfo.endIndex;
+ if (!specifierInfo.isType) {
+ namedImports.push({
+ importedName: specifierInfo.leftName,
+ localName: specifierInfo.rightName,
+ });
+ }
+
+ if (this.tokens.matches2AtIndex(index, tt.comma, tt.braceR)) {
+ index += 2;
+ break;
+ } else if (this.tokens.matches1AtIndex(index, tt.braceR)) {
+ index++;
+ break;
+ } else if (this.tokens.matches1AtIndex(index, tt.comma)) {
+ index++;
+ } else {
+ throw new Error(`Unexpected token: ${JSON.stringify(this.tokens.tokens[index])}`);
+ }
+ }
+ return {newIndex: index, namedImports};
+ }
+
+ /**
+ * Get a mutable import info object for this path, creating one if it doesn't
+ * exist yet.
+ */
+ getImportInfo(path) {
+ const existingInfo = this.importInfoByPath.get(path);
+ if (existingInfo) {
+ return existingInfo;
+ }
+ const newInfo = {
+ defaultNames: [],
+ wildcardNames: [],
+ namedImports: [],
+ namedExports: [],
+ hasBareImport: false,
+ exportStarNames: [],
+ hasStarExport: false,
+ };
+ this.importInfoByPath.set(path, newInfo);
+ return newInfo;
+ }
+
+ addExportBinding(localName, exportedName) {
+ if (!this.exportBindingsByLocalName.has(localName)) {
+ this.exportBindingsByLocalName.set(localName, []);
+ }
+ this.exportBindingsByLocalName.get(localName).push(exportedName);
+ }
+
+ /**
+ * Return the code to use for the import for this path, or the empty string if
+ * the code has already been "claimed" by a previous import.
+ */
+ claimImportCode(importPath) {
+ const result = this.importsToReplace.get(importPath);
+ this.importsToReplace.set(importPath, "");
+ return result || "";
+ }
+
+ getIdentifierReplacement(identifierName) {
+ return this.identifierReplacements.get(identifierName) || null;
+ }
+
+ /**
+ * Return a string like `exports.foo = exports.bar`.
+ */
+ resolveExportBinding(assignedName) {
+ const exportedNames = this.exportBindingsByLocalName.get(assignedName);
+ if (!exportedNames || exportedNames.length === 0) {
+ return null;
+ }
+ return exportedNames.map((exportedName) => `exports.${exportedName}`).join(" = ");
+ }
+
+ /**
+ * Return all imported/exported names where we might be interested in whether usages of those
+ * names are shadowed.
+ */
+ getGlobalNames() {
+ return new Set([
+ ...this.identifierReplacements.keys(),
+ ...this.exportBindingsByLocalName.keys(),
+ ]);
+ }
+}