author | Mark Banner <standard8@mozilla.com> |
Thu, 05 Jan 2017 07:31:13 +0000 | |
changeset 469060 | c57d277f969d4997a8717908ba1a6afbfd826fd6 |
parent 468867 | 1d025ac534a6333a8170a59a95a8a3673d4028ee |
child 544074 | 4674270d6d43ab762a52308e1a317fade6a28882 |
push id | 43596 |
push user | bmo:standard8@mozilla.com |
push date | Wed, 01 Feb 2017 14:34:32 +0000 |
reviewers | Mossop |
bugs | 1330545 |
milestone | 54.0a1 |
--- a/tools/lint/eslint/eslint-plugin-mozilla/lib/globals.js +++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/globals.js @@ -60,57 +60,91 @@ function parseBooleanConfig(string, comm 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 + * @param {String} filePath * The absolute path of the file being parsed. */ -function GlobalsForNode(path) { - this.path = path; - this.root = helpers.getRootDir(path); +function GlobalsForNode(filePath) { + this.path = filePath; + this.dirname = path.dirname(this.path) + this.root = helpers.getRootDir(this.path); + this.isWorker = helpers.getIsWorker(this.path); } GlobalsForNode.prototype = { + Program(node) { + if (!this.isWorker) { + return []; + } + + return [ + {name: "importScripts", writable: false}, + // Only available to workers. + {name: "FileReaderSync", writable: false}, + {name: "onmessage", writable: true}, + // Only available to chrome workers, but since we don't know which is which, + // we make it available anyway. + {name: "ctypes", writable: false} + ]; + }, + 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); + filePath = path.resolve(this.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 }}); + let globals = helpers.convertExpressionToGlobals(node, isGlobal, this.root); + // Map these globals now, as getGlobalsForFile is pre-mapped. + globals = globals.map(name => { return { name, writable: true }}); + + if (this.isWorker) { + let workerDetails = helpers.convertWorkerExpressionToGlobals(node, + isGlobal, this.root, this.dirname); + globals = globals.concat(workerDetails.map(name => { + return { name, writable: true }; + })); + } + + return globals; }, }; 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. + * @return {Array} + * An array of objects that contain details about the globals: + * - {String} name + * The name of the global. + * - {Boolean} writable + * If the global is writeable or not. */ getGlobalsForFile(path) { if (globalCache.has(path)) { return globalCache.get(path); } let content = fs.readFileSync(path, "utf8"); @@ -174,16 +208,19 @@ module.exports = { } }; // Install thin wrappers around GlobalsForNode let handler = new GlobalsForNode(helpers.getAbsoluteFilePath(context)); for (let type of Object.keys(GlobalsForNode.prototype)) { parser[type] = function(node) { + if (type === "Program") { + globalScope = context.getScope(); + } let globals = handler[type](node, context.getAncestors()); helpers.addGlobals(globals, globalScope); } } return parser; } };
--- a/tools/lint/eslint/eslint-plugin-mozilla/lib/helpers.js +++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/helpers.js @@ -33,16 +33,18 @@ var definitions = [ /^this\.__defineGetter__\("(\w+)"/, /^this\.(\w+) =/ ]; var imports = [ /^(?:Cu|Components\.utils)\.import\(".*\/((.*?)\.jsm?)"(?:, this)?\)/, ]; +var workerImportFilenameMatch = /(.*\/)*(.*?\.jsm?)/; + module.exports = { /** * Gets the abstract syntax tree (AST) of the JavaScript source code contained * in sourceText. * * @param {String} sourceText * Text containing valid JavaScript. * @@ -163,18 +165,50 @@ module.exports = { * @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. + * An array of global variable names defined. */ + convertWorkerExpressionToGlobals: function(node, isGlobal, repository, dirname) { + var getGlobalsForFile = require("./globals").getGlobalsForFile; + + if (!modules) { + modules = require(path.join(repository, "tools", "lint", "eslint", "modules.json")); + } + + let results = []; + let expr = node.expression; + + if (node.expression.type === "CallExpression" && + expr.callee && + expr.callee.type === "Identifier" && + expr.callee.name === "importScripts") { + for (var arg of expr.arguments) { + var match = arg.value.match(workerImportFilenameMatch); + if (match) { + if (!match[1]) { + let filePath = path.resolve(dirname, match[2]); + if (fs.existsSync(filePath)) { + let additionalGlobals = getGlobalsForFile(filePath); + results = results.concat(additionalGlobals); + } + } else if (match[2] in modules) { + results.push(modules[match[2]]); + } + } + } + } + return results; + }, + convertExpressionToGlobals: function(node, isGlobal, repository) { if (!modules) { modules = require(path.join(repository, "tools", "lint", "eslint", "modules.json")); } try { var source = this.getASTSource(node); } @@ -455,16 +489,22 @@ module.exports = { if (filename.startsWith("test_")) { return "xpcshell"; } return null; }, + getIsWorker: function(filePath) { + let filename = path.basename(this.cleanUpPath(filePath)).toLowerCase(); + + return filename.includes("worker"); + }, + /** * 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 */
--- a/tools/lint/eslint/eslint-plugin-mozilla/package.json +++ b/tools/lint/eslint/eslint-plugin-mozilla/package.json @@ -1,11 +1,11 @@ { "name": "eslint-plugin-mozilla", - "version": "0.2.16", + "version": "0.2.17", "description": "A collection of rules that help enforce JavaScript coding standard in the Mozilla project.", "keywords": [ "eslint", "eslintplugin", "eslint-plugin", "mozilla", "firefox" ],
--- a/tools/lint/eslint/modules.json +++ b/tools/lint/eslint/modules.json @@ -65,16 +65,17 @@ "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"], + "ExtensionsUI.jsm": ["ExtensionsUI"], "extension-storage.js": ["ExtensionStorageEngine", "EncryptionRemoteTransformer", "KeyRingEncryptionRemoteTransformer"], "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"], @@ -105,16 +106,17 @@ "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"], "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"], + "JSDOMParser.js": ["JSDOMParser"], "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": ["Kinto"], "kinto-storage-adapter.js": ["FirefoxAdapter"], @@ -144,17 +146,17 @@ "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.jsm": ["OS", "require"], "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"], @@ -171,27 +173,29 @@ "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"], + "Readability.js": ["Readability"], "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"], + "require.js": ["require"], "RTCStatsReport.jsm": ["convertToRTCStatsReport"], "scratchpad-manager.jsm": ["ScratchpadManager"], "server.js": ["MarionetteServer"], "service.js": ["Service"], "SharedPromptUtils.jsm": ["PromptUtils", "EnableDelayHelper"], "ShutdownLeaksCollector.jsm": ["ContentCollector"], "SignInToWebsite.jsm": ["SignInToWebsiteController"], "Social.jsm": ["Social", "OpenGraphBuilder", "DynamicResizeWatcher", "sizeSocialPanelToContent"],