Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

extension whitelist feature #143

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,28 @@ You may use `env` as an alias for `envs` (just don't specify both).
If you're using your custom linter globally (you installed it with `-g`), then you also need to
install `babel-eslint` globally with `npm install babel-eslint -g`.

### Extension whitelist

`standard-engine` can be set up to look for whitelisted
[ESLint shareable config](http://eslint.org/docs/developer-guide/shareable-configs)s
to extend the rules from.

For example, an `opts` object may include:

```js
{
whitelist: ['pocketlint-node', 'pocketlint-ava']
}
```

Then, if any of the packages `eslint-config-pocketlint-node`/`eslint-config-pocketlint-ava`
are found to be installed, the rules will extend from them, as well.

This feature allows users of this package to curate a set of official extensions.

Intentionally, this is not read from `package.json`. If it were, it would not have been
a whitelist feature.

## API Usage

### `standardEngine.lintText(text, [opts], callback)`
Expand All @@ -215,6 +237,7 @@ be provided:
globals: [], // custom global variables to declare
plugins: [], // custom eslint plugins
envs: [], // custom eslint environment
whitelist: []// shareable configs to extend from if installed
parser: '' // custom js parser (e.g. babel-eslint)
}
```
Expand Down Expand Up @@ -253,6 +276,7 @@ Lint the provided `files` globs. An `opts` object may be provided:
globals: [], // custom global variables to declare
plugins: [], // custom eslint plugins
envs: [], // custom eslint environment
whitelist: []// shareable configs to extend from if installed
parser: '' // custom js parser (e.g. babel-eslint)
}
```
Expand All @@ -276,6 +300,7 @@ This is the full set of options accepted by the above APIs. Not all options make
globals: [], // custom global variables to declare
plugins: [], // custom eslint plugins
envs: [], // custom eslint environment
whitelist: []// shareable configs to extend from if installed
parser: '' // custom js parser (e.g. babel-eslint)
}
```
Expand Down
18 changes: 18 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,24 @@ function Linter (opts) {
plugins: [],
useEslintrc: false
}, opts.eslintConfig)

if (opts.whitelist) {
let original = self.eslintConfig.extends
let ekstends = original
ekstends = typeof ekstends === 'string' ? [ekstends] : ekstends || []
opts.whitelist.forEach((shareable) => {
let installed = true
try {
require('eslint-config-' + shareable)
} catch (e) {
installed = false
}
if (installed) {
ekstends.push(shareable)
}
})
self.eslintConfig.extends = ekstends.length ? ekstends : original
}
}

/**
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
},
"devDependencies": {
"babel-eslint": "^7.1.1",
"clone": "^2.1.0",
"cross-spawn": "^5.0.0",
"eslint": "^3.0.1",
"eslint-config-standard": "^6.0.0-beta.0",
Expand All @@ -24,6 +25,8 @@
"eslint-plugin-react": "^6.0.0",
"eslint-plugin-standard": "^2.0.0",
"mkdirp": "^0.5.0",
"mock-require": "^2.0.0",
"require-uncached": "^1.0.3",
"run-parallel-limit": "^1.0.1",
"standard": "*",
"standard-packages": "^3.0.14",
Expand Down
77 changes: 72 additions & 5 deletions test/api.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
var eslint = require('eslint')
var Linter = require('../').linter
var test = require('tape')
const mock = require('mock-require')
const requireUncached = require('require-uncached')
const clone = require('clone')

const defaultOpts = {
eslint,
eslintConfig: require('../tmp/standard/options').eslintConfig
}

const getLinter = () => requireUncached('../').linter

function getStandard () {
return new Linter({
eslint: eslint,
eslintConfig: require('../tmp/standard/options').eslintConfig
})
const Linter = getLinter()
return new Linter(defaultOpts)
}

test('api: lintFiles', function (t) {
Expand Down Expand Up @@ -44,3 +51,63 @@ test('api: parseOpts -- avoid self.eslintConfig global mutation', function (t) {
t.deepEqual(opts.globals, ['what'])
t.deepEqual(standard.eslintConfig.globals, [])
})

const defaultWhitelist = ['foo', 'bar']
const defaultWhitelistPkgs = ['eslint-config-foo', 'eslint-config-bar']

test('api: whitelist -- provided whitelist, no existing extends', function (t) {
t.plan(1)
const opts = clone(defaultOpts)
opts.whitelist = clone(defaultWhitelist)
defaultWhitelistPkgs.forEach(name => mock(name, {}))
const Linter = getLinter()
let linter = new Linter(opts)
t.deepEqual(linter.eslintConfig.extends, defaultWhitelist)
defaultWhitelistPkgs.forEach(name => mock.stop(name))
})

test('api: whitelist -- provided whitelist, string existing extends', function (t) {
t.plan(1)
const opts = clone(defaultOpts)
opts.whitelist = clone(defaultWhitelist)
opts.eslintConfig.extends = 'baz'
defaultWhitelistPkgs.forEach(name => mock(name, {}))
const Linter = getLinter()
let linter = new Linter(opts)
const expected = ['baz'].concat(defaultWhitelist)
t.deepEqual(linter.eslintConfig.extends, expected)
defaultWhitelistPkgs.forEach(name => mock.stop(name))
})

test('api: whitelist -- provided whitelist, multiple existing extends', function (t) {
t.plan(1)
const opts = clone(defaultOpts)
opts.whitelist = clone(defaultWhitelist)
opts.eslintConfig.extends = ['baz', 'yad']
defaultWhitelistPkgs.forEach(name => mock(name, {}))
const Linter = getLinter()
let linter = new Linter(opts)
const expected = ['baz', 'yad'].concat(defaultWhitelist)
t.deepEqual(linter.eslintConfig.extends, expected)
defaultWhitelistPkgs.forEach(name => mock.stop(name))
})

test('api: whitelist -- packages not installed', function (t) {
t.plan(1)
const opts = clone(defaultOpts)
opts.whitelist = clone(defaultWhitelist)
const Linter = getLinter()
let linter = new Linter(opts)
t.equals(linter.eslintConfig.extends, undefined)
})

test('api: whitelist -- some packages installed', function (t) {
t.plan(1)
const opts = clone(defaultOpts)
opts.whitelist = clone(defaultWhitelist)
const Linter = getLinter()
mock(defaultWhitelistPkgs[0], {})
let linter = new Linter(opts)
t.deepEqual(linter.eslintConfig.extends, defaultWhitelist.slice(0, 1))
mock.stop(defaultWhitelistPkgs[0])
})