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/package.json b/package.json index 1d471f4f96..4cfa70d6b9 100644 --- a/package.json +++ b/package.json @@ -18,9 +18,11 @@ } ], "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 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/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/dev.js b/watch.js similarity index 88% rename from dev.js rename to watch.js index 8a473b918f..280ace0f24 100644 --- a/dev.js +++ b/watch.js @@ -1,16 +1,19 @@ /* eslint no-console: 0 */ +process.env.SCRIPT = 'watch'; + var webpack = require('webpack'), webpackDevMiddleware = require('webpack-dev-middleware'), webpackHotMiddleware = require('webpack-hot-middleware'), 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/webpack.config.js b/webpack.config.js index ef2b6fa1c6..b1e2a62490 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -7,28 +7,30 @@ 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 -}; +const DEBUG = (process.argv.lastIndexOf('-d') !== -1), + WATCH = (process.env.SCRIPT === 'watch'); -// 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 +38,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 +72,8 @@ webpackConfig = { ] }, output: { - path: sage.dist, - publicPath: sage.publicPath + path: path.join(__dirname, config.output.path), + publicPath: config.output.publicPath, }, module: { preLoaders: [ @@ -61,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' + 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' + 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' - } + 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' ] }, @@ -92,7 +135,7 @@ webpackConfig = { jquery: 'jQuery' }, plugins: [ - new Clean([sage.dist]), + new Clean([config.output.path]), new webpack.ProvidePlugin({ $: 'jquery', jQuery: 'jquery', @@ -100,8 +143,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 +156,20 @@ webpackConfig = { } }; -if ( SAGE_ENV === 'development' ) { +if (DEBUG || WATCH) { // 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: (WATCH || config.options.extractStyles === false) + })); } else { - // production - webpackConfig.output.filename = '[name].[hash].js'; - webpackConfig.output.sourceMapFilename = '[file].[hash].map'; - webpackConfig.plugins.push(new ExtractTextPlugin('[name].[hash].css')); + // default or production + 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, @@ -139,4 +178,12 @@ if ( SAGE_ENV === 'development' ) { })); } +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 = '#cheap-module-source-map'; +} + module.exports = webpackConfig;