Bug 1229859 - Introduce new import-globals-from eslint rule to import globals from other modules; r=Mossop

This commit is contained in:
Patrick Brosset 2015-12-04 11:21:21 +01:00
parent 4e4f8e5714
commit 79784d8925
8 changed files with 140 additions and 6 deletions

View File

@ -25,6 +25,7 @@
// Rules from the mozilla plugin
"mozilla/balanced-listeners": 2,
"mozilla/components-imports": 1,
"mozilla/import-globals-from": 1,
"mozilla/import-headjs-globals": 1,
"mozilla/mark-test-function-used": 1,
"mozilla/no-aArgs": 1,

View File

@ -3,7 +3,8 @@
/* 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/. */
/* globals AnimationsController, document, promise, gToolbox, gInspector */
/* import-globals-from animation-controller.js */
/* globals document */
"use strict";

View File

@ -0,0 +1,18 @@
.. _import-globals-from:
===================
import-globals-from
===================
Rule Details
------------
When the "import-globals-from <path>" comment is found in a file, then all
globals from the file at <path> will be imported in the current scope.
This is useful for tests that rely on globals defined in head.js files, or for
scripts that are loaded as <script> tag in a window in rely on eachother's
globals.
If <path> is a relative path, then it must be relative to the file being
checked by the rule.

View File

@ -10,6 +10,10 @@ removeEventListener (and does the same for on/off).
``components-imports`` adds the filename of imported files e.g.
``Cu.import("some/path/Blah.jsm")`` adds Blah to the global scope.
``import-globals-from`` When the "import-globals-from <path>" comment is found
in a file, then all globals from the file at <path> will be imported in the
current scope.
``import-headjs-globals`` imports globals from head.js and from any files that
should be imported by head.js (as far as we can correctly resolve the path).
@ -47,6 +51,7 @@ Example configuration::
"rules": {
"mozilla/balanced-listeners": 2,
"mozilla/components-imports": 1,
"mozilla/import-globals-from": 1,
"mozilla/import-headjs-globals": 1,
"mozilla/mark-test-function-used": 1,
"mozilla/var-only-at-top-level": 1,
@ -58,6 +63,7 @@ Example configuration::
balanced-listeners
components-imports
import-globals-from
import-headjs-globals
mark-test-function-used
no-aArgs

View File

@ -8,6 +8,7 @@
var escope = require("escope");
var espree = require("espree");
var path = require("path");
var regexes = [
/^(?:Cu|Components\.utils)\.import\(".*\/(.*?)\.jsm?"\);?$/,
@ -232,5 +233,33 @@ module.exports = {
var pathAndFilename = scope.getFilename();
return /.*[\\/]browser_.+\.js$/.test(pathAndFilename);
},
/**
* 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 = 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 {
// 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;
}
}
};

View File

@ -19,6 +19,7 @@ module.exports = {
rules: {
"balanced-listeners": require("../lib/rules/balanced-listeners"),
"components-imports": require("../lib/rules/components-imports"),
"import-globals-from": require("../lib/rules/import-globals-from"),
"import-headjs-globals": require("../lib/rules/import-headjs-globals"),
"mark-test-function-used": require("../lib/rules/mark-test-function-used"),
"no-aArgs": require("../lib/rules/no-aArgs"),
@ -28,6 +29,7 @@ module.exports = {
rulesConfig: {
"balanced-listeners": 0,
"components-imports": 0,
"import-globals-from": 0,
"import-headjs-globals": 0,
"mark-test-function-used": 0,
"no-aArgs": 0,

View File

@ -0,0 +1,78 @@
/**
* @fileoverview When the "import-globals-from: <path>" comment is found in a
* file, then all globals from the file at <path> will be imported in the
* current scope.
*
* 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 helpers = require("../helpers");
var path = require("path");
module.exports = function(context) {
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
function importGlobalsFrom(filePath) {
// If the file can't be found, let the error go up to the caller so it can
// be logged as an error in the current file.
var content = fs.readFileSync(filePath, "utf8");
// Parse the content and get the globals from the ast.
var ast = helpers.getAST(content);
var globalVars = helpers.getGlobals(ast);
for (var i = 0; i < globalVars.length; i++) {
var varName = globalVars[i];
helpers.addVarToScope(varName, context);
}
}
// ---------------------------------------------------------------------------
// Public
// ---------------------------------------------------------------------------
return {
Program: function(node) {
var comments = context.getSourceCode().getAllComments();
comments.forEach(function(comment) {
var value = comment.value.trim();
var match = /^import-globals-from\s+(.*)$/.exec(value);
if (match) {
var currentFilePath = helpers.getAbsoluteFilePath(context);
var filePath = match[1];
if (!path.isAbsolute(filePath)) {
var dirName = path.dirname(currentFilePath);
filePath = path.resolve(dirName, filePath);
}
try {
importGlobalsFrom(filePath);
} catch (e) {
context.report(
node,
"Could not load globals from file {{filePath}}: {{error}}",
{
filePath: filePath,
error: e
}
);
}
}
});
}
};
};

View File

@ -91,12 +91,11 @@ module.exports = function(context) {
return;
}
var testPath = this.getFilename();
var testFilename = path.basename(testPath);
var fullTestPath = path.resolve(testFilename);
var fullHeadjsPath = path.resolve("head.js");
var currentFilePath = helpers.getAbsoluteFilePath(context);
var dirName = path.dirname(currentFilePath);
var fullHeadjsPath = path.resolve(dirName, "head.js");
checkFile([fullTestPath, fullHeadjsPath]);
checkFile([currentFilePath, fullHeadjsPath]);
}
};
};