diff --git a/.babelrc b/.babelrc deleted file mode 100644 index 3c078e9f99..0000000000 --- a/.babelrc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "presets": [ - "es2015" - ] -} diff --git a/assets/config.json b/assets/config.json new file mode 100644 index 0000000000..c394d8e338 --- /dev/null +++ b/assets/config.json @@ -0,0 +1,18 @@ +{ + "context": "assets", + "entry": { + "main": [ + "./scripts/main.js", + "./styles/main.scss" + ], + "customizer": [ + "./scripts/customizer.js" + ] + }, + "output": { + "path": "dist", + "publicPath": "/app/themes/sage/dist/" + }, + "devUrl": "http://example.dev", + "devPort": 3000 +} diff --git a/assets/scripts/main.js b/assets/scripts/main.js index 1039fa2603..0ace482862 100644 --- a/assets/scripts/main.js +++ b/assets/scripts/main.js @@ -1,5 +1,3 @@ -import "../styles/main.scss"; - import $ from 'jquery'; import Router from './util/router'; diff --git a/assets/styles/common/_variables.scss b/assets/styles/common/_variables.scss index 079070a994..3a1ae7a760 100644 --- a/assets/styles/common/_variables.scss +++ b/assets/styles/common/_variables.scss @@ -1,4 +1,5 @@ // Grid settings +$enable-flex: true; $main-sm-columns: 12; $sidebar-sm-columns: 4; diff --git a/config.json b/config.json deleted file mode 100644 index 05b7fc3d7a..0000000000 --- a/config.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "output": { - "path": "dist", - "publicPath": "/app/themes/sage/dist/" - }, - "options": { - "extractStyles": true - }, - "devUrl" : "http://example.dev" -} diff --git a/package.json b/package.json index 4cfa70d6b9..5a67b18abc 100644 --- a/package.json +++ b/package.json @@ -18,10 +18,10 @@ } ], "scripts": { - "build:production": "webpack -p", - "build": "webpack -d", - "watch": "node watch.js", - "lint": "eslint -c .eslintrc assets/scripts", + "build:production": "webpack -p --progress --release", + "build": "webpack -d --progress", + "watch": "node watch.js --watch", + "lint": "eslint -c .eslintrc assets/scripts watch.js webpack.config.js", "install": "composer install" }, "engines": { @@ -29,39 +29,42 @@ "npm": ">=2.1.5" }, "devDependencies": { - "assets-webpack-plugin": "^3.3.0", + "assets-webpack-plugin": "^3.4.0", "autoprefixer": "^6.1.0", - "babel-cli": "^6.1.18", - "babel-core": "^6.1.20", - "babel-eslint": "^5.0.0", - "babel-loader": "^6.1.0", + "babel-cli": "^6.6.5", + "babel-core": "^6.7.4", + "babel-eslint": "^6.0.0", + "babel-loader": "^6.2.4", "babel-preset-es2015": "^6.1.18", "babel-register": "^6.5.2", "body-parser": "^1.14.1", - "browser-sync": "^2.11.1", - "clean-webpack-plugin": "^0.1.3", - "css-loader": "^0.22.0", + "browser-sync": "^2.11.2", + "clean-webpack-plugin": "^0.1.8", + "css-loader": "^0.23.1", "cssnano": "^3.5.2", - "eslint": "^1.9.0", - "eslint-config-airbnb": "^1.0.0", + "eslint": "^2.5.1", + "eslint-config-airbnb": "^6.2.0", "eslint-loader": "^1.3.0", - "extract-text-webpack-plugin": "^0.9.1", + "eslint-plugin-react": "^4.2.3", + "extract-text-webpack-plugin": "^1.0.1", "file-loader": "^0.8.5", "image-webpack-loader": "^1.6.3", "imagemin-pngcrush": "^4.1.0", "imports-loader": "^0.6.5", + "minimist": "^1.2.0", "monkey-hot-loader": "0.0.3", "node-sass": "^3.4.2", "optimize-css-assets-webpack-plugin": "^1.3.0", "postcss": "^5.0.18", "postcss-loader": "^0.8.0", + "qs": "^6.1.0", "resolve-url-loader": "^1.4.3", "sass-loader": "^3.1.1", "style-loader": "^0.13.0", "url-loader": "^0.5.7", - "webpack": "^1.12.4", - "webpack-dev-middleware": "^1.2.0", - "webpack-hot-middleware": "^2.5.0" + "webpack": "^1.12.14", + "webpack-dev-middleware": "^1.6", + "webpack-hot-middleware": "^2.10.0" }, "dependencies": { "babel-runtime": "^6.5.0", diff --git a/watch.js b/watch.js index 280ace0f24..95189ff8aa 100644 --- a/watch.js +++ b/watch.js @@ -1,25 +1,31 @@ -/* eslint no-console: 0 */ - -process.env.SCRIPT = 'watch'; - +// External dependencies 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); +// Internal dependencies +var webpackConfig = require('./webpack.config'), + config = require('./assets/config'); + +// Internal variables +var host = 'http://localhost', + port = config.devPort || '3000', + compiler; + +webpackConfig.output.publicPath = host + ':' + port + config.output.publicPath; +compiler = webpack(webpackConfig); browserSync.init({ + port: port, proxy: { target: config.devUrl, middleware: [ webpackDevMiddleware(compiler, { - publicPath: devBuildConfig.output.publicPath, + publicPath: webpackConfig.output.publicPath, stats: { colors: true - }, + } }), webpackHotMiddleware(compiler, { log: browserSync.notify diff --git a/webpack.config.js b/webpack.config.js index b1e2a62490..9414b4b082 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,5 +1,8 @@ +// External dependencies var webpack = require('webpack'), path = require('path'), + argv = require('minimist')(process.argv.slice(2)), + qs = require('qs'), autoprefixer = require('autoprefixer'), Clean = require("clean-webpack-plugin"), AssetsPlugin = require('assets-webpack-plugin'), @@ -7,15 +10,29 @@ var webpack = require('webpack'), OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin'), cssnano = require('cssnano'); -var config = require('./config'), +// Internal dependencies +var config = require('./assets/config'); + +// Internal variables +var scriptsFilename = (argv.release) ? 'scripts/[name]_[hash].js' : 'scripts/[name].js', + stylesFilename = (argv.release) ? 'styles/[name]_[hash].css' : 'styles/[name].css', + sourceMapQueryStr = (argv.release) ? '-sourceMap' : '+sourceMap', + jsLoader, webpackConfig; -const DEBUG = (process.argv.lastIndexOf('-d') !== -1), - WATCH = (process.env.SCRIPT === 'watch'); +jsLoader = { + test: /\.js$/, + exclude: /(node_modules|bower_components)/, + loaders: [ 'babel?presets[]=es2015&cacheDirectory' ] +}; + +if (argv.watch) { // '--watch' to add monkey-hot + jsLoader.loaders.unshift('monkey-hot'); +} /** - * Process AssetsPlugin output - * and format for Sage: {"[name].[ext]":"[hash].[ext]"} + * Process AssetsPlugin output and format it + * for Sage: {"[name].[ext]":"[name]_[hash].[ext]"} * @param {Object} assets passed by processOutput * @return {String} JSON */ @@ -27,10 +44,12 @@ var assetsPluginProcessOutput = function (assets) { for (name in assets) { if (assets.hasOwnProperty(name)) { - for (ext in assets[name]) { - if (assets[name].hasOwnProperty(ext)) { - filename = name + '.' + ext; - results[filename] = path.basename(assets[name][ext]); + if (path.extname(assets[name]) === '') { + for (ext in assets[name]) { + if (assets[name].hasOwnProperty(ext)) { + filename = name + '.' + ext; + results[filename] = path.basename(assets[name][ext]); + } } } } @@ -47,7 +66,10 @@ var assetsPluginProcessOutput = function (assets) { var addHotMiddleware = function (entry) { var name, results = {}, - hotMiddlewareScript = 'webpack-hot-middleware/client?reload=true'; + hotMiddlewareScript = 'webpack-hot-middleware/client?' + qs.stringify({ + timeout: 20000, + reload: true + }); for (name in entry) { if (entry.hasOwnProperty(name)) { @@ -55,7 +77,7 @@ var addHotMiddleware = function (entry) { results[name] = [entry[name]]; } else { results[name] = entry[name].slice(0); - }; + } results[name].push(hotMiddlewareScript); } } @@ -63,114 +85,142 @@ var addHotMiddleware = function (entry) { } webpackConfig = { - entry: { - main: [ - './assets/scripts/main' - ], - customizer: [ - './assets/scripts/customizer' - ] - }, + context: path.resolve(config.context), + entry: config.entry, output: { path: path.join(__dirname, config.output.path), publicPath: config.output.publicPath, + filename: scriptsFilename }, module: { preLoaders: [ { - test: /\.jsx?$/, - exclude: /node_modules/, + test: /\.js?$/, + exclude: /(node_modules|bower_components)/, loader: 'eslint' } ], loaders: [ - { - test: /\.js$/, - exclude: /node_modules/, - loaders: [ - 'monkey-hot', - 'babel' - ], - }, + jsLoader, { test: /\.css$/, - exclude: /node_modules/, - loader: (WATCH) ? - 'style!css?sourceMap!postcss' : - ExtractTextPlugin.extract('style', 'css?sourceMap!postcss'), + loader: ExtractTextPlugin.extract('style', [ + 'css?' + sourceMapQueryStr, + 'postcss' + ]) }, { test: /\.scss$/, - exclude: /node_modules/, - loader: (WATCH) ? - 'style!css?sourceMap!postcss!sass?sourceMap' : - ExtractTextPlugin.extract('style', 'css?sourceMap!postcss!sass?sourceMap'), + loader: ExtractTextPlugin.extract('style', [ + 'css?' + sourceMapQueryStr, + 'postcss', + 'resolve-url?' + sourceMapQueryStr, + 'sass?' + sourceMapQueryStr + ]) }, - { - test: /\.(png|jpg|jpeg|gif)(\?v=[0-9]+\.?[0-9]+?\.?[0-9]+?)?$/, + test: /\.(png|jpg|jpeg|gif)(\?.*)?$/, loaders: [ - 'file?name=[path][name].[ext]&context=assets/', - 'image-webpack?bypassOnDebug&optimizationLevel=7&interlaced=false' - ], + 'file?' + qs.stringify({ + name: '[path][name].[ext]' + }), + 'image-webpack?' + JSON.stringify({ + bypassOnDebug:true, + progressive: true, + optimizationLevel: 7, + interlaced: true, + pngquant: { + quality: "65-90", + speed: 4 + }, + svgo: { + removeUnknownsAndDefaults: false, + cleanupIDs: false + } + }) + ] }, { - test: /\.(ttf|eot|svg)(\?v=[0-9]+\.?[0-9]+?\.?[0-9]+?)?$/, - loader: 'file?name=[path][name].[ext]&context=assets/', + test: /\.(ttf|eot|svg)(\?.*)?$/, + loader: 'file?' + qs.stringify({ + name: '[path][name].[ext]' + }) }, { - test: /\.woff(2)?(\?v=[0-9]+\.?[0-9]+?\.?[0-9]+?)?$/, - loader: 'url', - query: { + test: /\.woff(2)?(\?.*)?$/, + loader: 'url?' + qs.stringify({ limit: 10000, mimetype: "application/font-woff", - name: "[path][name].[ext]", - context: "assets/", - } - }, - ], + name: "[path][name].[ext]" + }) + } + ] + }, + resolve: { + extensions: [ '', '.js', '.json' ], + modulesDirectories: [ + 'node_modules', + 'bower_components' + ] }, - resolve: { extensions: [ '', '.js', '.json' ] }, externals: { jquery: 'jQuery' }, plugins: [ new Clean([config.output.path]), + new ExtractTextPlugin(stylesFilename, { + allChunks: true, + disable: (argv.watch === true) // '--watch' disable ExtractTextPlugin + }), new webpack.ProvidePlugin({ $: 'jquery', jQuery: 'jquery', 'window.jQuery': 'jquery', 'window.Tether': 'tether' - }), - new AssetsPlugin({ - path: config.output.path, - filename: 'assets.json', - fullPath: false, - processOutput: assetsPluginProcessOutput, }) ], - postcss: [ autoprefixer ], + postcss: [ + autoprefixer({ + browsers: [ + 'last 2 versions', + 'android 4', + 'opera 12' + ] + }) + ], eslint: { failOnWarning: false, - failOnError: true, + failOnError: true + }, + stats: { + colors: true } }; -if (DEBUG || WATCH) { - // development - webpackConfig.output.filename = 'scripts/[name].js'; +// '--watch' to push additional plugins to webpackConfig +if (argv.watch) { + webpackConfig.entry = addHotMiddleware(webpackConfig.entry); + webpackConfig.output.pathinfo = true; + webpackConfig.debug = true; + webpackConfig.devtool = '#cheap-module-source-map'; webpackConfig.plugins.push(new webpack.optimize.OccurenceOrderPlugin()); webpackConfig.plugins.push(new webpack.HotModuleReplacementPlugin()); 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: (WATCH || config.options.extractStyles === false) +} + +// '--release' to push additional plugins to webpackConfig +if (argv.release) { + webpackConfig.plugins.push(new AssetsPlugin({ + path: path.join(__dirname, config.output.path), + filename: 'assets.json', + fullPath: false, + processOutput: assetsPluginProcessOutput + })); + webpackConfig.plugins.push(new webpack.optimize.UglifyJsPlugin({ + compress: { + 'drop_debugger': true + } })); -} else { - // 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, cssProcessorOptions: { discardComments: { removeAll: true } }, @@ -178,12 +228,4 @@ if (DEBUG || 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 = '#cheap-module-source-map'; -} - module.exports = webpackConfig;