summaryrefslogtreecommitdiffstats
path: root/tools/esmify/static-import.js
diff options
context:
space:
mode:
Diffstat (limited to 'tools/esmify/static-import.js')
-rw-r--r--tools/esmify/static-import.js147
1 files changed, 147 insertions, 0 deletions
diff --git a/tools/esmify/static-import.js b/tools/esmify/static-import.js
new file mode 100644
index 0000000000..e99bfb3380
--- /dev/null
+++ b/tools/esmify/static-import.js
@@ -0,0 +1,147 @@
+/* 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;