diff --git a/.gitignore b/.gitignore index ac10dfe528..ad7aa12883 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # gitignore node_modules +.idea # Only apps should have lockfiles yarn.lock diff --git a/linters/.jshintrc b/linters/.jshintrc index a7a08a349e..3f21707c47 100644 --- a/linters/.jshintrc +++ b/linters/.jshintrc @@ -20,28 +20,25 @@ * ENFORCING OPTIONS * ================= */ - - // Force all variable names to use either camelCase style or UPPER_CASE - // with underscores. - "camelcase": true, + // Don't enforce code style since those rules will be deprecated in JSHint. // Prohibit use of == and != in favor of === and !==. "eqeqeq": true, - // Enforce tab width of 2 spaces. - "indent": 2, + // Require curly braces around blocks + "curly": true, - // Prohibit use of a variable before it is defined. - "latedef": true, + // Must explicitly check an object's items when looping with for ... in + "forin": true, - // Enforce line length to 100 characters - "maxlen": 100, + // Prohibit overwriting native prototype functions + "freeze": true, - // Require capitalized names for constructor functions. - "newcap": true, + // Prohibit use of a variable before it is defined. + "latedef": true, - // Enforce use of single quotation marks for strings. - "quotmark": "single", + // Prohibit use of arguments.caller, arguments.callee + "noarg": true, // Enforce placing 'use strict' at the top function scope "strict": true, diff --git a/packages/eslint-config-npr-base/.babelrc b/packages/eslint-config-npr-base/.babelrc new file mode 100644 index 0000000000..e0aceaae1c --- /dev/null +++ b/packages/eslint-config-npr-base/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["airbnb"] +} diff --git a/packages/eslint-config-npr-base/.editorconfig b/packages/eslint-config-npr-base/.editorconfig new file mode 100644 index 0000000000..2895e4735d --- /dev/null +++ b/packages/eslint-config-npr-base/.editorconfig @@ -0,0 +1,11 @@ +root = true + +[*] +indent_style = space +indent_size = 4 +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +end_of_line = lf +# editorconfig-tools is unable to ignore longs strings or urls +max_line_length = null diff --git a/packages/eslint-config-npr-base/.eslintrc b/packages/eslint-config-npr-base/.eslintrc new file mode 100644 index 0000000000..224149fda6 --- /dev/null +++ b/packages/eslint-config-npr-base/.eslintrc @@ -0,0 +1,8 @@ +{ + "extends": "./index.js", + "rules": { + // disable requiring trailing commas because it might be nice to revert to + // being JSON at some point, and I don't want to make big changes now. + "comma-dangle": 0 + }, +} diff --git a/packages/eslint-config-npr-base/CHANGELOG.md b/packages/eslint-config-npr-base/CHANGELOG.md new file mode 100644 index 0000000000..2fda72332e --- /dev/null +++ b/packages/eslint-config-npr-base/CHANGELOG.md @@ -0,0 +1,3 @@ +12.1.0 / 2017-12-13 +================== + - Initial commit; building off of `eslint-config-airbnb-base` package. diff --git a/packages/eslint-config-npr-base/README.md b/packages/eslint-config-npr-base/README.md new file mode 100644 index 0000000000..5cb653944d --- /dev/null +++ b/packages/eslint-config-npr-base/README.md @@ -0,0 +1,46 @@ +# eslint-config-npr-base + +This package provides NPR's base JS .eslintrc (without React plugins) as an extensible shared config. + +It extends Airbnb's base configuration with NPR's style guide. + +## Usage + +Our default export contains all of our ESLint rules, including ECMAScript 6+. It requires `eslint` and `eslint-plugin-import`. + +If you use yarn, run `npm info "@npr/eslint-config-npr-base@latest" peerDependencies` to list the peer dependencies and versions, then run `yarn add --dev @` for each listed peer dependency. See below for npm instructions. + +1. Install the correct versions of each package, which are listed by the command: + + ```sh + npm info "@npr/eslint-config-npr-base@latest" peerDependencies + ``` + + Linux/OSX users can run + ```sh + ( + export PKG=@npr/eslint-config-npr-base; + npm info "$PKG@latest" peerDependencies --json | command sed 's/[\{\},]//g ; s/: /@/g' | xargs npm install --save-dev "$PKG@latest" + ) + ``` + + Which produces and runs a command like: + + ```sh + npm install --save-dev @npr/eslint-config-npr-base eslint@^#.#.# eslint-plugin-import@^#.#.# + ``` + + Windows users can either install all the peer dependencies manually, or use the [install-peerdeps](https://github.com/nathanhleung/install-peerdeps) cli tool. + + ```sh + npm install -g install-peerdeps + install-peerdeps --dev @npr/eslint-config-npr-base + ``` + + The cli will produce and run a command like: + + ```sh + npm install --save-dev @npr/eslint-config-npr-base eslint@^#.#.# eslint-plugin-import@^#.#.# + ``` + +2. Add `"extends": "@npr/eslint-config-npr-base"` to your .eslintrc. diff --git a/packages/eslint-config-npr-base/index.js b/packages/eslint-config-npr-base/index.js new file mode 100644 index 0000000000..9132bf19dd --- /dev/null +++ b/packages/eslint-config-npr-base/index.js @@ -0,0 +1,17 @@ +module.exports = { + extends: [ + 'eslint-config-airbnb-base', + 'eslint-config-airbnb-base/rules/strict', + './rules/general', + ].map(require.resolve), + parserOptions: { + ecmaVersion: 2017, + sourceType: 'module', + ecmaFeatures: { + experimentalObjectRestSpread: true, + }, + }, + rules: { + strict: 'error', + }, +}; diff --git a/packages/eslint-config-npr-base/package.json b/packages/eslint-config-npr-base/package.json new file mode 100644 index 0000000000..003a3efa9f --- /dev/null +++ b/packages/eslint-config-npr-base/package.json @@ -0,0 +1,50 @@ +{ + "name": "@npr/eslint-config-npr-base", + "version": "12.1.0", + "description": "NPR's base style guide for JavaScript", + "main": "index.js", + "scripts": { + "prelint": "editorconfig-tools check * rules/* test/*", + "lint": "eslint --report-unused-disable-directives .", + "tests-only": "babel-tape-runner ./test/test-*.js", + "prepublish": "(in-install || eslint-find-rules --unused) && (not-in-publish || npm test)", + "pretest": "npm run --silent lint", + "test": "npm run --silent tests-only" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/npr/javascript.git" + }, + "keywords": [ + "javascript", + "style" + ], + "author": "Stephen Thompson ", + "license": "UNLICENSED", + "bugs": { + "url": "https://github.com/npr/javascript/issues" + }, + "homepage": "https://github.com/npr/javascript#readme", + "devDependencies": { + "babel-preset-airbnb": "^2.4.0", + "babel-tape-runner": "^2.0.1", + "editorconfig-tools": "^0.1.1", + "eslint": "^4.9.0", + "eslint-find-rules": "^3.1.1", + "eslint-plugin-import": "^2.7.0", + "in-publish": "^2.0.0", + "safe-publish-latest": "^1.1.1", + "tape": "^4.8.0" + }, + "peerDependencies": { + "eslint": "^4.9.0", + "eslint-plugin-import": "^2.7.0" + }, + "engines": { + "node": ">= 6" + }, + "dependencies": { + "eslint-config-airbnb-base": "^12.1.0", + "eslint-restricted-globals": "^0.1.1" + } +} diff --git a/packages/eslint-config-npr-base/rules/general.js b/packages/eslint-config-npr-base/rules/general.js new file mode 100644 index 0000000000..cb600cc891 --- /dev/null +++ b/packages/eslint-config-npr-base/rules/general.js @@ -0,0 +1,28 @@ +module.exports = { + rules: { + indent: [ + 'error', + 4, + { SwitchCase: 1 } + ], + 'class-methods-use-this': 'off', + 'space-before-function-paren': [ + 'error', + { anonymous: 'always', named: 'never', asyncArrow: 'always' } + ], + 'comma-dangle': [ + 'error', + 'always-multiline' + ], + 'max-len': [ + 'warn', + { + code: 110, + ignoreComments: true, + ignoreUrls: true + } + ], + 'implicit-arrow-linebreak': 'off', + 'import/exports-last': 'off', + } +}; diff --git a/packages/eslint-config-npr-base/test/.eslintrc b/packages/eslint-config-npr-base/test/.eslintrc new file mode 100644 index 0000000000..5808be6186 --- /dev/null +++ b/packages/eslint-config-npr-base/test/.eslintrc @@ -0,0 +1,8 @@ +{ + "rules": { + // disabled because I find it tedious to write tests while following this rule + "no-shadow": 0, + // tests uses `t` for tape + "id-length": [2, {"min": 2, "properties": "never", "exceptions": ["t"]}], + } +} diff --git a/packages/eslint-config-npr-base/test/test-base.js b/packages/eslint-config-npr-base/test/test-base.js new file mode 100644 index 0000000000..aefbb5fe34 --- /dev/null +++ b/packages/eslint-config-npr-base/test/test-base.js @@ -0,0 +1,32 @@ +import fs from 'fs'; +import path from 'path'; +import test from 'tape'; + +import index from '../'; + +const files = { ...{ index } }; // object spread is to test parsing + +fs.readdirSync(path.join(__dirname, '../rules')).forEach((name) => { + // eslint-disable-next-line import/no-dynamic-require + files[name] = require(`../rules/${name}`); // eslint-disable-line global-require +}); + +Object.keys(files).forEach(( // eslint-disable-line function-paren-newline + name, // trailing function comma is to test parsing +) => { // eslint-disable-line function-paren-newline + const config = files[name]; + + test(`${name}: does not reference react`, (t) => { + t.plan(2); + + // scan plugins for react and fail if it is found + const hasReactPlugin = Object.prototype.hasOwnProperty.call(config, 'plugins') && + config.plugins.indexOf('react') !== -1; + t.notOk(hasReactPlugin, 'there is no react plugin'); + + // scan rules for react/ and fail if any exist + const reactRuleIds = Object.keys(config.rules) + .filter(ruleId => ruleId.indexOf('react/') === 0); + t.deepEquals(reactRuleIds, [], 'there are no react/ rules'); + }); +}); diff --git a/packages/eslint-config-npr-react/.babelrc b/packages/eslint-config-npr-react/.babelrc new file mode 100644 index 0000000000..e0aceaae1c --- /dev/null +++ b/packages/eslint-config-npr-react/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["airbnb"] +} diff --git a/packages/eslint-config-npr-react/.editorconfig b/packages/eslint-config-npr-react/.editorconfig new file mode 100644 index 0000000000..2895e4735d --- /dev/null +++ b/packages/eslint-config-npr-react/.editorconfig @@ -0,0 +1,11 @@ +root = true + +[*] +indent_style = space +indent_size = 4 +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +end_of_line = lf +# editorconfig-tools is unable to ignore longs strings or urls +max_line_length = null diff --git a/packages/eslint-config-npr-react/.eslintrc b/packages/eslint-config-npr-react/.eslintrc new file mode 100644 index 0000000000..224149fda6 --- /dev/null +++ b/packages/eslint-config-npr-react/.eslintrc @@ -0,0 +1,8 @@ +{ + "extends": "./index.js", + "rules": { + // disable requiring trailing commas because it might be nice to revert to + // being JSON at some point, and I don't want to make big changes now. + "comma-dangle": 0 + }, +} diff --git a/packages/eslint-config-npr-react/CHANGELOG.md b/packages/eslint-config-npr-react/CHANGELOG.md new file mode 100644 index 0000000000..a3458a5035 --- /dev/null +++ b/packages/eslint-config-npr-react/CHANGELOG.md @@ -0,0 +1,3 @@ +16.1.0 / 2017-12-13 +================== + - Initial commit; building off of `eslint-config-airbnb` package. diff --git a/packages/eslint-config-npr-react/README.md b/packages/eslint-config-npr-react/README.md new file mode 100644 index 0000000000..7b250bfc60 --- /dev/null +++ b/packages/eslint-config-npr-react/README.md @@ -0,0 +1,49 @@ +# eslint-config-npr-react + +This package provides NPR's JS .eslintrc with React plugins as an extensible shared config. + +It extends Airbnb's full configuration with NPR's style guide. + +## Usage + +Our default export contains all of our ESLint rules, including ECMAScript 6+. + +It requires `eslint`, `eslint-plugin-import`, `eslint-plugin-react`, and `eslint-plugin-jsx-a11y`. If you don't need React, see [eslint-config-npr-base](https://www.npmjs.com/package/@npr/eslint-config-npr-base). + +If you use yarn, run `npm info "@npr/eslint-config-npr-react@latest" peerDependencies` to list the peer dependencies and versions, then run `yarn add --dev @` for each listed peer dependency. See below for npm instructions. + +1. Install the correct versions of each package, which are listed by the command: + + ```sh + npm info "@npr/eslint-config-npr-react@latest" peerDependencies + ``` + + Linux/OSX users can run + + ```sh + ( + export PKG=@npr/eslint-config-npr-react; + npm info "$PKG@latest" peerDependencies --json | command sed 's/[\{\},]//g ; s/: /@/g' | xargs npm install --save-dev "$PKG@latest" + ) + ``` + + Which produces and runs a command like: + + ```sh + npm install --save-dev @npr/eslint-config-npr-react eslint@^#.#.# eslint-plugin-jsx-a11y@^#.#.# eslint-plugin-import@^#.#.# eslint-plugin-react@^#.#.# + ``` + + Windows users can either install all the peer dependencies manually, or use the [install-peerdeps](https://github.com/nathanhleung/install-peerdeps) cli tool. + + ```sh + npm install -g install-peerdeps + install-peerdeps --dev @npr/eslint-config-npr-react + ``` + + The cli will produce and run a command like: + + ```sh + npm install --save-dev @npr/eslint-config-npr-react eslint@^#.#.# eslint-plugin-jsx-a11y@^#.#.# eslint-plugin-import@^#.#.# eslint-plugin-react@^#.#.# + ``` + +2. Add `"extends": "@npr/esling-config-npr-react"` to your .eslintrc diff --git a/packages/eslint-config-npr-react/index.js b/packages/eslint-config-npr-react/index.js new file mode 100644 index 0000000000..14e4304ae5 --- /dev/null +++ b/packages/eslint-config-npr-react/index.js @@ -0,0 +1,8 @@ +module.exports = { + extends: [ + 'eslint-config-airbnb', + '@npr/eslint-config-npr-base', + './rules/react', + ].map(require.resolve), + rules: {}, +}; diff --git a/packages/eslint-config-npr-react/package.json b/packages/eslint-config-npr-react/package.json new file mode 100644 index 0000000000..2f17b8cc67 --- /dev/null +++ b/packages/eslint-config-npr-react/package.json @@ -0,0 +1,46 @@ +{ + "name": "@npr/eslint-config-npr-react", + "version": "16.1.2", + "description": "NPR's React style guide for JavaScript", + "main": "index.js", + "scripts": { + "prelint": "editorconfig-tools check * rules/* test/*", + "lint": "eslint --report-unused-disable-directives .", + "tests-only": "babel-tape-runner ./test/test-*.js", + "prepublish": "(in-install || eslint-find-rules --unused) && (not-in-publish || npm test)", + "pretest": "npm run --silent lint", + "test": "npm run --silent tests-only" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/npr/javascript.git" + }, + "keywords": [ + "javascript", + "style" + ], + "author": "Stephen Thompson ", + "license": "UNLICENSED", + "bugs": { + "url": "https://github.com/npr/javascript/issues" + }, + "homepage": "https://github.com/npr/javascript#readme", + "devDependencies": { + "babel-preset-airbnb": "^2.4.0", + "babel-tape-runner": "^2.0.1", + "editorconfig-tools": "^0.1.1", + "eslint": "^4.9.0", + "eslint-find-rules": "^3.1.1", + "eslint-plugin-import": "^2.7.0", + "in-publish": "^2.0.0", + "safe-publish-latest": "^1.1.1", + "tape": "^4.8.0" + }, + "engines": { + "node": ">= 6" + }, + "dependencies": { + "@npr/eslint-config-npr-base": "^12.1.0", + "eslint-config-airbnb": "^16.1.0" + } +} diff --git a/packages/eslint-config-npr-react/rules/react.js b/packages/eslint-config-npr-react/rules/react.js new file mode 100644 index 0000000000..48723be83a --- /dev/null +++ b/packages/eslint-config-npr-react/rules/react.js @@ -0,0 +1,26 @@ +module.exports = { + rules: { + // Use our 4-space indent rules for JSX as well + 'react/jsx-indent-props': ['error', 4], + 'react/jsx-indent': ['error', 4], + // We may want to require .jsx extension for files with JSX in the future + 'react/jsx-filename-extension': 'off', + // We should probably bump this up to error soon + 'react/button-has-type': 'warn', + // We are not requiring that functional components destructure their props + // right now, but we can reevaluate in the future + 'react/destructuring-assignment': 'off', + // I would love to have this turned on, but it is a little oversensitive. + // The following line is considered a violation even though the documentation + // does not even seem to be aware of a case like this: + //

Here is the title

+ // That seems to pretty clearly be one JSX expression on one line... + 'react/jsx-one-expression-per-line': 'off', + // This should be an error because violations can actually lead to bugs. + 'react/no-access-state-in-setstate': 'error', + // I'd really like to have this on, but the parser seems to flag cases even when + // there is a clear htmlFor on a label that matches up with the ID of another + // element. Turn this on when it parses better. + 'jsx-a11y/label-has-for': 'off', + } +}; diff --git a/packages/eslint-config-npr-react/test/.eslintrc b/packages/eslint-config-npr-react/test/.eslintrc new file mode 100644 index 0000000000..5808be6186 --- /dev/null +++ b/packages/eslint-config-npr-react/test/.eslintrc @@ -0,0 +1,8 @@ +{ + "rules": { + // disabled because I find it tedious to write tests while following this rule + "no-shadow": 0, + // tests uses `t` for tape + "id-length": [2, {"min": 2, "properties": "never", "exceptions": ["t"]}], + } +} diff --git a/packages/eslint-config-npr-react/test/test-base.js b/packages/eslint-config-npr-react/test/test-base.js new file mode 100644 index 0000000000..e69de29bb2