Skip to content

Commit

Permalink
Merge pull request #1 from yola/initial-plugin-structure
Browse files Browse the repository at this point in the history
Initial plugin structure
  • Loading branch information
RusinovAnton authored Nov 7, 2017
2 parents 18edb8f + b1e91d5 commit 8af3bde
Show file tree
Hide file tree
Showing 16 changed files with 940 additions and 1 deletion.
15 changes: 15 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module.exports = {
extends: 'airbnb-base',
env: {
browser: false,
jest: true,
node: true,
},

// This rule turned off in favor of support node@4
// Error: Block-scoped declarations (let, const, function, class)
// not yet supported outside strict mode
rules: {
strict: 'off'
}
};
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules/
*.log
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# 0.0.1

* Initial plugin version
49 changes: 48 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,48 @@
# eslint-plugin-yola
# eslint-plugin-yola

An eslint plugin to lint Yola's javascript.
ES6 code style is based on [Airbnb Javascript Style Guide](https://github.com/airbnb/javascript).

## Installation
Install eslint and plugin package with npm as development dependencies:
```
npm install --save-dev eslint eslint-plugin-yola
```

## Usage
There are two configurations available:
- **yola/base** - based on `eslint-config-airbnb-base` - used to lint ES6 code
- **yola/react** - based on `eslint-config-airbnb` - used to lint React components code

## Configuration
Create config file in the root of your project `.eslintrc.js` that extends `eslint-plugin-yola`:
```javascript
module.exports = { extends: 'plugin:yola/base' }
```
Note: configs are depend on eslint plugins `import`, `react`, `jsx-a11y`. In order to avoid usage conflicts, rules that extracted from these plugins are prefixed with `yola/`. So if you need to override some rules you must use prefix. eg override of `react/no-string-refs`
```javascript
module.exports = {
extends: 'plugin:yola/react',
rules: {
'yola/react/no-string-refs': 'warn',
}
}
```
## Run
With npm scripts:
```
"lint": "eslint ./src/**/*.js"
```

## FAQ

### Why eslint?

We were using jshint and found it too restrictive. We wanted to extend from other style guides.
Another reason to have eslint is a JSX support.

### Why a plugin?

The plugin solves problem of shareable eslint configs that depend on eslint plugins, which described in https://github.com/eslint/eslint/issues/3458.
Basically, to resolve eslint plugins dependencies this plugin wraps `eslint-config-airbnb`, `eslint-config-airbnb-base` rules with extracted rules from plugins.
The approach was inspired by [eslint-plugin-react-app](https://github.com/mmazzarolo/eslint-plugin-react-app/)
15 changes: 15 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
'use strict';

const baseConfig = require('eslint-config-airbnb-base');
const extendedConfig = require('eslint-config-airbnb');

const createConfig = require('./src/create-config');
const rules = require('./src/rules');

module.exports = {
configs: {
base: createConfig(baseConfig),
react: createConfig(extendedConfig),
},
rules
};
41 changes: 41 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"name": "eslint-plugin-yola",
"version": "0.0.1",
"main": "index.js",
"scripts": {
"lint": "eslint -c .eslintrc.js ./**/*.js",
"test": "jest",
"prepublish": "npm run lint && npm run test"
},
"repository": {
"type": "git",
"url": "git+https://github.com/yola/eslint-plugin-yola.git"
},
"author": "Yola Engineering <[email protected]>",
"license": "MIT",
"bugs": {
"url": "https://github.com/yola/eslint-plugin-yola/issues"
},
"homepage": "https://github.com/yola/eslint-plugin-yola#readme",
"dependencies": {
"babel-eslint": "8.x",
"eslint-config-airbnb": "16.x",
"eslint-config-airbnb-base": "12.x",
"eslint-plugin-import": "2.x",
"eslint-plugin-jsx-a11y": "6.x",
"eslint-plugin-react": "7.x",
"lodash": "^4.17.4"
},
"devDependencies": {
"eslint": "4.x",
"jest": "^21.2.1"
},
"peerDependencies": {
"eslint": "4.x"
},
"jest": {
"testMatch": [
"**/tests/*-spec.js?(x)"
]
}
}
47 changes: 47 additions & 0 deletions src/create-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
'use strict';

const merge = require('lodash/merge');
const pick = require('lodash/pick');

const getConfigBase = require('./get-config-base');
const pluginsDependencies = require('./plugins-dependencies');
const propsToPick = require('./props-to-pick');

const isPluginRule = ruleName => pluginsDependencies
.some(plugin => ruleName.indexOf(`${plugin}/`) === 0);

// To avoid conflicts, rules from dependency plugins are scoped by prefixing
const getConfigRules = config => Object
.keys(config.rules)
.reduce((rules, ruleName) => {
const result = Object.assign({}, rules);
const key = isPluginRule(ruleName) ? `yola/${ruleName}` : ruleName;

result[key] = config.rules[ruleName];

return result;
}, {});

const extendConfig = (config, ext) => {
let extension = ext;

if (typeof extension === 'string') {
extension = require(ext); // eslint-disable-line global-require, import/no-dynamic-require
}

const extensionRules = { rules: getConfigRules(extension) };

return merge(config, pick(extension, propsToPick), extensionRules);
};

const createConfig = (extension) => {
let config = extendConfig(getConfigBase(), extension);

if (extension.extends && extension.extends.length) {
config = extension.extends.reduce(extendConfig, config);
}

return config;
};

module.exports = createConfig;
4 changes: 4 additions & 0 deletions src/get-config-base.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = () => ({
parser: require.resolve('babel-eslint'),
plugins: ['yola'],
});
12 changes: 12 additions & 0 deletions src/plugins-dependencies.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
'use strict';

const pkg = require('../package.json');
const reduce = require('lodash/reduce');

module.exports = reduce(pkg.dependencies, (plugins, version, dependency) => {
if (dependency.indexOf('eslint-plugin') === 0) {
plugins.push(dependency.replace('eslint-plugin-', ''));
}

return plugins;
}, []);
1 change: 1 addition & 0 deletions src/props-to-pick.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = ['env', 'parserOptions', 'root', 'settings'];
15 changes: 15 additions & 0 deletions src/rules.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
'use strict';

const pluginsDependencies = require('./plugins-dependencies');

module.exports = pluginsDependencies.reduce((rules, pluginName) => {
const plugin = require(`eslint-plugin-${pluginName}`); // eslint-disable-line global-require, import/no-dynamic-require
const result = Object.assign(rules);

Object.keys(plugin.rules)
.forEach((ruleName) => {
result[`${pluginName}/${ruleName}`] = plugin.rules[ruleName];
});

return result;
}, {});
5 changes: 5 additions & 0 deletions tests/__mocks__/extension-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
rules: {
'extends-rule': true,
}
};
Loading

0 comments on commit 8af3bde

Please sign in to comment.