diff --git a/.eslintrc.js b/.eslintrc.js index 0cb8f3c4f7..fbd822507c 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,3 +1,4 @@ +const LEGACY_FILES = ["platform/**", "example/**"]; module.exports = { "env": { "browser": true, @@ -70,6 +71,56 @@ module.exports = { ], "dot-notation": "error", "indent": ["error", 4], + + // https://eslint.org/docs/rules/no-case-declarations + "no-case-declarations": "error", + // https://eslint.org/docs/rules/max-classes-per-file + "max-classes-per-file": ["error", 1], + // https://eslint.org/docs/rules/no-eq-null + "no-eq-null": "error", + // https://eslint.org/docs/rules/no-eval + "no-eval": "error", + // https://eslint.org/docs/rules/no-floating-decimal + "no-floating-decimal": "error", + // https://eslint.org/docs/rules/no-implicit-globals + "no-implicit-globals": "error", + // https://eslint.org/docs/rules/no-implied-eval + "no-implied-eval": "error", + // https://eslint.org/docs/rules/no-lone-blocks + "no-lone-blocks": "error", + // https://eslint.org/docs/rules/no-loop-func + "no-loop-func": "error", + // https://eslint.org/docs/rules/no-new-func + "no-new-func": "error", + // https://eslint.org/docs/rules/no-new-wrappers + "no-new-wrappers": "error", + // https://eslint.org/docs/rules/no-octal-escape + "no-octal-escape": "error", + // https://eslint.org/docs/rules/no-proto + "no-proto": "error", + // https://eslint.org/docs/rules/no-return-await + "no-return-await": "error", + // https://eslint.org/docs/rules/no-script-url + "no-script-url": "error", + // https://eslint.org/docs/rules/no-self-compare + "no-self-compare": "error", + // https://eslint.org/docs/rules/no-sequences + "no-sequences": "error", + // https://eslint.org/docs/rules/no-unmodified-loop-condition + "no-unmodified-loop-condition": "error", + // https://eslint.org/docs/rules/no-useless-call + "no-useless-call": "error", + // https://eslint.org/docs/rules/wrap-iife + "wrap-iife": "error", + // https://eslint.org/docs/rules/no-nested-ternary + "no-nested-ternary": "error", + // https://eslint.org/docs/rules/switch-colon-spacing + "switch-colon-spacing": "error", + // https://eslint.org/docs/rules/no-useless-computed-key + "no-useless-computed-key": "error", + // https://eslint.org/docs/rules/rest-spread-spacing + "rest-spread-spacing": ["error"], + "vue/html-indent": [ "error", 4, @@ -116,6 +167,13 @@ module.exports = { } ] } + }, { + "files": LEGACY_FILES, + "rules": { + // https://eslint.org/docs/rules/no-nested-ternary + "no-nested-ternary": "off", + "no-var": "off" + } } ] }; diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e84da4de69..9e43f38894 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -131,89 +131,96 @@ changes. ### Code Standards -JavaScript sources in Open MCT must satisfy JSLint under its default -settings. This is verified by the command line build. +JavaScript sources in Open MCT must satisfy the ESLint rules defined in +this repository. This is verified by the command line build. #### Code Guidelines JavaScript sources in Open MCT should: -* Use four spaces for indentation. Tabs should not be used. -* Include JSDoc for any exposed API (e.g. public methods, constructors). -* Include non-JSDoc comments as-needed for explaining private variables, - methods, or algorithms when they are non-obvious. -* Define one public class per script, expressed as a constructor function - returned from an AMD-style module. -* Follow “Java-like” naming conventions. These includes: - * Classes should use camel case, first letter capitalized - (e.g. SomeClassName). - * Methods, variables, fields, and function names should use camel case, - first letter lower-case (e.g. someVariableName). - * Constants (variables or fields which are meant to be declared and - initialized statically, and never changed) should use only capital - letters, with underscores between words (e.g. SOME_CONSTANT). - * File names should be the name of the exported class, plus a .js extension - (e.g. SomeClassName.js). -* Avoid anonymous functions, except when functions are short (a few lines) - and/or their inclusion makes sense within the flow of the code - (e.g. as arguments to a forEach call). -* Avoid deep nesting (especially of functions), except where necessary - (e.g. due to closure scope). -* End with a single new-line character. -* Expose public methods by declaring them on the class's prototype. -* Within a given function's scope, do not mix declarations and imperative - code, and present these in the following order: - * First, variable declarations and initialization. - * Second, function declarations. - * Third, imperative statements. - * Finally, the returned value. - +1. Write clean code. Here’s a good summary - https://github.com/ryanmcdermott/clean-code-javascript. +1. Include JSDoc for any exposed API (e.g. public methods, classes). +1. Include non-JSDoc comments as-needed for explaining private variables, + methods, or algorithms when they are non-obvious. Otherwise code + should be self-documenting. +1. Classes and Vue components should use camel case, first letter capitalized + (e.g. SomeClassName). +1. Methods, variables, fields, events, and function names should use camelCase, + first letter lower-case (e.g. someVariableName). +1. Source files that export functions should use camelCase, first letter lower-case (eg. testTools.js) +1. Constants (variables or fields which are meant to be declared and + initialized statically, and never changed) should use only capital + letters, with underscores between words (e.g. SOME_CONSTANT). They should always be declared as `const`s +1. File names should be the name of the exported class, plus a .js extension + (e.g. SomeClassName.js). +1. Avoid anonymous functions, except when functions are short (one or two lines) + and their inclusion makes sense within the flow of the code + (e.g. as arguments to a forEach call). Anonymous functions should always be arrow functions. +1. Named functions are preferred over functions assigned to variables. + eg. + ```JavaScript + function renameObject(object, newName) { + Object.name = newName; + } + ``` + is preferable to + ```JavaScript + const rename = (object, newName) => { + Object.name = newName; + } + ``` +1. Avoid deep nesting (especially of functions), except where necessary + (e.g. due to closure scope). +1. End with a single new-line character. +1. Always use ES6 `Class`es and inheritence rather than the pre-ES6 prototypal + pattern. +1. Within a given function's scope, do not mix declarations and imperative + code, and present these in the following order: + * First, variable declarations and initialization. + * Secondly, imperative statements. + * Finally, the returned value. Functions should only have a single return statement. +1. Avoid the use of "magic" values. + eg. + ```JavaScript + Const UNAUTHORIZED = 401 + if (responseCode === UNAUTHORIZED) + ``` + is preferable to + ```JavaScript + if (responseCode === 401) + ``` +1. Don’t use the ternary operator. Yes it's terse, but there's probably a clearer way of writing it. +1. Test specs should reside alongside the source code they test, not in a separate directory. +1. Organize code by feature, not by type. + eg. + ``` + - telemetryTable + - row + TableRow.js + TableRowCollection.js + TableRow.vue + - column + TableColumn.js + TableColumn.vue + plugin.js + pluginSpec.js + ``` + is preferable to + ``` + - telemetryTable + - components + TableRow.vue + TableColumn.vue + - collections + TableRowCollection.js + TableColumn.js + TableRow.js + plugin.js + pluginSpec.js + ``` Deviations from Open MCT code style guidelines require two-party agreement, typically from the author of the change and its reviewer. -#### Code Example - -```js -/*global define*/ - -/** - * Bundles should declare themselves as namespaces in whichever source - * file is most like the "main point of entry" to the bundle. - * @namespace some/bundle - */ -define( - ['./OtherClass'], - function (OtherClass) { - "use strict"; - - /** - * A summary of how to use this class goes here. - * - * @constructor - * @memberof some/bundle - */ - function ExampleClass() { - } - - // Methods which are not intended for external use should - // not have JSDoc (or should be marked @private) - ExampleClass.prototype.privateMethod = function () { - }; - - /** - * A summary of this method goes here. - * @param {number} n a parameter - * @returns {number} a return value - */ - ExampleClass.prototype.publicMethod = function (n) { - return n * 2; - } - - return ExampleClass; - } -); -``` - ### Test Standards Automated testing shall occur whenever changes are merged into the main