/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* eslint-env node */ const _path = require("path"); const { getESMFiles } = require(_path.resolve(__dirname, "./is-esmified.js")); const { esmifyExtension, isString, warnForPath, isMemberExpressionWithIdentifiers, } = require(_path.resolve(__dirname, "./utils.js")); function isTargetESM(resourceURI) { if ("ESMIFY_TARGET_PREFIX" in process.env) { const files = getESMFiles(resourceURI); const targetPrefix = process.env.ESMIFY_TARGET_PREFIX; for (const esm of files) { if (esm.startsWith(targetPrefix)) { return true; } } return false; } return true; } function isImportESModuleCall(node) { return isMemberExpressionWithIdentifiers(node.callee, [ "ChromeUtils", "importESModule", ]); } // Replace `ChromeUtils.import`, `Cu.import`, and `ChromeUtils.importESModule` // with static import if it's at the top-level of system ESM file. function tryReplacingWithStaticImport( jscodeshift, inputFile, path, resourceURINode, alwaysReplace ) { if (!alwaysReplace && !inputFile.endsWith(".sys.mjs")) { // Static import is available only in system ESM. return false; } // Check if it's at the top-level. if (path.parent.node.type !== "VariableDeclarator") { return false; } if (path.parent.parent.node.type !== "VariableDeclaration") { return false; } const decls = path.parent.parent.node; if (decls.declarations.length !== 1) { return false; } if (path.parent.parent.parent.node.type !== "Program") { return false; } if (path.node.arguments.length !== 1) { return false; } const resourceURI = resourceURINode.value; // Collect imported symbols. const specs = []; if (path.parent.node.id.type === "Identifier") { specs.push(jscodeshift.importNamespaceSpecifier(path.parent.node.id)); } else if (path.parent.node.id.type === "ObjectPattern") { for (const prop of path.parent.node.id.properties) { if (prop.shorthand) { specs.push(jscodeshift.importSpecifier(prop.key)); } else if (prop.value.type === "Identifier") { specs.push(jscodeshift.importSpecifier(prop.key, prop.value)); } else { return false; } } } else { return false; } // If this is `ChromeUtils.import` or `Cu.import`, replace the extension. // no-op for `ChromeUtils.importESModule`. resourceURINode.value = esmifyExtension(resourceURI); const e = jscodeshift.importDeclaration(specs, resourceURINode); e.comments = path.parent.parent.node.comments; path.parent.parent.node.comments = []; path.parent.parent.replace(e); return true; } function replaceImportESModuleCall( inputFile, jscodeshift, path, alwaysReplace ) { if (path.node.arguments.length !== 1) { warnForPath( inputFile, path, `importESModule call should have only one argument` ); return; } const resourceURINode = path.node.arguments[0]; if (!isString(resourceURINode)) { warnForPath(inputFile, path, `resource URI should be a string`); return; } if (!alwaysReplace) { const resourceURI = resourceURINode.value; if (!isTargetESM(resourceURI)) { return; } } // If this cannot be replaced with static import, do nothing. tryReplacingWithStaticImport( jscodeshift, inputFile, path, resourceURINode, alwaysReplace ); } exports.isImportESModuleCall = isImportESModuleCall; exports.tryReplacingWithStaticImport = tryReplacingWithStaticImport; exports.replaceImportESModuleCall = replaceImportESModuleCall;