From 7fa53e3e37aa3463482aff42efefc10b87ed388c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20V=C3=A9zina?= Date: Tue, 15 Mar 2016 10:47:02 -0400 Subject: [PATCH 1/4] remove SAGE_ENV; uses webpack -d/-p flags; add config file; subfolders in dist/ --- config.json | 10 +++++ dev.js | 3 +- package.json | 6 +-- src/admin.php | 2 +- src/setup.php | 4 +- webpack.config.js | 97 ++++++++++++++++++++++++++++++----------------- 6 files changed, 81 insertions(+), 41 deletions(-) create mode 100644 config.json diff --git a/config.json b/config.json new file mode 100644 index 0000000000..05b7fc3d7a --- /dev/null +++ b/config.json @@ -0,0 +1,10 @@ +{ + "output": { + "path": "dist", + "publicPath": "/app/themes/sage/dist/" + }, + "options": { + "extractStyles": true + }, + "devUrl" : "http://example.dev" +} diff --git a/dev.js b/dev.js index 8a473b918f..0cbbd1e357 100644 --- a/dev.js +++ b/dev.js @@ -6,11 +6,12 @@ var webpack = require('webpack'), browserSync = require('browser-sync'); var devBuildConfig = require('./webpack.config'), + config = require('./config'), compiler = webpack(devBuildConfig); browserSync.init({ proxy: { - target: 'http://example.dev', // change to dev server + target: config.devUrl, middleware: [ webpackDevMiddleware(compiler, { publicPath: devBuildConfig.output.publicPath, diff --git a/package.json b/package.json index 1d471f4f96..37e5013781 100644 --- a/package.json +++ b/package.json @@ -18,9 +18,9 @@ } ], "scripts": { - "build:production": "export SAGE_ENV=production && webpack", - "build": "export SAGE_ENV=development && webpack", - "watch": "export SAGE_ENV=development && node dev.js" + "build:production": "webpack -p", + "build": "webpack -d", + "watch": "NODE_ENV=development node dev.js" }, "engines": { "node": ">= 0.12.0", diff --git a/src/admin.php b/src/admin.php index 0313e8cfaf..863a6ced6d 100644 --- a/src/admin.php +++ b/src/admin.php @@ -12,5 +12,5 @@ * Customizer JS */ add_action('customize_preview_init', function () { - wp_enqueue_script('sage/customizer.js', asset_path('customizer.js'), ['customize-preview'], null, true); + wp_enqueue_script('sage/customizer.js', asset_path('scripts/customizer.js'), ['customize-preview'], null, true); }); diff --git a/src/setup.php b/src/setup.php index 7f32f0cec4..c6e84fe9e8 100755 --- a/src/setup.php +++ b/src/setup.php @@ -6,8 +6,8 @@ * Theme assets */ add_action('wp_enqueue_scripts', function () { - wp_enqueue_style('sage/main.css', asset_path('main.css'), false, null); - wp_enqueue_script('sage/main.js', asset_path('main.js'), ['jquery'], null, true); + wp_enqueue_style('sage/main.css', asset_path('styles/main.css'), false, null); + wp_enqueue_script('sage/main.js', asset_path('scripts/main.js'), ['jquery'], null, true); }, 100); /** diff --git a/webpack.config.js b/webpack.config.js index ef2b6fa1c6..7d7460099c 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -7,28 +7,27 @@ var webpack = require('webpack'), OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin'), cssnano = require('cssnano'); -var SAGE_ENV = process.env.SAGE_ENV || 'development', +var config = require('./config'), webpackConfig; -var sage = { - publicPath: '/app/themes/sage/dist/', - dist: path.join(__dirname, 'dist'), - manifest: 'assets.json', - // set to true to extract css in dev mode (prevents "hot" update) - extractStyles: false -}; - -// format output for Sage : { "name.ext": "hash.ext" } +/** + * Process AssetsPlugin output + * and format for Sage: {"[name].[ext]":"[hash].[ext]"} + * @param {Object} assets passed by processOutput + * @return {String} JSON + */ var assetsPluginProcessOutput = function (assets) { - var results = {}, - name, - ext; + var name, + ext, + filename, + results = {}; for (name in assets) { if (assets.hasOwnProperty(name)) { for (ext in assets[name]) { if (assets[name].hasOwnProperty(ext)) { - results[name + '.' + ext] = assets[name][ext]; + filename = name + '.' + ext; + results[filename] = path.basename(assets[name][ext]); } } } @@ -36,6 +35,30 @@ var assetsPluginProcessOutput = function (assets) { return JSON.stringify(results); } +/** + * Loop through webpack entry + * and add the hot middleware + * @param {Object} entry webpack entry + * @return {Object} entry with hot middleware + */ +var addHotMiddleware = function (entry) { + var name, + results = {}, + hotMiddlewareScript = 'webpack-hot-middleware/client?reload=true'; + + for (name in entry) { + if (entry.hasOwnProperty(name)) { + if (entry[name] instanceof Array !== true) { + results[name] = [entry[name]]; + } else { + results[name] = entry[name].slice(0); + }; + results[name].push(hotMiddlewareScript); + } + } + return results; +} + webpackConfig = { entry: { main: [ @@ -46,8 +69,8 @@ webpackConfig = { ] }, output: { - path: sage.dist, - publicPath: sage.publicPath + path: path.join(__dirname, config.output.path), + publicPath: config.output.publicPath, }, module: { preLoaders: [ @@ -75,15 +98,15 @@ webpackConfig = { }, { test: /\.(ttf|eot|svg)$/, - loader: 'url?limit=10000' + loader: 'url?limit=10000&name=fonts/[hash].[ext]' }, { test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/, - loader: 'url?limit=10000&mimetype=application/font-woff' + loader: 'url?limit=10000&mimetype=application/font-woff&name=fonts/[hash].[ext]' }, { test: /\.(png|jpg|jpeg|gif)(\?v=[0-9]\.[0-9]\.[0-9])?$/, - loader: 'file-loader' + loader: 'file-loader?name=images/[hash].[ext]' } ], }, @@ -92,7 +115,7 @@ webpackConfig = { jquery: 'jQuery' }, plugins: [ - new Clean([sage.dist]), + new Clean([config.output.path]), new webpack.ProvidePlugin({ $: 'jquery', jQuery: 'jquery', @@ -100,8 +123,8 @@ webpackConfig = { 'window.Tether': 'tether' }), new AssetsPlugin({ - path: sage.dist, - filename: sage.manifest, + path: config.output.path, + filename: 'assets.json', fullPath: false, processOutput: assetsPluginProcessOutput, }) @@ -113,24 +136,22 @@ webpackConfig = { } }; -if ( SAGE_ENV === 'development' ) { +if (process.argv.lastIndexOf('-d') !== -1 || + process.env.NODE_ENV === 'development') { // development - webpackConfig.entry.main.push('webpack-hot-middleware/client?reload=true'); - webpackConfig.entry.customizer.push('webpack-hot-middleware/client?reload=true'); - webpackConfig.output.filename = '[name].js'; - webpackConfig.output.sourceMapFilename = '[file].map'; - webpackConfig.output.pathinfo = true; - webpackConfig.debug = true; - webpackConfig.devtool = '#cheap-module-source-map'; + webpackConfig.output.filename = 'scripts/[name].js'; webpackConfig.plugins.push(new webpack.optimize.OccurenceOrderPlugin()); webpackConfig.plugins.push(new webpack.HotModuleReplacementPlugin()); webpackConfig.plugins.push(new webpack.NoErrorsPlugin()); - webpackConfig.plugins.push(new ExtractTextPlugin('[name].css', { disable: !sage.extractStyles })); + webpackConfig.plugins.push(new ExtractTextPlugin('styles/[name].css', { + // disable if webpack is called from the node.js api or set to false in config file + disable: (process.env.NODE_ENV === 'development' || config.options.extractStyles === false) + })); } else { - // production - webpackConfig.output.filename = '[name].[hash].js'; + // default or production + webpackConfig.output.filename = 'scripts/[name].[hash].js'; webpackConfig.output.sourceMapFilename = '[file].[hash].map'; - webpackConfig.plugins.push(new ExtractTextPlugin('[name].[hash].css')); + webpackConfig.plugins.push(new ExtractTextPlugin('styles/[name].[hash].css')); webpackConfig.plugins.push(new webpack.optimize.UglifyJsPlugin()); webpackConfig.plugins.push(new OptimizeCssAssetsPlugin({ cssProcessor: cssnano, @@ -139,4 +160,12 @@ if ( SAGE_ENV === 'development' ) { })); } +if (process.env.NODE_ENV === 'development') { + // development settings when called from the node.js api by the watch script + webpackConfig.entry = addHotMiddleware(webpackConfig.entry); + webpackConfig.output.pathinfo = true; + webpackConfig.debug = true; + webpackConfig.devtool = 'source-map'; +} + module.exports = webpackConfig; From 8e2aeb53ab6677a79e442f523e0ee8b0474c9b2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20V=C3=A9zina?= Date: Tue, 15 Mar 2016 11:25:50 -0400 Subject: [PATCH 2/4] images & fonts : keep the same name --- webpack.config.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/webpack.config.js b/webpack.config.js index 7d7460099c..3015d9c505 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -98,15 +98,15 @@ webpackConfig = { }, { test: /\.(ttf|eot|svg)$/, - loader: 'url?limit=10000&name=fonts/[hash].[ext]' + loader: 'url?limit=10000&name=fonts/[name].[ext]' }, { test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/, - loader: 'url?limit=10000&mimetype=application/font-woff&name=fonts/[hash].[ext]' + loader: 'url?limit=10000&mimetype=application/font-woff&name=fonts/[name].[ext]' }, { test: /\.(png|jpg|jpeg|gif)(\?v=[0-9]\.[0-9]\.[0-9])?$/, - loader: 'file-loader?name=images/[hash].[ext]' + loader: 'file-loader?name=images/[name].[ext]' } ], }, From 9bb62336a83c58e8aa3feb879df768377529fc4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20V=C3=A9zina?= Date: Tue, 15 Mar 2016 22:14:04 -0400 Subject: [PATCH 3/4] remove env variable in npm scrpts; rename watch script for clarity --- package.json | 2 +- dev.js => watch.js | 2 ++ webpack.config.js | 7 +++---- 3 files changed, 6 insertions(+), 5 deletions(-) rename dev.js => watch.js (95%) diff --git a/package.json b/package.json index 37e5013781..9cdc0cd139 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "scripts": { "build:production": "webpack -p", "build": "webpack -d", - "watch": "NODE_ENV=development node dev.js" + "watch": "node watch.js" }, "engines": { "node": ">= 0.12.0", diff --git a/dev.js b/watch.js similarity index 95% rename from dev.js rename to watch.js index 0cbbd1e357..280ace0f24 100644 --- a/dev.js +++ b/watch.js @@ -1,5 +1,7 @@ /* eslint no-console: 0 */ +process.env.SCRIPT = 'watch'; + var webpack = require('webpack'), webpackDevMiddleware = require('webpack-dev-middleware'), webpackHotMiddleware = require('webpack-hot-middleware'), diff --git a/webpack.config.js b/webpack.config.js index 3015d9c505..b182ae85fc 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -137,7 +137,7 @@ webpackConfig = { }; if (process.argv.lastIndexOf('-d') !== -1 || - process.env.NODE_ENV === 'development') { + process.env.SCRIPT === 'watch') { // development webpackConfig.output.filename = 'scripts/[name].js'; webpackConfig.plugins.push(new webpack.optimize.OccurenceOrderPlugin()); @@ -145,7 +145,7 @@ if (process.argv.lastIndexOf('-d') !== -1 || webpackConfig.plugins.push(new webpack.NoErrorsPlugin()); webpackConfig.plugins.push(new ExtractTextPlugin('styles/[name].css', { // disable if webpack is called from the node.js api or set to false in config file - disable: (process.env.NODE_ENV === 'development' || config.options.extractStyles === false) + disable: (process.env.SCRIPT === 'watch' || config.options.extractStyles === false) })); } else { // default or production @@ -159,8 +159,7 @@ if (process.argv.lastIndexOf('-d') !== -1 || canPrint: true })); } - -if (process.env.NODE_ENV === 'development') { +if (process.env.SCRIPT === 'watch') { // development settings when called from the node.js api by the watch script webpackConfig.entry = addHotMiddleware(webpackConfig.entry); webpackConfig.output.pathinfo = true; From c2658fc5b2c145719658d63271e88f8f4f916e05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20V=C3=A9zina?= Date: Thu, 24 Mar 2016 11:07:45 -0400 Subject: [PATCH 4/4] add image-webpack-loader; keep subfolder(s) for images & fonts: 'dist/images/foo/bar.jpg' --- package.json | 5 ++++- webpack.config.js | 55 +++++++++++++++++++++++++++++++---------------- 2 files changed, 41 insertions(+), 19 deletions(-) diff --git a/package.json b/package.json index 9cdc0cd139..4cfa70d6b9 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,9 @@ "scripts": { "build:production": "webpack -p", "build": "webpack -d", - "watch": "node watch.js" + "watch": "node watch.js", + "lint": "eslint -c .eslintrc assets/scripts", + "install": "composer install" }, "engines": { "node": ">= 0.12.0", @@ -45,6 +47,7 @@ "eslint-loader": "^1.3.0", "extract-text-webpack-plugin": "^0.9.1", "file-loader": "^0.8.5", + "image-webpack-loader": "^1.6.3", "imagemin-pngcrush": "^4.1.0", "imports-loader": "^0.6.5", "monkey-hot-loader": "0.0.3", diff --git a/webpack.config.js b/webpack.config.js index b182ae85fc..b1e2a62490 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -10,6 +10,9 @@ var webpack = require('webpack'), var config = require('./config'), webpackConfig; +const DEBUG = (process.argv.lastIndexOf('-d') !== -1), + WATCH = (process.env.SCRIPT === 'watch'); + /** * Process AssetsPlugin output * and format for Sage: {"[name].[ext]":"[hash].[ext]"} @@ -84,30 +87,47 @@ webpackConfig = { { test: /\.js$/, exclude: /node_modules/, - loaders: ['monkey-hot', 'babel'] + loaders: [ + 'monkey-hot', + 'babel' + ], }, { test: /\.css$/, exclude: /node_modules/, - loader: ExtractTextPlugin.extract('style', 'css?sourceMap!postcss') + loader: (WATCH) ? + 'style!css?sourceMap!postcss' : + ExtractTextPlugin.extract('style', 'css?sourceMap!postcss'), }, { test: /\.scss$/, exclude: /node_modules/, - loader: ExtractTextPlugin.extract('style', 'css?sourceMap!postcss!sass?sourceMap') + loader: (WATCH) ? + 'style!css?sourceMap!postcss!sass?sourceMap' : + ExtractTextPlugin.extract('style', 'css?sourceMap!postcss!sass?sourceMap'), }, + { - test: /\.(ttf|eot|svg)$/, - loader: 'url?limit=10000&name=fonts/[name].[ext]' + test: /\.(png|jpg|jpeg|gif)(\?v=[0-9]+\.?[0-9]+?\.?[0-9]+?)?$/, + loaders: [ + 'file?name=[path][name].[ext]&context=assets/', + 'image-webpack?bypassOnDebug&optimizationLevel=7&interlaced=false' + ], }, { - test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/, - loader: 'url?limit=10000&mimetype=application/font-woff&name=fonts/[name].[ext]' + test: /\.(ttf|eot|svg)(\?v=[0-9]+\.?[0-9]+?\.?[0-9]+?)?$/, + loader: 'file?name=[path][name].[ext]&context=assets/', }, { - test: /\.(png|jpg|jpeg|gif)(\?v=[0-9]\.[0-9]\.[0-9])?$/, - loader: 'file-loader?name=images/[name].[ext]' - } + test: /\.woff(2)?(\?v=[0-9]+\.?[0-9]+?\.?[0-9]+?)?$/, + loader: 'url', + query: { + limit: 10000, + mimetype: "application/font-woff", + name: "[path][name].[ext]", + context: "assets/", + } + }, ], }, resolve: { extensions: [ '', '.js', '.json' ] }, @@ -136,8 +156,7 @@ webpackConfig = { } }; -if (process.argv.lastIndexOf('-d') !== -1 || - process.env.SCRIPT === 'watch') { +if (DEBUG || WATCH) { // development webpackConfig.output.filename = 'scripts/[name].js'; webpackConfig.plugins.push(new webpack.optimize.OccurenceOrderPlugin()); @@ -145,13 +164,12 @@ if (process.argv.lastIndexOf('-d') !== -1 || webpackConfig.plugins.push(new webpack.NoErrorsPlugin()); webpackConfig.plugins.push(new ExtractTextPlugin('styles/[name].css', { // disable if webpack is called from the node.js api or set to false in config file - disable: (process.env.SCRIPT === 'watch' || config.options.extractStyles === false) + disable: (WATCH || config.options.extractStyles === false) })); } else { // default or production - webpackConfig.output.filename = 'scripts/[name].[hash].js'; - webpackConfig.output.sourceMapFilename = '[file].[hash].map'; - webpackConfig.plugins.push(new ExtractTextPlugin('styles/[name].[hash].css')); + webpackConfig.output.filename = 'scripts/[name]-[hash].js'; + webpackConfig.plugins.push(new ExtractTextPlugin('styles/[name]-[hash].css')); webpackConfig.plugins.push(new webpack.optimize.UglifyJsPlugin()); webpackConfig.plugins.push(new OptimizeCssAssetsPlugin({ cssProcessor: cssnano, @@ -159,12 +177,13 @@ if (process.argv.lastIndexOf('-d') !== -1 || canPrint: true })); } -if (process.env.SCRIPT === 'watch') { + +if (WATCH) { // development settings when called from the node.js api by the watch script webpackConfig.entry = addHotMiddleware(webpackConfig.entry); webpackConfig.output.pathinfo = true; webpackConfig.debug = true; - webpackConfig.devtool = 'source-map'; + webpackConfig.devtool = '#cheap-module-source-map'; } module.exports = webpackConfig;