Skip to content
This repository was archived by the owner on Feb 18, 2024. It is now read-only.

Commit cea73d6

Browse files
authored
Morph Neutrino API and CLI into middleware injectors for external CLI tools (#852)
__Edited to reflect current state of PR.__ Fixes #708. Fixes #736. Fixes #870. Fixes #842. Closes #773. Closes #839. Closes #849. Outstanding issues to file: - [x] Update docs to reflect these API changes (#896) - [x] ~~Determine need for config-loading bins instead of forcing config file boilerplate (still unsure if we should do this because of IDE integration)~~ - [x] Modify stats to produce friendlier build output (#897) This patch is a work-in-progress, and is quite the overhaul of Neutrino into something different. Right now there is no change to the documentation, which I will change if we decide to move forward with this. This patch morphs Neutrino into a "preset engine" for transforming managed webpack configuration into a format that can be consumed by other CLI tools. No longer would we wrap external CLI tools like webpack, eslint, jest, karma, mocha, stylelint, etc. Instead, Neutrino could augment these tools by exposing methods for returning usable configuration. This approach gives the user control of their CLI tool back, and they can programmatically invoke Neutrino to load their configuration, and override however they like: ```bash webpack --mode production ``` ```js // webpack.config.js const neutrino = require('neutrino'); // Load with no config overrides: module.exports = neutrino().webpack(); // Load with config overrides: module.exports = neutrino().webpack(config => { // return whatever config you want }); // Load with no config overrides using low-level API: module.exports = neutrino().output('webpack'); // Load with config overrides using low-level API: module.exports = neutrino().output('webpack', config => { // return whatever config you want }); ``` The same works for ESLint and friends too (except for mocha, since it has no config file we can be required from): ```bash eslint src ``` ```js // .eslintrc.js const neutrino = require('neutrino'); // Load with no config overrides: module.exports = neutrino().eslintrc(); // Load with config overrides: module.exports = neutrino().eslintrc(config => { // return whatever config you want, like manipulating rules, etc. }); ``` --- - This approach is predicated on having middleware defined in `.neutrinorc.js`, which shouldn't change at all. - ~~This does do away with a bunch of CLI functionality like `--inspect` or `--use`, but those become less useful if we are just an interface to external CLI tools. We can discuss potentially bringing those back.~~ This is now back in the form of the CLI with `neutrino --inspect`. - ~~All environment variables are gone. We rely only on webpack's `mode` value.~~ We internally only use webpack's `mode` value, but still set `process.env.NODE_ENV` if not set based on the mode, or defaulted to `production`. - ~~There is no longer a create-project tool, since we cannot force users to choose between competing tools like webpack-cli or webpack-command, and webpack-dev-server or webpack-serve.~~ The create-project tool is back, and we opt for `webpack-cli` and `webpack-dev-server` as opinions for now. I'm sure there are other major things I am neglecting to mention. Let's discuss! I see so many benefits to this, including reduced maintenance burden and the ability to allow people to use Neutrino without dumping their existing tools. cc: @mozilla-neutrino/core-contributors
1 parent 30dde2b commit cea73d6

File tree

134 files changed

+2373
-3305
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

134 files changed

+2373
-3305
lines changed

.eslintrc.js

+2-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
const { Neutrino } = require('./packages/neutrino');
1+
const neutrino = require('./packages/neutrino');
22

3-
module.exports = Neutrino({ root: __dirname })
4-
.use('.neutrinorc.js')
5-
.call('eslintrc');
3+
module.exports = neutrino(require('./.neutrinorc')).eslintrc();

package.json

+10-3
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
"docs:bootstrap": "pip install -r docs/requirements.txt",
2020
"docs:serve": "mkdocs serve",
2121
"link:all": "lerna exec yarn link",
22-
"lint": "node packages/neutrino/bin/neutrino lint",
22+
"lint": "eslint packages .neutrinorc.js",
2323
"precommit": "lint-staged",
2424
"release": "lerna publish --force-publish=*",
2525
"release:preview": "lerna publish --force-publish=* --skip-git --skip-npm",
@@ -31,7 +31,7 @@
3131
"validate:eslintrc:airbnb-base": "eslint --no-eslintrc --print-config . -c ./packages/airbnb-base/eslintrc.js > /dev/null",
3232
"validate:eslintrc:standardjs": "eslint --no-eslintrc --print-config . -c ./packages/standardjs/eslintrc.js > /dev/null",
3333
"validate:eslintrc": "yarn validate:eslintrc:eslint && yarn validate:eslintrc:airbnb-base && yarn validate:eslintrc:airbnb && yarn validate:eslintrc:standardjs && yarn validate:eslintrc:root",
34-
"version": "test -v SKIP_CHANGELOG || yarn changelog --package && git add CHANGELOG.md"
34+
"version": "[[ -z \"$SKIP_CHANGELOG\" ]] || yarn changelog --package && git add CHANGELOG.md"
3535
},
3636
"devDependencies": {
3737
"auto-changelog": "^1.4.6",
@@ -40,12 +40,19 @@
4040
"eslint-config-prettier": "^2.9.0",
4141
"eslint-plugin-prettier": "^2.6.0",
4242
"husky": "^0.14.3",
43+
"jest": "^22.4.3",
44+
"karma": "^2.0.2",
45+
"karma-cli": "^1.0.1",
4346
"lerna": "^2.11.0",
4447
"lint-staged": "^7.0.5",
48+
"mocha": "^5.2.0",
4549
"nyc": "^11.7.1",
4650
"prettier": "^1.12.1",
51+
"stylelint": "^8.4.0",
4752
"verdaccio": "3.0.0-beta.10",
48-
"verdaccio-memory": "^1.0.1"
53+
"verdaccio-memory": "^1.0.1",
54+
"webpack": "^4.8.3",
55+
"webpack-dev-server": "^3.1.4"
4956
},
5057
"lint-staged": {
5158
"*.js": [

packages/airbnb-base/eslintrc.js

+2-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
const { Neutrino } = require('../neutrino');
1+
const neutrino = require('../neutrino');
22

3-
module.exports = Neutrino({ cwd: __dirname })
4-
.use(require('.')) // eslint-disable-line global-require
5-
.call('eslintrc');
3+
module.exports = neutrino(require('.')).eslintrc();

packages/airbnb-base/package.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,12 @@
2424
},
2525
"dependencies": {
2626
"@neutrinojs/eslint": "^8.2.0",
27-
"eslint": "^4.19.1",
2827
"eslint-config-airbnb-base": "^12.1.0",
2928
"eslint-plugin-import": "^2.11.0"
3029
},
3130
"peerDependencies": {
32-
"neutrino": "^8.0.0"
31+
"eslint": "^4.0.0",
32+
"neutrino": "^8.0.0",
33+
"webpack": "^4.0.0"
3334
}
3435
}
+23-10
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import test from 'ava';
2-
import { Neutrino } from 'neutrino';
2+
import Neutrino from '../../neutrino/Neutrino';
3+
import neutrino from '../../neutrino';
34

