summaryrefslogtreecommitdiffstats
path: root/tools/lint/eslint
diff options
context:
space:
mode:
Diffstat (limited to 'tools/lint/eslint')
-rw-r--r--tools/lint/eslint/eslint-plugin-mozilla/LICENSE363
-rw-r--r--tools/lint/eslint/eslint-plugin-mozilla/lib/globals.js188
-rw-r--r--tools/lint/eslint/eslint-plugin-mozilla/lib/helpers.js524
-rw-r--r--tools/lint/eslint/eslint-plugin-mozilla/lib/index.js45
-rw-r--r--tools/lint/eslint/eslint-plugin-mozilla/lib/processors/xbl-bindings.js363
-rw-r--r--tools/lint/eslint/eslint-plugin-mozilla/lib/rules/.eslintrc.js51
-rw-r--r--tools/lint/eslint/eslint-plugin-mozilla/lib/rules/balanced-listeners.js113
-rw-r--r--tools/lint/eslint/eslint-plugin-mozilla/lib/rules/import-browserjs-globals.js83
-rw-r--r--tools/lint/eslint/eslint-plugin-mozilla/lib/rules/import-globals.js15
-rw-r--r--tools/lint/eslint/eslint-plugin-mozilla/lib/rules/import-headjs-globals.js49
-rw-r--r--tools/lint/eslint/eslint-plugin-mozilla/lib/rules/mark-test-function-used.js37
-rw-r--r--tools/lint/eslint/eslint-plugin-mozilla/lib/rules/no-aArgs.js55
-rw-r--r--tools/lint/eslint/eslint-plugin-mozilla/lib/rules/no-cpows-in-tests.js112
-rw-r--r--tools/lint/eslint/eslint-plugin-mozilla/lib/rules/no-single-arg-cu-import.js39
-rw-r--r--tools/lint/eslint/eslint-plugin-mozilla/lib/rules/reject-importGlobalProperties.js37
-rw-r--r--tools/lint/eslint/eslint-plugin-mozilla/lib/rules/reject-some-requires.js48
-rw-r--r--tools/lint/eslint/eslint-plugin-mozilla/lib/rules/var-only-at-top-level.js34
-rw-r--r--tools/lint/eslint/eslint-plugin-mozilla/package.json29
-rw-r--r--tools/lint/eslint/manifest.tt9
-rw-r--r--tools/lint/eslint/modules.json247
-rw-r--r--tools/lint/eslint/npm-shrinkwrap.json718
-rw-r--r--tools/lint/eslint/package.json16
-rwxr-xr-xtools/lint/eslint/update70
23 files changed, 3245 insertions, 0 deletions
diff --git a/tools/lint/eslint/eslint-plugin-mozilla/LICENSE b/tools/lint/eslint/eslint-plugin-mozilla/LICENSE
new file mode 100644
index 000000000..e87a115e4
--- /dev/null
+++ b/tools/lint/eslint/eslint-plugin-mozilla/LICENSE
@@ -0,0 +1,363 @@
+Mozilla Public License, version 2.0
+
+1. Definitions
+
+1.1. "Contributor"
+
+ means each individual or legal entity that creates, contributes to the
+ creation of, or owns Covered Software.
+
+1.2. "Contributor Version"
+
+ means the combination of the Contributions of others (if any) used by a
+ Contributor and that particular Contributor's Contribution.
+
+1.3. "Contribution"
+
+ means Covered Software of a particular Contributor.
+
+1.4. "Covered Software"
+
+ means Source Code Form to which the initial Contributor has attached the
+ notice in Exhibit A, the Executable Form of such Source Code Form, and
+ Modifications of such Source Code Form, in each case including portions
+ thereof.
+
+1.5. "Incompatible With Secondary Licenses"
+ means
+
+ a. that the initial Contributor has attached the notice described in
+ Exhibit B to the Covered Software; or
+
+ b. that the Covered Software was made available under the terms of
+ version 1.1 or earlier of the License, but not also under the terms of
+ a Secondary License.
+
+1.6. "Executable Form"
+
+ means any form of the work other than Source Code Form.
+
+1.7. "Larger Work"
+
+ means a work that combines Covered Software with other material, in a
+ separate file or files, that is not Covered Software.
+
+1.8. "License"
+
+ means this document.
+
+1.9. "Licensable"
+
+ means having the right to grant, to the maximum extent possible, whether
+ at the time of the initial grant or subsequently, any and all of the
+ rights conveyed by this License.
+
+1.10. "Modifications"
+
+ means any of the following:
+
+ a. any file in Source Code Form that results from an addition to,
+ deletion from, or modification of the contents of Covered Software; or
+
+ b. any new file in Source Code Form that contains any Covered Software.
+
+1.11. "Patent Claims" of a Contributor
+
+ means any patent claim(s), including without limitation, method,
+ process, and apparatus claims, in any patent Licensable by such
+ Contributor that would be infringed, but for the grant of the License,
+ by the making, using, selling, offering for sale, having made, import,
+ or transfer of either its Contributions or its Contributor Version.
+
+1.12. "Secondary License"
+
+ means either the GNU General Public License, Version 2.0, the GNU Lesser
+ General Public License, Version 2.1, the GNU Affero General Public
+ License, Version 3.0, or any later versions of those licenses.
+
+1.13. "Source Code Form"
+
+ means the form of the work preferred for making modifications.
+
+1.14. "You" (or "Your")
+
+ means an individual or a legal entity exercising rights under this
+ License. For legal entities, "You" includes any entity that controls, is
+ controlled by, or is under common control with You. For purposes of this
+ definition, "control" means (a) the power, direct or indirect, to cause
+ the direction or management of such entity, whether by contract or
+ otherwise, or (b) ownership of more than fifty percent (50%) of the
+ outstanding shares or beneficial ownership of such entity.
+
+
+2. License Grants and Conditions
+
+2.1. Grants
+
+ Each Contributor hereby grants You a world-wide, royalty-free,
+ non-exclusive license:
+
+ a. under intellectual property rights (other than patent or trademark)
+ Licensable by such Contributor to use, reproduce, make available,
+ modify, display, perform, distribute, and otherwise exploit its
+ Contributions, either on an unmodified basis, with Modifications, or
+ as part of a Larger Work; and
+
+ b. under Patent Claims of such Contributor to make, use, sell, offer for
+ sale, have made, import, and otherwise transfer either its
+ Contributions or its Contributor Version.
+
+2.2. Effective Date
+
+ The licenses granted in Section 2.1 with respect to any Contribution
+ become effective for each Contribution on the date the Contributor first
+ distributes such Contribution.
+
+2.3. Limitations on Grant Scope
+
+ The licenses granted in this Section 2 are the only rights granted under
+ this License. No additional rights or licenses will be implied from the
+ distribution or licensing of Covered Software under this License.
+ Notwithstanding Section 2.1(b) above, no patent license is granted by a
+ Contributor:
+
+ a. for any code that a Contributor has removed from Covered Software; or
+
+ b. for infringements caused by: (i) Your and any other third party's
+ modifications of Covered Software, or (ii) the combination of its
+ Contributions with other software (except as part of its Contributor
+ Version); or
+
+ c. under Patent Claims infringed by Covered Software in the absence of
+ its Contributions.
+
+ This License does not grant any rights in the trademarks, service marks,
+ or logos of any Contributor (except as may be necessary to comply with
+ the notice requirements in Section 3.4).
+
+2.4. Subsequent Licenses
+
+ No Contributor makes additional grants as a result of Your choice to
+ distribute the Covered Software under a subsequent version of this
+ License (see Section 10.2) or under the terms of a Secondary License (if
+ permitted under the terms of Section 3.3).
+
+2.5. Representation
+
+ Each Contributor represents that the Contributor believes its
+ Contributions are its original creation(s) or it has sufficient rights to
+ grant the rights to its Contributions conveyed by this License.
+
+2.6. Fair Use
+
+ This License is not intended to limit any rights You have under
+ applicable copyright doctrines of fair use, fair dealing, or other
+ equivalents.
+
+2.7. Conditions
+
+ Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
+ Section 2.1.
+
+
+3. Responsibilities
+
+3.1. Distribution of Source Form
+
+ All distribution of Covered Software in Source Code Form, including any
+ Modifications that You create or to which You contribute, must be under
+ the terms of this License. You must inform recipients that the Source
+ Code Form of the Covered Software is governed by the terms of this
+ License, and how they can obtain a copy of this License. You may not
+ attempt to alter or restrict the recipients' rights in the Source Code
+ Form.
+
+3.2. Distribution of Executable Form
+
+ If You distribute Covered Software in Executable Form then:
+
+ a. such Covered Software must also be made available in Source Code Form,
+ as described in Section 3.1, and You must inform recipients of the
+ Executable Form how they can obtain a copy of such Source Code Form by
+ reasonable means in a timely manner, at a charge no more than the cost
+ of distribution to the recipient; and
+
+ b. You may distribute such Executable Form under the terms of this
+ License, or sublicense it under different terms, provided that the
+ license for the Executable Form does not attempt to limit or alter the
+ recipients' rights in the Source Code Form under this License.
+
+3.3. Distribution of a Larger Work
+
+ You may create and distribute a Larger Work under terms of Your choice,
+ provided that You also comply with the requirements of this License for
+ the Covered Software. If the Larger Work is a combination of Covered
+ Software with a work governed by one or more Secondary Licenses, and the
+ Covered Software is not Incompatible With Secondary Licenses, this
+ License permits You to additionally distribute such Covered Software
+ under the terms of such Secondary License(s), so that the recipient of
+ the Larger Work may, at their option, further distribute the Covered
+ Software under the terms of either this License or such Secondary
+ License(s).
+
+3.4. Notices
+
+ You may not remove or alter the substance of any license notices
+ (including copyright notices, patent notices, disclaimers of warranty, or
+ limitations of liability) contained within the Source Code Form of the
+ Covered Software, except that You may alter any license notices to the
+ extent required to remedy known factual inaccuracies.
+
+3.5. Application of Additional Terms
+
+ You may choose to offer, and to charge a fee for, warranty, support,
+ indemnity or liability obligations to one or more recipients of Covered
+ Software. However, You may do so only on Your own behalf, and not on
+ behalf of any Contributor. You must make it absolutely clear that any
+ such warranty, support, indemnity, or liability obligation is offered by
+ You alone, and You hereby agree to indemnify every Contributor for any
+ liability incurred by such Contributor as a result of warranty, support,
+ indemnity or liability terms You offer. You may include additional
+ disclaimers of warranty and limitations of liability specific to any
+ jurisdiction.
+
+4. Inability to Comply Due to Statute or Regulation
+
+ If it is impossible for You to comply with any of the terms of this License
+ with respect to some or all of the Covered Software due to statute,
+ judicial order, or regulation then You must: (a) comply with the terms of
+ this License to the maximum extent possible; and (b) describe the
+ limitations and the code they affect. Such description must be placed in a
+ text file included with all distributions of the Covered Software under
+ this License. Except to the extent prohibited by statute or regulation,
+ such description must be sufficiently detailed for a recipient of ordinary
+ skill to be able to understand it.
+
+5. Termination
+
+5.1. The rights granted under this License will terminate automatically if You
+ fail to comply with any of its terms. However, if You become compliant,
+ then the rights granted under this License from a particular Contributor
+ are reinstated (a) provisionally, unless and until such Contributor
+ explicitly and finally terminates Your grants, and (b) on an ongoing
+ basis, if such Contributor fails to notify You of the non-compliance by
+ some reasonable means prior to 60 days after You have come back into
+ compliance. Moreover, Your grants from a particular Contributor are
+ reinstated on an ongoing basis if such Contributor notifies You of the
+ non-compliance by some reasonable means, this is the first time You have
+ received notice of non-compliance with this License from such
+ Contributor, and You become compliant prior to 30 days after Your receipt
+ of the notice.
+
+5.2. If You initiate litigation against any entity by asserting a patent
+ infringement claim (excluding declaratory judgment actions,
+ counter-claims, and cross-claims) alleging that a Contributor Version
+ directly or indirectly infringes any patent, then the rights granted to
+ You by any and all Contributors for the Covered Software under Section
+ 2.1 of this License shall terminate.
+
+5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
+ license agreements (excluding distributors and resellers) which have been
+ validly granted by You or Your distributors under this License prior to
+ termination shall survive termination.
+
+6. Disclaimer of Warranty
+
+ Covered Software is provided under this License on an "as is" basis,
+ without warranty of any kind, either expressed, implied, or statutory,
+ including, without limitation, warranties that the Covered Software is free
+ of defects, merchantable, fit for a particular purpose or non-infringing.
+ The entire risk as to the quality and performance of the Covered Software
+ is with You. Should any Covered Software prove defective in any respect,
+ You (not any Contributor) assume the cost of any necessary servicing,
+ repair, or correction. This disclaimer of warranty constitutes an essential
+ part of this License. No use of any Covered Software is authorized under
+ this License except under this disclaimer.
+
+7. Limitation of Liability
+
+ Under no circumstances and under no legal theory, whether tort (including
+ negligence), contract, or otherwise, shall any Contributor, or anyone who
+ distributes Covered Software as permitted above, be liable to You for any
+ direct, indirect, special, incidental, or consequential damages of any
+ character including, without limitation, damages for lost profits, loss of
+ goodwill, work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses, even if such party shall have been
+ informed of the possibility of such damages. This limitation of liability
+ shall not apply to liability for death or personal injury resulting from
+ such party's negligence to the extent applicable law prohibits such
+ limitation. Some jurisdictions do not allow the exclusion or limitation of
+ incidental or consequential damages, so this exclusion and limitation may
+ not apply to You.
+
+8. Litigation
+
+ Any litigation relating to this License may be brought only in the courts
+ of a jurisdiction where the defendant maintains its principal place of
+ business and such litigation shall be governed by laws of that
+ jurisdiction, without reference to its conflict-of-law provisions. Nothing
+ in this Section shall prevent a party's ability to bring cross-claims or
+ counter-claims.
+
+9. Miscellaneous
+
+ This License represents the complete agreement concerning the subject
+ matter hereof. If any provision of this License is held to be
+ unenforceable, such provision shall be reformed only to the extent
+ necessary to make it enforceable. Any law or regulation which provides that
+ the language of a contract shall be construed against the drafter shall not
+ be used to construe this License against a Contributor.
+
+
+10. Versions of the License
+
+10.1. New Versions
+
+ Mozilla Foundation is the license steward. Except as provided in Section
+ 10.3, no one other than the license steward has the right to modify or
+ publish new versions of this License. Each version will be given a
+ distinguishing version number.
+
+10.2. Effect of New Versions
+
+ You may distribute the Covered Software under the terms of the version
+ of the License under which You originally received the Covered Software,
+ or under the terms of any subsequent version published by the license
+ steward.
+
+10.3. Modified Versions
+
+ If you create software not governed by this License, and you want to
+ create a new license for such software, you may create and use a
+ modified version of this License if you rename the license and remove
+ any references to the name of the license steward (except to note that
+ such modified license differs from this License).
+
+10.4. Distributing Source Code Form that is Incompatible With Secondary
+ Licenses If You choose to distribute Source Code Form that is
+ Incompatible With Secondary Licenses under the terms of this version of
+ the License, the notice described in Exhibit B of this License must be
+ attached.
+
+Exhibit A - Source Code Form License Notice
+
+ 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/.
+
+If it is not possible or desirable to put the notice in a particular file,
+then You may include the notice in a location (such as a LICENSE file in a
+relevant directory) where a recipient would be likely to look for such a
+notice.
+
+You may add additional accurate notices of copyright ownership.
+
+Exhibit B - "Incompatible With Secondary Licenses" Notice
+
+ This Source Code Form is "Incompatible
+ With Secondary Licenses", as defined by
+ the Mozilla Public License, v. 2.0.
+
diff --git a/tools/lint/eslint/eslint-plugin-mozilla/lib/globals.js b/tools/lint/eslint/eslint-plugin-mozilla/lib/globals.js
new file mode 100644
index 000000000..acbfa0684
--- /dev/null
+++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/globals.js
@@ -0,0 +1,188 @@
+/**
+ * @fileoverview functions for scanning an AST for globals including
+ * traversing referenced scripts.
+ * 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 path = require("path");
+const fs = require("fs");
+const helpers = require("./helpers");
+const escope = require("escope");
+const estraverse = require("estraverse");
+
+/**
+ * Parses a list of "name:boolean_value" or/and "name" options divided by comma or
+ * whitespace.
+ *
+ * This function was copied from eslint.js
+ *
+ * @param {string} string The string to parse.
+ * @param {Comment} comment The comment node which has the string.
+ * @returns {Object} Result map object of names and boolean values
+ */
+function parseBooleanConfig(string, comment) {
+ let items = {};
+
+ // Collapse whitespace around : to make parsing easier
+ string = string.replace(/\s*:\s*/g, ":");
+ // Collapse whitespace around ,
+ string = string.replace(/\s*,\s*/g, ",");
+
+ string.split(/\s|,+/).forEach(function(name) {
+ if (!name) {
+ return;
+ }
+
+ let pos = name.indexOf(":");
+ let value = undefined;
+ if (pos !== -1) {
+ value = name.substring(pos + 1, name.length);
+ name = name.substring(0, pos);
+ }
+
+ items[name] = {
+ value: (value === "true"),
+ comment: comment
+ };
+ });
+
+ return items;
+}
+
+/**
+ * Global discovery can require parsing many files. This map of
+ * {String} => {Object} caches what globals were discovered for a file path.
+ */
+const globalCache = new Map();
+
+/**
+ * An object that returns found globals for given AST node types. Each prototype
+ * property should be named for a node type and accepts a node parameter and a
+ * parents parameter which is a list of the parent nodes of the current node.
+ * Each returns an array of globals found.
+ *
+ * @param {String} path
+ * The absolute path of the file being parsed.
+ */
+function GlobalsForNode(path) {
+ this.path = path;
+ this.root = helpers.getRootDir(path);
+}
+
+GlobalsForNode.prototype = {
+ BlockComment(node, parents) {
+ let value = node.value.trim();
+ let match = /^import-globals-from\s+(.+)$/.exec(value);
+ if (!match) {
+ return [];
+ }
+
+ let filePath = match[1].trim();
+
+ if (!path.isAbsolute(filePath)) {
+ let dirName = path.dirname(this.path);
+ filePath = path.resolve(dirName, filePath);
+ }
+
+ return module.exports.getGlobalsForFile(filePath);
+ },
+
+ ExpressionStatement(node, parents) {
+ let isGlobal = helpers.getIsGlobalScope(parents);
+ let names = helpers.convertExpressionToGlobals(node, isGlobal, this.root);
+ return names.map(name => { return { name, writable: true }});
+ },
+};
+
+module.exports = {
+ /**
+ * Returns all globals for a given file. Recursively searches through
+ * import-globals-from directives and also includes globals defined by
+ * standard eslint directives.
+ *
+ * @param {String} path
+ * The absolute path of the file to be parsed.
+ */
+ getGlobalsForFile(path) {
+ if (globalCache.has(path)) {
+ return globalCache.get(path);
+ }
+
+ let content = fs.readFileSync(path, "utf8");
+
+ // Parse the content into an AST
+ let ast = helpers.getAST(content);
+
+ // Discover global declarations
+ let scopeManager = escope.analyze(ast);
+ let globalScope = scopeManager.acquire(ast);
+
+ let globals = Object.keys(globalScope.variables).map(v => ({
+ name: globalScope.variables[v].name,
+ writable: true,
+ }));
+
+ // Walk over the AST to find any of our custom globals
+ let handler = new GlobalsForNode(path);
+
+ helpers.walkAST(ast, (type, node, parents) => {
+ // We have to discover any globals that ESLint would have defined through
+ // comment directives
+ if (type == "BlockComment") {
+ let value = node.value.trim();
+ let match = /^globals?\s+(.+)$/.exec(value);
+ if (match) {
+ let values = parseBooleanConfig(match[1].trim(), node);
+ for (let name of Object.keys(values)) {
+ globals.push({
+ name,
+ writable: values[name].value
+ })
+ }
+ }
+ }
+
+ if (type in handler) {
+ let newGlobals = handler[type](node, parents);
+ globals.push.apply(globals, newGlobals);
+ }
+ });
+
+ globalCache.set(path, globals);
+
+ return globals;
+ },
+
+ /**
+ * Intended to be used as-is for an ESLint rule that parses for globals in
+ * the current file and recurses through import-globals-from directives.
+ *
+ * @param {Object} context
+ * The ESLint parsing context.
+ */
+ getESLintGlobalParser(context) {
+ let globalScope;
+
+ let parser = {
+ Program(node) {
+ globalScope = context.getScope();
+ }
+ };
+
+ // Install thin wrappers around GlobalsForNode
+ let handler = new GlobalsForNode(helpers.getAbsoluteFilePath(context));
+
+ for (let type of Object.keys(GlobalsForNode.prototype)) {
+ parser[type] = function(node) {
+ let globals = handler[type](node, context.getAncestors());
+ helpers.addGlobals(globals, globalScope);
+ }
+ }
+
+ return parser;
+ }
+};
diff --git a/tools/lint/eslint/eslint-plugin-mozilla/lib/helpers.js b/tools/lint/eslint/eslint-plugin-mozilla/lib/helpers.js
new file mode 100644
index 000000000..50e00ab97
--- /dev/null
+++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/helpers.js
@@ -0,0 +1,524 @@
+/**
+ * @fileoverview A collection of helper functions.
+ * 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";
+
+var escope = require("escope");
+var espree = require("espree");
+var estraverse = require("estraverse");
+var path = require("path");
+var fs = require("fs");
+var ini = require("ini-parser");
+
+var modules = null;
+var directoryManifests = new Map();
+
+var definitions = [
+ /^loader\.lazyGetter\(this, "(\w+)"/,
+ /^loader\.lazyImporter\(this, "(\w+)"/,
+ /^loader\.lazyServiceGetter\(this, "(\w+)"/,
+ /^loader\.lazyRequireGetter\(this, "(\w+)"/,
+ /^XPCOMUtils\.defineLazyGetter\(this, "(\w+)"/,
+ /^XPCOMUtils\.defineLazyModuleGetter\(this, "(\w+)"/,
+ /^XPCOMUtils\.defineLazyServiceGetter\(this, "(\w+)"/,
+ /^XPCOMUtils\.defineConstant\(this, "(\w+)"/,
+ /^DevToolsUtils\.defineLazyModuleGetter\(this, "(\w+)"/,
+ /^DevToolsUtils\.defineLazyGetter\(this, "(\w+)"/,
+ /^Object\.defineProperty\(this, "(\w+)"/,
+ /^Reflect\.defineProperty\(this, "(\w+)"/,
+ /^this\.__defineGetter__\("(\w+)"/,
+ /^this\.(\w+) =/
+];
+
+var imports = [
+ /^(?:Cu|Components\.utils)\.import\(".*\/((.*?)\.jsm?)"(?:, this)?\)/,
+];
+
+module.exports = {
+ /**
+ * Gets the abstract syntax tree (AST) of the JavaScript source code contained
+ * in sourceText.
+ *
+ * @param {String} sourceText
+ * Text containing valid JavaScript.
+ *
+ * @return {Object}
+ * The resulting AST.
+ */
+ getAST: function(sourceText) {
+ // Use a permissive config file to allow parsing of anything that Espree
+ // can parse.
+ var config = this.getPermissiveConfig();
+
+ return espree.parse(sourceText, config);
+ },
+
+ /**
+ * A simplistic conversion of some AST nodes to a standard string form.
+ *
+ * @param {Object} node
+ * The AST node to convert.
+ *
+ * @return {String}
+ * The JS source for the node.
+ */
+ getASTSource: function(node) {
+ switch (node.type) {
+ case "MemberExpression":
+ if (node.computed)
+ throw new Error("getASTSource unsupported computed MemberExpression");
+ return this.getASTSource(node.object) + "." + this.getASTSource(node.property);
+ case "ThisExpression":
+ return "this";
+ case "Identifier":
+ return node.name;
+ case "Literal":
+ return JSON.stringify(node.value);
+ case "CallExpression":
+ var args = node.arguments.map(a => this.getASTSource(a)).join(", ");
+ return this.getASTSource(node.callee) + "(" + args + ")";
+ case "ObjectExpression":
+ return "{}";
+ case "ExpressionStatement":
+ return this.getASTSource(node.expression) + ";";
+ case "FunctionExpression":
+ return "function() {}";
+ case "ArrowFunctionExpression":
+ return "() => {}";
+ case "AssignmentExpression":
+ return this.getASTSource(node.left) + " = " + this.getASTSource(node.right);
+ default:
+ throw new Error("getASTSource unsupported node type: " + node.type);
+ }
+ },
+
+ /**
+ * This walks an AST in a manner similar to ESLint passing node and comment
+ * events to the listener. The listener is expected to be a simple function
+ * which accepts node type, node and parents arguments.
+ *
+ * @param {Object} ast
+ * The AST to walk.
+ * @param {Function} listener
+ * A callback function to call for the nodes. Passed three arguments,
+ * event type, node and an array of parent nodes for the current node.
+ */
+ walkAST(ast, listener) {
+ let parents = [];
+
+ let seenComments = new Set();
+ function sendCommentEvents(comments) {
+ if (!comments) {
+ return;
+ }
+
+ for (let comment of comments) {
+ if (seenComments.has(comment)) {
+ return;
+ }
+ seenComments.add(comment);
+
+ listener(comment.type + "Comment", comment, parents);
+ }
+ }
+
+ estraverse.traverse(ast, {
+ enter(node, parent) {
+ // Comments are held in node.comments for empty programs
+ let leadingComments = node.leadingComments;
+ if (node.type === "Program" && node.body.length == 0) {
+ leadingComments = node.comments;
+ }
+
+ sendCommentEvents(leadingComments);
+ listener(node.type, node, parents);
+ sendCommentEvents(node.trailingComments);
+
+ parents.push(node);
+ },
+
+ leave(node, parent) {
+ // TODO send comment exit events
+ listener(node.type + ":exit", node, parents);
+
+ if (parents.length == 0) {
+ throw new Error("Left more nodes than entered.");
+ }
+ parents.pop();
+ }
+ });
+ if (parents.length) {
+ throw new Error("Entered more nodes than left.");
+ }
+ },
+
+ /**
+ * Attempts to convert an ExpressionStatement to likely global variable
+ * definitions.
+ *
+ * @param {Object} node
+ * The AST node to convert.
+ * @param {boolean} isGlobal
+ * True if the current node is in the global scope.
+ * @param {String} repository
+ * The root of the repository.
+ *
+ * @return {Array}
+ * An array of variable names defined.
+ */
+ convertExpressionToGlobals: function(node, isGlobal, repository) {
+ if (!modules) {
+ modules = require(path.join(repository, "tools", "lint", "eslint", "modules.json"));
+ }
+
+ try {
+ var source = this.getASTSource(node);
+ }
+ catch (e) {
+ return [];
+ }
+
+ for (var reg of definitions) {
+ var match = source.match(reg);
+ if (match) {
+ // Must be in the global scope
+ if (!isGlobal) {
+ return [];
+ }
+
+ return [match[1]];
+ }
+ }
+
+ for (reg of imports) {
+ var match = source.match(reg);
+ if (match) {
+ // The two argument form is only acceptable in the global scope
+ if (node.expression.arguments.length > 1 && !isGlobal) {
+ return [];
+ }
+
+ if (match[1] in modules) {
+ return modules[match[1]];
+ }
+
+ return [match[2]];
+ }
+ }
+
+ return [];
+ },
+
+ /**
+ * Add a variable to the current scope.
+ * HACK: This relies on eslint internals so it could break at any time.
+ *
+ * @param {String} name
+ * The variable name to add to the scope.
+ * @param {ASTScope} scope
+ * The scope to add to.
+ * @param {boolean} writable
+ * Whether the global can be overwritten.
+ */
+ addVarToScope: function(name, scope, writable) {
+ scope.__defineGeneric(name, scope.set, scope.variables, null, null);
+
+ let variable = scope.set.get(name);
+ variable.eslintExplicitGlobal = false;
+ variable.writeable = writable;
+
+ // Walk to the global scope which holds all undeclared variables.
+ while (scope.type != "global") {
+ scope = scope.upper;
+ }
+
+ // "through" contains all references with no found definition.
+ scope.through = scope.through.filter(function(reference) {
+ if (reference.identifier.name != name) {
+ return true;
+ }
+
+ // Links the variable and the reference.
+ // And this reference is removed from `Scope#through`.
+ reference.resolved = variable;
+ variable.references.push(reference);
+ return false;
+ });
+ },
+
+ /**
+ * Adds a set of globals to a scope.
+ *
+ * @param {Array} globalVars
+ * An array of global variable names.
+ * @param {ASTScope} scope
+ * The scope.
+ */
+ addGlobals: function(globalVars, scope) {
+ globalVars.forEach(v => this.addVarToScope(v.name, scope, v.writable));
+ },
+
+ /**
+ * To allow espree to parse almost any JavaScript we need as many features as
+ * possible turned on. This method returns that config.
+ *
+ * @return {Object}
+ * Espree compatible permissive config.
+ */
+ getPermissiveConfig: function() {
+ return {
+ range: true,
+ loc: true,
+ comment: true,
+ attachComment: true,
+ ecmaVersion: 8,
+ sourceType: "script",
+ ecmaFeatures: {
+ experimentalObjectRestSpread: true,
+ globalReturn: true,
+ }
+ };
+ },
+
+ /**
+ * Check whether the context is the global scope.
+ *
+ * @param {Array} ancestors
+ * The parents of the current node.
+ *
+ * @return {Boolean}
+ * True or false
+ */
+ getIsGlobalScope: function(ancestors) {
+ for (let parent of ancestors) {
+ if (parent.type == "FunctionExpression" ||
+ parent.type == "FunctionDeclaration") {
+ return false;
+ }
+ }
+ return true;
+ },
+
+ /**
+ * Check whether we might be in a test head file.
+ *
+ * @param {RuleContext} scope
+ * You should pass this from within a rule
+ * e.g. helpers.getIsHeadFile(this)
+ *
+ * @return {Boolean}
+ * True or false
+ */
+ getIsHeadFile: function(scope) {
+ var pathAndFilename = this.cleanUpPath(scope.getFilename());
+
+ return /.*[\\/]head(_.+)?\.js$/.test(pathAndFilename);
+ },
+
+ /**
+ * Gets the head files for a potential test file
+ *
+ * @param {RuleContext} scope
+ * You should pass this from within a rule
+ * e.g. helpers.getIsHeadFile(this)
+ *
+ * @return {String[]}
+ * Paths to head files to load for the test
+ */
+ getTestHeadFiles: function(scope) {
+ if (!this.getIsTest(scope)) {
+ return [];
+ }
+
+ let filepath = this.cleanUpPath(scope.getFilename());
+ let dir = path.dirname(filepath);
+
+ let names = fs.readdirSync(dir)
+ .filter(name => name.startsWith("head") && name.endsWith(".js"))
+ .map(name => path.join(dir, name));
+ return names;
+ },
+
+ /**
+ * Gets all the test manifest data for a directory
+ *
+ * @param {String} dir
+ * The directory
+ *
+ * @return {Array}
+ * An array of objects with file and manifest properties
+ */
+ getManifestsForDirectory: function(dir) {
+ if (directoryManifests.has(dir)) {
+ return directoryManifests.get(dir);
+ }
+
+ let manifests = [];
+
+ let names = fs.readdirSync(dir);
+ for (let name of names) {
+ if (!name.endsWith(".ini")) {
+ continue;
+ }
+
+ try {
+ let manifest = ini.parse(fs.readFileSync(path.join(dir, name), 'utf8'));
+
+ manifests.push({
+ file: path.join(dir, name),
+ manifest
+ })
+ } catch (e) {
+ }
+ }
+
+ directoryManifests.set(dir, manifests);
+ return manifests;
+ },
+
+ /**
+ * Gets the manifest file a test is listed in
+ *
+ * @param {RuleContext} scope
+ * You should pass this from within a rule
+ * e.g. helpers.getIsHeadFile(this)
+ *
+ * @return {String}
+ * The path to the test manifest file
+ */
+ getTestManifest: function(scope) {
+ let filepath = this.cleanUpPath(scope.getFilename());
+
+ let dir = path.dirname(filepath);
+ let filename = path.basename(filepath);
+
+ for (let manifest of this.getManifestsForDirectory(dir)) {
+ if (filename in manifest.manifest) {
+ return manifest.file;
+ }
+ }
+
+ return null;
+ },
+
+ /**
+ * Check whether we are in a test of some kind.
+ *
+ * @param {RuleContext} scope
+ * You should pass this from within a rule
+ * e.g. helpers.getIsTest(this)
+ *
+ * @return {Boolean}
+ * True or false
+ */
+ getIsTest: function(scope) {
+ // Regardless of the manifest name being in a manifest means we're a test.
+ let manifest = this.getTestManifest(scope);
+ if (manifest) {
+ return true;
+ }
+
+ return !!this.getTestType(scope);
+ },
+
+ /**
+ * Gets the type of test or null if this isn't a test.
+ *
+ * @param {RuleContext} scope
+ * You should pass this from within a rule
+ * e.g. helpers.getIsHeadFile(this)
+ *
+ * @return {String or null}
+ * Test type: xpcshell, browser, chrome, mochitest
+ */
+ getTestType: function(scope) {
+ let manifest = this.getTestManifest(scope);
+ if (manifest) {
+ let name = path.basename(manifest);
+ for (let testType of ["browser", "xpcshell", "chrome", "mochitest"]) {
+ if (name.startsWith(testType)) {
+ return testType;
+ }
+ }
+ }
+
+ let filepath = this.cleanUpPath(scope.getFilename());
+ let filename = path.basename(filepath);
+
+ if (filename.startsWith("browser_")) {
+ return "browser";
+ }
+
+ if (filename.startsWith("test_")) {
+ return "xpcshell";
+ }
+
+ return null;
+ },
+
+ /**
+ * Gets the root directory of the repository by walking up directories until
+ * a .eslintignore file is found.
+ * @param {String} fileName
+ * The absolute path of a file in the repository
+ *
+ * @return {String} The absolute path of the repository directory
+ */
+ getRootDir: function(fileName) {
+ var dirName = path.dirname(fileName);
+
+ while (dirName && !fs.existsSync(path.join(dirName, ".eslintignore"))) {
+ dirName = path.dirname(dirName);
+ }
+
+ if (!dirName) {
+ throw new Error("Unable to find root of repository");
+ }
+
+ return dirName;
+ },
+
+ /**
+ * ESLint may be executed from various places: from mach, at the root of the
+ * repository, or from a directory in the repository when, for instance,
+ * executed by a text editor's plugin.
+ * The value returned by context.getFileName() varies because of this.
+ * This helper function makes sure to return an absolute file path for the
+ * current context, by looking at process.cwd().
+ * @param {Context} context
+ * @return {String} The absolute path
+ */
+ getAbsoluteFilePath: function(context) {
+ var fileName = this.cleanUpPath(context.getFilename());
+ var cwd = process.cwd();
+
+ if (path.isAbsolute(fileName)) {
+ // Case 2: executed from the repo's root with mach:
+ // fileName: /path/to/mozilla/repo/a/b/c/d.js
+ // cwd: /path/to/mozilla/repo
+ return fileName;
+ } else if (path.basename(fileName) == fileName) {
+ // Case 1b: executed from a nested directory, fileName is the base name
+ // without any path info (happens in Atom with linter-eslint)
+ return path.join(cwd, fileName);
+ } else {
+ // Case 1: executed form in a nested directory, e.g. from a text editor:
+ // fileName: a/b/c/d.js
+ // cwd: /path/to/mozilla/repo/a/b/c
+ var dirName = path.dirname(fileName);
+ return cwd.slice(0, cwd.length - dirName.length) + fileName;
+ }
+ },
+
+ /**
+ * When ESLint is run from SublimeText, paths retrieved from
+ * context.getFileName contain leading and trailing double-quote characters.
+ * These characters need to be removed.
+ */
+ cleanUpPath: function(path) {
+ return path.replace(/^"/, "").replace(/"$/, "");
+ }
+};
diff --git a/tools/lint/eslint/eslint-plugin-mozilla/lib/index.js b/tools/lint/eslint/eslint-plugin-mozilla/lib/index.js
new file mode 100644
index 000000000..e1f694c36
--- /dev/null
+++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/index.js
@@ -0,0 +1,45 @@
+/**
+ * @fileoverview A collection of rules that help enforce JavaScript coding
+ * standard and avoid common errors in the Mozilla project.
+ * 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";
+
+//------------------------------------------------------------------------------
+// Plugin Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ processors: {
+ ".xml": require("../lib/processors/xbl-bindings"),
+ },
+ rules: {
+ "balanced-listeners": require("../lib/rules/balanced-listeners"),
+ "import-globals": require("../lib/rules/import-globals"),
+ "import-headjs-globals": require("../lib/rules/import-headjs-globals"),
+ "import-browserjs-globals": require("../lib/rules/import-browserjs-globals"),
+ "mark-test-function-used": require("../lib/rules/mark-test-function-used"),
+ "no-aArgs": require("../lib/rules/no-aArgs"),
+ "no-cpows-in-tests": require("../lib/rules/no-cpows-in-tests"),
+ "no-single-arg-cu-import": require("../lib/rules/no-single-arg-cu-import"),
+ "reject-importGlobalProperties": require("../lib/rules/reject-importGlobalProperties"),
+ "reject-some-requires": require("../lib/rules/reject-some-requires"),
+ "var-only-at-top-level": require("../lib/rules/var-only-at-top-level")
+ },
+ rulesConfig: {
+ "balanced-listeners": 0,
+ "import-globals": 0,
+ "import-headjs-globals": 0,
+ "import-browserjs-globals": 0,
+ "mark-test-function-used": 0,
+ "no-aArgs": 0,
+ "no-cpows-in-tests": 0,
+ "no-single-arg-cu-import": 0,
+ "reject-importGlobalProperties": 0,
+ "reject-some-requires": 0,
+ "var-only-at-top-level": 0
+ }
+};
diff --git a/tools/lint/eslint/eslint-plugin-mozilla/lib/processors/xbl-bindings.js b/tools/lint/eslint/eslint-plugin-mozilla/lib/processors/xbl-bindings.js
new file mode 100644
index 000000000..dc09550f2
--- /dev/null
+++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/processors/xbl-bindings.js
@@ -0,0 +1,363 @@
+/**
+ * @fileoverview Converts functions and handlers from XBL bindings into JS
+ * functions
+ *
+ * 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 NS_XBL = "http://www.mozilla.org/xbl";
+
+let sax = require("sax");
+
+// Converts sax's error message to something that eslint will understand
+let errorRegex = /(.*)\nLine: (\d+)\nColumn: (\d+)\nChar: (.*)/
+function parseError(err) {
+ let matches = err.message.match(errorRegex);
+ if (!matches)
+ return null;
+
+ return {
+ fatal: true,
+ message: matches[1],
+ line: parseInt(matches[2]) + 1,
+ column: parseInt(matches[3])
+ }
+}
+
+let entityRegex = /&[\w][\w-\.]*;/g;
+
+// A simple sax listener that generates a tree of element information
+function XMLParser(parser) {
+ this.parser = parser;
+ parser.onopentag = this.onOpenTag.bind(this);
+ parser.onclosetag = this.onCloseTag.bind(this);
+ parser.ontext = this.onText.bind(this);
+ parser.onopencdata = this.onOpenCDATA.bind(this);
+ parser.oncdata = this.onCDATA.bind(this);
+ parser.oncomment = this.onComment.bind(this);
+
+ this.document = {
+ local: "#document",
+ uri: null,
+ children: [],
+ comments: [],
+ }
+ this._currentNode = this.document;
+}
+
+XMLParser.prototype = {
+ parser: null,
+
+ onOpenTag: function(tag) {
+ let node = {
+ parentNode: this._currentNode,
+ local: tag.local,
+ namespace: tag.uri,
+ attributes: {},
+ children: [],
+ comments: [],
+ textContent: "",
+ textLine: this.parser.line,
+ textColumn: this.parser.column,
+ textEndLine: this.parser.line
+ }
+
+ for (let attr of Object.keys(tag.attributes)) {
+ if (tag.attributes[attr].uri == "") {
+ node.attributes[attr] = tag.attributes[attr].value;
+ }
+ }
+
+ this._currentNode.children.push(node);
+ this._currentNode = node;
+ },
+
+ onCloseTag: function(tagname) {
+ this._currentNode.textEndLine = this.parser.line;
+ this._currentNode = this._currentNode.parentNode;
+ },
+
+ addText: function(text) {
+ this._currentNode.textContent += text;
+ },
+
+ onText: function(text) {
+ // Replace entities with some valid JS token.
+ this.addText(text.replace(entityRegex, "null"));
+ },
+
+ onOpenCDATA: function() {
+ // Turn the CDATA opening tag into whitespace for indent alignment
+ this.addText(" ".repeat("<![CDATA[".length));
+ },
+
+ onCDATA: function(text) {
+ this.addText(text);
+ },
+
+ onComment: function(text) {
+ this._currentNode.comments.push(text);
+ }
+}
+
+// -----------------------------------------------------------------------------
+// Processor Definition
+// -----------------------------------------------------------------------------
+
+const INDENT_LEVEL = 2;
+
+function indent(count) {
+ return " ".repeat(count * INDENT_LEVEL);
+}
+
+// Stores any XML parse error
+let xmlParseError = null;
+
+// Stores the lines of JS code generated from the XBL
+let scriptLines = [];
+// Stores a map from the synthetic line number to the real line number
+// and column offset.
+let lineMap = [];
+
+function addSyntheticLine(line, linePos, addDisableLine) {
+ lineMap[scriptLines.length] = { line: linePos, offset: null };
+ scriptLines.push(line + (addDisableLine ? "" : " // eslint-disable-line"));
+}
+
+/**
+ * Adds generated lines from an XBL node to the script to be passed back to eslint.
+ */
+function addNodeLines(node, reindent) {
+ let lines = node.textContent.split("\n");
+ let startLine = node.textLine;
+ let startColumn = node.textColumn;
+
+ // The case where there are no whitespace lines before the first text is
+ // treated differently for indentation
+ let indentFirst = false;
+
+ // Strip off any preceeding whitespace only lines. These are often used to
+ // format the XML and CDATA blocks.
+ while (lines.length && lines[0].trim() == "") {
+ indentFirst = true;
+ startLine++;
+ lines.shift();
+ }
+
+ // Strip off any whitespace lines at the end. These are often used to line
+ // up the closing tags
+ while (lines.length && lines[lines.length - 1].trim() == "") {
+ lines.pop();
+ }
+
+ if (!indentFirst) {
+ let firstLine = lines.shift();
+ firstLine = " ".repeat(reindent * INDENT_LEVEL) + firstLine;
+ // ESLint counts columns starting at 1 rather than 0
+ lineMap[scriptLines.length] = { line: startLine, offset: reindent * INDENT_LEVEL - (startColumn - 1) };
+ scriptLines.push(firstLine);
+ startLine++;
+ }
+
+ // Find the preceeding whitespace for all lines that aren't entirely whitespace
+ let indents = lines.filter(s => s.trim().length > 0)
+ .map(s => s.length - s.trimLeft().length);
+ // Find the smallest indent level in use
+ let minIndent = Math.min.apply(null, indents);
+
+ for (let line of lines) {
+ if (line.trim().length == 0) {
+ // Don't offset lines that are only whitespace, the only possible JS error
+ // is trailing whitespace and we want it to point at the right place
+ lineMap[scriptLines.length] = { line: startLine, offset: 0 };
+ } else {
+ line = " ".repeat(reindent * INDENT_LEVEL) + line.substring(minIndent);
+ lineMap[scriptLines.length] = { line: startLine, offset: reindent * INDENT_LEVEL - (minIndent - 1) };
+ }
+
+ scriptLines.push(line);
+ startLine++;
+ }
+}
+
+module.exports = {
+ preprocess: function(text, filename) {
+ xmlParseError = null;
+ scriptLines = [];
+ lineMap = [];
+
+ // Non-strict allows us to ignore many errors from entities and
+ // preprocessing at the expense of failing to report some XML errors.
+ // Unfortunately it also throws away the case of tagnames and attributes
+ let parser = sax.parser(false, {
+ lowercase: true,
+ xmlns: true,
+ });
+
+ parser.onerror = function(err) {
+ xmlParseError = parseError(err);
+ }
+
+ let xp = new XMLParser(parser);
+ parser.write(text);
+
+ // Sanity checks to make sure we're dealing with an XBL document
+ let document = xp.document;
+ if (document.children.length != 1) {
+ return [];
+ }
+
+ let bindings = document.children[0];
+ if (bindings.local != "bindings" || bindings.namespace != NS_XBL) {
+ return [];
+ }
+
+ for (let comment of document.comments) {
+ addSyntheticLine(`/*`, 0, true);
+ for (let line of comment.split("\n")) {
+ addSyntheticLine(`${line.trim()}`, 0, true);
+ }
+ addSyntheticLine(`*/`, 0, true);
+ }
+
+ addSyntheticLine(`this.bindings = {`, bindings.textLine);
+
+ for (let binding of bindings.children) {
+ if (binding.local != "binding" || binding.namespace != NS_XBL) {
+ continue;
+ }
+
+ addSyntheticLine(indent(1) + `"${binding.attributes.id}": {`, binding.textLine);
+
+ for (let part of binding.children) {
+ if (part.namespace != NS_XBL) {
+ continue;
+ }
+
+ if (part.local == "implementation") {
+ addSyntheticLine(indent(2) + `implementation: {`, part.textLine);
+ } else if (part.local == "handlers") {
+ addSyntheticLine(indent(2) + `handlers: [`, part.textLine);
+ } else {
+ continue;
+ }
+
+ for (let item of part.children) {
+ if (item.namespace != NS_XBL) {
+ continue;
+ }
+
+ switch (item.local) {
+ case "field": {
+ // Fields are something like lazy getter functions
+
+ // Ignore empty fields
+ if (item.textContent.trim().length == 0) {
+ continue;
+ }
+
+ addSyntheticLine(indent(3) + `get ${item.attributes.name}() {`, item.textLine);
+ addSyntheticLine(indent(4) + `return (`, item.textLine);
+
+ // Remove trailing semicolons, as we are adding our own
+ item.textContent = item.textContent.replace(/;(?=\s*$)/, "");
+ addNodeLines(item, 5);
+
+ addSyntheticLine(indent(4) + `);`, item.textLine);
+ addSyntheticLine(indent(3) + `},`, item.textEndLine);
+ break;
+ }
+ case "constructor":
+ case "destructor": {
+ // Constructors and destructors become function declarations
+ addSyntheticLine(indent(3) + `${item.local}() {`, item.textLine);
+ addNodeLines(item, 4);
+ addSyntheticLine(indent(3) + `},`, item.textEndLine);
+ break;
+ }
+ case "method": {
+ // Methods become function declarations with the appropriate params
+
+ let params = item.children.filter(n => n.local == "parameter" && n.namespace == NS_XBL)
+ .map(n => n.attributes.name)
+ .join(", ");
+ let body = item.children.filter(n => n.local == "body" && n.namespace == NS_XBL)[0];
+
+ addSyntheticLine(indent(3) + `${item.attributes.name}(${params}) {`, item.textLine);
+ addNodeLines(body, 4);
+ addSyntheticLine(indent(3) + `},`, item.textEndLine);
+ break;
+ }
+ case "property": {
+ // Properties become one or two function declarations
+ for (let propdef of item.children) {
+ if (propdef.namespace != NS_XBL) {
+ continue;
+ }
+
+ if (propdef.local == "setter") {
+ addSyntheticLine(indent(3) + `set ${item.attributes.name}(val) {`, propdef.textLine);
+ } else if (propdef.local == "getter") {
+ addSyntheticLine(indent(3) + `get ${item.attributes.name}() {`, propdef.textLine);
+ } else {
+ continue;
+ }
+ addNodeLines(propdef, 4);
+ addSyntheticLine(indent(3) + `},`, propdef.textEndLine);
+ }
+ break;
+ }
+ case "handler": {
+ // Handlers become a function declaration with an `event` parameter
+ addSyntheticLine(indent(3) + `function(event) {`, item.textLine);
+ addNodeLines(item, 4);
+ addSyntheticLine(indent(3) + `},`, item.textEndLine);
+ break;
+ }
+ default:
+ continue;
+ }
+ }
+
+ addSyntheticLine(indent(2) + (part.local == "implementation" ? `},` : `],`), part.textEndLine);
+ }
+ addSyntheticLine(indent(1) + `},`, binding.textEndLine);
+ }
+ addSyntheticLine(`};`, bindings.textEndLine);
+
+ let script = scriptLines.join("\n") + "\n";
+ return [script];
+ },
+
+ postprocess: function(messages, filename) {
+ // If there was an XML parse error then just return that
+ if (xmlParseError) {
+ return [xmlParseError];
+ }
+
+ // For every message from every script block update the line to point to the
+ // correct place.
+ let errors = [];
+ for (let i = 0; i < messages.length; i++) {
+ for (let message of messages[i]) {
+ // ESLint indexes lines starting at 1 but our arrays start at 0
+ let mapped = lineMap[message.line - 1];
+
+ message.line = mapped.line + 1;
+ if (mapped.offset) {
+ message.column -= mapped.offset;
+ } else {
+ message.column = NaN;
+ }
+
+ errors.push(message);
+ }
+ }
+
+ return errors;
+ }
+};
diff --git a/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/.eslintrc.js b/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/.eslintrc.js
new file mode 100644
index 000000000..505a3ea82
--- /dev/null
+++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/.eslintrc.js
@@ -0,0 +1,51 @@
+"use strict";
+
+/**
+ * Based on npm coding standards at https://docs.npmjs.com/misc/coding-style.
+ *
+ * The places we differ from the npm coding style:
+ * - Commas should be at the end of a line.
+ * - Always use semicolons.
+ * - Functions should not have whitespace before params.
+ */
+
+module.exports = {
+ "env": {
+ "node": true
+ },
+
+ "rules": {
+ "brace-style": ["error", "1tbs"],
+ "camelcase": "error",
+ "comma-dangle": ["error", "never"],
+ "comma-spacing": "error",
+ "comma-style": ["error", "last"],
+ "curly": ["error", "multi-line"],
+ "handle-callback-err": ["error", "er"],
+ "indent": ["error", 2, {"SwitchCase": 1}],
+ "max-len": ["error", 80, "error"],
+ "no-multiple-empty-lines": ["error", {"max": 1}],
+ "no-undef": "error",
+ "no-undef-init": "error",
+ "no-unexpected-multiline": "error",
+ "object-curly-spacing": "off",
+ "one-var": ["error", "never"],
+ "operator-linebreak": ["error", "after"],
+ "semi": ["error", "always"],
+ "space-before-blocks": "error",
+ "space-before-function-paren": ["error", "never"],
+ "keyword-spacing": "error",
+ "strict": ["error", "global"],
+ },
+
+ // Globals accessible within node modules.
+ "globals": {
+ "DTRACE_HTTP_CLIENT_REQUEST": true,
+ "DTRACE_HTTP_CLIENT_RESPONSE": true,
+ "DTRACE_HTTP_SERVER_REQUEST": true,
+ "DTRACE_HTTP_SERVER_RESPONSE": true,
+ "DTRACE_NET_SERVER_CONNECTION": true,
+ "DTRACE_NET_STREAM_END": true,
+ "Intl": true,
+ },
+};
diff --git a/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/balanced-listeners.js b/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/balanced-listeners.js
new file mode 100644
index 000000000..c658a6b44
--- /dev/null
+++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/balanced-listeners.js
@@ -0,0 +1,113 @@
+/**
+ * @fileoverview Check that there's a removeEventListener for each
+ * addEventListener and an off for each on.
+ * Note that for now, this rule is rather simple in that it only checks that
+ * for each event name there is both an add and remove listener. It doesn't
+ * check that these are called on the right objects or with the same callback.
+ *
+ * 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";
+
+// -----------------------------------------------------------------------------
+// Rule Definition
+// -----------------------------------------------------------------------------
+
+module.exports = function(context) {
+ // ---------------------------------------------------------------------------
+ // Helpers
+ // ---------------------------------------------------------------------------
+
+ var DICTIONARY = {
+ "addEventListener": "removeEventListener",
+ "on": "off"
+ };
+ // Invert this dictionary to make it easy later.
+ var INVERTED_DICTIONARY = {};
+ for (var i in DICTIONARY) {
+ INVERTED_DICTIONARY[DICTIONARY[i]] = i;
+ }
+
+ // Collect the add/remove listeners in these 2 arrays.
+ var addedListeners = [];
+ var removedListeners = [];
+
+ function addAddedListener(node) {
+ addedListeners.push({
+ functionName: node.callee.property.name,
+ type: node.arguments[0].value,
+ node: node.callee.property,
+ useCapture: node.arguments[2] ? node.arguments[2].value : null
+ });
+ }
+
+ function addRemovedListener(node) {
+ removedListeners.push({
+ functionName: node.callee.property.name,
+ type: node.arguments[0].value,
+ useCapture: node.arguments[2] ? node.arguments[2].value : null
+ });
+ }
+
+ function getUnbalancedListeners() {
+ var unbalanced = [];
+
+ for (var j = 0; j < addedListeners.length; j++) {
+ if (!hasRemovedListener(addedListeners[j])) {
+ unbalanced.push(addedListeners[j]);
+ }
+ }
+ addedListeners = removedListeners = [];
+
+ return unbalanced;
+ }
+
+ function hasRemovedListener(addedListener) {
+ for (var k = 0; k < removedListeners.length; k++) {
+ var listener = removedListeners[k];
+ if (DICTIONARY[addedListener.functionName] === listener.functionName &&
+ addedListener.type === listener.type &&
+ addedListener.useCapture === listener.useCapture) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ // ---------------------------------------------------------------------------
+ // Public
+ // ---------------------------------------------------------------------------
+
+ return {
+ CallExpression: function(node) {
+ if (node.arguments.length === 0) {
+ return;
+ }
+
+ if (node.callee.type === "MemberExpression") {
+ var listenerMethodName = node.callee.property.name;
+
+ if (DICTIONARY.hasOwnProperty(listenerMethodName)) {
+ addAddedListener(node);
+ } else if (INVERTED_DICTIONARY.hasOwnProperty(listenerMethodName)) {
+ addRemovedListener(node);
+ }
+ }
+ },
+
+ "Program:exit": function() {
+ getUnbalancedListeners().forEach(function(listener) {
+ context.report(listener.node,
+ "No corresponding '{{functionName}}({{type}})' was found.",
+ {
+ functionName: DICTIONARY[listener.functionName],
+ type: listener.type
+ });
+ });
+ }
+ };
+};
diff --git a/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/import-browserjs-globals.js b/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/import-browserjs-globals.js
new file mode 100644
index 000000000..313af2d71
--- /dev/null
+++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/import-browserjs-globals.js
@@ -0,0 +1,83 @@
+/**
+ * @fileoverview Import globals from browser.js.
+ *
+ * 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";
+
+// -----------------------------------------------------------------------------
+// Rule Definition
+// -----------------------------------------------------------------------------
+
+var fs = require("fs");
+var path = require("path");
+var helpers = require("../helpers");
+var globals = require("../globals");
+
+const SCRIPTS = [
+ //"browser/base/content/nsContextMenu.js",
+ "toolkit/content/contentAreaUtils.js",
+ "browser/components/places/content/editBookmarkOverlay.js",
+ "toolkit/components/printing/content/printUtils.js",
+ "toolkit/content/viewZoomOverlay.js",
+ "browser/components/places/content/browserPlacesViews.js",
+ "browser/base/content/browser.js",
+ "browser/components/downloads/content/downloads.js",
+ "browser/components/downloads/content/indicator.js",
+ "browser/components/customizableui/content/panelUI.js",
+ "toolkit/components/viewsource/content/viewSourceUtils.js",
+ "browser/base/content/browser-addons.js",
+ "browser/base/content/browser-ctrlTab.js",
+ "browser/base/content/browser-customization.js",
+ "browser/base/content/browser-devedition.js",
+ "browser/base/content/browser-feeds.js",
+ "browser/base/content/browser-fullScreenAndPointerLock.js",
+ "browser/base/content/browser-fullZoom.js",
+ "browser/base/content/browser-gestureSupport.js",
+ "browser/base/content/browser-media.js",
+ "browser/base/content/browser-places.js",
+ "browser/base/content/browser-plugins.js",
+ "browser/base/content/browser-refreshblocker.js",
+ "browser/base/content/browser-safebrowsing.js",
+ "browser/base/content/browser-sidebar.js",
+ "browser/base/content/browser-social.js",
+ "browser/base/content/browser-syncui.js",
+ "browser/base/content/browser-tabsintitlebar.js",
+ "browser/base/content/browser-thumbnails.js",
+ "browser/base/content/browser-trackingprotection.js",
+ "browser/base/content/browser-data-submission-info-bar.js",
+ "browser/base/content/browser-fxaccounts.js"
+];
+
+module.exports = function(context) {
+ return {
+ Program: function(node) {
+ if (helpers.getTestType(this) != "browser" &&
+ !helpers.getIsHeadFile(this)) {
+ return;
+ }
+
+ let filepath = helpers.getAbsoluteFilePath(context);
+ let root = helpers.getRootDir(filepath);
+ for (let script of SCRIPTS) {
+ let fileName = path.join(root, script);
+ try {
+ let newGlobals = globals.getGlobalsForFile(fileName);
+ helpers.addGlobals(newGlobals, context.getScope());
+ } catch (e) {
+ context.report(
+ node,
+ "Could not load globals from file {{filePath}}: {{error}}",
+ {
+ filePath: path.relative(root, fileName),
+ error: e
+ }
+ );
+ }
+ }
+ }
+ };
+};
diff --git a/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/import-globals.js b/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/import-globals.js
new file mode 100644
index 000000000..053a9e702
--- /dev/null
+++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/import-globals.js
@@ -0,0 +1,15 @@
+/**
+ * @fileoverview Discovers all globals for the current file.
+ *
+ * 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";
+
+// -----------------------------------------------------------------------------
+// Rule Definition
+// -----------------------------------------------------------------------------
+
+module.exports = require("../globals").getESLintGlobalParser;
diff --git a/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/import-headjs-globals.js b/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/import-headjs-globals.js
new file mode 100644
index 000000000..783642f58
--- /dev/null
+++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/import-headjs-globals.js
@@ -0,0 +1,49 @@
+/**
+ * @fileoverview Import globals from head.js and from any files that were
+ * imported by head.js (as far as we can correctly resolve the path).
+ *
+ * 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";
+
+// -----------------------------------------------------------------------------
+// Rule Definition
+// -----------------------------------------------------------------------------
+
+var fs = require("fs");
+var path = require("path");
+var helpers = require("../helpers");
+var globals = require("../globals");
+
+module.exports = function(context) {
+
+ function importHead(path, node) {
+ try {
+ let stats = fs.statSync(path);
+ if (!stats.isFile()) {
+ return;
+ }
+ } catch (e) {
+ return;
+ }
+
+ let newGlobals = globals.getGlobalsForFile(path);
+ helpers.addGlobals(newGlobals, context.getScope());
+ }
+
+ // ---------------------------------------------------------------------------
+ // Public
+ // ---------------------------------------------------------------------------
+
+ return {
+ Program: function(node) {
+ let heads = helpers.getTestHeadFiles(this);
+ for (let head of heads) {
+ importHead(head, node);
+ }
+ }
+ };
+};
diff --git a/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/mark-test-function-used.js b/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/mark-test-function-used.js
new file mode 100644
index 000000000..b2e8ec294
--- /dev/null
+++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/mark-test-function-used.js
@@ -0,0 +1,37 @@
+/**
+ * @fileoverview Simply marks `test` (the test method) or `run_test` as used when
+ * in mochitests or xpcshell tests respectively. This avoids ESLint telling us
+ * that the function is never called.
+ *
+ * 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";
+
+// -----------------------------------------------------------------------------
+// Rule Definition
+// -----------------------------------------------------------------------------
+
+var helpers = require("../helpers");
+
+module.exports = function(context) {
+ // ---------------------------------------------------------------------------
+ // Public
+ // ---------------------------------------------------------------------------
+
+ return {
+ Program: function() {
+ if (helpers.getTestType(this) == "browser") {
+ context.markVariableAsUsed("test");
+ return;
+ }
+
+ if (helpers.getTestType(this) == "xpcshell") {
+ context.markVariableAsUsed("run_test");
+ return;
+ }
+ }
+ };
+};
diff --git a/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/no-aArgs.js b/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/no-aArgs.js
new file mode 100644
index 000000000..267f6836f
--- /dev/null
+++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/no-aArgs.js
@@ -0,0 +1,55 @@
+/**
+ * @fileoverview warns against using hungarian notation in function arguments
+ * (i.e. aArg).
+ *
+ * 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";
+
+// -----------------------------------------------------------------------------
+// Rule Definition
+// -----------------------------------------------------------------------------
+
+module.exports = function(context) {
+ // ---------------------------------------------------------------------------
+ // Helpers
+ // ---------------------------------------------------------------------------
+
+ function isPrefixed(name) {
+ return name.length >= 2 && /^a[A-Z]/.test(name);
+ }
+
+ function deHungarianize(name) {
+ return name.substring(1, 2).toLowerCase() +
+ name.substring(2, name.length);
+ }
+
+ function checkFunction(node) {
+ for (var i = 0; i < node.params.length; i++) {
+ var param = node.params[i];
+ if (param.name && isPrefixed(param.name)) {
+ var errorObj = {
+ name: param.name,
+ suggestion: deHungarianize(param.name)
+ };
+ context.report(param,
+ "Parameter '{{name}}' uses Hungarian Notation, " +
+ "consider using '{{suggestion}}' instead.",
+ errorObj);
+ }
+ }
+ }
+
+ // ---------------------------------------------------------------------------
+ // Public
+ // ---------------------------------------------------------------------------
+
+ return {
+ "FunctionDeclaration": checkFunction,
+ "ArrowFunctionExpression": checkFunction,
+ "FunctionExpression": checkFunction
+ };
+};
diff --git a/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/no-cpows-in-tests.js b/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/no-cpows-in-tests.js
new file mode 100644
index 000000000..415cb2fc9
--- /dev/null
+++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/no-cpows-in-tests.js
@@ -0,0 +1,112 @@
+/**
+ * @fileoverview Prevent access to CPOWs in browser mochitests.
+ *
+ * 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";
+
+// -----------------------------------------------------------------------------
+// Rule Definition
+// -----------------------------------------------------------------------------
+
+var helpers = require("../helpers");
+
+var cpows = [
+ /^gBrowser\.contentWindow/,
+ /^gBrowser\.contentDocument/,
+ /^gBrowser\.selectedBrowser.contentWindow/,
+ /^browser\.contentDocument/,
+ /^window\.content/
+];
+
+var isInContentTask = false;
+
+module.exports = function(context) {
+ // ---------------------------------------------------------------------------
+ // Helpers
+ // ---------------------------------------------------------------------------
+
+ function showError(node, identifier) {
+ if (isInContentTask) {
+ return;
+ }
+
+ context.report({
+ node: node,
+ message: identifier +
+ " is a possible Cross Process Object Wrapper (CPOW)."
+ });
+ }
+
+ function isContentTask(node) {
+ return node &&
+ node.type === "MemberExpression" &&
+ node.property.type === "Identifier" &&
+ node.property.name === "spawn" &&
+ node.object.type === "Identifier" &&
+ node.object.name === "ContentTask";
+ }
+
+ // ---------------------------------------------------------------------------
+ // Public
+ // ---------------------------------------------------------------------------
+
+ return {
+ CallExpression: function(node) {
+ if (isContentTask(node.callee)) {
+ isInContentTask = true;
+ }
+ },
+
+ "CallExpression:exit": function(node) {
+ if (isContentTask(node.callee)) {
+ isInContentTask = false;
+ }
+ },
+
+ MemberExpression: function(node) {
+ if (helpers.getTestType(this) != "browser") {
+ return;
+ }
+
+ var expression = context.getSource(node);
+
+ // Only report a single CPOW error per node -- so if checking
+ // |cpows| reports one, don't report another below.
+ var someCpowFound = cpows.some(function(cpow) {
+ if (cpow.test(expression)) {
+ showError(node, expression);
+ return true;
+ }
+ return false;
+ });
+ if (!someCpowFound && helpers.getIsGlobalScope(context.getAncestors())) {
+ if (/^content\./.test(expression)) {
+ showError(node, expression);
+ return;
+ }
+ }
+ },
+
+ Identifier: function(node) {
+ if (helpers.getTestType(this) != "browser") {
+ return;
+ }
+
+ var expression = context.getSource(node);
+ if (expression == "content" || /^content\./.test(expression)) {
+ if (node.parent.type === "MemberExpression" &&
+ node.parent.object &&
+ node.parent.object.type === "Identifier" &&
+ node.parent.object.name != "content") {
+ return;
+ }
+ showError(node, expression);
+ return;
+ }
+ }
+ };
+};
diff --git a/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/no-single-arg-cu-import.js b/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/no-single-arg-cu-import.js
new file mode 100644
index 000000000..b295f3555
--- /dev/null
+++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/no-single-arg-cu-import.js
@@ -0,0 +1,39 @@
+/**
+ * @fileoverview Reject use of single argument Cu.import
+ *
+ * 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";
+
+// -----------------------------------------------------------------------------
+// Rule Definition
+// -----------------------------------------------------------------------------
+
+var helpers = require("../helpers");
+
+module.exports = function(context) {
+
+ // ---------------------------------------------------------------------------
+ // Public
+ // --------------------------------------------------------------------------
+
+ return {
+ "CallExpression": function(node) {
+ if (node.callee.type === "MemberExpression") {
+ let memexp = node.callee;
+ if (memexp.object.type === "Identifier" &&
+ // Only Cu, not Components.utils; see bug 1230369.
+ memexp.object.name === "Cu" &&
+ memexp.property.type === "Identifier" &&
+ memexp.property.name === "import" &&
+ node.arguments.length === 1) {
+ context.report(node, "Single argument Cu.import exposes new " +
+ "globals to all modules");
+ }
+ }
+ }
+ };
+};
diff --git a/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/reject-importGlobalProperties.js b/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/reject-importGlobalProperties.js
new file mode 100644
index 000000000..0661c91d4
--- /dev/null
+++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/reject-importGlobalProperties.js
@@ -0,0 +1,37 @@
+/**
+ * @fileoverview Reject use of Cu.importGlobalProperties
+ *
+ * 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";
+
+// -----------------------------------------------------------------------------
+// Rule Definition
+// -----------------------------------------------------------------------------
+
+var helpers = require("../helpers");
+
+module.exports = function(context) {
+
+ // ---------------------------------------------------------------------------
+ // Public
+ // --------------------------------------------------------------------------
+
+ return {
+ "CallExpression": function(node) {
+ if (node.callee.type === "MemberExpression") {
+ let memexp = node.callee;
+ if (memexp.object.type === "Identifier" &&
+ // Only Cu, not Components.utils; see bug 1230369.
+ memexp.object.name === "Cu" &&
+ memexp.property.type === "Identifier" &&
+ memexp.property.name === "importGlobalProperties") {
+ context.report(node, "Unexpected call to Cu.importGlobalProperties");
+ }
+ }
+ }
+ };
+};
diff --git a/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/reject-some-requires.js b/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/reject-some-requires.js
new file mode 100644
index 000000000..746f98a1f
--- /dev/null
+++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/reject-some-requires.js
@@ -0,0 +1,48 @@
+/**
+ * @fileoverview Reject some uses of require.
+ *
+ * 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";
+
+// -----------------------------------------------------------------------------
+// Rule Definition
+// -----------------------------------------------------------------------------
+
+module.exports = function(context) {
+
+ // ---------------------------------------------------------------------------
+ // Public
+ // --------------------------------------------------------------------------
+
+ if (typeof(context.options[0]) !== "string") {
+ throw new Error("reject-some-requires expects a regexp");
+ }
+ const RX = new RegExp(context.options[0]);
+
+ const checkPath = function(node, path) {
+ if (RX.test(path)) {
+ context.report(node, `require(${path}) is not allowed`);
+ }
+ };
+
+ return {
+ "CallExpression": function(node) {
+ if (node.callee.type == "Identifier" &&
+ node.callee.name == "require" &&
+ node.arguments.length == 1 &&
+ node.arguments[0].type == "Literal") {
+ checkPath(node, node.arguments[0].value);
+ } else if (node.callee.type == "MemberExpression" &&
+ node.callee.property.type == "Identifier" &&
+ node.callee.property.name == "lazyRequireGetter" &&
+ node.arguments.length >= 3 &&
+ node.arguments[2].type == "Literal") {
+ checkPath(node, node.arguments[2].value);
+ }
+ }
+ };
+};
diff --git a/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/var-only-at-top-level.js b/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/var-only-at-top-level.js
new file mode 100644
index 000000000..a1e14e166
--- /dev/null
+++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/var-only-at-top-level.js
@@ -0,0 +1,34 @@
+/**
+ * @fileoverview Marks all var declarations that are not at the top level
+ * invalid.
+ *
+ * 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";
+
+// -----------------------------------------------------------------------------
+// Rule Definition
+// -----------------------------------------------------------------------------
+
+var helpers = require("../helpers");
+
+module.exports = function(context) {
+ // ---------------------------------------------------------------------------
+ // Public
+ // --------------------------------------------------------------------------
+
+ return {
+ "VariableDeclaration": function(node) {
+ if (node.kind === "var") {
+ if (helpers.getIsGlobalScope(context.getAncestors())) {
+ return;
+ }
+
+ context.report(node, "Unexpected var, use let or const instead.");
+ }
+ }
+ };
+};
diff --git a/tools/lint/eslint/eslint-plugin-mozilla/package.json b/tools/lint/eslint/eslint-plugin-mozilla/package.json
new file mode 100644
index 000000000..2f4a85172
--- /dev/null
+++ b/tools/lint/eslint/eslint-plugin-mozilla/package.json
@@ -0,0 +1,29 @@
+{
+ "name": "eslint-plugin-mozilla",
+ "version": "0.2.5",
+ "description": "A collection of rules that help enforce JavaScript coding standard in the Mozilla project.",
+ "keywords": [
+ "eslint",
+ "eslintplugin",
+ "eslint-plugin",
+ "mozilla",
+ "firefox"
+ ],
+ "bugs": {
+ "url": "https://bugzilla.mozilla.org/enter_bug.cgi?product=Firefox&component=Developer%20Tools"
+ },
+ "homepage": "https://bugzilla.mozilla.org/show_bug.cgi?id=1203520",
+ "author": "Mike Ratcliffe",
+ "main": "lib/index.js",
+ "dependencies": {
+ "escope": "^3.6.0",
+ "espree": "^3.2.0",
+ "estraverse": "^4.2.0",
+ "ini-parser": "^0.0.2",
+ "sax": "^1.1.4"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "license": "MPL-2.0"
+}
diff --git a/tools/lint/eslint/manifest.tt b/tools/lint/eslint/manifest.tt
new file mode 100644
index 000000000..c98f6455f
--- /dev/null
+++ b/tools/lint/eslint/manifest.tt
@@ -0,0 +1,9 @@
+[
+{
+"size": 2768586,
+"visibility": "public",
+"digest": "1b9a7c5b1ca8d7d2aeb803055129d1cb0fa0d17b90dd3cf852ea77d86d1b1d9d09f34007a71908074afef9ce1ab87972a5cf16a36952e1a6159f7abfc6fa15b3",
+"algorithm": "sha512",
+"filename": "eslint.tar.gz"
+}
+]
diff --git a/tools/lint/eslint/modules.json b/tools/lint/eslint/modules.json
new file mode 100644
index 000000000..34ebfc6c1
--- /dev/null
+++ b/tools/lint/eslint/modules.json
@@ -0,0 +1,247 @@
+{
+ "AboutHome.jsm": ["AboutHomeUtils", "AboutHome"],
+ "AddonLogging.jsm": ["LogManager"],
+ "AddonManager.jsm": ["AddonManager", "AddonManagerPrivate"],
+ "addons.js": ["AddonsEngine", "AddonValidator"],
+ "addons.jsm": ["Addon", "STATE_ENABLED", "STATE_DISABLED"],
+ "addonsreconciler.js": ["AddonsReconciler", "CHANGE_INSTALLED", "CHANGE_UNINSTALLED", "CHANGE_ENABLED", "CHANGE_DISABLED"],
+ "AddonTestUtils.jsm": ["AddonTestUtils", "MockAsyncShutdown"],
+ "addonutils.js": ["AddonUtils"],
+ "ajv-4.1.1.js": ["Ajv"],
+ "AlertsHelper.jsm": [],
+ "AppData.jsm": ["makeFakeAppDir"],
+ "AppInfo.jsm": ["newAppInfo", "getAppInfo", "updateAppInfo"],
+ "AppsServiceChild.jsm": ["DOMApplicationRegistry", "WrappedManifestCache"],
+ "arrays.js": ["inArray", "getSet", "indexOf", "remove", "rindexOf", "compare"],
+ "assertions.js": ["Assert", "Expect"],
+ "async.js": ["Async"],
+ "AsyncSpellCheckTestHelper.jsm": ["onSpellCheck"],
+ "Battery.jsm": ["GetBattery", "Battery"],
+ "blocklist-clients.js": ["AddonBlocklistClient", "GfxBlocklistClient", "OneCRLBlocklistClient", "PluginBlocklistClient", "FILENAME_ADDONS_JSON", "FILENAME_GFX_JSON", "FILENAME_PLUGINS_JSON"],
+ "blocklist-updater.js": ["checkVersions", "addTestBlocklistClient"],
+ "bogus_element_type.jsm": [],
+ "bookmark_validator.js": ["BookmarkValidator", "BookmarkProblemData"],
+ "bookmarks.js": ["BookmarksEngine", "PlacesItem", "Bookmark", "BookmarkFolder", "BookmarkQuery", "Livemark", "BookmarkSeparator"],
+ "bookmarks.jsm": ["PlacesItem", "Bookmark", "Separator", "Livemark", "BookmarkFolder", "DumpBookmarks"],
+ "BootstrapMonitor.jsm": ["monitor"],
+ "browser-loader.js": ["BrowserLoader"],
+ "browserid_identity.js": ["BrowserIDManager", "AuthenticationError"],
+ "CertUtils.jsm": ["BadCertHandler", "checkCert", "readCertPrefs", "validateCert"],
+ "clients.js": ["ClientEngine", "ClientsRec"],
+ "CloudSyncAdapters.jsm": ["Adapters"],
+ "CloudSyncBookmarks.jsm": ["Bookmarks"],
+ "CloudSyncBookmarksFolderCache.jsm": ["FolderCache"],
+ "CloudSyncEventSource.jsm": ["EventSource"],
+ "CloudSyncLocal.jsm": ["Local"],
+ "CloudSyncPlacesWrapper.jsm": ["PlacesWrapper"],
+ "CloudSyncTabs.jsm": ["Tabs"],
+ "cluster.js": ["ClusterManager"],
+ "collection_validator.js": ["CollectionValidator", "CollectionProblemData"],
+ "Console.jsm": ["console", "ConsoleAPI"],
+ "Constants.jsm": ["Roles", "Events", "Relations", "Filters", "States", "Prefilters"],
+ "ContactDB.jsm": ["ContactDB", "DB_NAME", "STORE_NAME", "SAVED_GETALL_STORE_NAME", "REVISION_STORE", "DB_VERSION"],
+ "content-server.jsm": ["init"],
+ "content.jsm": ["registerContentFrame"],
+ "ContentCrashHandlers.jsm": ["TabCrashHandler", "PluginCrashReporter", "UnsubmittedCrashHandler"],
+ "ContentObservers.jsm": [],
+ "ContentPrefUtils.jsm": ["ContentPref", "cbHandleResult", "cbHandleError", "cbHandleCompletion", "safeCallback"],
+ "controller.js": ["MozMillController", "globalEventRegistry", "sleep", "windowMap"],
+ "cookies.js": ["Cookies"],
+ "CoverageUtils.jsm": ["CoverageCollector"],
+ "CrashManagerTest.jsm": ["configureLogging", "getManager", "sleep", "TestingCrashManager"],
+ "dbg-client.jsm": ["DebuggerTransport", "DebuggerClient", "RootClient", "LongStringClient", "EnvironmentClient", "ObjectClient"],
+ "dbg-server.jsm": ["DebuggerServer", "ActorPool", "OriginalLocation"],
+ "debug.js": ["NS_ASSERT"],
+ "declined.js": ["DeclinedEngines"],
+ "dispatcher.js": ["Dispatcher"],
+ "distribution.js": ["DistributionCustomizer"],
+ "DNSTypes.jsm": ["DNS_QUERY_RESPONSE_CODES", "DNS_AUTHORITATIVE_ANSWER_CODES", "DNS_CLASS_CODES", "DNS_RECORD_TYPES"],
+ "dom.js": ["getAttributes"],
+ "DOMRequestHelper.jsm": ["DOMRequestIpcHelper"],
+ "DownloadCore.jsm": ["Download", "DownloadSource", "DownloadTarget", "DownloadError", "DownloadSaver", "DownloadCopySaver", "DownloadLegacySaver", "DownloadPDFSaver"],
+ "DownloadList.jsm": ["DownloadList", "DownloadCombinedList", "DownloadSummary"],
+ "E10SAddonsRollout.jsm": ["isAddonPartOfE10SRollout"],
+ "elementslib.js": ["ID", "Link", "XPath", "Selector", "Name", "Anon", "AnonXPath", "Lookup", "_byID", "_byName", "_byAttrib", "_byAnonAttrib"],
+ "engines.js": ["EngineManager", "Engine", "SyncEngine", "Tracker", "Store", "Changeset"],
+ "enginesync.js": ["EngineSynchronizer"],
+ "errors.js": ["BaseError", "ApplicationQuitError", "AssertionError", "TimeoutError"],
+ "evaluate.js": ["evaluate", "sandbox", "Sandboxes"],
+ "event-emitter.js": ["EventEmitter"],
+ "EventUtils.js": ["disableNonTestMouseEvents", "sendMouseEvent", "sendChar", "sendString", "sendKey", "synthesizeMouse", "synthesizeTouch", "synthesizeMouseAtPoint", "synthesizeTouchAtPoint", "synthesizeMouseAtCenter", "synthesizeTouchAtCenter", "synthesizeWheel", "synthesizeKey", "synthesizeMouseExpectEvent", "synthesizeKeyExpectEvent", "synthesizeText", "synthesizeComposition", "synthesizeQuerySelectedText"],
+ "Extension.jsm": ["Extension", "ExtensionData"],
+ "ExtensionAPI.jsm": ["ExtensionAPI", "ExtensionAPIs"],
+ "ExtensionXPCShellUtils.jsm": ["ExtensionTestUtils"],
+ "fakeservices.js": ["FakeCryptoService", "FakeFilesystemService", "FakeGUIDService", "fakeSHA256HMAC"],
+ "file_expandosharing.jsm": ["checkFromJSM"],
+ "file_stringencoding.jsm": ["checkFromJSM"],
+ "file_url.jsm": ["checkFromJSM"],
+ "file_worker_url.jsm": ["checkFromJSM"],
+ "Finder.jsm": ["Finder", "GetClipboardSearchString"],
+ "FormAutofillContent.jsm": ["FormAutofillHandler"],
+ "forms.js": ["FormEngine", "FormRec", "FormValidator"],
+ "forms.jsm": ["FormData"],
+ "frame.js": ["Collector", "Runner", "events", "runTestFile", "log", "timers", "persisted", "shutdownApplication"],
+ "FrameScriptManager.jsm": ["getNewLoaderID"],
+ "fxa_utils.js": ["initializeIdentityWithTokenServerResponse"],
+ "fxaccounts.jsm": ["Authentication"],
+ "FxAccounts.jsm": ["fxAccounts", "FxAccounts"],
+ "FxAccountsOAuthGrantClient.jsm": ["FxAccountsOAuthGrantClient", "FxAccountsOAuthGrantClientError"],
+ "FxAccountsProfileClient.jsm": ["FxAccountsProfileClient", "FxAccountsProfileClientError"],
+ "FxAccountsPush.js": ["FxAccountsPushService"],
+ "FxAccountsStorage.jsm": ["FxAccountsStorageManagerCanStoreField", "FxAccountsStorageManager"],
+ "FxAccountsWebChannel.jsm": ["EnsureFxAccountsWebChannel"],
+ "FxaMigrator.jsm": ["fxaMigrator"],
+ "gDevTools.jsm": ["gDevTools", "gDevToolsBrowser"],
+ "gDevTools.jsm": ["gDevTools", "gDevToolsBrowser"],
+ "Geometry.jsm": ["Point", "Rect"],
+ "Gestures.jsm": ["GestureSettings", "GestureTracker"],
+ "GMPInstallManager.jsm": ["GMPInstallManager", "GMPExtractor", "GMPDownloader", "GMPAddon"],
+ "GMPProvider.jsm": [],
+ "GMPUtils.jsm": ["EME_ADOBE_ID", "GMP_PLUGIN_IDS", "GMPPrefs", "GMPUtils", "OPEN_H264_ID", "WIDEVINE_ID"],
+ "hawkclient.js": ["HawkClient"],
+ "hawkrequest.js": ["HAWKAuthenticatedRESTRequest", "deriveHawkCredentials"],
+ "HelperApps.jsm": ["App", "HelperApps"],
+ "history.js": ["HistoryEngine", "HistoryRec"],
+ "history.jsm": ["HistoryEntry", "DumpHistory"],
+ "Http.jsm": ["httpRequest", "percentEncode"],
+ "httpd.js": ["HTTP_400", "HTTP_401", "HTTP_402", "HTTP_403", "HTTP_404", "HTTP_405", "HTTP_406", "HTTP_407", "HTTP_408", "HTTP_409", "HTTP_410", "HTTP_411", "HTTP_412", "HTTP_413", "HTTP_414", "HTTP_415", "HTTP_417", "HTTP_500", "HTTP_501", "HTTP_502", "HTTP_503", "HTTP_504", "HTTP_505", "HttpError", "HttpServer"],
+ "identity.js": ["IdentityManager"],
+ "Identity.jsm": ["IdentityService"],
+ "IdentityUtils.jsm": ["checkDeprecated", "checkRenamed", "getRandomId", "objectCopy", "makeMessageObject"],
+ "import_module.jsm": ["MODULE_IMPORTED", "MODULE_URI", "SUBMODULE_IMPORTED", "same_scope", "SUBMODULE_IMPORTED_TO_SCOPE"],
+ "import_sub_module.jsm": ["SUBMODULE_IMPORTED", "test_obj"],
+ "InlineSpellChecker.jsm": ["InlineSpellChecker", "SpellCheckHelper"],
+ "JNI.jsm": ["JNI", "android_log"],
+ "jpakeclient.js": ["JPAKEClient", "SendCredentialsController"],
+ "Jsbeautify.jsm": ["jsBeautify"],
+ "jsdebugger.jsm": ["addDebuggerToGlobal"],
+ "json2.js": ["JSON"],
+ "keys.js": ["BulkKeyBundle", "SyncKeyBundle"],
+ "KeyValueParser.jsm": ["parseKeyValuePairsFromLines", "parseKeyValuePairs", "parseKeyValuePairsFromFile"],
+ "kinto-http-client.js": ["KintoHttpClient"],
+ "kinto-offline-client.js": ["loadKinto"],
+ "loader-plugin-raw.jsm": ["requireRawId"],
+ "loader.js": ["WorkerDebuggerLoader", "worker"],
+ "Loader.jsm": ["DevToolsLoader", "devtools", "BuiltinProvider", "require", "loader"],
+ "logger.jsm": ["Logger"],
+ "logging.js": ["getTestLogger", "initTestLogging"],
+ "LoginManagerContent.jsm": ["LoginManagerContent", "LoginFormFactory", "UserAutoCompleteResult"],
+ "LoginRecipes.jsm": ["LoginRecipesContent", "LoginRecipesParent"],
+ "logmanager.js": ["LogManager"],
+ "LogUtils.jsm": ["Logger"],
+ "lz4.js": ["Lz4"],
+ "lz4_internal.js": ["Primitives"],
+ "main.js": ["Weave"],
+ "MatchPattern.jsm": ["MatchPattern", "MatchGlobs", "MatchURLFilters"],
+ "mcc_iso3166_table.jsm": ["MCC_ISO3166_TABLE"],
+ "message.js": ["Command", "Message", "MessageOrigin", "Response"],
+ "Messaging.jsm": ["sendMessageToJava", "Messaging"],
+ "microformat-shiv.js": ["Microformats"],
+ "MigrationUtils.jsm": ["MigrationUtils", "MigratorPrototype"],
+ "MinimalIdentity.jsm": ["IdentityService"],
+ "mozelement.js": ["Elem", "Selector", "ID", "Link", "XPath", "Name", "Lookup", "MozMillElement", "MozMillCheckBox", "MozMillRadio", "MozMillDropList", "MozMillTextBox", "subclasses"],
+ "mozmill.js": ["controller", "utils", "elementslib", "os", "getBrowserController", "newBrowserController", "getAddonsController", "getPreferencesController", "newMail3PaneController", "getMail3PaneController", "wm", "platform", "getAddrbkController", "getMsgComposeController", "getDownloadsController", "Application", "findElement", "getPlacesController", "isMac", "isLinux", "isWindows", "firePythonCallback", "getAddons"],
+ "msgbroker.js": ["addListener", "addObject", "removeListener", "sendMessage", "log", "pass", "fail"],
+ "MulticastDNSAndroid.jsm": ["MulticastDNS"],
+ "NativeMessaging.jsm": ["HostManifestManager", "NativeApp"],
+ "NetworkPrioritizer.jsm": ["trackBrowserWindow"],
+ "NotificationDB.jsm": [],
+ "nsFormAutoCompleteResult.jsm": ["FormAutoCompleteResult"],
+ "objects.js": ["getLength"],
+ "observers.js": ["Observers"],
+ "offlineAppCache.jsm": ["OfflineAppCacheHelper"],
+ "OrientationChangeHandler.jsm": [],
+ "os.js": ["listDirectory", "getFileForPath", "abspath", "getPlatform"],
+ "osfile.jsm": ["OS"],
+ "osfile_async_front.jsm": ["OS"],
+ "osfile_native.jsm": ["read"],
+ "osfile_shared_allthreads.jsm": ["LOG", "clone", "Config", "Constants", "Type", "HollowStructure", "OSError", "Library", "declareFFI", "declareLazy", "declareLazyFFI", "normalizeBufferArgs", "projectValue", "isArrayBuffer", "isTypedArray", "defineLazyGetter", "OS"],
+ "osfile_unix_allthreads.jsm": ["declareFFI", "libc", "Error", "AbstractInfo", "AbstractEntry", "Type", "POS_START", "POS_CURRENT", "POS_END"],
+ "osfile_win_allthreads.jsm": ["declareFFI", "libc", "Error", "AbstractInfo", "AbstractEntry", "Type", "POS_START", "POS_CURRENT", "POS_END"],
+ "ospath_unix.jsm": ["basename", "dirname", "join", "normalize", "split", "toFileURI", "fromFileURI"],
+ "ospath_win.jsm": ["basename", "dirname", "join", "normalize", "split", "winGetDrive", "winIsAbsolute", "toFileURI", "fromFileURI"],
+ "OutputGenerator.jsm": ["UtteranceGenerator", "BrailleGenerator"],
+ "PageMenu.jsm": ["PageMenuParent", "PageMenuChild"],
+ "PageThumbs.jsm": ["PageThumbs", "PageThumbsStorage"],
+ "Parser.jsm": ["Parser", "ParserHelpers", "SyntaxTreeVisitor"],
+ "parsingTestHelpers.jsm": ["generateURIsFromDirTree"],
+ "passwords.js": ["PasswordEngine", "LoginRec", "PasswordValidator"],
+ "passwords.jsm": ["Password", "DumpPasswords"],
+ "PdfJsNetwork.jsm": ["NetworkManager"],
+ "PermissionSettings.jsm": ["PermissionSettingsModule"],
+ "PermissionsTable.jsm": ["PermissionsTable", "PermissionsReverseTable", "expandPermissions", "appendAccessToPermName", "isExplicitInPermissionsTable", "AllPossiblePermissions"],
+ "PhoneNumberMetaData.jsm": ["PHONE_NUMBER_META_DATA"],
+ "PlacesUtils.jsm": ["PlacesUtils", "PlacesAggregatedTransaction", "PlacesCreateFolderTransaction", "PlacesCreateBookmarkTransaction", "PlacesCreateSeparatorTransaction", "PlacesCreateLivemarkTransaction", "PlacesMoveItemTransaction", "PlacesRemoveItemTransaction", "PlacesEditItemTitleTransaction", "PlacesEditBookmarkURITransaction", "PlacesSetItemAnnotationTransaction", "PlacesSetPageAnnotationTransaction", "PlacesEditBookmarkKeywordTransaction", "PlacesEditBookmarkPostDataTransaction", "PlacesEditItemDateAddedTransaction", "PlacesEditItemLastModifiedTransaction", "PlacesSortFolderByNameTransaction", "PlacesTagURITransaction", "PlacesUntagURITransaction"],
+ "PluginProvider.jsm": [],
+ "PointerAdapter.jsm": ["PointerRelay", "PointerAdapter"],
+ "policies.js": ["ErrorHandler", "SyncScheduler"],
+ "prefs.js": ["PrefsEngine", "PrefRec"],
+ "prefs.jsm": ["Preference"],
+ "PresentationDeviceInfoManager.jsm": ["PresentationDeviceInfoService"],
+ "PromiseWorker.jsm": ["BasePromiseWorker"],
+ "PushCrypto.jsm": ["PushCrypto", "concatArray"],
+ "quit.js": ["goQuitApplication"],
+ "record.js": ["WBORecord", "RecordManager", "CryptoWrapper", "CollectionKeyManager", "Collection"],
+ "recursive_importA.jsm": ["foo", "bar"],
+ "recursive_importB.jsm": ["baz", "qux"],
+ "reflect.jsm": ["Reflect"],
+ "RemoteFinder.jsm": ["RemoteFinder", "RemoteFinderListener"],
+ "RemotePageManager.jsm": ["RemotePages", "RemotePageManager", "PageListener"],
+ "RemoteWebProgress.jsm": ["RemoteWebProgressManager"],
+ "resource.js": ["AsyncResource", "Resource"],
+ "responsivedesign.jsm": ["ResponsiveUIManager"],
+ "rest.js": ["RESTRequest", "RESTResponse", "TokenAuthenticatedRESTRequest", "SyncStorageRequest"],
+ "rotaryengine.js": ["RotaryEngine", "RotaryRecord", "RotaryStore", "RotaryTracker"],
+ "RTCStatsReport.jsm": ["convertToRTCStatsReport"],
+ "scratchpad-manager.jsm": ["ScratchpadManager"],
+ "server.js": ["MarionetteServer"],
+ "service.js": ["Service"],
+ "SettingsDB.jsm": ["SettingsDB", "SETTINGSDB_NAME", "SETTINGSSTORE_NAME"],
+ "SharedPromptUtils.jsm": ["PromptUtils", "EnableDelayHelper"],
+ "ShutdownLeaksCollector.jsm": ["ContentCollector"],
+ "SignInToWebsite.jsm": ["SignInToWebsiteController"],
+ "Social.jsm": ["Social", "OpenGraphBuilder", "DynamicResizeWatcher", "sizeSocialPanelToContent"],
+ "SpecialPowersObserver.jsm": ["SpecialPowersObserver", "SpecialPowersObserverFactory"],
+ "stack.js": ["findCallerFrame"],
+ "StateMachineHelper.jsm": ["State", "CommandType"],
+ "status.js": ["Status"],
+ "storageserver.js": ["ServerBSO", "StorageServerCallback", "StorageServerCollection", "StorageServer", "storageServerForUsers"],
+ "stringbundle.js": ["StringBundle"],
+ "strings.js": ["trim", "vslice"],
+ "StructuredLog.jsm": ["StructuredLogger", "StructuredFormatter"],
+ "StyleEditorUtil.jsm": ["getString", "assert", "log", "text", "wire", "showFilePicker"],
+ "subprocess_common.jsm": ["BaseProcess", "PromiseWorker", "SubprocessConstants"],
+ "subprocess_unix.jsm": ["SubprocessImpl"],
+ "subprocess_win.jsm": ["SubprocessImpl"],
+ "sync.jsm": ["Authentication"],
+ "tabs.js": ["TabEngine", "TabSetRecord"],
+ "tabs.jsm": ["BrowserTabs"],
+ "tcpsocket_test.jsm": ["createSocket", "createServer", "enablePrefsAndPermissions", "socketCompartmentInstanceOfArrayBuffer"],
+ "telemetry.js": ["SyncTelemetry"],
+ "test.jsm": ["Foo"],
+ "test2.jsm": ["Bar"],
+ "test_bug883784.jsm": ["Test"],
+ "Timer.jsm": ["setTimeout", "clearTimeout", "setInterval", "clearInterval"],
+ "tokenserverclient.js": ["TokenServerClient", "TokenServerClientError", "TokenServerClientNetworkError", "TokenServerClientServerError"],
+ "ToolboxProcess.jsm": ["BrowserToolboxProcess"],
+ "tps.jsm": ["ACTIONS", "TPS"],
+ "Translation.jsm": ["Translation", "TranslationTelemetry"],
+ "Traversal.jsm": ["TraversalRules", "TraversalHelper"],
+ "UpdateTelemetry.jsm": ["AUSTLMY"],
+ "userapi.js": ["UserAPI10Client"],
+ "util.js": ["getChromeWindow", "XPCOMUtils", "Services", "Utils", "Async", "Svc", "Str"],
+ "utils.js": ["applicationName", "assert", "Copy", "getBrowserObject", "getChromeWindow", "getWindows", "getWindowByTitle", "getWindowByType", "getWindowId", "getMethodInWindows", "getPreference", "saveDataURL", "setPreference", "sleep", "startTimer", "stopTimer", "takeScreenshot", "unwrapNode", "waitFor", "btoa", "encryptPayload", "isConfiguredWithLegacyIdentity", "ensureLegacyIdentityManager", "setBasicCredentials", "makeIdentityConfig", "makeFxAccountsInternalMock", "configureFxAccountIdentity", "configureIdentity", "SyncTestingInfrastructure", "waitForZeroTimer", "Promise", "add_identity_test", "MockFxaStorageManager", "AccountState", "sumHistogram", "CommonUtils", "CryptoUtils", "TestingUtils"],
+ "Utils.jsm": ["Utils", "Logger", "PivotContext", "PrefCache", "SettingCache"],
+ "VariablesView.jsm": ["VariablesView", "escapeHTML"],
+ "VariablesViewController.jsm": ["VariablesViewController", "StackFrameUtils"],
+ "version.jsm": ["VERSION"],
+ "vtt.jsm": ["WebVTT"],
+ "WebChannel.jsm": ["WebChannel", "WebChannelBroker"],
+ "WindowDraggingUtils.jsm": ["WindowDraggingElement"],
+ "windows.js": ["init", "map"],
+ "windows.jsm": ["BrowserWindows"],
+ "WindowsJumpLists.jsm": ["WinTaskbarJumpList"],
+ "WindowsPreviewPerTab.jsm": ["AeroPeek"],
+ "withs.js": ["startsWith", "endsWith"],
+ "xul-app.jsm": ["XulApp"]
+}
diff --git a/tools/lint/eslint/npm-shrinkwrap.json b/tools/lint/eslint/npm-shrinkwrap.json
new file mode 100644
index 000000000..4421c4cd3
--- /dev/null
+++ b/tools/lint/eslint/npm-shrinkwrap.json
@@ -0,0 +1,718 @@
+{
+ "name": "mach-eslint",
+ "dependencies": {
+ "acorn": {
+ "version": "4.0.3",
+ "from": "acorn@>=4.0.1 <5.0.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.3.tgz"
+ },
+ "acorn-jsx": {
+ "version": "3.0.1",
+ "from": "acorn-jsx@>=3.0.0 <4.0.0",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz",
+ "dependencies": {
+ "acorn": {
+ "version": "3.3.0",
+ "from": "acorn@>=3.0.4 <4.0.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz"
+ }
+ }
+ },
+ "ajv": {
+ "version": "4.8.2",
+ "from": "ajv@>=4.7.0 <5.0.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.8.2.tgz"
+ },
+ "ajv-keywords": {
+ "version": "1.1.1",
+ "from": "ajv-keywords@>=1.0.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-1.1.1.tgz"
+ },
+ "ansi-escapes": {
+ "version": "1.4.0",
+ "from": "ansi-escapes@>=1.1.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz"
+ },
+ "ansi-regex": {
+ "version": "2.0.0",
+ "from": "ansi-regex@>=2.0.0 <3.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz"
+ },
+ "ansi-styles": {
+ "version": "2.2.1",
+ "from": "ansi-styles@>=2.2.1 <3.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz"
+ },
+ "argparse": {
+ "version": "1.0.9",
+ "from": "argparse@>=1.0.7 <2.0.0",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz"
+ },
+ "array-union": {
+ "version": "1.0.2",
+ "from": "array-union@>=1.0.1 <2.0.0",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz"
+ },
+ "array-uniq": {
+ "version": "1.0.3",
+ "from": "array-uniq@>=1.0.1 <2.0.0",
+ "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz"
+ },
+ "arrify": {
+ "version": "1.0.1",
+ "from": "arrify@>=1.0.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz"
+ },
+ "balanced-match": {
+ "version": "0.4.2",
+ "from": "balanced-match@>=0.4.1 <0.5.0",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz"
+ },
+ "brace-expansion": {
+ "version": "1.1.6",
+ "from": "brace-expansion@>=1.0.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz"
+ },
+ "caller-path": {
+ "version": "0.1.0",
+ "from": "caller-path@>=0.1.0 <0.2.0",
+ "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz"
+ },
+ "callsites": {
+ "version": "0.2.0",
+ "from": "callsites@>=0.2.0 <0.3.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz"
+ },
+ "chalk": {
+ "version": "1.1.3",
+ "from": "chalk@>=1.1.3 <2.0.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz"
+ },
+ "circular-json": {
+ "version": "0.3.1",
+ "from": "circular-json@>=0.3.0 <0.4.0",
+ "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.1.tgz"
+ },
+ "cli-cursor": {
+ "version": "1.0.2",
+ "from": "cli-cursor@>=1.0.1 <2.0.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz"
+ },
+ "cli-width": {
+ "version": "2.1.0",
+ "from": "cli-width@>=2.0.0 <3.0.0",
+ "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.1.0.tgz"
+ },
+ "co": {
+ "version": "4.6.0",
+ "from": "co@>=4.6.0 <5.0.0",
+ "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz"
+ },
+ "code-point-at": {
+ "version": "1.1.0",
+ "from": "code-point-at@>=1.0.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz"
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "from": "concat-map@0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz"
+ },
+ "concat-stream": {
+ "version": "1.5.2",
+ "from": "concat-stream@>=1.4.6 <2.0.0",
+ "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.2.tgz"
+ },
+ "core-util-is": {
+ "version": "1.0.2",
+ "from": "core-util-is@>=1.0.0 <1.1.0",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz"
+ },
+ "d": {
+ "version": "0.1.1",
+ "from": "d@>=0.1.1 <0.2.0",
+ "resolved": "https://registry.npmjs.org/d/-/d-0.1.1.tgz"
+ },
+ "debug": {
+ "version": "2.3.0",
+ "from": "debug@>=2.1.1 <3.0.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.0.tgz"
+ },
+ "deep-is": {
+ "version": "0.1.3",
+ "from": "deep-is@>=0.1.3 <0.2.0",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz"
+ },
+ "del": {
+ "version": "2.2.2",
+ "from": "del@>=2.0.2 <3.0.0",
+ "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz"
+ },
+ "doctrine": {
+ "version": "1.5.0",
+ "from": "doctrine@>=1.2.2 <2.0.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz"
+ },
+ "dom-serializer": {
+ "version": "0.1.0",
+ "from": "dom-serializer@>=0.0.0 <1.0.0",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz",
+ "dependencies": {
+ "domelementtype": {
+ "version": "1.1.3",
+ "from": "domelementtype@>=1.1.1 <1.2.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz"
+ }
+ }
+ },
+ "domelementtype": {
+ "version": "1.3.0",
+ "from": "domelementtype@>=1.3.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz"
+ },
+ "domhandler": {
+ "version": "2.3.0",
+ "from": "domhandler@>=2.3.0 <3.0.0",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz"
+ },
+ "domutils": {
+ "version": "1.5.1",
+ "from": "domutils@>=1.5.1 <2.0.0",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz"
+ },
+ "entities": {
+ "version": "1.1.1",
+ "from": "entities@>=1.1.1 <2.0.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz"
+ },
+ "es5-ext": {
+ "version": "0.10.12",
+ "from": "es5-ext@>=0.10.11 <0.11.0",
+ "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.12.tgz"
+ },
+ "es6-iterator": {
+ "version": "2.0.0",
+ "from": "es6-iterator@>=2.0.0 <3.0.0",
+ "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.0.tgz"
+ },
+ "es6-map": {
+ "version": "0.1.4",
+ "from": "es6-map@>=0.1.3 <0.2.0",
+ "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.4.tgz"
+ },
+ "es6-set": {
+ "version": "0.1.4",
+ "from": "es6-set@>=0.1.3 <0.2.0",
+ "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.4.tgz"
+ },
+ "es6-symbol": {
+ "version": "3.1.0",
+ "from": "es6-symbol@>=3.1.0 <3.2.0",
+ "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.0.tgz"
+ },
+ "es6-weak-map": {
+ "version": "2.0.1",
+ "from": "es6-weak-map@>=2.0.1 <3.0.0",
+ "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.1.tgz"
+ },
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "from": "escape-string-regexp@>=1.0.2 <2.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz"
+ },
+ "escope": {
+ "version": "3.6.0",
+ "from": "escope@>=3.6.0 <4.0.0",
+ "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz"
+ },
+ "eslint": {
+ "version": "3.8.1",
+ "from": "eslint@3.8.1",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-3.8.1.tgz"
+ },
+ "eslint-plugin-html": {
+ "version": "1.5.2",
+ "from": "eslint-plugin-html@1.5.2",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-html/-/eslint-plugin-html-1.5.2.tgz"
+ },
+ "eslint-plugin-react": {
+ "version": "4.2.3",
+ "from": "eslint-plugin-react@4.2.3",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-4.2.3.tgz"
+ },
+ "espree": {
+ "version": "3.3.2",
+ "from": "espree@>=3.2.0 <4.0.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-3.3.2.tgz"
+ },
+ "esprima": {
+ "version": "2.7.3",
+ "from": "esprima@>=2.6.0 <3.0.0",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz"
+ },
+ "esrecurse": {
+ "version": "4.1.0",
+ "from": "esrecurse@>=4.1.0 <5.0.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.1.0.tgz",
+ "dependencies": {
+ "estraverse": {
+ "version": "4.1.1",
+ "from": "estraverse@>=4.1.0 <4.2.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.1.1.tgz"
+ }
+ }
+ },
+ "estraverse": {
+ "version": "4.2.0",
+ "from": "estraverse@>=4.2.0 <5.0.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz"
+ },
+ "esutils": {
+ "version": "2.0.2",
+ "from": "esutils@>=2.0.2 <3.0.0",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz"
+ },
+ "event-emitter": {
+ "version": "0.3.4",
+ "from": "event-emitter@>=0.3.4 <0.4.0",
+ "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.4.tgz"
+ },
+ "exit-hook": {
+ "version": "1.1.1",
+ "from": "exit-hook@>=1.0.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz"
+ },
+ "fast-levenshtein": {
+ "version": "2.0.5",
+ "from": "fast-levenshtein@>=2.0.4 <2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.5.tgz"
+ },
+ "figures": {
+ "version": "1.7.0",
+ "from": "figures@>=1.3.5 <2.0.0",
+ "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz"
+ },
+ "file-entry-cache": {
+ "version": "2.0.0",
+ "from": "file-entry-cache@>=2.0.0 <3.0.0",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz"
+ },
+ "flat-cache": {
+ "version": "1.2.1",
+ "from": "flat-cache@>=1.2.1 <2.0.0",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.2.1.tgz"
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "from": "fs.realpath@>=1.0.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz"
+ },
+ "generate-function": {
+ "version": "2.0.0",
+ "from": "generate-function@>=2.0.0 <3.0.0",
+ "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz"
+ },
+ "generate-object-property": {
+ "version": "1.2.0",
+ "from": "generate-object-property@>=1.1.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz"
+ },
+ "glob": {
+ "version": "7.1.1",
+ "from": "glob@>=7.0.3 <8.0.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz"
+ },
+ "globals": {
+ "version": "9.12.0",
+ "from": "globals@>=9.2.0 <10.0.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-9.12.0.tgz"
+ },
+ "globby": {
+ "version": "5.0.0",
+ "from": "globby@>=5.0.0 <6.0.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz"
+ },
+ "graceful-fs": {
+ "version": "4.1.10",
+ "from": "graceful-fs@>=4.1.2 <5.0.0",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.10.tgz"
+ },
+ "has-ansi": {
+ "version": "2.0.0",
+ "from": "has-ansi@>=2.0.0 <3.0.0",
+ "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz"
+ },
+ "htmlparser2": {
+ "version": "3.9.2",
+ "from": "htmlparser2@>=3.8.2 <4.0.0",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.9.2.tgz"
+ },
+ "ignore": {
+ "version": "3.2.0",
+ "from": "ignore@>=3.1.5 <4.0.0",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.2.0.tgz"
+ },
+ "imurmurhash": {
+ "version": "0.1.4",
+ "from": "imurmurhash@>=0.1.4 <0.2.0",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz"
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "from": "inflight@>=1.0.4 <2.0.0",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz"
+ },
+ "inherits": {
+ "version": "2.0.3",
+ "from": "inherits@>=2.0.1 <2.1.0",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz"
+ },
+ "ini-parser": {
+ "version": "0.0.2",
+ "from": "ini-parser@>=0.0.2 <0.0.3",
+ "resolved": "https://registry.npmjs.org/ini-parser/-/ini-parser-0.0.2.tgz"
+ },
+ "inquirer": {
+ "version": "0.12.0",
+ "from": "inquirer@>=0.12.0 <0.13.0",
+ "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-0.12.0.tgz"
+ },
+ "is-fullwidth-code-point": {
+ "version": "1.0.0",
+ "from": "is-fullwidth-code-point@>=1.0.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz"
+ },
+ "is-my-json-valid": {
+ "version": "2.15.0",
+ "from": "is-my-json-valid@>=2.10.0 <3.0.0",
+ "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.15.0.tgz"
+ },
+ "is-path-cwd": {
+ "version": "1.0.0",
+ "from": "is-path-cwd@>=1.0.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz"
+ },
+ "is-path-in-cwd": {
+ "version": "1.0.0",
+ "from": "is-path-in-cwd@>=1.0.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz"
+ },
+ "is-path-inside": {
+ "version": "1.0.0",
+ "from": "is-path-inside@>=1.0.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.0.tgz"
+ },
+ "is-property": {
+ "version": "1.0.2",
+ "from": "is-property@>=1.0.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz"
+ },
+ "is-resolvable": {
+ "version": "1.0.0",
+ "from": "is-resolvable@>=1.0.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.0.0.tgz"
+ },
+ "isarray": {
+ "version": "1.0.0",
+ "from": "isarray@>=1.0.0 <1.1.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz"
+ },
+ "js-yaml": {
+ "version": "3.6.1",
+ "from": "js-yaml@>=3.5.1 <4.0.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.6.1.tgz"
+ },
+ "json-stable-stringify": {
+ "version": "1.0.1",
+ "from": "json-stable-stringify@>=1.0.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz"
+ },
+ "jsonify": {
+ "version": "0.0.0",
+ "from": "jsonify@>=0.0.0 <0.1.0",
+ "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz"
+ },
+ "jsonpointer": {
+ "version": "4.0.0",
+ "from": "jsonpointer@>=4.0.0 <5.0.0",
+ "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.0.tgz"
+ },
+ "levn": {
+ "version": "0.3.0",
+ "from": "levn@>=0.3.0 <0.4.0",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz"
+ },
+ "lodash": {
+ "version": "4.16.6",
+ "from": "lodash@>=4.0.0 <5.0.0",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.16.6.tgz"
+ },
+ "minimatch": {
+ "version": "3.0.3",
+ "from": "minimatch@>=3.0.2 <4.0.0",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz"
+ },
+ "minimist": {
+ "version": "0.0.8",
+ "from": "minimist@0.0.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz"
+ },
+ "mkdirp": {
+ "version": "0.5.1",
+ "from": "mkdirp@>=0.5.0 <0.6.0",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz"
+ },
+ "ms": {
+ "version": "0.7.2",
+ "from": "ms@0.7.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz"
+ },
+ "mute-stream": {
+ "version": "0.0.5",
+ "from": "mute-stream@0.0.5",
+ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.5.tgz"
+ },
+ "natural-compare": {
+ "version": "1.4.0",
+ "from": "natural-compare@>=1.4.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz"
+ },
+ "number-is-nan": {
+ "version": "1.0.1",
+ "from": "number-is-nan@>=1.0.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz"
+ },
+ "object-assign": {
+ "version": "4.1.0",
+ "from": "object-assign@>=4.0.1 <5.0.0",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz"
+ },
+ "once": {
+ "version": "1.4.0",
+ "from": "once@>=1.3.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz"
+ },
+ "onetime": {
+ "version": "1.1.0",
+ "from": "onetime@>=1.0.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz"
+ },
+ "optionator": {
+ "version": "0.8.2",
+ "from": "optionator@>=0.8.2 <0.9.0",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz"
+ },
+ "os-homedir": {
+ "version": "1.0.2",
+ "from": "os-homedir@>=1.0.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz"
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "from": "path-is-absolute@>=1.0.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz"
+ },
+ "path-is-inside": {
+ "version": "1.0.2",
+ "from": "path-is-inside@>=1.0.1 <2.0.0",
+ "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz"
+ },
+ "pify": {
+ "version": "2.3.0",
+ "from": "pify@>=2.0.0 <3.0.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz"
+ },
+ "pinkie": {
+ "version": "2.0.4",
+ "from": "pinkie@>=2.0.0 <3.0.0",
+ "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz"
+ },
+ "pinkie-promise": {
+ "version": "2.0.1",
+ "from": "pinkie-promise@>=2.0.0 <3.0.0",
+ "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz"
+ },
+ "pluralize": {
+ "version": "1.2.1",
+ "from": "pluralize@>=1.2.1 <2.0.0",
+ "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-1.2.1.tgz"
+ },
+ "prelude-ls": {
+ "version": "1.1.2",
+ "from": "prelude-ls@>=1.1.2 <1.2.0",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz"
+ },
+ "process-nextick-args": {
+ "version": "1.0.7",
+ "from": "process-nextick-args@>=1.0.6 <1.1.0",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz"
+ },
+ "progress": {
+ "version": "1.1.8",
+ "from": "progress@>=1.1.8 <2.0.0",
+ "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz"
+ },
+ "readable-stream": {
+ "version": "2.0.6",
+ "from": "readable-stream@>=2.0.0 <2.1.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz"
+ },
+ "readline2": {
+ "version": "1.0.1",
+ "from": "readline2@>=1.0.1 <2.0.0",
+ "resolved": "https://registry.npmjs.org/readline2/-/readline2-1.0.1.tgz"
+ },
+ "require-uncached": {
+ "version": "1.0.3",
+ "from": "require-uncached@>=1.0.2 <2.0.0",
+ "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz"
+ },
+ "resolve-from": {
+ "version": "1.0.1",
+ "from": "resolve-from@>=1.0.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz"
+ },
+ "restore-cursor": {
+ "version": "1.0.1",
+ "from": "restore-cursor@>=1.0.1 <2.0.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz"
+ },
+ "rimraf": {
+ "version": "2.5.4",
+ "from": "rimraf@>=2.2.8 <3.0.0",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.4.tgz"
+ },
+ "run-async": {
+ "version": "0.1.0",
+ "from": "run-async@>=0.1.0 <0.2.0",
+ "resolved": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz"
+ },
+ "rx-lite": {
+ "version": "3.1.2",
+ "from": "rx-lite@>=3.1.2 <4.0.0",
+ "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz"
+ },
+ "sax": {
+ "version": "1.2.1",
+ "from": "sax@>=1.1.4 <2.0.0",
+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz"
+ },
+ "shelljs": {
+ "version": "0.6.1",
+ "from": "shelljs@>=0.6.0 <0.7.0",
+ "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.6.1.tgz"
+ },
+ "slice-ansi": {
+ "version": "0.0.4",
+ "from": "slice-ansi@0.0.4",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz"
+ },
+ "sprintf-js": {
+ "version": "1.0.3",
+ "from": "sprintf-js@>=1.0.2 <1.1.0",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz"
+ },
+ "string_decoder": {
+ "version": "0.10.31",
+ "from": "string_decoder@>=0.10.0 <0.11.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz"
+ },
+ "string-width": {
+ "version": "1.0.2",
+ "from": "string-width@>=1.0.1 <2.0.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz"
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "from": "strip-ansi@>=3.0.0 <4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz"
+ },
+ "strip-bom": {
+ "version": "3.0.0",
+ "from": "strip-bom@>=3.0.0 <4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz"
+ },
+ "strip-json-comments": {
+ "version": "1.0.4",
+ "from": "strip-json-comments@>=1.0.1 <1.1.0",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz"
+ },
+ "supports-color": {
+ "version": "2.0.0",
+ "from": "supports-color@>=2.0.0 <3.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz"
+ },
+ "table": {
+ "version": "3.8.3",
+ "from": "table@>=3.7.8 <4.0.0",
+ "resolved": "https://registry.npmjs.org/table/-/table-3.8.3.tgz",
+ "dependencies": {
+ "is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "from": "is-fullwidth-code-point@>=2.0.0 <3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz"
+ },
+ "string-width": {
+ "version": "2.0.0",
+ "from": "string-width@>=2.0.0 <3.0.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.0.0.tgz"
+ }
+ }
+ },
+ "text-table": {
+ "version": "0.2.0",
+ "from": "text-table@>=0.2.0 <0.3.0",
+ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz"
+ },
+ "through": {
+ "version": "2.3.8",
+ "from": "through@>=2.3.6 <3.0.0",
+ "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz"
+ },
+ "tryit": {
+ "version": "1.0.3",
+ "from": "tryit@>=1.0.1 <2.0.0",
+ "resolved": "https://registry.npmjs.org/tryit/-/tryit-1.0.3.tgz"
+ },
+ "type-check": {
+ "version": "0.3.2",
+ "from": "type-check@>=0.3.2 <0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz"
+ },
+ "typedarray": {
+ "version": "0.0.6",
+ "from": "typedarray@>=0.0.5 <0.1.0",
+ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz"
+ },
+ "user-home": {
+ "version": "2.0.0",
+ "from": "user-home@>=2.0.0 <3.0.0",
+ "resolved": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz"
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "from": "util-deprecate@>=1.0.1 <1.1.0",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz"
+ },
+ "wordwrap": {
+ "version": "1.0.0",
+ "from": "wordwrap@>=1.0.0 <1.1.0",
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz"
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "from": "wrappy@>=1.0.0 <2.0.0",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz"
+ },
+ "write": {
+ "version": "0.2.1",
+ "from": "write@>=0.2.1 <0.3.0",
+ "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz"
+ },
+ "xtend": {
+ "version": "4.0.1",
+ "from": "xtend@>=4.0.0 <5.0.0",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz"
+ }
+ }
+}
diff --git a/tools/lint/eslint/package.json b/tools/lint/eslint/package.json
new file mode 100644
index 000000000..9c0a8f803
--- /dev/null
+++ b/tools/lint/eslint/package.json
@@ -0,0 +1,16 @@
+{
+ "name": "mach-eslint",
+ "description": "ESLint and external plugins for use with mach",
+ "repository": {},
+ "license": "MPL-2.0",
+ "dependencies": {
+ "eslint": "3.8.1",
+ "eslint-plugin-html": "1.5.2",
+ "eslint-plugin-react": "4.2.3",
+ "escope": "^3.6.0",
+ "espree": "^3.2.0",
+ "estraverse": "^4.2.0",
+ "ini-parser": "^0.0.2",
+ "sax": "^1.1.4"
+ }
+}
diff --git a/tools/lint/eslint/update b/tools/lint/eslint/update
new file mode 100755
index 000000000..477584236
--- /dev/null
+++ b/tools/lint/eslint/update
@@ -0,0 +1,70 @@
+#!/bin/sh
+# Force the scripts working directory to be projdir/tools/lint/eslint.
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+cd $DIR
+
+echo "To complete this script you will need the following tokens from https://api.pub.build.mozilla.org/tokenauth/"
+echo " - tooltool.upload.public"
+echo " - tooltool.download.public"
+echo ""
+read -p "Are these tokens visible at the above URL (y/n)?" choice
+case "$choice" in
+ y|Y )
+ echo ""
+ echo "1. Go to https://api.pub.build.mozilla.org/"
+ echo "2. Log in using your Mozilla LDAP account."
+ echo "3. Click on \"Tokens.\""
+ echo "4. Issue a user token with the permissions tooltool.upload.public and tooltool.download.public."
+ echo ""
+ echo "When you click issue you will be presented with a long string. Paste the string into a temporary file called ~/.tooltool-token."
+ echo ""
+ read -rsp $'Press any key to continue...\n' -n 1
+ ;;
+ n|N )
+ echo ""
+ echo "You will need to contact somebody that has these permissions... people most likely to have these permissions are members of the releng, ateam, a sheriff, mratcliffe, or jryans"
+ exit 1
+ ;;
+ * )
+ echo ""
+ echo "Invalid input."
+ continue
+ ;;
+esac
+
+echo ""
+echo "Removing node_modules and npm_shrinkwrap.json..."
+rm -rf node_modules/
+rm npm-shrinkwrap.json
+
+echo "Installing eslint and external plugins..."
+# ESLint and all _external_ plugins are listed in this directory's package.json,
+# so a regular `npm install` will install them at the specified versions.
+# The in-tree eslint-plugin-mozilla is kept out of this tooltool archive on
+# purpose so that it can be changed by any developer without requiring tooltool
+# access to make changes.
+npm install
+
+echo "Creating npm shrinkwrap..."
+npm shrinkwrap
+
+echo "Creating eslint.tar.gz..."
+tar cvfz eslint.tar.gz node_modules
+
+echo "Downloading tooltool..."
+wget https://raw.githubusercontent.com/mozilla/build-tooltool/master/tooltool.py
+chmod +x tooltool.py
+
+echo "Adding eslint.tar.gz to tooltool..."
+rm manifest.tt
+./tooltool.py add --visibility public eslint.tar.gz
+
+echo "Uploading eslint.tar.gz to tooltool..."
+./tooltool.py upload --authentication-file=~/.tooltool-token --message "node_modules folder update for tools/lint/eslint"
+
+echo "Cleaning up..."
+rm eslint.tar.gz
+rm tooltool.py
+
+echo ""
+echo "Update complete, please commit and check in your changes."