diff --git a/docs/webpack-chain.md b/docs/webpack-chain.md index 19a888fd4..f5d23c414 100644 --- a/docs/webpack-chain.md +++ b/docs/webpack-chain.md @@ -5,8 +5,10 @@ webpack configurations. This documentation corresponds to v4 of webpack-chain, which Neutrino utilizes. -_Note: while webpack-chain is utilized extensively in Neutrino, the package is completely -standalone and can be used by any project. See the webpack-chain repo for standalone documentation._ +_Note: while webpack-chain is utilized extensively in Neutrino, the package is +completely standalone and can be used by any project. +See the [webpack-chain](https://github.com/neutrinojs/webpack-chain) repository +for standalone documentation._ ## Introduction @@ -94,7 +96,7 @@ module.exports = (neutrino) => { .loader('babel-loader') .options({ presets: [ - ['babel-preset-es2015', { modules: false }] + ['@babel/preset-env', { modules: false }] ] }); @@ -107,11 +109,13 @@ module.exports = (neutrino) => { ## ChainedMap -One of the core API interfaces in Neutrino configuration is a `ChainedMap`. A `ChainedMap` operates -similar to a JavaScript Map, with some conveniences for chaining and generating configuration. -If a property is marked as being a `ChainedMap`, it will have an API and methods as described below: +One of the core API interfaces in Neutrino configuration is a `ChainedMap`. A +`ChainedMap` operates similar to a JavaScript Map, with some conveniences for +chaining and generating configuration. If a property is marked as being a +`ChainedMap`, it will have an API and methods as described below: -**Unless stated otherwise, these methods will return the `ChainedMap`, allowing you to chain these methods.** +**Unless stated otherwise, these methods will return the `ChainedMap`, allowing +you to chain these methods.** ```js // Remove all entries from a Map. @@ -131,6 +135,15 @@ delete(key) get(key) ``` +```js +// Fetch the value from a Map located at the corresponding key. +// If the key is missing, the key is set to the result of function fn. +// key: * +// fn: Function () -> value +// returns: value +getOrCompute(key, fn) +``` + ```js // Set a value on the Map stored at the `key` location. // key: * @@ -139,7 +152,8 @@ set(key, value) ``` ```js -// Returns `true` or `false` based on whether a Map as has a value set at a particular key. +// Returns `true` or `false` based on whether a Map as has a value set at a +// particular key. // key: * // returns: Boolean has(key) @@ -191,11 +205,13 @@ when(condition, whenTruthy, whenFalsy) ## ChainedSet -Another of the core API interfaces in Neutrino configuration is a `ChainedSet`. A `ChainedSet` operates -similar to a JavaScript Set, with some conveniences for chaining and generating configuration. -If a property is marked as being a `ChainedSet`, it will have an API and methods as described below: +Another of the core API interfaces in Neutrino configuration is a `ChainedSet`. A +`ChainedSet` operates similar to a JavaScript Set, with some conveniences for +chaining and generating configuration. If a property is marked as being a +`ChainedSet`, it will have an API and methods as described below: -**Unless stated otherwise, these methods will return the `ChainedSet`, allowing you to chain these methods.** +**Unless stated otherwise, these methods will return the `ChainedSet`, allowing +you to chain these methods.** ```js // Add/append a value to the end of a Set. @@ -271,8 +287,8 @@ neutrino.config.devServer.hot(true); neutrino.config.devServer.set('hot', true); ``` -A shorthand method is chainable, so calling it will return the original instance, -allowing you to continue to chain. +A shorthand method is chainable, so calling it will return the original +instance, allowing you to continue to chain. ### Config @@ -283,8 +299,8 @@ If you are familiar with jQuery, `.end()` works similarly. All API calls will return the API instance at the current context unless otherwise specified. This is so you may chain API calls continuously if desired. -For details on the specific values that are valid for all shorthand and low-level methods, -please refer to their corresponding name in the +For details on the specific values that are valid for all shorthand and +low-level methods, please refer to their corresponding name in the [webpack docs hierarchy](https://webpack.js.org/configuration/). ```js @@ -469,31 +485,8 @@ neutrino.config.resolve.mainFiles #### Config resolveLoader -```js -neutrino.config.resolveLoader : ChainedMap -``` - -#### Config resolveLoader extensions - -```js -neutrino.config.resolveLoader.extensions : ChainedSet - -neutrino.config.resolveLoader.extensions - .add(value) - .prepend(value) - .clear() -``` - -#### Config resolveLoader modules - -```js -neutrino.config.resolveLoader.modules : ChainedSet - -neutrino.config.resolveLoader.modules - .add(value) - .prepend(value) - .clear() -``` +The API for `neutrino.config.resolveLoader` is identical to `neutrino.config.resolve` with +the following additions: #### Config resolveLoader moduleExtensions @@ -569,16 +562,19 @@ _NOTE: Do not use `new` to create the plugin, as this will be done for you._ ```js neutrino.config .plugin(name) - .use(WebpackPlugin, args) + .use(WebpackPlugin, args) // Examples + neutrino.config .plugin('hot') - .use(webpack.HotModuleReplacementPlugin); + .use(webpack.HotModuleReplacementPlugin); +// Plugins can also be specified by their path, allowing the expensive require()s to be +// skipped in cases where the plugin or webpack configuration won't end up being used. neutrino.config .plugin('env') - .use(webpack.EnvironmentPlugin, ['NODE_ENV']); + .use(require.resolve('webpack/lib/EnvironmentPlugin'), [{ 'VAR': false }]); ``` #### Config plugins: modify arguments @@ -586,12 +582,12 @@ neutrino.config ```js neutrino.config .plugin(name) - .tap(args => newArgs) + .tap(args => newArgs) // Example neutrino.config .plugin('env') - .tap(args => [...args, 'SECRET_KEY']); + .tap(args => [...args, 'SECRET_KEY']); ``` #### Config plugins: modify instantiation @@ -599,7 +595,7 @@ neutrino.config ```js neutrino.config .plugin(name) - .init((Plugin, args) => new Plugin(...args)); + .init((Plugin, args) => new Plugin(...args)); ``` #### Config plugins: removing @@ -610,14 +606,14 @@ neutrino.config.plugins.delete(name) #### Config plugins: ordering before -Specify that the current `plugin` context should operate before another named `plugin`. -You cannot use both `.before()` and `.after()` on the same plugin. +Specify that the current `plugin` context should operate before another named +`plugin`. You cannot use both `.before()` and `.after()` on the same plugin. ```js neutrino.config .plugin(name) .before(otherName) - + // Example neutrino.config @@ -631,8 +627,8 @@ neutrino.config #### Config plugins: ordering after -Specify that the current `plugin` context should operate after another named `plugin`. -You cannot use both `.before()` and `.after()` on the same plugin. +Specify that the current `plugin` context should operate after another named +`plugin`. You cannot use both `.before()` and `.after()` on the same plugin. ```js neutrino.config @@ -664,7 +660,7 @@ _NOTE: Do not use `new` to create the plugin, as this will be done for you._ ```js neutrino.config.resolve .plugin(name) - .use(WebpackPlugin, args) + .use(WebpackPlugin, args) ``` #### Config resolve plugins: modify arguments @@ -680,7 +676,7 @@ neutrino.config.resolve ```js neutrino.config.resolve .plugin(name) - .init((Plugin, args) => new Plugin(...args)) + .init((Plugin, args) => new Plugin(...args)) ``` #### Config resolve plugins: removing @@ -691,8 +687,9 @@ neutrino.config.resolve.plugins.delete(name) #### Config resolve plugins: ordering before -Specify that the current `plugin` context should operate before another named `plugin`. -You cannot use both `.before()` and `.after()` on the same resolve plugin. +Specify that the current `plugin` context should operate before another named +`plugin`. You cannot use both `.before()` and `.after()` on the same resolve +plugin. ```js neutrino.config.resolve @@ -712,8 +709,9 @@ neutrino.config.resolve #### Config resolve plugins: ordering after -Specify that the current `plugin` context should operate after another named `plugin`. -You cannot use both `.before()` and `.after()` on the same resolve plugin. +Specify that the current `plugin` context should operate after another named +`plugin`. You cannot use both `.before()` and `.after()` on the same resolve +plugin. ```js neutrino.config.resolve @@ -845,7 +843,7 @@ neutrino.config.module .rule('compile') .use('babel') .loader('babel-loader') - .options({ presets: ['babel-preset-es2015'] }); + .options({ presets: ['@babel/preset-env'] }); ``` #### Config module rules uses (loaders): modifying options @@ -861,7 +859,9 @@ neutrino.config.module neutrino.config.module .rule('compile') .use('babel') - .tap(options => merge(options, { plugins: ['babel-plugin-syntax-object-rest-spread'] })); + .tap(options => merge(options, { + plugins: ['@babel/plugin-proposal-class-properties'] + })); ``` #### Config module rules oneOfs (conditional rules): @@ -893,10 +893,10 @@ neutrino.config.module ### Merging Config -Neutrino config supports merging in an object to the configuration instance which matches a layout -similar to how the configuration schema is laid out. Note that this is not a webpack configuration -object, but you may transform a webpack configuration object before providing it to Neutrino configuration -to match its layout. +Neutrino config supports merging in an object to the configuration instance which +matches a layout similar to how the configuration schema is laid out. Note that +this is not a webpack configuration object, but you may transform a webpack +configuration object before providing it to Neutrino configuration to match its layout. ```js neutrino.config.merge({ devtool: 'source-map' }); @@ -1028,10 +1028,26 @@ neutrino.config.merge({ resolveLoader: { [key]: value, + alias: { + [key]: value + }, + aliasFields: [...values], + descriptionFields: [...values], extensions: [...values], + mainFields: [...values], + mainFiles: [...values], modules: [...values], moduleExtensions: [...values], - packageMains: [...values] + packageMains: [...values], + + plugin: { + [name]: { + plugin: WebpackPlugin, + args: [...args], + before, + after + } + } }, module: { @@ -1071,10 +1087,13 @@ neutrino.config.merge({ ### Conditional configuration -When working with instances of `ChainedMap` and `ChainedSet`, you can perform conditional configuration using `when`. -You must specify an expression to `when()` which will be evaluated for truthiness or falsiness. If the expression is -truthy, the first function argument will be invoked with an instance of the current chained instance. You can optionally -provide a second function to be invoked when the condition is falsy, which is also given the current chained instance. +When working with instances of `ChainedMap` and `ChainedSet`, you can perform +conditional configuration using `when`. You must specify an expression to +`when()` which will be evaluated for truthiness or falsiness. If the expression +is truthy, the first function argument will be invoked with an instance of the +current chained instance. You can optionally provide a second function to be +invoked when the condition is falsy, which is also given the current chained +instance. ```js // Example: Only add minify plugin during production @@ -1098,7 +1117,9 @@ neutrino.config ### Inspecting generated configuration -You can inspect the generated webpack config using `neutrino.config.toString()`. This will generate a stringified version of the config with comment hints for named rules, uses and plugins: +You can inspect the generated webpack config using `neutrino.config.toString()`. This +will generate a stringified version of the config with comment hints for named +rules, uses and plugins: ``` js neutrino.config @@ -1130,7 +1151,10 @@ neutrino.config.toString({ configPrefix: 'neutrino.config' }); */ ``` -By default the generated string cannot be used directly as real webpack config if it contains functions and plugins that need to be required. In order to generate usable config, you can customize how functions and plugins are stringified by setting a special `__expression` property on them: +By default the generated string cannot be used directly as real webpack config +if it contains functions and plugins that need to be required. In order to +generate usable config, you can customize how functions and plugins are +stringified by setting a special `__expression` property on them: ``` js class MyPlugin {} @@ -1156,6 +1180,29 @@ neutrino.config.toString({ configPrefix: 'neutrino.config' }); */ ``` +Plugins specified via their path will have their `require()` statement generated +automatically: + +``` js +neutrino.config + .plugin('env') + .use(require.resolve('webpack/lib/ProvidePlugin'), [{ jQuery: 'jquery' }]) + +neutrino.config.toString({ configPrefix: 'neutrino.config' }); + +/* +{ + plugins: [ + new (require('/foo/bar/src/node_modules/webpack/lib/EnvironmentPlugin.js'))( + { + jQuery: 'jquery' + } + ) + ] +} +*/ +``` + You can also call `toString` as a static method on `Config` in order to modify the configuration object prior to stringifying. diff --git a/packages/banner/index.js b/packages/banner/index.js index d8ab0926c..58365f70d 100644 --- a/packages/banner/index.js +++ b/packages/banner/index.js @@ -1,9 +1,7 @@ -const { BannerPlugin } = require('webpack'); - module.exports = (neutrino, { pluginId = 'banner', ...options } = {}) => { neutrino.config .plugin(pluginId) - .use(BannerPlugin, [{ + .use(require.resolve('webpack/lib/BannerPlugin'), [{ banner: 'require(\'source-map-support\').install();', test: neutrino.regexFromExtensions(), raw: true, diff --git a/packages/clean/index.js b/packages/clean/index.js index cfc0e93ac..c9016a165 100644 --- a/packages/clean/index.js +++ b/packages/clean/index.js @@ -1,5 +1,3 @@ -const CleanPlugin = require('clean-webpack-plugin'); - module.exports = (neutrino, { pluginId = 'clean', paths = [], ...opts } = {}) => { const options = { root: neutrino.options.root, @@ -9,5 +7,5 @@ module.exports = (neutrino, { pluginId = 'clean', paths = [], ...opts } = {}) => neutrino.config .plugin(pluginId) - .use(CleanPlugin, [paths, options]); + .use(require.resolve('clean-webpack-plugin'), [paths, options]); }; diff --git a/packages/copy/index.js b/packages/copy/index.js index 33aa955cb..d091132cc 100644 --- a/packages/copy/index.js +++ b/packages/copy/index.js @@ -1,9 +1,7 @@ -const CopyPlugin = require('copy-webpack-plugin'); - module.exports = (neutrino, { pluginId = 'copy', patterns = [], options = {} } = {}) => { neutrino.config .plugin(pluginId) - .use(CopyPlugin, [ + .use(require.resolve('copy-webpack-plugin'), [ patterns, { debug: neutrino.options.debug, diff --git a/packages/html-template/index.js b/packages/html-template/index.js index ba7ceb6cc..6db8e94c9 100644 --- a/packages/html-template/index.js +++ b/packages/html-template/index.js @@ -1,12 +1,11 @@ const { resolve } = require('path'); -const HtmlPlugin = require('html-webpack-plugin'); const merge = require('deepmerge'); module.exports = (neutrino, { pluginId = 'html', ...options } = {}) => { neutrino.config .plugin(pluginId) - .use(HtmlPlugin, [ + .use(require.resolve('html-webpack-plugin'), [ merge({ // Use a custom template that has more features (appMountId, lang) than the default: // https://github.com/jantimon/html-webpack-plugin/blob/master/default_index.ejs diff --git a/packages/image-minify/index.js b/packages/image-minify/index.js index ec805b04e..37b937260 100644 --- a/packages/image-minify/index.js +++ b/packages/image-minify/index.js @@ -1,5 +1,4 @@ const merge = require('deepmerge'); -const ImageminWebpackPlugin = require('imagemin-webpack/ImageminWebpackPlugin'); const gifsicle = require('imagemin-gifsicle'); const svgo = require('imagemin-svgo'); const pngquant = require('imagemin-pngquant'); @@ -50,5 +49,5 @@ module.exports = (neutrino, opts = {}) => { neutrino.config .plugin(options.pluginId) - .use(ImageminWebpackPlugin, [options.plugin]); + .use(require.resolve('imagemin-webpack/ImageminWebpackPlugin'), [options.plugin]); }; diff --git a/packages/neutrino/package.json b/packages/neutrino/package.json index 649026a97..7b57de087 100644 --- a/packages/neutrino/package.json +++ b/packages/neutrino/package.json @@ -28,7 +28,7 @@ "deepmerge": "^1.5.2", "is-plain-object": "^2.0.4", "lodash.clonedeep": "^4.5.0", - "webpack-chain": "^4.8.0", + "webpack-chain": "^4.11.0", "yargs-parser": "^10.1.0" } } diff --git a/packages/node/index.js b/packages/node/index.js index d4df4284f..8bba5ea42 100644 --- a/packages/node/index.js +++ b/packages/node/index.js @@ -3,7 +3,6 @@ const compile = require('@neutrinojs/compile-loader'); const clean = require('@neutrinojs/clean'); const startServer = require('@neutrinojs/start-server'); const nodeExternals = require('webpack-node-externals'); -const { HotModuleReplacementPlugin } = require('webpack'); const { basename, parse, format } = require('path'); @@ -98,7 +97,7 @@ module.exports = (neutrino, opts = {}) => { .devtoolModuleFilenameTemplate('[absolute-resource-path]') .end() .when(options.hot, (config) => { - config.plugin('hot').use(HotModuleReplacementPlugin); + config.plugin('hot').use(require.resolve('webpack/lib/HotModuleReplacementPlugin')); mainKeys.forEach(key => { config .entry(key) diff --git a/packages/pwa/index.js b/packages/pwa/index.js index 0c06dce98..bda399945 100644 --- a/packages/pwa/index.js +++ b/packages/pwa/index.js @@ -1,4 +1,3 @@ -const OfflinePlugin = require('offline-plugin'); const merge = require('deepmerge'); module.exports = (neutrino, { pluginId = 'pwa', ...options } = {}) => { @@ -8,7 +7,7 @@ module.exports = (neutrino, { pluginId = 'pwa', ...options } = {}) => { neutrino.config .plugin(pluginId) - .use(OfflinePlugin, [ + .use(require.resolve('offline-plugin'), [ merge({ ServiceWorker: { events: true diff --git a/packages/start-server/index.js b/packages/start-server/index.js index cfc7acc0a..76f5add94 100644 --- a/packages/start-server/index.js +++ b/packages/start-server/index.js @@ -1,9 +1,7 @@ -const StartServerPlugin = require('start-server-webpack-plugin'); - module.exports = (neutrino, { pluginId = 'start-server', ...options } = {}) => { neutrino.config .plugin(pluginId) - .use(StartServerPlugin, [{ + .use(require.resolve('start-server-webpack-plugin'), [{ name: options.name, nodeArgs: neutrino.options.debug ? ['--inspect'] : [] }]); diff --git a/packages/style-loader/index.js b/packages/style-loader/index.js index 97c12f3fc..bf12b3beb 100644 --- a/packages/style-loader/index.js +++ b/packages/style-loader/index.js @@ -1,4 +1,3 @@ -const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const merge = require('deepmerge'); module.exports = (neutrino, opts = {}) => { @@ -51,7 +50,7 @@ module.exports = (neutrino, opts = {}) => { const styleRule = neutrino.config.module.rule(options.ruleId); const loaders = [ { - loader: options.extract ? MiniCssExtractPlugin.loader : require.resolve('style-loader'), + loader: options.extract ? require.resolve('mini-css-extract-plugin/dist/loader') : require.resolve('style-loader'), options: options.extract ? options.extract.loader : options.style, useId: options.extract ? options.extractId : options.styleUseId }, @@ -80,6 +79,6 @@ module.exports = (neutrino, opts = {}) => { if (options.extract) { neutrino.config .plugin(options.extractId) - .use(MiniCssExtractPlugin, [options.extract.plugin]); + .use(require.resolve('mini-css-extract-plugin'), [options.extract.plugin]); } }; diff --git a/packages/style-minify/index.js b/packages/style-minify/index.js index 0cbfefda4..5b2181122 100644 --- a/packages/style-minify/index.js +++ b/packages/style-minify/index.js @@ -1,7 +1,5 @@ -const OptimizeCssPlugin = require('optimize-css-assets-webpack-plugin'); - module.exports = (neutrino, { pluginId = 'optimize-css', plugin = {} } = {}) => { neutrino.config .plugin(pluginId) - .use(OptimizeCssPlugin, [plugin]); + .use(require.resolve('optimize-css-assets-webpack-plugin'), [plugin]); }; diff --git a/packages/stylelint/index.js b/packages/stylelint/index.js index 7650db8ab..c9a8d67a3 100644 --- a/packages/stylelint/index.js +++ b/packages/stylelint/index.js @@ -1,4 +1,3 @@ -const StylelintPlugin = require('stylelint-webpack-plugin'); const { formatters } = require('stylelint'); module.exports = (neutrino, { pluginId = 'stylelint', ...opts } = {}) => { @@ -14,7 +13,7 @@ module.exports = (neutrino, { pluginId = 'stylelint', ...opts } = {}) => { neutrino.config .plugin(pluginId) - .use(StylelintPlugin, [options]); + .use(require.resolve('stylelint-webpack-plugin'), [options]); neutrino.register('stylelintrc', (neutrino) => neutrino.config diff --git a/packages/vue/index.js b/packages/vue/index.js index d5a696296..e09ba4e43 100644 --- a/packages/vue/index.js +++ b/packages/vue/index.js @@ -1,7 +1,6 @@ const loaderMerge = require('@neutrinojs/loader-merge'); const web = require('@neutrinojs/web'); const merge = require('deepmerge'); -const VueLoaderPlugin = require('vue-loader/lib/plugin'); module.exports = (neutrino, opts = {}) => { // vue-loader extracts