diff --git a/.travis.yml b/.travis.yml index 458da317..7e15e509 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,21 +17,27 @@ script: sudo: false env: matrix: + - 'TEST=true ESLINT=6 PACKAGE=eslint-config-airbnb-prettier' - 'TEST=true ESLINT=6 PACKAGE=eslint-config-airbnb-base' - 'TEST=true ESLINT=6 PACKAGE=eslint-config-airbnb' - 'TEST=true ESLINT=6 REACT_HOOKS=1.7 PACKAGE=eslint-config-airbnb' + - 'TEST=true ESLINT=5 PACKAGE=eslint-config-airbnb-prettier' - 'TEST=true ESLINT=5 PACKAGE=eslint-config-airbnb-base' - 'TEST=true ESLINT=5 PACKAGE=eslint-config-airbnb' - 'TEST=true ESLINT=5 REACT_HOOKS=1.7 PACKAGE=eslint-config-airbnb' matrix: fast_finish: true include: + - node_js: "lts/*" + env: PREPUBLISH=true ESLINT=6 PACKAGE=eslint-config-airbnb-prettier - node_js: "lts/*" env: PREPUBLISH=true ESLINT=6 PACKAGE=eslint-config-airbnb-base - node_js: "lts/*" env: PREPUBLISH=true ESLINT=6 PACKAGE=eslint-config-airbnb - node_js: "lts/*" env: PREPUBLISH=true ESLINT=6 REACT_HOOKS=1.7 PACKAGE=eslint-config-airbnb + - node_js: "lts/*" + env: PREPUBLISH=true ESLINT=5 PACKAGE=eslint-config-airbnb-prettier - node_js: "lts/*" env: PREPUBLISH=true ESLINT=5 PACKAGE=eslint-config-airbnb-base - node_js: "lts/*" diff --git a/packages/eslint-config-airbnb-base/.eslintrc b/packages/eslint-config-airbnb-base/.eslintrc index ab2c306f..6997edcf 100644 --- a/packages/eslint-config-airbnb-base/.eslintrc +++ b/packages/eslint-config-airbnb-base/.eslintrc @@ -1,8 +1,4 @@ { + "root": true, "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-airbnb-base/base.js b/packages/eslint-config-airbnb-base/base.js new file mode 100644 index 00000000..825456b8 --- /dev/null +++ b/packages/eslint-config-airbnb-base/base.js @@ -0,0 +1,17 @@ +module.exports = { + extends: [ + './rules/best-practices', + './rules/errors', + './rules/node', + './rules/style', + './rules/variables', + './rules/es6', + './rules/imports', + './rules/strict', + ].map(require.resolve), + parserOptions: { + ecmaVersion: 2018, + sourceType: 'module', + }, + rules: {}, +}; diff --git a/packages/eslint-config-airbnb-base/index.js b/packages/eslint-config-airbnb-base/index.js index 825456b8..0b4d9085 100644 --- a/packages/eslint-config-airbnb-base/index.js +++ b/packages/eslint-config-airbnb-base/index.js @@ -1,17 +1,3 @@ module.exports = { - extends: [ - './rules/best-practices', - './rules/errors', - './rules/node', - './rules/style', - './rules/variables', - './rules/es6', - './rules/imports', - './rules/strict', - ].map(require.resolve), - parserOptions: { - ecmaVersion: 2018, - sourceType: 'module', - }, - rules: {}, + extends: ['./base', './rules/prettier'].map(require.resolve), }; diff --git a/packages/eslint-config-airbnb-base/package.json b/packages/eslint-config-airbnb-base/package.json index 9d32da97..6999981d 100644 --- a/packages/eslint-config-airbnb-base/package.json +++ b/packages/eslint-config-airbnb-base/package.json @@ -60,19 +60,23 @@ "eslint": "^5.16.0 || ^6.7.2", "eslint-find-rules": "^3.4.0", "eslint-plugin-import": "^2.20.1", + "eslint-plugin-prettier": "^3.1.2", "in-publish": "^2.0.0", "safe-publish-latest": "^1.1.4", "tape": "^5.0.0-next.4" }, "peerDependencies": { "eslint": "^5.16.0 || ^6.7.2", - "eslint-plugin-import": "^2.20.1" + "eslint-plugin-import": "^2.20.1", + "eslint-plugin-prettier": "^3.1.2" }, "engines": { - "node": ">= 6" + "node": ">= 8.10" }, "dependencies": { "confusing-browser-globals": "^1.0.9", + "eslint-config-prettier": "^6.10.0", + "prettier": "^1.19.1", "object.assign": "^4.1.0", "object.entries": "^1.1.1" } diff --git a/packages/eslint-config-airbnb-base/rules/prettier.js b/packages/eslint-config-airbnb-base/rules/prettier.js new file mode 100644 index 00000000..b61cd6b0 --- /dev/null +++ b/packages/eslint-config-airbnb-base/rules/prettier.js @@ -0,0 +1,26 @@ +module.exports = { + extends: ['prettier'], + plugins: ['prettier'], + rules: { + // Prettier Rule + Prettier config overrides + 'prettier/prettier': [ + 'error', + { + arrowParens: 'always', + bracketSpacing: true, + jsxBracketSameLine: false, + printWidth: 100, + proseWrap: 'preserve', + requirePragma: false, + semi: true, + singleQuote: true, + tabWidth: 2, + trailingComma: 'all', + useTabs: false, + }, + { + usePrettierrc: false, + }, + ], + }, +}; diff --git a/packages/eslint-config-airbnb-prettier/.babelrc b/packages/eslint-config-airbnb-prettier/.babelrc new file mode 100644 index 00000000..e0aceaae --- /dev/null +++ b/packages/eslint-config-airbnb-prettier/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["airbnb"] +} diff --git a/packages/eslint-config-airbnb-prettier/.editorconfig b/packages/eslint-config-airbnb-prettier/.editorconfig new file mode 100644 index 00000000..49c154da --- /dev/null +++ b/packages/eslint-config-airbnb-prettier/.editorconfig @@ -0,0 +1,11 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +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-airbnb-prettier/.eslintrc b/packages/eslint-config-airbnb-prettier/.eslintrc new file mode 100644 index 00000000..ab2c306f --- /dev/null +++ b/packages/eslint-config-airbnb-prettier/.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-airbnb-prettier/LICENSE.md b/packages/eslint-config-airbnb-prettier/LICENSE.md new file mode 100644 index 00000000..69d80c02 --- /dev/null +++ b/packages/eslint-config-airbnb-prettier/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2012 Airbnb + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/eslint-config-airbnb-prettier/README.md b/packages/eslint-config-airbnb-prettier/README.md new file mode 100644 index 00000000..685e5006 --- /dev/null +++ b/packages/eslint-config-airbnb-prettier/README.md @@ -0,0 +1,99 @@ +# eslint-config-airbnb-base + +[![npm version](https://badge.fury.io/js/eslint-config-airbnb-base.svg)](http://badge.fury.io/js/eslint-config-airbnb-base) + +This package provides Airbnb's base JS .eslintrc (without React plugins) as an extensible shared config. + +## Usage + +We export two ESLint configurations for your usage. + +### eslint-config-airbnb-base + +Our default export contains all of our ESLint rules, including ECMAScript 6+. It requires `eslint` and `eslint-plugin-import`. + +1. Install the correct versions of each package, which are listed by the command: + + ```sh + npm info "eslint-config-airbnb-base@latest" peerDependencies + ``` + + If using **npm 5+**, use this shortcut + + ```sh + npx install-peerdeps --dev eslint-config-airbnb-base + ``` + + If using **yarn**, you can also use the shortcut described above if you have npm 5+ installed on your machine, as the command will detect that you are using yarn and will act accordingly. + Otherwise, run `npm info "eslint-config-airbnb-base@latest" peerDependencies` to list the peer dependencies and versions, then run `yarn add --dev @` for each listed peer dependency. + + + If using **npm < 5**, Linux/OSX users can run + + ```sh + ( + export PKG=eslint-config-airbnb-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 eslint-config-airbnb-base eslint@^#.#.# eslint-plugin-import@^#.#.# + ``` + + If using **npm < 5**, 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 eslint-config-airbnb-base + ``` + + The cli will produce and run a command like: + + ```sh + npm install --save-dev eslint-config-airbnb-base eslint@^#.#.# eslint-plugin-import@^#.#.# + ``` + +2. Add `"extends": "airbnb-base"` to your .eslintrc. + +### eslint-config-airbnb-base/legacy + +Lints ES5 and below. Requires `eslint` and `eslint-plugin-import`. + +1. Install the correct versions of each package, which are listed by the command: + + ```sh + npm info "eslint-config-airbnb-base@latest" peerDependencies + ``` + + Linux/OSX users can run + ```sh + ( + export PKG=eslint-config-airbnb-base; + npm info "$PKG" peerDependencies --json | command sed 's/[\{\},]//g ; s/: /@/g' | xargs npm install --save-dev "$PKG" + ) + ``` + + Which produces and runs a command like: + + ```sh + npm install --save-dev eslint-config-airbnb-base eslint@^3.0.1 eslint-plugin-import@^1.10.3 + ``` + +2. Add `"extends": "airbnb-base/legacy"` to your .eslintrc + +See [Airbnb's overarching ESLint config](https://npmjs.com/eslint-config-airbnb), [Airbnb's JavaScript styleguide](https://github.com/airbnb/javascript), and the [ESlint config docs](https://eslint.org/docs/user-guide/configuring#extending-configuration-files) for more information. + +### eslint-config-airbnb-base/whitespace + +This entry point only errors on whitespace rules and sets all other rules to warnings. View the list of whitespace rules [here](https://github.com/airbnb/javascript/blob/master/packages/eslint-config-airbnb-base/whitespace.js). + +## Improving this config + +Consider adding test cases if you're making complicated rules changes, like anything involving regexes. Perhaps in a distant future, we could use literate programming to structure our README as test cases for our .eslintrc? + +You can run tests with `npm test`. + +You can make sure this module lints with itself using `npm run lint`. diff --git a/packages/eslint-config-airbnb-prettier/index.js b/packages/eslint-config-airbnb-prettier/index.js new file mode 100644 index 00000000..97a477df --- /dev/null +++ b/packages/eslint-config-airbnb-prettier/index.js @@ -0,0 +1,7 @@ +module.exports = { + extends: [ + 'eslint-config-airbnb-base', + './rules/prettier', + ].map(require.resolve), + rules: {} +}; diff --git a/packages/eslint-config-airbnb-prettier/package.json b/packages/eslint-config-airbnb-prettier/package.json new file mode 100644 index 00000000..874cbfaf --- /dev/null +++ b/packages/eslint-config-airbnb-prettier/package.json @@ -0,0 +1,77 @@ +{ + "name": "eslint-config-airbnb-prettier", + "version": "13.2.0", + "description": "Airbnb's base JS ESLint config, following our styleguide", + "main": "index.js", + "scripts": { + "prelint": "editorconfig-tools check * rules/* test/*", + "lint": "eslint --report-unused-disable-directives .", + "pretests-only": "node ./test/requires", + "tests-only": "babel-tape-runner ./test/test-*.js", + "prepublish": "(in-install || eslint-find-rules --unused) && (not-in-publish || npm test) && safe-publish-latest", + "pretest": "npm run --silent lint", + "test": "npm run --silent tests-only", + "pretravis": ":", + "travis": "npm run --silent test", + "posttravis": ":" + }, + "repository": { + "type": "git", + "url": "https://github.com/airbnb/javascript" + }, + "keywords": [ + "eslint", + "eslintconfig", + "config", + "airbnb", + "javascript", + "styleguide", + "es2015", + "es2016", + "es2017", + "es2018" + ], + "author": "Sharmila Jesupaul (jesupaul.com)", + "contributors": [ + { + "name": "Jordan Harband", + "email": "ljharb@gmail.com", + "url": "http://ljharb.codes" + } + ], + "license": "MIT", + "bugs": { + "url": "https://github.com/airbnb/javascript/issues" + }, + "homepage": "https://github.com/airbnb/javascript", + "devDependencies": { + "babel-preset-airbnb": "^2.6.0", + "babel-tape-runner": "^2.0.1", + "editorconfig-tools": "^0.1.1", + "eslint": "^4.19.1 || ^5.3.0", + "eslint-config-prettier": "^6.10.0", + "eslint-find-rules": "^3.4.0", + "eslint-plugin-import": "^2.18.0", + "eslint-plugin-prettier": "^3.1.2", + "in-publish": "^2.0.0", + "prettier": "^1.19.1", + "safe-publish-latest": "^1.1.2", + "tape": "^4.11.0" + }, + "peerDependencies": { + "eslint": "^5.16.0 || ^6.7.2", + "eslint-config-prettier": "^6.10.0", + "eslint-plugin-import": "^2.20.1", + "eslint-plugin-prettier": "^3.1.2", + "prettier": "^1.19.1" + }, + "engines": { + "node": ">= 6" + }, + "dependencies": { + "eslint-config-airbnb-base": "^14.0.0", + "confusing-browser-globals": "^1.0.7", + "object.assign": "^4.1.0", + "object.entries": "^1.1.0" + } +} diff --git a/packages/eslint-config-airbnb-prettier/rules/prettier.js b/packages/eslint-config-airbnb-prettier/rules/prettier.js new file mode 100644 index 00000000..d6d4833d --- /dev/null +++ b/packages/eslint-config-airbnb-prettier/rules/prettier.js @@ -0,0 +1,29 @@ +/* eslint-disable no-async-promise-executor, no-useless-catch, no-misleading-character-class */ + +module.exports = { + extends: ['prettier', 'prettier/react'], + plugins: ['prettier'], + // View link below for react rules documentation + // https://github.com/yannickcr/eslint-plugin-react#list-of-supported-rules + rules: { + // Prettier Rule + Prettier config overrides + 'prettier/prettier': [ + 'error', + { + arrowParens: 'always', + bracketSpacing: true, + jsxBracketSameLine: false, + printWidth: 100, + proseWrap: 'preserve', + requirePragma: false, + semi: true, + singleQuote: true, + tabWidth: 2, + trailingComma: 'all', + useTabs: false, + }, + { + usePrettierrc: false, + }, + ], + }, diff --git a/packages/eslint-config-airbnb-prettier/test/requires.js b/packages/eslint-config-airbnb-prettier/test/requires.js new file mode 100644 index 00000000..69615d3c --- /dev/null +++ b/packages/eslint-config-airbnb-prettier/test/requires.js @@ -0,0 +1,11 @@ +/* eslint strict: 0, global-require: 0 */ + +'use strict'; + +const test = require('tape'); + +test('all entry points parse', (t) => { + t.doesNotThrow(() => require('..'), 'index does not throw'); + + t.end(); +}); diff --git a/packages/eslint-config-airbnb-prettier/test/test-prettier.js b/packages/eslint-config-airbnb-prettier/test/test-prettier.js new file mode 100644 index 00000000..47390b2f --- /dev/null +++ b/packages/eslint-config-airbnb-prettier/test/test-prettier.js @@ -0,0 +1,76 @@ +import test from 'tape'; +import { CLIEngine } from 'eslint'; +import eslintrc from '..'; +import prettierRule from '../rules/prettier'; + +const cli = new CLIEngine({ + useEslintrc: false, + baseConfig: eslintrc, + + rules: { + // It is okay to import devDependencies in tests. + 'import/no-extraneous-dependencies': [2, { devDependencies: true }], + // this doesn't matter for tests + 'lines-between-class-members': 0, + }, +}); + +function lint(text) { + // @see https://eslint.org/docs/developer-guide/nodejs-api.html#executeonfiles + // @see https://eslint.org/docs/developer-guide/nodejs-api.html#executeontext + const linter = cli.executeOnText(text); + return linter.results[0]; +} + + +test('validate react prop order', (t) => { + t.test('make sure our eslintrc has the prettier plugin and config as dependencies', (t) => { + t.plan(2); + t.deepEqual(prettierRule.plugins, ['prettier']); + t.deepEqual(prettierRule.extends, ['prettier', 'prettier/react']); + }); + + t.test('passes a good component', (t) => { + t.plan(3); + const result = lint(` + function HelloWorld({ + greeting = "hello", + greeted = '"World"', + silent = false, + onMouseOver + }) { + if (!greeting) { + return null; + } + + // TODO: Don't use random in render + let num = Math.floor(Math.random() * 1e7) + .toString() + .replace(/\.\d+/gi, ""); + + return ( +
+ + {greeting.slice(0, 1).toUpperCase() + greeting.slice(1).toLowerCase()} + + {greeting.endsWith(",") ? ( + " " + ) : ( + ", " + )} + {greeted} + {silent ? "." : "!"} +
+ ); + } +`); + + t.notOk(result.warningCount, 'no warnings'); + t.deepEquals(result.messages, [], 'no messages in results'); + t.notOk(result.errorCount, 'no errors'); + }); +});