Bug 1380003 - Create avoid-Date-timing eslint rule, r?standard8
MozReview-Commit-ID: 6b6GtBUpZUZ
--- a/tools/lint/docs/linters/eslint-plugin-mozilla.rst
+++ b/tools/lint/docs/linters/eslint-plugin-mozilla.rst
@@ -34,16 +34,30 @@ the fact that `ctypes` can be accessed a
frame-script
------------
Defines the environment for frame scripts.
Rules
=====
+avoid-Date-timing
+-----------------
+
+Rejects grabbing the current time via Date.now() or new Date() for timing
+purposes when the less problematic performance.now() can be used instead.
+
+The performance.now() function returns milliseconds since page load. To
+convert that to milliseconds since the epoch, use:
+
+ performance.timing.navigationStart + performance.now()
+
+Often timing relative to the page load is adequate and that conversion may not
+be necessary.
+
avoid-removeChild
-----------------
Rejects using element.parentNode.removeChild(element) when element.remove()
can be used instead.
avoid-nsISupportsString-preferences
-----------------------------------
--- a/tools/lint/eslint/eslint-plugin-mozilla/lib/index.js
+++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/index.js
@@ -25,16 +25,17 @@ module.exports = {
"frame-script": require("../lib/environments/frame-script.js"),
"places-overlay": require("../lib/environments/places-overlay.js"),
"simpletest": require("../lib/environments/simpletest.js")
},
processors: {
".xml": require("../lib/processors/xbl-bindings")
},
rules: {
+ "avoid-Date-timing": require("../lib/rules/avoid-Date-timing"),
"avoid-removeChild": require("../lib/rules/avoid-removeChild"),
"avoid-nsISupportsString-preferences":
require("../lib/rules/avoid-nsISupportsString-preferences"),
"balanced-listeners": require("../lib/rules/balanced-listeners"),
"import-browser-window-globals":
require("../lib/rules/import-browser-window-globals"),
"import-content-task-globals":
require("../lib/rules/import-content-task-globals"),
@@ -54,16 +55,17 @@ module.exports = {
require("../lib/rules/reject-importGlobalProperties"),
"reject-some-requires": require("../lib/rules/reject-some-requires"),
"use-default-preference-values":
require("../lib/rules/use-default-preference-values"),
"use-ownerGlobal": require("../lib/rules/use-ownerGlobal"),
"var-only-at-top-level": require("../lib/rules/var-only-at-top-level")
},
rulesConfig: {
+ "avoid-Date-timing": "off",
"avoid-removeChild": "off",
"avoid-nsISupportsString-preferences": "off",
"balanced-listeners": "off",
"import-browser-window-globals": "off",
"import-content-task-globals": "off",
"import-globals": "off",
"import-headjs-globals": "off",
"mark-test-function-used": "off",
new file mode 100644
--- /dev/null
+++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/avoid-Date-timing.js
@@ -0,0 +1,58 @@
+/**
+ * @fileoverview Disallow using Date for timing in performance sensitive code
+ *
+ * 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 = {
+ meta: {
+ docs: {
+ description: "disallow use of Date for timing measurements",
+ category: "Best Practices"
+ },
+ schema: []
+ },
+
+ // ---------------------------------------------------------------------------
+ // Public
+ // --------------------------------------------------------------------------
+
+ create(context) {
+ return {
+ "CallExpression": function(node) {
+ let callee = node.callee;
+ if (callee.type !== "MemberExpression" ||
+ callee.object.type !== "Identifier" ||
+ callee.object.name !== "Date" ||
+ callee.property.type !== "Identifier" ||
+ callee.property.name !== "now") {
+ return;
+ }
+
+ context.report(node, "use performance.now() instead of Date.now() for timing " +
+ "measurements");
+ },
+
+ "NewExpression": function(node) {
+ let callee = node.callee;
+ if (callee.type !== "Identifier" ||
+ callee.name !== "Date" ||
+ node.arguments.length > 0) {
+ return;
+ }
+
+ context.report(node, "use performance.now() instead of new Date() for timing " +
+ "measurements");
+ }
+ };
+ }
+};
+
new file mode 100644
--- /dev/null
+++ b/tools/lint/eslint/eslint-plugin-mozilla/tests/avoid-Date-timing.js
@@ -0,0 +1,39 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+var rule = require("../lib/rules/avoid-Date-timing");
+var RuleTester = require("eslint/lib/testers/rule-tester");
+
+const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
+
+// ------------------------------------------------------------------------------
+// Tests
+// ------------------------------------------------------------------------------
+
+function invalidCode(code, type, message) {
+ return {code, errors: [{message, type}]};
+}
+
+ruleTester.run("avoid-Date-timing", rule, {
+ valid: [
+ "new Date('2017-07-11');",
+ "new Date(1499790192440);",
+ "new Date(2017, 7, 11);",
+ "Date.UTC(2017, 7);"
+ ],
+ invalid: [
+ invalidCode("Date.now();", "CallExpression",
+ "use performance.now() instead of Date.now() " +
+ "for timing measurements"),
+ invalidCode("new Date();", "NewExpression",
+ "use performance.now() instead of new Date() " +
+ "for timing measurements")
+ ]
+});
+