UNPKG

3.69 kBJavaScriptView Raw
1const Future = require('fluture');
2const merge = require('deepmerge');
3const clone = require('lodash.clonedeep');
4const { CLIEngine } = require('eslint');
5const { assoc, curry, evolve, keys, omit, pipe, prop, reduce } = require('ramda');
6const { basename, join } = require('path');
7
8const MODULES = join(__dirname, 'node_modules');
9const getEslintOptions = config => config.module.rule('lint').use('eslint').get('options');
10const renameKeys = curry((definition, obj) =>
11 reduce((acc, key) => assoc(definition[key] || key, obj[key], acc), {}, keys(obj)));
12const reduceToTrueKeys = reduce((acc, key) => assoc(key, true, acc), {});
13const getEslintRcConfig = pipe(
14 getEslintOptions,
15 clone,
16 assoc('useEslintrc', true),
17 omit(['failOnError', 'emitWarning', 'emitError']),
18 renameKeys({ envs: 'env', baseConfig: 'extends' }),
19 evolve({
20 extends: prop('extends'),
21 globals: reduceToTrueKeys,
22 env: reduceToTrueKeys
23 })
24);
25
26module.exports = (neutrino, opts = {}) => {
27 const isNotDev = process.env.NODE_ENV !== 'development';
28 const options = merge.all([
29 opts,
30 !opts.include && !opts.exclude ? { include: [neutrino.options.source], exclude: [neutrino.options.static] } : {}
31 ]);
32
33 neutrino.config
34 .resolve
35 .modules
36 .add(MODULES)
37 .end()
38 .end()
39 .resolveLoader
40 .modules
41 .add(MODULES)
42 .end()
43 .end()
44 .module
45 .rule('lint')
46 .test(options.test || /\.(js|jsx)$/)
47 .pre()
48 .when(options.include, rule => rule.include.merge(options.include))
49 .when(options.exclude, rule => rule.exclude.merge(options.exclude))
50 .use('eslint')
51 .loader(require.resolve('eslint-loader'))
52 .options(merge({
53 failOnError: isNotDev,
54 emitWarning: isNotDev,
55 emitError: isNotDev,
56 cwd: neutrino.options.root,
57 useEslintrc: false,
58 root: true,
59 // eslint-loader uses executeOnText(), which ignores the `extensions` setting.
60 // However it's still needed for the lint command, as it uses executeOnFiles().
61 extensions: ['js', 'jsx'],
62 plugins: ['babel'],
63 baseConfig: {},
64 envs: ['es6'],
65 parser: 'babel-eslint',
66 parserOptions: {
67 ecmaVersion: 2017,
68 sourceType: 'module',
69 ecmaFeatures: {
70 objectLiteralDuplicateProperties: false,
71 generators: true,
72 impliedStrict: true
73 }
74 },
75 settings: {},
76 globals: ['process'],
77 rules: {}
78 }, options.eslint || {}));
79
80 neutrino.register('eslintrc', () => getEslintRcConfig(neutrino.config));
81 neutrino.register('lint', () => {
82 const { fix = false } = neutrino.options.args;
83 const ignorePattern = (options.exclude || [])
84 .map(exclude => join(
85 basename(neutrino.options.source),
86 basename(exclude),
87 '**/*'
88 ));
89 const eslintConfig = merge(getEslintOptions(neutrino.config), { ignorePattern, fix });
90
91 return Future
92 .of(eslintConfig)
93 .map(options => new CLIEngine(options))
94 .chain(cli => Future.both(Future.of(cli.executeOnFiles(options.include)), Future.of(cli.getFormatter())))
95 .map(([report, formatter]) => {
96 fix && CLIEngine.outputFixes(report);
97 return [report, formatter];
98 })
99 .chain(([report, formatter]) => {
100 const errors = CLIEngine.getErrorResults(report.results);
101 const formatted = formatter(report.results);
102
103 return errors.length ? Future.reject(formatted) : Future.of(formatted);
104 });
105 });
106};