summaryrefslogtreecommitdiffstats
path: root/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/no-redeclare-with-import-autofix.js
diff options
context:
space:
mode:
Diffstat (limited to 'tools/lint/eslint/eslint-plugin-mozilla/lib/rules/no-redeclare-with-import-autofix.js')
-rw-r--r--tools/lint/eslint/eslint-plugin-mozilla/lib/rules/no-redeclare-with-import-autofix.js160
1 files changed, 160 insertions, 0 deletions
diff --git a/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/no-redeclare-with-import-autofix.js b/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/no-redeclare-with-import-autofix.js
new file mode 100644
index 0000000000..0eb5b9a3d7
--- /dev/null
+++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/no-redeclare-with-import-autofix.js
@@ -0,0 +1,160 @@
+/**
+ * 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/.
+ */
+
+"use strict";
+
+const { dirname, join } = require("path");
+
+const eslintBasePath = dirname(require.resolve("eslint"));
+
+const noredeclarePath = join(eslintBasePath, "rules/no-redeclare.js");
+const baseRule = require(noredeclarePath);
+const astUtils = require(join(eslintBasePath, "rules/utils/ast-utils.js"));
+
+// Hack alert: our eslint env is pretty confused about `require` and
+// `loader` for devtools modules - so ignore it for now.
+// See bug 1812547
+const gIgnoredImports = new Set(["loader", "require"]);
+
+/**
+ * Create a trap for a call to `report` that the original rule is
+ * trying to make on `context`.
+ *
+ * Returns a function that forwards to `report` but provides a fixer
+ * for redeclared imports that just removes those imports.
+ *
+ * @return {function}
+ */
+function trapReport(context) {
+ return function (obj) {
+ let declarator = obj.node.parent;
+ while (
+ declarator &&
+ declarator.parent &&
+ declarator.type != "VariableDeclarator"
+ ) {
+ declarator = declarator.parent;
+ }
+ if (
+ declarator &&
+ declarator.type == "VariableDeclarator" &&
+ declarator.id.type == "ObjectPattern" &&
+ declarator.init.type == "CallExpression"
+ ) {
+ let initialization = declarator.init;
+ if (
+ astUtils.isSpecificMemberAccess(
+ initialization.callee,
+ "ChromeUtils",
+ /^import(ESModule|)$/
+ )
+ ) {
+ // Hack alert: our eslint env is pretty confused about `require` and
+ // `loader` for devtools modules - so ignore it for now.
+ // See bug 1812547
+ if (gIgnoredImports.has(obj.node.name)) {
+ return;
+ }
+ // OK, we've got something we can fix. But we should be careful in case
+ // there are multiple imports being destructured.
+ // Do the easy (and common) case first - just one property:
+ if (declarator.id.properties.length == 1) {
+ context.report({
+ node: declarator.parent,
+ messageId: "duplicateImport",
+ data: {
+ name: declarator.id.properties[0].key.name,
+ },
+ fix(fixer) {
+ return fixer.remove(declarator.parent);
+ },
+ });
+ return;
+ }
+
+ // OK, figure out which import is duplicated here:
+ let node = obj.node.parent;
+ // Then remove a comma after it, or a comma before
+ // if there's no comma after it.
+ let sourceCode = context.getSourceCode();
+ let rangeToRemove = node.range;
+ let tokenAfter = sourceCode.getTokenAfter(node);
+ let tokenBefore = sourceCode.getTokenBefore(node);
+ if (astUtils.isCommaToken(tokenAfter)) {
+ rangeToRemove[1] = tokenAfter.range[1];
+ } else if (astUtils.isCommaToken(tokenBefore)) {
+ rangeToRemove[0] = tokenBefore.range[0];
+ }
+ context.report({
+ node,
+ messageId: "duplicateImport",
+ data: {
+ name: node.key.name,
+ },
+ fix(fixer) {
+ return fixer.removeRange(rangeToRemove);
+ },
+ });
+ return;
+ }
+ }
+ if (context.options[0]?.errorForNonImports) {
+ // Report the result from no-redeclare - we can't autofix it.
+ // This can happen for other redeclaration issues, e.g. naming
+ // variables in a way that conflicts with builtins like "URL" or
+ // "escape".
+ context.report(obj);
+ }
+ };
+}
+
+module.exports = {
+ meta: {
+ docs: {
+ url: "https://firefox-source-docs.mozilla.org/code-quality/lint/linters/eslint-plugin-mozilla/no-redeclare-with-import-autofix.html",
+ },
+ messages: {
+ ...baseRule.meta.messages,
+ duplicateImport:
+ "The import of '{{ name }}' is redundant with one set up earlier (e.g. head.js or the browser window environment). It should be removed.",
+ },
+ schema: [
+ {
+ type: "object",
+ properties: {
+ errorForNonImports: {
+ type: "boolean",
+ default: true,
+ },
+ },
+ additionalProperties: false,
+ },
+ ],
+ type: "suggestion",
+ fixable: "code",
+ },
+
+ create(context) {
+ // Test modules get the browser env applied wrongly in some cases,
+ // don't try and remove imports there. This works out of the box
+ // for sys.mjs modules because eslint won't check builtinGlobals
+ // for the no-redeclare rule.
+ if (context.getFilename().endsWith(".jsm")) {
+ return {};
+ }
+ let newOptions = [{ builtinGlobals: true }];
+ const contextForBaseRule = Object.create(context, {
+ report: {
+ value: trapReport(context),
+ writable: false,
+ },
+ options: {
+ value: newOptions,
+ },
+ });
+ return baseRule.create(contextForBaseRule);
+ },
+};