45
const mw = () => require('..');
56
const options = { eslint: { rules: { semi: false } } };
@@ -9,37 +10,49 @@ test('loads preset', t => {
910
});
1011

1112
test('uses preset', t => {
12-
t.notThrows(() => Neutrino().use(mw()));
13+
t.notThrows(() => new Neutrino().use(mw()));
1314
});
1415

1516
test('uses with options', t => {
16-
t.notThrows(() => Neutrino().use(mw(), options));
17+
t.notThrows(() => new Neutrino().use(mw(), options));
1718
});
1819

1920
test('instantiates', t => {
20-
const api = Neutrino();
21+
const api = new Neutrino();
2122

2223
api.use(mw());
2324

2425
t.notThrows(() => api.config.toConfig());
2526
});
2627

2728
test('instantiates with options', t => {
28-
const api = Neutrino();
29+
const api = new Neutrino();
2930

3031
api.use(mw(), options);
3132

3233
t.notThrows(() => api.config.toConfig());
3334
});
3435

35-
test('exposes lint command', t => {
36-
const api = Neutrino();
36+
test('exposes eslintrc output handler', t => {
37+
const api = new Neutrino();
3738

3839
api.use(mw());
3940

40-
t.is(typeof api.commands.lint, 'function');
41+
const handler = api.outputHandlers.get('eslintrc');
42+
43+
t.is(typeof handler, 'function');
44+
});
45+
46+
test('exposes eslintrc config from output', t => {
47+
const config = neutrino(mw()).output('eslintrc');
48+
49+
t.is(typeof config, 'object');
50+
});
51+
52+
test('exposes eslintrc method', t => {
53+
t.is(typeof neutrino(mw()).eslintrc, 'function');
4154
});
4255

43-
test('exposes eslintrc config', t => {
44-
t.is(typeof Neutrino().use(mw()).call('eslintrc'), 'object');
56+
test('exposes eslintrc config from method', t => {
57+
t.is(typeof neutrino(mw()).eslintrc(), 'object');
4558
});

packages/airbnb/eslintrc.js

+2-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
const { Neutrino } = require('../neutrino');
1+
const neutrino = require('../neutrino');
22

3-
module.exports = Neutrino({ cwd: __dirname })
4-
.use(require('.')) // eslint-disable-line global-require
5-
.call('eslintrc');
3+
module.exports = neutrino(require('.')).eslintrc();

packages/airbnb/package.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,14 @@
2424
},
2525
"dependencies": {
2626
"@neutrinojs/eslint": "^8.2.0",
27-
"eslint": "^4.19.1",
2827
"eslint-config-airbnb": "^16.1.0",
2928
"eslint-plugin-import": "^2.11.0",
3029
"eslint-plugin-jsx-a11y": "^6.0.3",
3130
"eslint-plugin-react": "^7.7.0"
3231
},
3332
"peerDependencies": {
34-
"neutrino": "^8.0.0"
33+
"eslint": "^4.0.0",
34+
"neutrino": "^8.0.0",
35+
"webpack": "^4.0.0"
3536
}
3637
}

packages/airbnb/test/airbnb_test.js

+23-10
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import test from 'ava';
2-
import { Neutrino } from 'neutrino';
2+
import Neutrino from '../../neutrino/Neutrino';
3+
import neutrino from '../../neutrino';
34

45
const mw = () => require('..');
56
const options = { eslint: { rules: { semi: false } } };
@@ -9,37 +10,49 @@ test('loads preset', t => {
910
});
1011

1112
test('uses preset', t => {
12-
t.notThrows(() => Neutrino().use(mw()));
13+
t.notThrows(() => new Neutrino().use(mw()));
1314
});
1415

1516
test('uses with options', t => {
16-
t.notThrows(() => Neutrino().use(mw(), options));
17+
t.notThrows(() => new Neutrino().use(mw(), options));
1718
});
1819

1920
test('instantiates', t => {
20-
const api = Neutrino();
21+
const api = new Neutrino();
2122

2223
api.use(mw());
2324

2425
t.notThrows(() => api.config.toConfig());
2526
});
2627

2728
test('instantiates with options', t => {
28-
const api = Neutrino();
29+
const api = new Neutrino();
2930

3031
api.use(mw(), options);
3132

3233
t.notThrows(() => api.config.toConfig());
3334
});
3435

35-
test('exposes lint command', t => {
36-
const api = Neutrino();
36+
test('exposes eslintrc output handler', t => {
37+
const api = new Neutrino();
3738

3839
api.use(mw());
3940

40-
t.is(typeof api.commands.lint, 'function');
41+
const handler = api.outputHandlers.get('eslintrc');
42+
43+
t.is(typeof handler, 'function');
44+
});
45+
46+
test('exposes eslintrc config from output', t => {
47+
const config = neutrino(mw()).output('eslintrc');
48+
49+
t.is(typeof config, 'object');
50+
});
51+
52+
test('exposes eslintrc method', t => {
53+
t.is(typeof neutrino(mw()).eslintrc, 'function');
4154
});
4255

43-
test('exposes eslintrc config', t => {
44-
t.is(typeof Neutrino().use(mw()).call('eslintrc'), 'object');
56+
test('exposes eslintrc config from method', t => {
57+
t.is(typeof neutrino(mw()).eslintrc(), 'object');
4558
});

packages/banner/package.json

+2-4
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,8 @@
2121
"npm": ">=5.4.0",
2222
"yarn": ">=1.2.1"
2323
},
24-
"dependencies": {
25-
"webpack": "^4.7.0"
26-
},
2724
"peerDependencies": {
28-
"neutrino": "^8.0.0"
25+
"neutrino": "^8.0.0",
26+
"webpack": "^4.0.0"
2927
}
3028
}

packages/banner/test/middleware_test.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import test from 'ava';
2-
import { Neutrino } from 'neutrino';
2+
import Neutrino from '../../neutrino/Neutrino';
33

44
const mw = () => require('..');
55
const options = { raw: false, entryOnly: false };
@@ -9,23 +9,23 @@ test('loads middleware', t => {
99
});
1010

1111
test('uses middleware', t => {
12-
t.notThrows(() => Neutrino().use(mw()));
12+
t.notThrows(() => new Neutrino().use(mw()));
1313
});
1414

1515
test('uses with options', t => {
16-
t.notThrows(() => Neutrino().use(mw(), options));
16+
t.notThrows(() => new Neutrino().use(mw(), options));
1717
});
1818

1919
test('instantiates', t => {
20-
const api = Neutrino();
20+
const api = new Neutrino();
2121

2222
api.use(mw());
2323

2424
t.notThrows(() => api.config.toConfig());
2525
});
2626

2727
test('instantiates with options', t => {
28-
const api = Neutrino();
28+
const api = new Neutrino();
2929

3030
api.use(mw(), options);
3131

packages/clean/package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"clean-webpack-plugin": "^0.1.19"
2626
},
2727
"peerDependencies": {
28-
"neutrino": "^8.0.0"
28+
"neutrino": "^8.0.0",
29+
"webpack": "^4.0.0"
2930
}
3031
}

packages/clean/test/middleware_test.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import test from 'ava';
2-
import { Neutrino } from 'neutrino';
2+
import Neutrino from '../../neutrino/Neutrino';
33

44
const mw = () => require('..');
55
const options = { paths: ['sample'], root: __dirname };
@@ -9,23 +9,23 @@ test('loads middleware', t => {
99
});
1010

1111
test('uses middleware', t => {
12-
t.notThrows(() => Neutrino().use(mw()));
12+
t.notThrows(() => new Neutrino().use(mw()));
1313
});
1414

1515
test('uses with options', t => {
16-
t.notThrows(() => Neutrino().use(mw(), options));
16+
t.notThrows(() => new Neutrino().use(mw(), options));
1717
});
1818

1919
test('instantiates', t => {
20-
const api = Neutrino();
20+
const api = new Neutrino();
2121

2222
api.use(mw());
2323

2424
t.notThrows(() => api.config.toConfig());
2525
});
2626

2727
test('instantiates with options', t => {
28-
const api = Neutrino();
28+
const api = new Neutrino();
2929

3030
api.use(mw(), options);
3131

packages/compile-loader/index.js

+17-8
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,25 @@
11
const babelMerge = require('babel-merge');
22

3-
module.exports = (neutrino, options = {}) => neutrino.config.module
4-
.rule(options.ruleId || 'compile')
3+
module.exports = (neutrino, options = {}) => {
4+
neutrino.config.module
5+
.rule(options.ruleId || 'compile')
56
.test(options.test || neutrino.regexFromExtensions())
67
.when(options.include, rule => rule.include.merge(options.include))
78
.when(options.exclude, rule => rule.exclude.merge(options.exclude))
89
.use(options.useId || 'babel')
9-
.loader(require.resolve('babel-loader'))
10-
.options({
11-
cacheDirectory: true,
12-
babelrc: false,
13-
...(options.babel || {})
14-
});
10+
.loader(require.resolve('babel-loader'))
11+
.options({
12+
cacheDirectory: true,
13+
babelrc: false,
14+
...(options.babel || {})
15+
});
16+
17+
neutrino.register('babel', (neutrino, override) => override(
18+
neutrino.config.module
19+
.rule('compile')
20+
.use('babel')
21+
.get('options')
22+
));
23+
};
1524

1625
module.exports.merge = babelMerge;

packages/compile-loader/package.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@
2525
"dependencies": {
2626
"@babel/core": "^7.0.0-beta.46",
2727
"babel-loader": "^8.0.0-beta.2",
28-
"babel-merge": "^1.1.1",
29-
"webpack": "^4.7.0"
28+
"babel-merge": "^1.1.1"
3029
},
3130
"peerDependencies": {
32-
"neutrino": "^8.0.0"
31+
"neutrino": "^8.0.0",
32+
"webpack": "^4.0.0"
3333
}
3434
}

0 commit comments

Comments
 (0)