From 8e422569dd456f39c288107d87551b161f261259 Mon Sep 17 00:00:00 2001 From: Brett Uglow Date: Wed, 28 Dec 2016 16:04:35 +1100 Subject: [PATCH] feat(build): make webpack config more flexible by exporting a function BREAKING CHANGE: The webpack.config.js file has been changed to export a function instead of an object. You will need to change the last line from `module.exports = config;` to `module.exports.mergeConfig = mergeConfig;`. This change allows the prod/dev/test configs to inject some metadata into this file, allowing them to be simpler and have more control over the output. ISSUES CLOSED: #101 --- .../templates/dev.webpack.config.js | 83 ++++--------------- .../templates/prod.webpack.config.js | 82 ++++-------------- .../templates/webpack.build.config.js.tpl | 48 +++++++---- .../templates/webpack.config.js.tpl | 7 +- .../templates/webpack.finalize.config.js.tpl | 42 ++++++++++ .../templates/webpack.buildCSS.config.js.tpl | 79 +++++++++++++----- .../templates/webpack.buildHTML.config.js.tpl | 2 +- 7 files changed, 171 insertions(+), 172 deletions(-) create mode 100644 lib/buildTool/webpack/buildBrowser/templates/webpack.finalize.config.js.tpl diff --git a/lib/buildTool/webpack/buildBrowser/templates/dev.webpack.config.js b/lib/buildTool/webpack/buildBrowser/templates/dev.webpack.config.js index eff5d6b..1509fa6 100644 --- a/lib/buildTool/webpack/buildBrowser/templates/dev.webpack.config.js +++ b/lib/buildTool/webpack/buildBrowser/templates/dev.webpack.config.js @@ -1,77 +1,24 @@ 'use strict'; // START_CONFIT_GENERATED_CONTENT -<% -var configPath = paths.config.configDir + 'webpack/'; -var relativePath = configPath.replace(/([^/]+)/g, '..'); --%> -const path = require('path'); -const webpack = require('webpack'); -const DefinePlugin = require('webpack/lib/DefinePlugin'); -const LoaderOptionsPlugin = require('webpack/lib/LoaderOptionsPlugin'); - -let config = require('./webpack.config.js'); -const helpers = require('./webpackHelpers')(path.resolve(__dirname, '<%- relativePath %>')); - -const ENV = process.env.ENV = process.env.NODE_ENV = 'development'; -const HMR = helpers.hasProcessFlag('hot'); - -/** - * Devtool - * Reference: https://webpack.js.org/configuration/devtool/ - * Type of sourcemap to use per build type - */ -Object.assign(config, { - devtool: 'cheap-module-source-map', -}); - -config.node.process = true; // Not sure why we do this for development? - - -/** - * Add additional plugins to the compiler. - * - * See: http://webpack.github.io/docs/configuration.html#plugins - */ - -/** - * Plugin: DefinePlugin - * Description: Define free variables. - * Useful for having development builds with debug logging or adding global constants. - * - * Environment helpers - * - * See: https://webpack.github.io/docs/list-of-plugins.html#defineplugin - */ -// NOTE: when adding more properties make sure you include them in custom-typings.d.ts -config.plugins.push( - new DefinePlugin({ - 'ENV': JSON.stringify(ENV), - 'HMR': HMR, - 'process.env': { - 'ENV': JSON.stringify(ENV), - 'NODE_ENV': JSON.stringify(ENV), - 'HMR': HMR, - } - }) -); - - -/** - * Plugin LoaderOptionsPlugin (experimental) - * - * See: https://gist.github.com/sokra/27b24881210b56bbaff7 - */ -let loaderOptions = { - debug: true, - options: Object.assign({}, config.loaderOptions) +let configOptions = { + metaData: { + title: 'Confit Sample Project', + baseUrl: '/', + ENV: process.env.ENV = process.env.NODE_ENV = 'development', + jsSourceMap: 'cheap-module-source-map', + cssSourceMap: false, + }, + loaderOptions: { + debug: true + } }; +// END_CONFIT_GENERATED_CONTENT -delete config.loaderOptions; // Remove the property so that config is valid - -let loaderOptionsPlugin = new LoaderOptionsPlugin(loaderOptions); -config.plugins.push(loaderOptionsPlugin); +// START_CONFIT_GENERATED_CONTENT +let config = require('./webpack.config.js').mergeConfig(configOptions); +config.node.process = true; // Not sure why we do this for development? // END_CONFIT_GENERATED_CONTENT module.exports = config; diff --git a/lib/buildTool/webpack/buildBrowser/templates/prod.webpack.config.js b/lib/buildTool/webpack/buildBrowser/templates/prod.webpack.config.js index 201a2f3..dbcc895 100644 --- a/lib/buildTool/webpack/buildBrowser/templates/prod.webpack.config.js +++ b/lib/buildTool/webpack/buildBrowser/templates/prod.webpack.config.js @@ -1,76 +1,24 @@ 'use strict'; // START_CONFIT_GENERATED_CONTENT -<% -var configPath = paths.config.configDir + 'webpack/'; -var relativePath = configPath.replace(/([^/]+)/g, '..'); --%> -const path = require('path'); -const webpack = require('webpack'); -const DefinePlugin = require('webpack/lib/DefinePlugin'); -const LoaderOptionsPlugin = require('webpack/lib/LoaderOptionsPlugin'); - -let config = require('./webpack.config.js'); -const helpers = require('./webpackHelpers')(path.resolve(__dirname, '<%- relativePath %>')); - -const ENV = process.env.ENV = process.env.NODE_ENV = 'production'; -const HMR = helpers.hasProcessFlag('hot'); - -/** - * Devtool - * Reference: https://webpack.js.org/configuration/devtool/ - * Type of sourcemap to use per build type - */ -Object.assign(config, { - devtool: 'source-map' -}); - - -/** - * Add additional plugins to the compiler. - * - * See: http://webpack.github.io/docs/configuration.html#plugins - */ - -/** - * Plugin: DefinePlugin - * Description: Define free variables. - * Useful for having development builds with debug logging or adding global constants. - * - * Environment helpers - * - * See: https://webpack.github.io/docs/list-of-plugins.html#defineplugin - */ -// NOTE: when adding more properties make sure you include them in custom-typings.d.ts -config.plugins.push( - new DefinePlugin({ - 'ENV': JSON.stringify(ENV), - 'HMR': HMR, - 'process.env': { - 'ENV': JSON.stringify(ENV), - 'NODE_ENV': JSON.stringify(ENV), - 'HMR': HMR, - } - }) -); - -/** - * Plugin LoaderOptionsPlugin (experimental) - * - * See: https://gist.github.com/sokra/27b24881210b56bbaff7 - */ -let loaderOptions = { - debug: false, - minimize: true, - options: Object.assign({}, config.loaderOptions) +let configOptions = { + metaData: { + title: 'Confit Sample Project', + baseUrl: '/', + ENV: process.env.ENV = process.env.NODE_ENV = 'production', + jsSourceMap: 'source-map', + cssSourceMap: false, + }, + loaderOptions: { + debug: false, + minimize: true + } }; +// END_CONFIT_GENERATED_CONTENT -delete config.loaderOptions; // Remove the property so that config is valid - -let loaderOptionsPlugin = new LoaderOptionsPlugin(loaderOptions); - -config.plugins.push(loaderOptionsPlugin); +// START_CONFIT_GENERATED_CONTENT +let config = require('./webpack.config.js').mergeConfig(configOptions); // END_CONFIT_GENERATED_CONTENT module.exports = config; diff --git a/lib/buildTool/webpack/buildBrowser/templates/webpack.build.config.js.tpl b/lib/buildTool/webpack/buildBrowser/templates/webpack.build.config.js.tpl index 9808cd0..1d8f891 100644 --- a/lib/buildTool/webpack/buildBrowser/templates/webpack.build.config.js.tpl +++ b/lib/buildTool/webpack/buildBrowser/templates/webpack.build.config.js.tpl @@ -1,5 +1,4 @@ /** Build START */ - <% var configPath = paths.config.configDir + 'webpack/'; var relativePath = configPath.replace(/([^/]+)/g, '..'); @@ -14,13 +13,18 @@ const helpers = require('./webpackHelpers')(path.resolve(__dirname, '<%- relativ * Webpack Constants */ const HMR = helpers.hasProcessFlag('hot'); -const METADATA = { - title: 'Confit Sample Project', - baseUrl: '/', - isDevServer: helpers.isWebpackDevServer(), - HMR: HMR -}; - +const METADATA = Object.assign( + { + title: 'Confit Sample Project', + baseUrl: '/', + isDevServer: helpers.isWebpackDevServer(), + HMR: HMR, + jsSourceMap: 'cheap-source-map', // See https://webpack.js.org/configuration/devtool/#devtool + cssSourceMap: false, // There are issues with this option for the css-loader: https://github.com/webpack/css-loader#sourcemaps + performanceHints: 'warning' + }, + mergeOptions.metaData +); // https://gist.github.com/sokra/27b24881210b56bbaff7#resolving-options @@ -35,13 +39,19 @@ let moduleDirectories = ['node_modules', 'bower_components']; // Only needed to let config = { context: helpers.root('<%= paths.input.srcDir.slice(0, -1) %>'), // The baseDir for resolving the entry option and the HTML-Webpack-Plugin + /** + * Performance settings - https://webpack.js.org/configuration/performance/#performance + */ + performance: { + hints: METADATA.performanceHints + }, /** * Devtool * Reference: https://webpack.js.org/configuration/devtool/ * Type of sourcemap to use per build type */ - devtool: 'source-map', + devtool: METADATA.jsSourceMap, /** * Options affecting the output of the compilation. @@ -76,7 +86,6 @@ let config = { * See: http://webpack.github.io/docs/configuration.html#resolve */ resolve: { - /* * An array of extensions that should be used to resolve modules. * @@ -101,7 +110,7 @@ let config = { * Include polyfills or mocks for various node stuff * Description: Node configuration * - * See: https://webpack.github.io/docs/configuration.html#node + * See: https://webpack.js.org/configuration/node/ */ node: { global: true, @@ -110,8 +119,19 @@ let config = { module: false, clearImmediate: false, setImmediate: false - }, - - loaderOptions: {} // Temporary property to allow multiple builders to add their options, then the dev/prod builds will add the LoaderOptionsPlugin + } }; + + +/* + * Loader options (for backwards compatibility) + * See https://webpack.js.org/guides/migrating/#loaderoptionsplugin-context + * E.g. https://github.com/jtangelder/sass-loader/issues/285 + */ +let LOADER_OPTIONS = Object.assign( + {options: { + context: config.context + }}, + mergeOptions.loaderOptions +); /* **/ diff --git a/lib/buildTool/webpack/buildBrowser/templates/webpack.config.js.tpl b/lib/buildTool/webpack/buildBrowser/templates/webpack.config.js.tpl index 5f8f119..0ca130f 100644 --- a/lib/buildTool/webpack/buildBrowser/templates/webpack.config.js.tpl +++ b/lib/buildTool/webpack/buildBrowser/templates/webpack.config.js.tpl @@ -1,6 +1,7 @@ 'use strict'; // START_CONFIT_GENERATED_CONTENT +function mergeConfig(mergeOptions) { <% include ../../buildBrowser/templates/webpack.build.config.js.tpl %> <% include ../../entryPoint/templates/webpack.entryPoint.config.js.tpl %> <% include ../../buildJS/templates/webpack.buildJS.config.js.tpl %> @@ -9,9 +10,13 @@ <% include ../../buildCSS/templates/webpack.buildCSS.config.js.tpl %> <% include ../../buildHTML/templates/webpack.buildHTML.config.js.tpl %> <% include ../../serverDev/templates/webpack.serverDev.config.js.tpl %> +<% include ../../buildBrowser/templates/webpack.finalize.config.js.tpl %> + +return config; +} // To remove content hashes, call helpers.removeHash(config.prop.parent, propertyName, regExMatcher (optional)); // For example helpers.removeHash(config.output, 'fileName', /\[(contentHash|hash).*?\]/) // END_CONFIT_GENERATED_CONTENT -module.exports = config; +module.exports.mergeConfig = mergeConfig; diff --git a/lib/buildTool/webpack/buildBrowser/templates/webpack.finalize.config.js.tpl b/lib/buildTool/webpack/buildBrowser/templates/webpack.finalize.config.js.tpl new file mode 100644 index 0000000..2e0fbf6 --- /dev/null +++ b/lib/buildTool/webpack/buildBrowser/templates/webpack.finalize.config.js.tpl @@ -0,0 +1,42 @@ +/** Finalise Config START */ +/** + * Add additional plugins to the compiler. + * + * See: http://webpack.github.io/docs/configuration.html#plugins + */ + +/** + * Plugin: DefinePlugin + * Description: Define free variables. + * Useful for having development builds with debug logging or adding global constants. + * + * Environment helpers + * + * See: https://webpack.github.io/docs/list-of-plugins.html#defineplugin + */ +// NOTE: when adding more properties make sure you include them in custom-typings.d.ts +const DefinePlugin = require('webpack/lib/DefinePlugin'); + +let definePlugin = new DefinePlugin({ + 'ENV': JSON.stringify(METADATA.ENV), + 'HMR': METADATA.HMR, + 'process.env': { + 'ENV': JSON.stringify(METADATA.ENV), + 'NODE_ENV': JSON.stringify(METADATA.ENV), + 'HMR': METADATA.HMR, + } +}); + +config.plugins.push(definePlugin); + +/** + * Plugin LoaderOptionsPlugin (experimental) + * + * See: https://gist.github.com/sokra/27b24881210b56bbaff7 + */ +const LoaderOptionsPlugin = require('webpack/lib/LoaderOptionsPlugin'); + +let loaderOptionsPlugin = new LoaderOptionsPlugin(LOADER_OPTIONS); + +config.plugins.push(loaderOptionsPlugin); +/* **/ diff --git a/lib/buildTool/webpack/buildCSS/templates/webpack.buildCSS.config.js.tpl b/lib/buildTool/webpack/buildCSS/templates/webpack.buildCSS.config.js.tpl index afb18f7..5ccbb61 100644 --- a/lib/buildTool/webpack/buildCSS/templates/webpack.buildCSS.config.js.tpl +++ b/lib/buildTool/webpack/buildCSS/templates/webpack.buildCSS.config.js.tpl @@ -16,43 +16,80 @@ const autoprefixer = require('autoprefixer'); let supportedBrowsers = { browsers: <%- printJson(browserStringArray, 4) %> }; -config.loaderOptions.postcss = [ +LOADER_OPTIONS.options.postcss = [ autoprefixer(supportedBrowsers) ]; <% } var cssExtensions = ''; -var cssLoaderOptions = '' +var cssLoaderName = ''; +var cssLoaderOptions = {}; if (buildCSS.sourceFormat === 'sass') { cssExtensions = resources.buildCSS.sourceFormat.sass.ext.join('|'); - cssLoaderOptions = '!sass-loader?indentedSyntax=true'; + cssLoaderName = 'sass-loader'; + cssLoaderOptions.indentedSyntax = true; } else if (buildCSS.sourceFormat === 'stylus') { cssExtensions = resources.buildCSS.sourceFormat.stylus.ext.join('|'); - cssLoaderOptions = '!stylus-loader'; + cssLoaderName = 'stylus-loader'; } else { cssExtensions = 'css'; - cssLoaderOptions = ''; + cssLoaderName = ''; +} + +function getLoaderQueryStr(loaderName, optionsObj) { + if (!loaderName) return ''; + + var optionsArr = []; + for (var key in optionsObj) { + optionsArr.push(key + '=' + optionsObj[key]); + } + var optionStr = optionsArr.join('&'); + + if (optionStr) { + optionStr = '?' + optionStr; + } + return '!' + loaderName + optionStr; } %> -// ExtractTextPlugin still uses the older Webpack 1 syntax. See https://github.com/webpack/extract-text-webpack-plugin/issues/275 -let cssLoader = { - test: helpers.pathRegEx(/\.(<%= cssExtensions %>)$/), - loader: ExtractTextPlugin.extract({ - fallbackLoader: 'style-loader', - loader: 'css-loader!postcss-loader<%- cssLoaderOptions %>', - publicPath: '/' // This is relative to 'extractCSSTextPlugin.filename' below. - }) -}; -config.module.rules.push(cssLoader); +// If we are in development, we want live reloading for our CSS. So we cannot use ExtractTextPlugin +// (see https://github.com/css-modules/webpack-demo/issues/8#issuecomment-135647584 and https://ihaveabackup.net/article/sass-with-sourcemaps-webpack-and-live-reload) +let cssLoader; +let cssLoaderOptions = <%- printJson(cssLoaderOptions) %>; -// For any entry-point CSS file definitions, extract them as text files as well -let extractCSSTextPlugin = new ExtractTextPlugin({ - filename: 'css/[name].[contenthash:8].css', // This affects the cssLoader.loader.publicPath (see above) - allChunks: true -}); -config.plugins.push(extractCSSTextPlugin); +if (METADATA.ENV === 'development') { + // If you use the 'sourceMap' option with the css-loader, it has trouble resolving 'url(...)' properties in the CSS, + // meaning your fonts may not show up. + cssLoaderOptions.sourceMap = METADATA.cssSourceMap; + cssLoader = { + test: helpers.pathRegEx(/\.(<%= cssExtensions %>)$/), + use: [ + {loader: 'style-loader'}, + {loader: 'css-loader', options: {sourceMap: METADATA.cssSourceMap}}, + {loader: 'postcss-loader'}<% if (cssLoaderName) { %>, + {loader: '<%- cssLoaderName %>', options: cssLoaderOptions}<% } %> + ] + }; +} else { + // ExtractTextPlugin still uses the older Webpack 1 syntax. See https://github.com/webpack/extract-text-webpack-plugin/issues/275 + cssLoader = { + test: helpers.pathRegEx(/\.(<%= cssExtensions %>)$/), + loader: ExtractTextPlugin.extract({ + fallbackLoader: 'style-loader', + loader: 'css-loader!postcss-loader<%- getLoaderQueryStr(cssLoaderName, cssLoaderOptions) %>', + publicPath: '/' // This is relative to 'extractCSSTextPlugin.filename' below. + }) + }; + + // For any entry-point CSS file definitions, extract them as text files as well + let extractCSSTextPlugin = new ExtractTextPlugin({ + filename: 'css/[name].[contenthash:8].css', // This affects the cssLoader.loader.publicPath (see above) + allChunks: true + }); + config.plugins.push(extractCSSTextPlugin); +} +config.module.rules.push(cssLoader); /* **/ diff --git a/lib/buildTool/webpack/buildHTML/templates/webpack.buildHTML.config.js.tpl b/lib/buildTool/webpack/buildHTML/templates/webpack.buildHTML.config.js.tpl index 7321525..bfbc8ab 100644 --- a/lib/buildTool/webpack/buildHTML/templates/webpack.buildHTML.config.js.tpl +++ b/lib/buildTool/webpack/buildHTML/templates/webpack.buildHTML.config.js.tpl @@ -28,7 +28,7 @@ config.module.rules.push(htmlLoader); var selectedFramework = buildJS.framework[0] || ''; if (selectedFramework === 'AngularJS 2.x') { %> // Configuration that works with Angular 2 -config.loaderOptions.htmlLoader = { +LOADER_OPTIONS.options.htmlLoader = { minimize: true, removeAttributeQuotes: false, caseSensitive: true,