diff --git a/assets/build/config.js b/assets/build/config.js index 5725b08..8ba63b4 100644 --- a/assets/build/config.js +++ b/assets/build/config.js @@ -21,15 +21,14 @@ const config = mergeWithConcat({ }, enabled: { sourceMaps: !isProduction, - minify: isProduction, + optimize: isProduction, cacheBusting: isProduction, watcher: !!argv.watch, - uglifyJs: !(argv.p || argv.optimizeMinimize), }, watch: [], }, userConfig); -config.watch.push(config.copy); +config.watch.push(`${path.basename(config.paths.assets)}/${config.copy}`); config.watch = uniq(config.watch); Object.keys(config.entry).forEach(id => diff --git a/assets/build/util/assetManifestsFormatter.js b/assets/build/util/assetManifestsFormatter.js new file mode 100644 index 0000000..c9f0d52 --- /dev/null +++ b/assets/build/util/assetManifestsFormatter.js @@ -0,0 +1,34 @@ +const path = require('path'); + +module.exports = (key, value) => { + if (typeof value === 'string') { + return value; + } + const manifest = value; + /** + * Hack to prepend scripts/ or styles/ to manifest keys + * + * This might need to be reworked at some point. + * + * Before: + * { + * "main.js": "scripts/main_abcdef.js" + * "main.css": "styles/main_abcdef.css" + * } + * After: + * { + * "scripts/main.js": "scripts/main_abcdef.js" + * "styles/main.css": "styles/main_abcdef.css" + * } + */ + Object.keys(manifest).forEach((src) => { + const sourcePath = path.basename(path.dirname(src)); + const targetPath = path.basename(path.dirname(manifest[src])); + if (sourcePath === targetPath) { + return; + } + manifest[`${targetPath}/${src}`] = manifest[src]; + delete manifest[src]; + }); + return manifest; +}; diff --git a/assets/build/webpack.config.js b/assets/build/webpack.config.js index 4d17fa1..b33b1ee 100644 --- a/assets/build/webpack.config.js +++ b/assets/build/webpack.config.js @@ -1,16 +1,13 @@ +'use strict'; // eslint-disable-line + const webpack = require('webpack'); const qs = require('qs'); const autoprefixer = require('autoprefixer'); const CleanPlugin = require('clean-webpack-plugin'); const ExtractTextPlugin = require('extract-text-webpack-plugin'); -const ImageminPlugin = require('imagemin-webpack-plugin').default; -const imageminMozjpeg = require('imagemin-mozjpeg'); const CopyGlobsPlugin = require('./webpack.plugin.copyglobs'); const mergeWithConcat = require('./util/mergeWithConcat'); -const addHotMiddleware = require('./util/addHotMiddleware'); -const webpackConfigProduction = require('./webpack.config.production'); -const webpackConfigWatch = require('./webpack.config.watch'); const config = require('./config'); const assetsFilenames = (config.enabled.cacheBusting) ? config.cacheBusting : '[name]'; @@ -29,7 +26,7 @@ if (config.enabled.watcher) { jsLoader.loaders.unshift('monkey-hot?sourceType=module'); } -const webpackConfig = { +let webpackConfig = { context: config.paths.assets, entry: config.entry, devtool: (config.enabled.sourceMaps ? '#source-map' : undefined), @@ -118,7 +115,10 @@ const webpackConfig = { jquery: 'jQuery', }, plugins: [ - new CleanPlugin([config.paths.dist], config.paths.root), + new CleanPlugin([config.paths.dist], { + root: config.paths.root, + verbose: false, + }), new CopyGlobsPlugin({ // It would be nice to switch to copy-webpack-plugin, but unfortunately it doesn't // provide a reliable way of tracking the before/after file names @@ -126,14 +126,6 @@ const webpackConfig = { output: `[path]${assetsFilenames}.[ext]`, manifest: config.manifest, }), - new ImageminPlugin({ - optipng: { optimizationLevel: 7 }, - gifsicle: { optimizationLevel: 3 }, - pngquant: { quality: '65-90', speed: 4 }, - svgo: { removeUnknownsAndDefaults: false, cleanupIDs: false }, - plugins: [imageminMozjpeg({ quality: 75 })], - disable: (config.enabled.watcher), - }), new ExtractTextPlugin({ filename: `styles/${assetsFilenames}.css`, allChunks: true, @@ -152,7 +144,7 @@ const webpackConfig = { : false, }), new webpack.LoaderOptionsPlugin({ - minimize: config.enabled.minify, + minimize: config.enabled.optimize, debug: config.enabled.watcher, stats: { colors: true }, }), @@ -175,21 +167,33 @@ const webpackConfig = { ], }; -module.exports = webpackConfig; +/* eslint-disable global-require */ /** Let's only load dependencies as needed */ + +if (config.env.optimize) { + webpackConfig = mergeWithConcat(webpackConfig, require('./webpack.config.optimize')); +} if (config.env.production) { - module.exports = mergeWithConcat(webpackConfig, webpackConfigProduction); + webpackConfig.plugins.push(new webpack.NoErrorsPlugin()); } -if (config.enabled.watcher) { - module.exports.entry = addHotMiddleware(webpackConfig.entry); - module.exports = mergeWithConcat(webpackConfig, webpackConfigWatch); -} +if (config.enabled.cacheBusting) { + const WebpackAssetsManifest = require('webpack-assets-manifest'); -if (config.enabled.uglifyJs) { - module.exports.plugins.push( - new webpack.optimize.UglifyJsPlugin({ - sourceMap: config.enabled.sourceMaps, + webpackConfig.plugins.push( + new WebpackAssetsManifest({ + output: 'assets.json', + space: 2, + writeToDisk: false, + assets: config.manifest, + replacer: require('./util/assetManifestsFormatter'), }) ); } + +if (config.enabled.watcher) { + webpackConfig.entry = require('./util/addHotMiddleware')(webpackConfig.entry); + webpackConfig = mergeWithConcat(webpackConfig, require('./webpack.config.watch')); +} + +module.exports = webpackConfig; diff --git a/assets/build/webpack.config.optimize.js b/assets/build/webpack.config.optimize.js new file mode 100644 index 0000000..5de2720 --- /dev/null +++ b/assets/build/webpack.config.optimize.js @@ -0,0 +1,26 @@ +'use strict'; // eslint-disable-line + +const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin'); +const ImageminPlugin = require('imagemin-webpack-plugin').default; +const imageminMozjpeg = require('imagemin-mozjpeg'); +const cssnano = require('cssnano'); + +const config = require('./config'); + +module.exports = { + plugins: [ + new OptimizeCssAssetsPlugin({ + cssProcessor: cssnano, + cssProcessorOptions: { discardComments: { removeAll: true } }, + canPrint: true, + }), + new ImageminPlugin({ + optipng: { optimizationLevel: 7 }, + gifsicle: { optimizationLevel: 3 }, + pngquant: { quality: '65-90', speed: 4 }, + svgo: { removeUnknownsAndDefaults: false, cleanupIDs: false }, + plugins: [imageminMozjpeg({ quality: 75 })], + disable: (config.enabled.watcher), + }), + ], +}; diff --git a/assets/build/webpack.config.production.js b/assets/build/webpack.config.production.js deleted file mode 100644 index 346cf7e..0000000 --- a/assets/build/webpack.config.production.js +++ /dev/null @@ -1,56 +0,0 @@ -'use strict'; // eslint-disable-line - -const WebpackAssetsManifest = require('webpack-assets-manifest'); -const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin'); -const cssnano = require('cssnano'); -const path = require('path'); - -const config = require('./config'); - -module.exports = { - plugins: [ - new WebpackAssetsManifest({ - output: 'assets.json', - space: 2, - writeToDisk: false, - assets: config.manifest, - replacer(key, value) { - if (typeof value === 'string') { - return value; - } - const manifest = value; - /** - * Hack to prepend scripts/ or styles/ to manifest keys - * - * This might need to be reworked at some point. - * - * Before: - * { - * "main.js": "scripts/main_abcdef.js" - * "main.css": "styles/main_abcdef.css" - * } - * After: - * { - * "scripts/main.js": "scripts/main_abcdef.js" - * "styles/main.css": "styles/main_abcdef.css" - * } - */ - Object.keys(manifest).forEach((src) => { - const sourcePath = path.basename(path.dirname(src)); - const targetPath = path.basename(path.dirname(manifest[src])); - if (sourcePath === targetPath) { - return; - } - manifest[`${targetPath}/${src}`] = manifest[src]; - delete manifest[src]; - }); - return manifest; - }, - }), - new OptimizeCssAssetsPlugin({ - cssProcessor: cssnano, - cssProcessorOptions: { discardComments: { removeAll: true } }, - canPrint: true, - }), - ], -}; diff --git a/assets/build/webpack.config.watch.js b/assets/build/webpack.config.watch.js index d312986..d58750d 100644 --- a/assets/build/webpack.config.watch.js +++ b/assets/build/webpack.config.watch.js @@ -1,12 +1,12 @@ const webpack = require('webpack'); const BrowserSyncPlugin = require('./webpack.plugin.browsersync'); -const mergeWithConcat = require('./util/mergeWithConcat'); const config = require('./config'); module.exports = { output: { pathinfo: true }, devtool: '#cheap-module-source-map', + stats: false, plugins: [ new webpack.optimize.OccurrenceOrderPlugin(), new webpack.HotModuleReplacementPlugin(), @@ -15,9 +15,7 @@ module.exports = { target: config.devUrl, publicPath: config.publicPath, proxyUrl: config.proxyUrl, - browserSyncOptions: mergeWithConcat({ - files: config.watch, - }, config.browserSyncOptions), + watch: config.watch, }), ], }; diff --git a/assets/build/webpack.plugin.browsersync.js b/assets/build/webpack.plugin.browsersync.js index f0f3a88..ebb850d 100644 --- a/assets/build/webpack.plugin.browsersync.js +++ b/assets/build/webpack.plugin.browsersync.js @@ -4,6 +4,7 @@ const webpackDevMiddleware = require('webpack-dev-middleware'); const webpackHotMiddleware = require('webpack-hot-middleware'); const browserSync = require('browser-sync'); const url = require('url'); +const uniq = require('lodash/uniq'); const mergeWithConcat = require('./util/mergeWithConcat'); @@ -13,6 +14,7 @@ module.exports = class { this.compiler = null; this.options = mergeWithConcat({ proxyUrl: 'https://localhost:3000', + watch: [], callback() {}, }, options); } @@ -27,7 +29,9 @@ module.exports = class { compiler.plugin('compilation', () => this.watcher.notify('Rebuilding...')); this.start(); } - // Optionally add logic for this.watcher.reload() + /* You may optionally add custom logic here to trigger either of the following */ + // this.watcher.reload() + // this.watcher.reload({ stream: true }) }); } start() { @@ -38,13 +42,16 @@ module.exports = class { target: this.options.target, middleware: this.middleware(), }, + files: [], }, this.options.browserSyncOptions); + watcherConfig.files = uniq(watcherConfig.files.concat(this.options.watch)); this.watcher.init(watcherConfig, this.options.callback.bind(this)); } middleware() { this.webpackDevMiddleware = webpackDevMiddleware(this.compiler, { publicPath: this.options.publicPath, - stats: { colors: true }, + stats: false, + noInfo: true, }); this.webpackHotMiddleware = webpackHotMiddleware(this.compiler, { log: this.watcher.notify.bind(this.watcher), diff --git a/package.json b/package.json index 31afd82..508ef55 100644 --- a/package.json +++ b/package.json @@ -19,11 +19,12 @@ ], "scripts": { "build": "webpack --progress --config assets/build/webpack.config.js", - "build:production": "npm run build -s -- -p", - "start": "npm run build -s -- --watch", + "build:production": "webpack --progress -p --config assets/build/webpack.config.js", + "build:profile": "webpack --progress --profile --json --config assets/build/webpack.config.js", + "start": "webpack --hide-modules --watch --config assets/build/webpack.config.js", "clean": "rimraf dist", "lint": "eslint assets/scripts assets/build", - "test": "npm run lint -s" + "test": "npm run lint" }, "engines": { "node": ">= 4.5"