Bug 1795880 - Add an ESLint rule to enforce using static imports where possible. r=arai,geckoview-reviewers,m_kato

Differential Revision: https://phabricator.services.mozilla.com/D160131
This commit is contained in:
Mark Banner 2022-10-26 09:37:46 +00:00
parent b1de2a15c2
commit 9613c19f16
24 changed files with 238 additions and 51 deletions

View File

@ -11,9 +11,7 @@ const RECENT_TABS_SYNC = "services.sync.lastTabFetch";
const SHOULD_NOTIFY_FOR_TABS = "browser.tabs.firefox-view.notify-for-tabs";
const lazy = {};
const { XPCOMUtils } = ChromeUtils.importESModule(
"resource://gre/modules/XPCOMUtils.sys.mjs"
);
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
XPCOMUtils.defineLazyModuleGetters(lazy, {
BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm",

View File

@ -7,9 +7,7 @@
* diverse inputs which drive the Firefox View synced tabs setup flow
*/
const { XPCOMUtils } = ChromeUtils.importESModule(
"resource://gre/modules/XPCOMUtils.sys.mjs"
);
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
const lazy = {};

View File

@ -2,9 +2,7 @@
* 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/. */
const { XPCOMUtils } = ChromeUtils.importESModule(
"resource://gre/modules/XPCOMUtils.sys.mjs"
);
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
const lazy = {};

View File

@ -2,9 +2,7 @@
* 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/. */
const { SearchOneOffs } = ChromeUtils.importESModule(
"resource:///modules/SearchOneOffs.sys.mjs"
);
import { SearchOneOffs } from "resource:///modules/SearchOneOffs.sys.mjs";
const lazy = {};

View File

@ -3,9 +3,7 @@
* 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/. */
var { AppConstants } = ChromeUtils.importESModule(
"resource://gre/modules/AppConstants.sys.mjs"
);
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
export async function runBackgroundTask(commandLine) {
if (AppConstants.platform !== "win") {

View File

@ -2,9 +2,7 @@
* 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/. */
var { WindowsRegistry: Registry } = ChromeUtils.importESModule(
"resource://gre/modules/WindowsRegistry.sys.mjs"
);
import { WindowsRegistry as Registry } from "resource://gre/modules/WindowsRegistry.sys.mjs";
export var Windows8WindowFrameColor = {
_windowFrameColor: null,

View File

@ -14,9 +14,8 @@ const ITEM_FLASH_DURATION = 300; // ms
import { require } from "resource://devtools/shared/loader/Loader.sys.mjs";
const { XPCOMUtils } = ChromeUtils.importESModule(
"resource://gre/modules/XPCOMUtils.sys.mjs"
);
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
const EventEmitter = require("resource://devtools/shared/event-emitter.js");
const DevToolsUtils = require("resource://devtools/shared/DevToolsUtils.js");
const {

View File

@ -72,6 +72,7 @@ The plugin implements the following rules:
eslint-plugin-mozilla/use-ownerGlobal
eslint-plugin-mozilla/use-returnValue
eslint-plugin-mozilla/use-services
eslint-plugin-mozilla/use-static-import
eslint-plugin-mozilla/valid-ci-uses
eslint-plugin-mozilla/valid-lazy
eslint-plugin-mozilla/valid-services

View File

@ -0,0 +1,21 @@
use-static-import
=================
Requires the use of static imports in system ES module files (``.sys.mjs``)
where possible.
Examples of incorrect code for this rule:
-----------------------------------------
.. code-block:: js
const { XPCOMUtils } = ChromeUtils.importESModule("resource://gre/modules/XPCOMUtils.sys.mjs");
const { XPCOMUtils: foo } = ChromeUtils.importESModule("resource://gre/modules/XPCOMUtils.sys.mjs");
Examples of correct code for this rule:
---------------------------------------
.. code-block:: js
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
import { XPCOMUtils as foo } from "resource://gre/modules/XPCOMUtils.sys.mjs";

View File

@ -18,9 +18,7 @@
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
const { AppConstants } = ChromeUtils.importESModule(
"resource://gre/modules/AppConstants.sys.mjs"
);
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
const lazy = {};

View File

@ -4,9 +4,7 @@
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
const { clearTimeout, setTimeout } = ChromeUtils.importESModule(
"resource://gre/modules/Timer.sys.mjs"
);
import { clearTimeout, setTimeout } from "resource://gre/modules/Timer.sys.mjs";
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
export var PushServiceWebSocket;

View File

@ -7,9 +7,7 @@ import { PushRecord } from "resource://gre/modules/PushRecord.sys.mjs";
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
const { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
const { clearTimeout, setTimeout } = ChromeUtils.importESModule(
"resource://gre/modules/Timer.sys.mjs"
);
import { clearTimeout, setTimeout } from "resource://gre/modules/Timer.sys.mjs";
import { PushCrypto } from "resource://gre/modules/PushCrypto.sys.mjs";

View File

@ -2,9 +2,7 @@
* 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/. */
const { XPCOMUtils } = ChromeUtils.importESModule(
"resource://gre/modules/XPCOMUtils.sys.mjs"
);
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
const lazy = {};

View File

@ -4,9 +4,7 @@
* 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/. */
const { AppConstants } = ChromeUtils.importESModule(
"resource://gre/modules/AppConstants.sys.mjs"
);
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
let global = Cu.getGlobalForObject({});

View File

@ -5,9 +5,7 @@
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
import { Log } from "resource://gre/modules/Log.sys.mjs";
const { clearTimeout, setTimeout } = ChromeUtils.importESModule(
"resource://gre/modules/Timer.sys.mjs"
);
import { clearTimeout, setTimeout } from "resource://gre/modules/Timer.sys.mjs";
const lazy = {};

View File

@ -3,9 +3,7 @@
* 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/. */
const { setTimeout } = ChromeUtils.importESModule(
"resource://gre/modules/Timer.sys.mjs"
);
import { setTimeout } from "resource://gre/modules/Timer.sys.mjs";
export function runBackgroundTask(commandLine) {
let delay = 10;

View File

@ -4,9 +4,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
const { XPCOMUtils } = ChromeUtils.importESModule(
"resource://gre/modules/XPCOMUtils.sys.mjs"
);
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
const lazy = {};

View File

@ -2,9 +2,7 @@
* 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/. */
const { XPCOMUtils } = ChromeUtils.importESModule(
"resource://gre/modules/XPCOMUtils.sys.mjs"
);
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
let lazy = {};

View File

@ -11,9 +11,7 @@ import { E10SUtils } from "resource://gre/modules/E10SUtils.sys.mjs";
const { FeatureGate } = ChromeUtils.import(
"resource://featuregates/FeatureGate.jsm"
);
const { PlacesDBUtils } = ChromeUtils.importESModule(
"resource://gre/modules/PlacesDBUtils.sys.mjs"
);
import { PlacesDBUtils } from "resource://gre/modules/PlacesDBUtils.sys.mjs";
// We use a list of prefs for display to make sure we only show prefs that
// are useful for support and won't compromise the user's privacy. Note that

View File

@ -60,6 +60,12 @@ module.exports = {
],
},
},
{
files: ["**/*.sys.mjs"],
rules: {
"mozilla/use-static-import": "error",
},
},
{
excludedFiles: ["**/*.sys.mjs"],
files: ["**/*.mjs"],
@ -71,6 +77,7 @@ module.exports = {
{
files: ["**/*.mjs"],
rules: {
"mozilla/use-static-import": "error",
// This rule defaults to not allowing "use strict" in module files since
// they are always loaded in strict mode.
strict: "error",

View File

@ -554,6 +554,21 @@ module.exports = {
return true;
},
isTopLevel(ancestors) {
for (let parent of ancestors) {
switch (parent.type) {
case "ArrowFunctionExpression":
case "FunctionDeclaration":
case "FunctionExpression":
case "PropertyDefinition":
case "StaticBlock":
case "BlockStatement":
return false;
}
}
return true;
},
/**
* Check whether `this` expression points the global this.
*

View File

@ -85,6 +85,7 @@ module.exports = {
"use-isInstance": require("./rules/use-isInstance"),
"use-returnValue": require("../lib/rules/use-returnValue"),
"use-services": require("../lib/rules/use-services"),
"use-static-import": require("../lib/rules/use-static-import"),
"valid-ci-uses": require("../lib/rules/valid-ci-uses"),
"valid-lazy": require("../lib/rules/valid-lazy"),
"valid-services": require("../lib/rules/valid-services"),

View File

@ -0,0 +1,87 @@
/**
* @fileoverview Require use of static imports where possible.
*
* 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 helpers = require("../helpers");
function isIdentifier(node, id) {
return node && node.type === "Identifier" && node.name === id;
}
module.exports = {
meta: {
docs: {
url:
"https://firefox-source-docs.mozilla.org/code-quality/lint/linters/eslint-plugin-mozilla/use-static-import.html",
},
fixable: "code",
messages: {
useStaticImport:
"Please use static import instead of ChromeUtils.importESModule",
},
type: "suggestion",
},
create(context) {
return {
VariableDeclarator(node) {
if (
node.init?.type != "CallExpression" ||
node.init?.callee?.type != "MemberExpression" ||
!context.getFilename().endsWith(".sys.mjs") ||
!helpers.isTopLevel(context.getAncestors())
) {
return;
}
let callee = node.init.callee;
if (
isIdentifier(callee.object, "ChromeUtils") &&
isIdentifier(callee.property, "importESModule") &&
callee.parent.arguments.length == 1
) {
let sourceCode = context.getSourceCode();
let importItemSource;
if (node.id.type != "ObjectPattern") {
importItemSource = sourceCode.getText(node.id);
} else {
importItemSource = "{ ";
let initial = true;
for (let property of node.id.properties) {
if (!initial) {
importItemSource += ", ";
}
initial = false;
if (property.key.name == property.value.name) {
importItemSource += property.key.name;
} else {
importItemSource += `${property.key.name} as ${property.value.name}`;
}
}
importItemSource += " }";
}
context.report({
node: node.parent,
messageId: "useStaticImport",
fix(fixer) {
return fixer.replaceText(
node.parent,
`import ${importItemSource} from ${sourceCode.getText(
callee.parent.arguments[0]
)}`
);
},
});
}
},
};
},
};

View File

@ -0,0 +1,88 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// ------------------------------------------------------------------------------
// Requirements
// ------------------------------------------------------------------------------
var rule = require("../lib/rules/use-static-import");
var RuleTester = require("eslint").RuleTester;
const ruleTester = new RuleTester({
parserOptions: { ecmaVersion: 13, sourceType: "module" },
});
// ------------------------------------------------------------------------------
// Tests
// ------------------------------------------------------------------------------
function callError() {
return [{ messageId: "useStaticImport", type: "VariableDeclaration" }];
}
ruleTester.run("use-static-import", rule, {
valid: [
{
// Already converted, no issues.
code:
'import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";',
filename: "test.sys.mjs",
},
{
// Inside an if statement.
code:
'if (foo) { const { XPCOMUtils } = ChromeUtils.importESModule("resource://gre/modules/XPCOMUtils.sys.mjs") }',
filename: "test.sys.mjs",
},
{
// Inside a function.
code:
'function foo() { const { XPCOMUtils } = ChromeUtils.importESModule("resource://gre/modules/XPCOMUtils.sys.mjs") }',
filename: "test.sys.mjs",
},
{
// importESModule with two args cannot be converted.
code:
'const { f } = ChromeUtils.importESModule("some/module.sys.mjs", { loadInDevToolsLoader : true });',
filename: "test.sys.mjs",
},
{
// A non-system file attempting to import a system file should not be
// converted.
code:
'const { XPCOMUtils } = ChromeUtils.importESModule("resource://gre/modules/XPCOMUtils.sys.mjs")',
filename: "test.mjs",
},
],
invalid: [
{
// Simple import in system module should be converted.
code:
'const { XPCOMUtils } = ChromeUtils.importESModule("resource://gre/modules/XPCOMUtils.sys.mjs")',
errors: callError(),
filename: "test.sys.mjs",
output:
'import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"',
},
{
// Should handle rewritten variables as well.
code:
'const { XPCOMUtils: foo } = ChromeUtils.importESModule("resource://gre/modules/XPCOMUtils.sys.mjs")',
errors: callError(),
filename: "test.sys.mjs",
output:
'import { XPCOMUtils as foo } from "resource://gre/modules/XPCOMUtils.sys.mjs"',
},
{
// Should handle multiple variables.
code:
'const { foo, XPCOMUtils } = ChromeUtils.importESModule("resource://gre/modules/XPCOMUtils.sys.mjs")',
errors: callError(),
filename: "test.sys.mjs",
output:
'import { foo, XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"',
},
],
});