diff --git a/packages/eslint-config-airbnb/.eslintrc b/packages/eslint-config-airbnb/.eslintrc new file mode 100644 index 00000000..cbf450f0 --- /dev/null +++ b/packages/eslint-config-airbnb/.eslintrc @@ -0,0 +1,11 @@ +{ + "extends": "airbnb", + "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, + // disabled because I find it tedious to write tests while following this + // rule + "no-shadow": 0 + } +} diff --git a/packages/eslint-config-airbnb/README.md b/packages/eslint-config-airbnb/README.md index 6edb34f3..3bc20dc4 100644 --- a/packages/eslint-config-airbnb/README.md +++ b/packages/eslint-config-airbnb/README.md @@ -4,9 +4,26 @@ This package provides Airbnb's .eslintrc as an extensible shared config. ## Usage +### With React Style + 1. `npm install --save-dev eslint-config-airbnb babel-eslint eslint-plugin-react` -2. add `"extends": "eslint-config-airbnb"` to your .eslintrc +2. add `"extends": "airbnb"` to your .eslintrc + +### Without React Style + +1. `npm install --save-dev eslint-config-airbnb babel-eslint ` +2. add `"extends": "airbnb/base"` to your .eslintrc See [Airbnb's Javascript styleguide](https://github.com/airbnb/javascript) and the [ESlint config docs](http://eslint.org/docs/user-guide/configuring#extending-configuration-files) for more information. + +## 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/base/index.js b/packages/eslint-config-airbnb/base/index.js new file mode 100644 index 00000000..65174d85 --- /dev/null +++ b/packages/eslint-config-airbnb/base/index.js @@ -0,0 +1,173 @@ +module.exports = { + 'parser': 'babel-eslint', // https://github.com/babel/babel-eslint + 'env': { // http://eslint.org/docs/user-guide/configuring.html#specifying-environments + 'browser': true, // browser global variables + 'node': true // Node.js global variables and Node.js-specific rules + }, + 'ecmaFeatures': { + 'arrowFunctions': true, + 'blockBindings': true, + 'classes': true, + 'defaultParams': true, + 'destructuring': true, + 'forOf': true, + 'generators': false, + 'modules': true, + 'objectLiteralComputedProperties': true, + 'objectLiteralDuplicateProperties': false, + 'objectLiteralShorthandMethods': true, + 'objectLiteralShorthandProperties': true, + 'spread': true, + 'superInFunctions': true, + 'templateStrings': true, + 'jsx': true + }, + 'rules': { + /** + * Strict mode + */ + // babel inserts 'use strict'; for us + 'strict': [2, 'never'], // http://eslint.org/docs/rules/strict + + /** + * ES6 + */ + 'no-var': 2, // http://eslint.org/docs/rules/no-var + 'prefer-const': 2, // http://eslint.org/docs/rules/prefer-const + + /** + * Variables + */ + 'no-shadow': 2, // http://eslint.org/docs/rules/no-shadow + 'no-shadow-restricted-names': 2, // http://eslint.org/docs/rules/no-shadow-restricted-names + 'no-unused-vars': [2, { // http://eslint.org/docs/rules/no-unused-vars + 'vars': 'local', + 'args': 'after-used' + }], + 'no-use-before-define': 2, // http://eslint.org/docs/rules/no-use-before-define + + /** + * Possible errors + */ + 'comma-dangle': [2, 'always-multiline'], // http://eslint.org/docs/rules/comma-dangle + 'no-cond-assign': [2, 'always'], // http://eslint.org/docs/rules/no-cond-assign + 'no-console': 1, // http://eslint.org/docs/rules/no-console + 'no-debugger': 1, // http://eslint.org/docs/rules/no-debugger + 'no-alert': 1, // http://eslint.org/docs/rules/no-alert + 'no-constant-condition': 1, // http://eslint.org/docs/rules/no-constant-condition + 'no-dupe-keys': 2, // http://eslint.org/docs/rules/no-dupe-keys + 'no-duplicate-case': 2, // http://eslint.org/docs/rules/no-duplicate-case + 'no-empty': 2, // http://eslint.org/docs/rules/no-empty + 'no-ex-assign': 2, // http://eslint.org/docs/rules/no-ex-assign + 'no-extra-boolean-cast': 0, // http://eslint.org/docs/rules/no-extra-boolean-cast + 'no-extra-semi': 2, // http://eslint.org/docs/rules/no-extra-semi + 'no-func-assign': 2, // http://eslint.org/docs/rules/no-func-assign + 'no-inner-declarations': 2, // http://eslint.org/docs/rules/no-inner-declarations + 'no-invalid-regexp': 2, // http://eslint.org/docs/rules/no-invalid-regexp + 'no-irregular-whitespace': 2, // http://eslint.org/docs/rules/no-irregular-whitespace + 'no-obj-calls': 2, // http://eslint.org/docs/rules/no-obj-calls + 'no-sparse-arrays': 2, // http://eslint.org/docs/rules/no-sparse-arrays + 'no-unreachable': 2, // http://eslint.org/docs/rules/no-unreachable + 'use-isnan': 2, // http://eslint.org/docs/rules/use-isnan + 'block-scoped-var': 2, // http://eslint.org/docs/rules/block-scoped-var + + /** + * Best practices + */ + 'consistent-return': 2, // http://eslint.org/docs/rules/consistent-return + 'curly': [2, 'multi-line'], // http://eslint.org/docs/rules/curly + 'default-case': 2, // http://eslint.org/docs/rules/default-case + 'dot-notation': [2, { // http://eslint.org/docs/rules/dot-notation + 'allowKeywords': true + }], + 'eqeqeq': 2, // http://eslint.org/docs/rules/eqeqeq + 'guard-for-in': 2, // http://eslint.org/docs/rules/guard-for-in + 'no-caller': 2, // http://eslint.org/docs/rules/no-caller + 'no-else-return': 2, // http://eslint.org/docs/rules/no-else-return + 'no-eq-null': 2, // http://eslint.org/docs/rules/no-eq-null + 'no-eval': 2, // http://eslint.org/docs/rules/no-eval + 'no-extend-native': 2, // http://eslint.org/docs/rules/no-extend-native + 'no-extra-bind': 2, // http://eslint.org/docs/rules/no-extra-bind + 'no-fallthrough': 2, // http://eslint.org/docs/rules/no-fallthrough + 'no-floating-decimal': 2, // http://eslint.org/docs/rules/no-floating-decimal + 'no-implied-eval': 2, // http://eslint.org/docs/rules/no-implied-eval + 'no-lone-blocks': 2, // http://eslint.org/docs/rules/no-lone-blocks + 'no-loop-func': 2, // http://eslint.org/docs/rules/no-loop-func + 'no-multi-str': 2, // http://eslint.org/docs/rules/no-multi-str + 'no-native-reassign': 2, // http://eslint.org/docs/rules/no-native-reassign + 'no-new': 2, // http://eslint.org/docs/rules/no-new + 'no-new-func': 2, // http://eslint.org/docs/rules/no-new-func + 'no-new-wrappers': 2, // http://eslint.org/docs/rules/no-new-wrappers + 'no-octal': 2, // http://eslint.org/docs/rules/no-octal + 'no-octal-escape': 2, // http://eslint.org/docs/rules/no-octal-escape + 'no-param-reassign': 2, // http://eslint.org/docs/rules/no-param-reassign + 'no-proto': 2, // http://eslint.org/docs/rules/no-proto + 'no-redeclare': 2, // http://eslint.org/docs/rules/no-redeclare + 'no-return-assign': 2, // http://eslint.org/docs/rules/no-return-assign + 'no-script-url': 2, // http://eslint.org/docs/rules/no-script-url + 'no-self-compare': 2, // http://eslint.org/docs/rules/no-self-compare + 'no-sequences': 2, // http://eslint.org/docs/rules/no-sequences + 'no-throw-literal': 2, // http://eslint.org/docs/rules/no-throw-literal + 'no-with': 2, // http://eslint.org/docs/rules/no-with + 'radix': 2, // http://eslint.org/docs/rules/radix + 'vars-on-top': 2, // http://eslint.org/docs/rules/vars-on-top + 'wrap-iife': [2, 'any'], // http://eslint.org/docs/rules/wrap-iife + 'yoda': 2, // http://eslint.org/docs/rules/yoda + + /** + * Style + */ + 'indent': [2, 2], // http://eslint.org/docs/rules/indent + 'brace-style': [ + 2, // http://eslint.org/docs/rules/brace-style + '1tbs', { + 'allowSingleLine': true + } + ], + 'quotes': [ + 2, 'single', 'avoid-escape' // http://eslint.org/docs/rules/quotes + ], + 'camelcase': [2, { // http://eslint.org/docs/rules/camelcase + 'properties': 'never' + }], + 'comma-spacing': [2, { // http://eslint.org/docs/rules/comma-spacing + 'before': false, + 'after': true + }], + 'comma-style': [2, 'last'], // http://eslint.org/docs/rules/comma-style + 'eol-last': 2, // http://eslint.org/docs/rules/eol-last + 'func-names': 1, // http://eslint.org/docs/rules/func-names + 'key-spacing': [2, { // http://eslint.org/docs/rules/key-spacing + 'beforeColon': false, + 'afterColon': true + }], + 'new-cap': [2, { // http://eslint.org/docs/rules/new-cap + 'newIsCap': true + }], + 'no-multiple-empty-lines': [2, { // http://eslint.org/docs/rules/no-multiple-empty-lines + 'max': 2 + }], + 'no-nested-ternary': 2, // http://eslint.org/docs/rules/no-nested-ternary + 'no-new-object': 2, // http://eslint.org/docs/rules/no-new-object + 'no-spaced-func': 2, // http://eslint.org/docs/rules/no-spaced-func + 'no-trailing-spaces': 2, // http://eslint.org/docs/rules/no-trailing-spaces + 'no-extra-parens': [2, 'functions'], // http://eslint.org/docs/rules/no-extra-parens + 'no-underscore-dangle': 0, // http://eslint.org/docs/rules/no-underscore-dangle + 'one-var': [2, 'never'], // http://eslint.org/docs/rules/one-var + 'padded-blocks': [2, 'never'], // http://eslint.org/docs/rules/padded-blocks + 'semi': [2, 'always'], // http://eslint.org/docs/rules/semi + 'semi-spacing': [2, { // http://eslint.org/docs/rules/semi-spacing + 'before': false, + 'after': true + }], + 'space-after-keywords': 2, // http://eslint.org/docs/rules/space-after-keywords + 'space-before-blocks': 2, // http://eslint.org/docs/rules/space-before-blocks + 'space-before-function-paren': [2, 'never'], // http://eslint.org/docs/rules/space-before-function-paren + 'space-infix-ops': 2, // http://eslint.org/docs/rules/space-infix-ops + 'space-return-throw-case': 2, // http://eslint.org/docs/rules/space-return-throw-case + 'spaced-comment': [2, 'always', {// http://eslint.org/docs/rules/spaced-comment + 'exceptions': ['-', '+'], + 'markers': ['=', '!'] // space here to support sprockets directives + }], + } +}; diff --git a/packages/eslint-config-airbnb/index.js b/packages/eslint-config-airbnb/index.js index 0d87e4ad..3cda9b0f 100644 --- a/packages/eslint-config-airbnb/index.js +++ b/packages/eslint-config-airbnb/index.js @@ -1,221 +1,13 @@ -module.exports = { - 'parser': 'babel-eslint', // https://github.com/babel/babel-eslint - 'plugins': [ - 'react' // https://github.com/yannickcr/eslint-plugin-react - ], - 'env': { // http://eslint.org/docs/user-guide/configuring.html#specifying-environments - 'browser': true, // browser global variables - 'node': true // Node.js global variables and Node.js-specific rules - }, - 'ecmaFeatures': { - 'arrowFunctions': true, - 'blockBindings': true, - 'classes': true, - 'defaultParams': true, - 'destructuring': true, - 'forOf': true, - 'generators': false, - 'modules': true, - 'objectLiteralComputedProperties': true, - 'objectLiteralDuplicateProperties': false, - 'objectLiteralShorthandMethods': true, - 'objectLiteralShorthandProperties': true, - 'spread': true, - 'superInFunctions': true, - 'templateStrings': true, - 'jsx': true - }, - 'rules': { -/** - * Strict mode - */ - // babel inserts 'use strict'; for us - 'strict': [2, 'never'], // http://eslint.org/docs/rules/strict +const reactRules = require('./react'); +const base = require('./base'); -/** - * ES6 - */ - 'no-var': 2, // http://eslint.org/docs/rules/no-var - 'prefer-const': 2, // http://eslint.org/docs/rules/prefer-const +// clone this so we aren't mutating a module +const eslintrc = JSON.parse(JSON.stringify(base)); -/** - * Variables - */ - 'no-shadow': 2, // http://eslint.org/docs/rules/no-shadow - 'no-shadow-restricted-names': 2, // http://eslint.org/docs/rules/no-shadow-restricted-names - 'no-unused-vars': [2, { // http://eslint.org/docs/rules/no-unused-vars - 'vars': 'local', - 'args': 'after-used' - }], - 'no-use-before-define': 2, // http://eslint.org/docs/rules/no-use-before-define +// manually merge in React rules +eslintrc.plugins = reactRules.plugins; +Object.keys(reactRules.rules).forEach(function assignRule(ruleId) { + eslintrc.rules[ruleId] = reactRules.rules[ruleId]; +}); -/** - * Possible errors - */ - 'comma-dangle': [2, 'always-multiline'], // http://eslint.org/docs/rules/comma-dangle - 'no-cond-assign': [2, 'always'], // http://eslint.org/docs/rules/no-cond-assign - 'no-console': 1, // http://eslint.org/docs/rules/no-console - 'no-debugger': 1, // http://eslint.org/docs/rules/no-debugger - 'no-alert': 1, // http://eslint.org/docs/rules/no-alert - 'no-constant-condition': 1, // http://eslint.org/docs/rules/no-constant-condition - 'no-dupe-keys': 2, // http://eslint.org/docs/rules/no-dupe-keys - 'no-duplicate-case': 2, // http://eslint.org/docs/rules/no-duplicate-case - 'no-empty': 2, // http://eslint.org/docs/rules/no-empty - 'no-ex-assign': 2, // http://eslint.org/docs/rules/no-ex-assign - 'no-extra-boolean-cast': 0, // http://eslint.org/docs/rules/no-extra-boolean-cast - 'no-extra-semi': 2, // http://eslint.org/docs/rules/no-extra-semi - 'no-func-assign': 2, // http://eslint.org/docs/rules/no-func-assign - 'no-inner-declarations': 2, // http://eslint.org/docs/rules/no-inner-declarations - 'no-invalid-regexp': 2, // http://eslint.org/docs/rules/no-invalid-regexp - 'no-irregular-whitespace': 2, // http://eslint.org/docs/rules/no-irregular-whitespace - 'no-obj-calls': 2, // http://eslint.org/docs/rules/no-obj-calls - 'no-sparse-arrays': 2, // http://eslint.org/docs/rules/no-sparse-arrays - 'no-unreachable': 2, // http://eslint.org/docs/rules/no-unreachable - 'use-isnan': 2, // http://eslint.org/docs/rules/use-isnan - 'block-scoped-var': 2, // http://eslint.org/docs/rules/block-scoped-var - -/** - * Best practices - */ - 'consistent-return': 2, // http://eslint.org/docs/rules/consistent-return - 'curly': [2, 'multi-line'], // http://eslint.org/docs/rules/curly - 'default-case': 2, // http://eslint.org/docs/rules/default-case - 'dot-notation': [2, { // http://eslint.org/docs/rules/dot-notation - 'allowKeywords': true - }], - 'eqeqeq': 2, // http://eslint.org/docs/rules/eqeqeq - 'guard-for-in': 2, // http://eslint.org/docs/rules/guard-for-in - 'no-caller': 2, // http://eslint.org/docs/rules/no-caller - 'no-else-return': 2, // http://eslint.org/docs/rules/no-else-return - 'no-eq-null': 2, // http://eslint.org/docs/rules/no-eq-null - 'no-eval': 2, // http://eslint.org/docs/rules/no-eval - 'no-extend-native': 2, // http://eslint.org/docs/rules/no-extend-native - 'no-extra-bind': 2, // http://eslint.org/docs/rules/no-extra-bind - 'no-fallthrough': 2, // http://eslint.org/docs/rules/no-fallthrough - 'no-floating-decimal': 2, // http://eslint.org/docs/rules/no-floating-decimal - 'no-implied-eval': 2, // http://eslint.org/docs/rules/no-implied-eval - 'no-lone-blocks': 2, // http://eslint.org/docs/rules/no-lone-blocks - 'no-loop-func': 2, // http://eslint.org/docs/rules/no-loop-func - 'no-multi-str': 2, // http://eslint.org/docs/rules/no-multi-str - 'no-native-reassign': 2, // http://eslint.org/docs/rules/no-native-reassign - 'no-new': 2, // http://eslint.org/docs/rules/no-new - 'no-new-func': 2, // http://eslint.org/docs/rules/no-new-func - 'no-new-wrappers': 2, // http://eslint.org/docs/rules/no-new-wrappers - 'no-octal': 2, // http://eslint.org/docs/rules/no-octal - 'no-octal-escape': 2, // http://eslint.org/docs/rules/no-octal-escape - 'no-param-reassign': 2, // http://eslint.org/docs/rules/no-param-reassign - 'no-proto': 2, // http://eslint.org/docs/rules/no-proto - 'no-redeclare': 2, // http://eslint.org/docs/rules/no-redeclare - 'no-return-assign': 2, // http://eslint.org/docs/rules/no-return-assign - 'no-script-url': 2, // http://eslint.org/docs/rules/no-script-url - 'no-self-compare': 2, // http://eslint.org/docs/rules/no-self-compare - 'no-sequences': 2, // http://eslint.org/docs/rules/no-sequences - 'no-throw-literal': 2, // http://eslint.org/docs/rules/no-throw-literal - 'no-with': 2, // http://eslint.org/docs/rules/no-with - 'radix': 2, // http://eslint.org/docs/rules/radix - 'vars-on-top': 2, // http://eslint.org/docs/rules/vars-on-top - 'wrap-iife': [2, 'any'], // http://eslint.org/docs/rules/wrap-iife - 'yoda': 2, // http://eslint.org/docs/rules/yoda - -/** - * Style - */ - 'indent': [2, 2], // http://eslint.org/docs/rules/indent - 'brace-style': [2, // http://eslint.org/docs/rules/brace-style - '1tbs', { - 'allowSingleLine': true - }], - 'quotes': [ - 2, 'single', 'avoid-escape' // http://eslint.org/docs/rules/quotes - ], - 'camelcase': [2, { // http://eslint.org/docs/rules/camelcase - 'properties': 'never' - }], - 'comma-spacing': [2, { // http://eslint.org/docs/rules/comma-spacing - 'before': false, - 'after': true - }], - 'comma-style': [2, 'last'], // http://eslint.org/docs/rules/comma-style - 'eol-last': 2, // http://eslint.org/docs/rules/eol-last - 'func-names': 1, // http://eslint.org/docs/rules/func-names - 'key-spacing': [2, { // http://eslint.org/docs/rules/key-spacing - 'beforeColon': false, - 'afterColon': true - }], - 'new-cap': [2, { // http://eslint.org/docs/rules/new-cap - 'newIsCap': true - }], - 'no-multiple-empty-lines': [2, { // http://eslint.org/docs/rules/no-multiple-empty-lines - 'max': 2 - }], - 'no-nested-ternary': 2, // http://eslint.org/docs/rules/no-nested-ternary - 'no-new-object': 2, // http://eslint.org/docs/rules/no-new-object - 'no-spaced-func': 2, // http://eslint.org/docs/rules/no-spaced-func - 'no-trailing-spaces': 2, // http://eslint.org/docs/rules/no-trailing-spaces - 'no-extra-parens': [2, 'functions'], // http://eslint.org/docs/rules/no-extra-parens - 'no-underscore-dangle': 0, // http://eslint.org/docs/rules/no-underscore-dangle - 'one-var': [2, 'never'], // http://eslint.org/docs/rules/one-var - 'padded-blocks': [2, 'never'], // http://eslint.org/docs/rules/padded-blocks - 'semi': [2, 'always'], // http://eslint.org/docs/rules/semi - 'semi-spacing': [2, { // http://eslint.org/docs/rules/semi-spacing - 'before': false, - 'after': true - }], - 'space-after-keywords': 2, // http://eslint.org/docs/rules/space-after-keywords - 'space-before-blocks': 2, // http://eslint.org/docs/rules/space-before-blocks - 'space-before-function-paren': [2, 'never'], // http://eslint.org/docs/rules/space-before-function-paren - 'space-infix-ops': 2, // http://eslint.org/docs/rules/space-infix-ops - 'space-return-throw-case': 2, // http://eslint.org/docs/rules/space-return-throw-case - 'spaced-comment': [2, 'always', {// http://eslint.org/docs/rules/spaced-comment - 'exceptions': ['-', '+'], - 'markers': ['=', '!'] // space here to support sprockets directives - }], - -/** - * JSX style - */ - 'react/display-name': 0, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/display-name.md - 'react/jsx-boolean-value': 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-boolean-value.md - 'react/jsx-quotes': [2, 'double'], // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-quotes.md - 'react/jsx-no-undef': 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-undef.md - 'react/jsx-sort-props': 0, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-sort-props.md - 'react/jsx-sort-prop-types': 0, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-sort-prop-types.md - 'react/jsx-uses-react': 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-uses-react.md - 'react/jsx-uses-vars': 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-uses-vars.md - 'react/no-did-mount-set-state': [2, 'allow-in-func'], // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-did-mount-set-state.md - 'react/no-did-update-set-state': 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-did-update-set-state.md - 'react/no-multi-comp': 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-multi-comp.md - 'react/no-unknown-property': 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-unknown-property.md - 'react/prop-types': 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prop-types.md - 'react/react-in-jsx-scope': 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/react-in-jsx-scope.md - 'react/self-closing-comp': 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/self-closing-comp.md - 'react/wrap-multilines': 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/wrap-multilines.md - 'react/sort-comp': [2, { // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/sort-comp.md - 'order': [ - 'displayName', - 'propTypes', - 'contextTypes', - 'childContextTypes', - 'mixins', - 'statics', - 'defaultProps', - 'constructor', - 'getDefaultProps', - 'getInitialState', - 'getChildContext', - 'componentWillMount', - 'componentDidMount', - 'componentWillReceiveProps', - 'shouldComponentUpdate', - 'componentWillUpdate', - 'componentDidUpdate', - 'componentWillUnmount', - '/^on.+$/', - 'everything-else', - '/^get.+$/', - '/^render.+$/', - 'render' - ] - }] - } -}; +module.exports = eslintrc; diff --git a/packages/eslint-config-airbnb/node_modules/eslint-config-airbnb b/packages/eslint-config-airbnb/node_modules/eslint-config-airbnb new file mode 120000 index 00000000..a96aa0ea --- /dev/null +++ b/packages/eslint-config-airbnb/node_modules/eslint-config-airbnb @@ -0,0 +1 @@ +.. \ No newline at end of file diff --git a/packages/eslint-config-airbnb/package.json b/packages/eslint-config-airbnb/package.json index a0825185..f277fa24 100644 --- a/packages/eslint-config-airbnb/package.json +++ b/packages/eslint-config-airbnb/package.json @@ -4,7 +4,8 @@ "description": "Airbnb's ESLint config, following our styleguide", "main": "index.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "lint": "./node_modules/.bin/eslint .", + "test": "./node_modules/.bin/babel-tape-runner ./test/test-*.js" }, "repository": { "type": "git", @@ -23,5 +24,13 @@ "bugs": { "url": "https://github.com/airbnb/javascript/issues" }, - "homepage": "https://github.com/airbnb/javascript" + "homepage": "https://github.com/airbnb/javascript", + "devDependencies": { + "babel-eslint": "4.0.10", + "babel-tape-runner": "1.2.0", + "eslint": "1.1.0", + "eslint-plugin-react": "3.2.3", + "react": "0.13.3", + "tape": "4.2.0" + } } diff --git a/packages/eslint-config-airbnb/react.js b/packages/eslint-config-airbnb/react.js new file mode 100644 index 00000000..a0ab17f3 --- /dev/null +++ b/packages/eslint-config-airbnb/react.js @@ -0,0 +1,36 @@ +module.exports = { + 'plugins': [ + 'react' // https://github.com/yannickcr/eslint-plugin-react + ], + rules: { + /** + * JSX style + */ + 'react/display-name': 0, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/display-name.md + 'react/jsx-boolean-value': 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-boolean-value.md + 'react/jsx-quotes': [2, 'double'], // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-quotes.md + 'react/jsx-no-undef': 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-undef.md + 'react/jsx-sort-props': 0, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-sort-props.md + 'react/jsx-sort-prop-types': 0, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-sort-prop-types.md + 'react/jsx-uses-react': 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-uses-react.md + 'react/jsx-uses-vars': 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-uses-vars.md + 'react/no-did-mount-set-state': [2, 'allow-in-func'], // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-did-mount-set-state.md + 'react/no-did-update-set-state': 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-did-update-set-state.md + 'react/no-multi-comp': 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-multi-comp.md + 'react/no-unknown-property': 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-unknown-property.md + 'react/prop-types': 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prop-types.md + 'react/react-in-jsx-scope': 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/react-in-jsx-scope.md + 'react/self-closing-comp': 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/self-closing-comp.md + 'react/wrap-multilines': 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/wrap-multilines.md + 'react/sort-comp': [2, { // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/sort-comp.md + 'order': [ + 'lifecycle', + '/^on.+$/', + '/^(get|set)(?!(InitialState$|DefaultProps$|ChildContext$)).+$/', + 'everything-else', + '/^render.+$/', + 'render' + ] + }] + } +}; diff --git a/packages/eslint-config-airbnb/test/test-base.js b/packages/eslint-config-airbnb/test/test-base.js new file mode 100644 index 00000000..ecf06cdf --- /dev/null +++ b/packages/eslint-config-airbnb/test/test-base.js @@ -0,0 +1,13 @@ +import test from 'tape'; +import base from '../base'; + +test('base: does not reference react', t => { + t.plan(2); + + t.notOk(base.plugins, 'plugins is unspecified'); + + // scan rules for react/ and fail if any exist + const reactRuleIds = Object.keys(base.rules) + .filter(ruleId => ruleId.indexOf('react/') === 0); + t.deepEquals(reactRuleIds, [], 'there are no react/ rules'); +}); diff --git a/packages/eslint-config-airbnb/test/test-react-order.js b/packages/eslint-config-airbnb/test/test-react-order.js new file mode 100644 index 00000000..9ef23f67 --- /dev/null +++ b/packages/eslint-config-airbnb/test/test-react-order.js @@ -0,0 +1,83 @@ +import test from 'tape'; +import { CLIEngine } from 'eslint'; +import eslintrc from '../'; + +const cli = new CLIEngine({ + useEslintrc: false, + baseConfig: eslintrc, +}); + +function lint(text) { + // @see http://eslint.org/docs/developer-guide/nodejs-api.html#executeonfiles + // @see http://eslint.org/docs/developer-guide/nodejs-api.html#executeontext + return cli.executeOnText(text).results[0]; +} + +function wrapComponent(body) { + return ` +import React from 'react'; +export default class MyComponent extends React.Component { +${body} +} +`; +} + +test('validate react prop order', t => { + t.test('make sure our eslintrc has React linting dependencies', t => { + t.plan(2); + t.equal(eslintrc.parser, 'babel-eslint', 'uses babel-eslint'); + t.equal(eslintrc.plugins[0], 'react', 'uses eslint-plugin-react'); + }); + + t.test('passes a good component', t => { + t.plan(3); + const result = lint(wrapComponent(` + componentWillMount() { } + componentDidMount() { } + setFoo() { } + getFoo() { } + setBar() { } + someMethod() { } + renderDogs() { } + render() { return
; } +`)); + + t.notOk(result.warningCount, 'no warnings'); + t.notOk(result.errorCount, 'no errors'); + t.deepEquals(result.messages, [], 'no messages in results'); + }); + + t.test('order: when random method is first', t => { + t.plan(2); + const result = lint(wrapComponent(` + someMethod() { } + componentWillMount() { } + componentDidMount() { } + setFoo() { } + getFoo() { } + setBar() { } + renderDogs() { } + render() { return ; } +`)); + + t.ok(result.errorCount, 'fails'); + t.equal(result.messages[0].ruleId, 'react/sort-comp', 'fails due to sort'); + }); + + t.test('order: when random method after lifecycle methods', t => { + t.plan(2); + const result = lint(wrapComponent(` + componentWillMount() { } + componentDidMount() { } + someMethod() { } + setFoo() { } + getFoo() { } + setBar() { } + renderDogs() { } + render() { return ; } +`)); + + t.ok(result.errorCount, 'fails'); + t.equal(result.messages[0].ruleId, 'react/sort-comp', 'fails due to sort'); + }); +});