123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220 |
- /**
- * @fileoverview Ensures that definitions and uses of properties on the
- * ``lazy`` object are valid.
- *
- * 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");
- const items = [
- "loader",
- "XPCOMUtils",
- "Integration",
- "ChromeUtils",
- "DevToolsUtils",
- "Object",
- "Reflect",
- ];
- const callExpressionDefinitions = [
- /^loader\.lazyGetter\(lazy, "(\w+)"/,
- /^loader\.lazyServiceGetter\(lazy, "(\w+)"/,
- /^loader\.lazyRequireGetter\(lazy, "(\w+)"/,
- /^XPCOMUtils\.defineLazyGetter\(lazy, "(\w+)"/,
- /^Integration\.downloads\.defineESModuleGetter\(lazy, "(\w+)"/,
- /^XPCOMUtils\.defineLazyModuleGetter\(lazy, "(\w+)"/,
- /^ChromeUtils\.defineModuleGetter\(lazy, "(\w+)"/,
- /^XPCOMUtils\.defineLazyPreferenceGetter\(lazy, "(\w+)"/,
- /^XPCOMUtils\.defineLazyProxy\(lazy, "(\w+)"/,
- /^XPCOMUtils\.defineLazyScriptGetter\(lazy, "(\w+)"/,
- /^XPCOMUtils\.defineLazyServiceGetter\(lazy, "(\w+)"/,
- /^XPCOMUtils\.defineConstant\(lazy, "(\w+)"/,
- /^DevToolsUtils\.defineLazyModuleGetter\(lazy, "(\w+)"/,
- /^DevToolsUtils\.defineLazyGetter\(lazy, "(\w+)"/,
- /^Object\.defineProperty\(lazy, "(\w+)"/,
- /^Reflect\.defineProperty\(lazy, "(\w+)"/,
- ];
- const callExpressionMultiDefinitions = [
- "ChromeUtils.defineESModuleGetters(lazy,",
- "XPCOMUtils.defineLazyModuleGetters(lazy,",
- "XPCOMUtils.defineLazyServiceGetters(lazy,",
- "Object.defineProperties(lazy,",
- "loader.lazyRequireGetter(lazy,",
- ];
- module.exports = {
- meta: {
- docs: {
- url:
- "https://firefox-source-docs.mozilla.org/code-quality/lint/linters/eslint-plugin-mozilla/valid-lazy.html",
- },
- messages: {
- duplicateSymbol: "Duplicate symbol {{name}} being added to lazy.",
- incorrectType: "Unexpected literal for property name {{name}}",
- unknownProperty: "Unknown lazy member property {{name}}",
- unusedProperty: "Unused lazy property {{name}}",
- topLevelAndUnconditional:
- "Lazy property {{name}} is used at top-level unconditionally. It should be non-lazy.",
- },
- type: "problem",
- },
- create(context) {
- let lazyProperties = new Map();
- let unknownProperties = [];
- function addProp(node, name) {
- if (lazyProperties.has(name)) {
- context.report({
- node,
- messageId: "duplicateSymbol",
- data: { name },
- });
- return;
- }
- lazyProperties.set(name, { used: false, node });
- }
- function setPropertiesFromArgument(node, arg) {
- if (arg.type === "ObjectExpression") {
- for (let prop of arg.properties) {
- if (prop.key.type == "Literal") {
- context.report({
- node,
- messageId: "incorrectType",
- data: { name: prop.key.value },
- });
- continue;
- }
- addProp(node, prop.key.name);
- }
- } else if (arg.type === "ArrayExpression") {
- for (let prop of arg.elements) {
- if (prop.type != "Literal") {
- continue;
- }
- addProp(node, prop.value);
- }
- }
- }
- return {
- VariableDeclarator(node) {
- if (
- node.id.type === "Identifier" &&
- node.id.name == "lazy" &&
- node.init.type == "CallExpression" &&
- node.init.callee.name == "createLazyLoaders"
- ) {
- setPropertiesFromArgument(node, node.init.arguments[0]);
- }
- },
- CallExpression(node) {
- if (
- node.callee.type != "MemberExpression" ||
- (node.callee.object.type == "MemberExpression" &&
- !items.includes(node.callee.object.object.name)) ||
- (node.callee.object.type != "MemberExpression" &&
- !items.includes(node.callee.object.name))
- ) {
- return;
- }
- let source;
- try {
- source = helpers.getASTSource(node);
- } catch (e) {
- return;
- }
- for (let reg of callExpressionDefinitions) {
- let match = source.match(reg);
- if (match) {
- if (lazyProperties.has(match[1])) {
- context.report({
- node,
- messageId: "duplicateSymbol",
- data: { name: match[1] },
- });
- return;
- }
- lazyProperties.set(match[1], { used: false, node });
- break;
- }
- }
- if (
- callExpressionMultiDefinitions.some(expr =>
- source.startsWith(expr)
- ) &&
- node.arguments[1]
- ) {
- setPropertiesFromArgument(node, node.arguments[1]);
- }
- },
- MemberExpression(node) {
- if (node.computed || node.object.type !== "Identifier") {
- return;
- }
- let name;
- if (node.object.name == "lazy") {
- name = node.property.name;
- } else {
- return;
- }
- let property = lazyProperties.get(name);
- if (!property) {
- // These will be reported on Program:exit - some definitions may
- // be after first use, so we need to wait until we've processed
- // the whole file before reporting.
- unknownProperties.push({ name, node });
- } else {
- property.used = true;
- }
- if (
- helpers.getIsTopLevelAndUnconditionallyExecuted(
- context.getAncestors()
- )
- ) {
- context.report({
- node,
- messageId: "topLevelAndUnconditional",
- data: { name },
- });
- }
- },
- "Program:exit": function() {
- for (let { name, node } of unknownProperties) {
- let property = lazyProperties.get(name);
- if (!property) {
- context.report({
- node,
- messageId: "unknownProperty",
- data: { name },
- });
- } else {
- property.used = true;
- }
- }
- for (let [name, property] of lazyProperties.entries()) {
- if (!property.used) {
- context.report({
- node: property.node,
- messageId: "unusedProperty",
- data: { name },
- });
- }
- }
- },
- };
- },
- };
|