webpack implementation

This commit is contained in:
Patrick Vézina
2016-03-13 17:08:30 -04:00
parent 610c46b3b1
commit 7d1baa0fe7
15 changed files with 319 additions and 445 deletions

5
.babelrc Normal file
View File

@@ -0,0 +1,5 @@
{
"presets": [
"es2015"
]
}

View File

@@ -1,3 +0,0 @@
{
"directory": "bower_components"
}

View File

@@ -10,6 +10,7 @@
"node": true,
"amd": true
},
"parser": "babel-eslint",
"rules": {
"no-console": 0
}

View File

@@ -1,27 +0,0 @@
{
"dependencies": {
"main.js": {
"files": [
"scripts/main.js"
],
"main": true
},
"main.css": {
"files": [
"styles/main.scss"
],
"main": true
},
"customizer.js": {
"files": [
"scripts/customizer.js"
]
},
"jquery.js": {
"bower": ["jquery"]
}
},
"config": {
"devUrl": "http://example.dev"
}
}

View File

@@ -1,77 +1,51 @@
/* ========================================================================
* DOM-based Routing
* Based on http://goo.gl/EUTi53 by Paul Irish
*
* Only fires on body classes that match. If a body class contains a dash,
* replace the dash with an underscore when adding it to the object below.
*
* .noConflict()
* The routing is enclosed within an anonymous function so that you can
* always reference jQuery with $, even when in .noConflict() mode.
* ======================================================================== */
import "../styles/main.scss";
(function($) {
import $ from 'jquery';
import Router from './util/router';
// Use this variable to set up the common and page specific functions. If you
// rename this variable, you will also need to rename the namespace below.
var Sage = {
// All pages
'common': {
init: function() {
// JavaScript to be fired on all pages
},
finalize: function() {
// JavaScript to be fired on all pages, after page specific JS is fired
}
// Import Bootstrap
import "bootstrap/dist/js/umd/util.js";
import "bootstrap/dist/js/umd/alert.js";
import "bootstrap/dist/js/umd/button.js";
import "bootstrap/dist/js/umd/carousel.js";
import "bootstrap/dist/js/umd/collapse.js";
import "bootstrap/dist/js/umd/dropdown.js";
import "bootstrap/dist/js/umd/modal.js";
import "bootstrap/dist/js/umd/scrollspy.js";
import "bootstrap/dist/js/umd/tab.js";
import "bootstrap/dist/js/umd/tooltip.js";
import "bootstrap/dist/js/umd/popover.js";
// Use this variable to set up the common and page specific functions. If you
// rename this variable, you will also need to rename the namespace below.
var Sage = {
// All pages
'common': {
init: function() {
// JavaScript to be fired on all pages
},
// Home page
'home': {
init: function() {
// JavaScript to be fired on the home page
},
finalize: function() {
// JavaScript to be fired on the home page, after the init JS
}
},
// About us page, note the change from about-us to about_us.
'about_us': {
init: function() {
// JavaScript to be fired on the about us page
}
finalize: function() {
// JavaScript to be fired on all pages, after page specific JS is fired
}
};
// The routing fires all common scripts, followed by the page specific scripts.
// Add additional events for more control over timing e.g. a finalize event
var UTIL = {
fire: function(func, funcname, args) {
var fire;
var namespace = Sage;
funcname = (funcname === undefined) ? 'init' : funcname;
fire = func !== '';
fire = fire && namespace[func];
fire = fire && typeof namespace[func][funcname] === 'function';
if (fire) {
namespace[func][funcname](args);
}
},
// Home page
'home': {
init: function() {
// JavaScript to be fired on the home page
},
loadEvents: function() {
// Fire common init JS
UTIL.fire('common');
// Fire page-specific init JS, and then finalize JS
$.each(document.body.className.replace(/-/g, '_').split(/\s+/), function(i, classnm) {
UTIL.fire(classnm);
UTIL.fire(classnm, 'finalize');
});
// Fire common finalize JS
UTIL.fire('common', 'finalize');
finalize: function() {
// JavaScript to be fired on the home page, after the init JS
}
};
},
// About us page, note the change from about-us to about_us.
'about_us': {
init: function() {
// JavaScript to be fired on the about us page
}
}
};
// Load Events
$(document).ready(UTIL.loadEvents);
})(jQuery); // Fully reference jQuery after this point.
// Load Events
$(document).ready(function () {
new Router(Sage).loadEvents();
});

View File

@@ -0,0 +1,45 @@
import $ from 'jquery';
/* ========================================================================
* DOM-based Routing
* Based on http://goo.gl/EUTi53 by Paul Irish
*
* Only fires on body classes that match. If a body class contains a dash,
* replace the dash with an underscore when adding it to the object below.
* ======================================================================== */
// The routing fires all common scripts, followed by the page specific scripts.
// Add additional events for more control over timing e.g. a finalize event
export default class Router {
constructor(namespace) {
this.namespace = namespace;
}
fire(func, funcname, args) {
funcname = (funcname === undefined) ? 'init' : funcname;
let fire = func !== '';
fire = fire && this.namespace[func];
fire = fire && typeof this.namespace[func][funcname] === 'function';
if (fire) {
this.namespace[func][funcname](args);
}
}
loadEvents() {
// Fire common init JS
this.fire('common');
// Fire page-specific init JS, and then finalize JS
$.each(
document.body.className.replace(/-/g, '_').split(/\s+/),
(i, className) => {
this.fire(className);
this.fire(className, 'finalize');
}
);
// Fire common finalize JS
this.fire('common', 'finalize');
}
}

View File

@@ -1,15 +1,18 @@
// Grid system
.main {
@include make-col($main-sm-columns, $main-sm-columns, $grid-gutter-width);
@include make-col();
@include media-breakpoint-up(sm) {
@include make-col-span($main-sm-columns);
.sidebar-primary & {
@include make-col($main-sm-columns - $sidebar-sm-columns, $main-sm-columns, $grid-gutter-width);
@include make-col-span($main-sm-columns - $sidebar-sm-columns);
}
}
}
.sidebar {
@include make-col();
@include media-breakpoint-up(sm) {
@include make-col($sidebar-sm-columns, $main-sm-columns, $grid-gutter-width);
@include make-col-span($sidebar-sm-columns);
}
}

View File

@@ -1,9 +1,7 @@
@import "common/variables";
// Automatically injected Bower dependencies via wiredep (never manually edit this block)
// bower:scss
@import "../../bower_components/bootstrap/scss/bootstrap.scss";
// endbower
// Import Boostrap from node_modules/...
@import "~bootstrap/scss/bootstrap";
@import "common/global";
@import "components/buttons";

View File

@@ -1,13 +0,0 @@
{
"name": "sage",
"homepage": "https://roots.io/sage/",
"authors": [
"Roots <team@roots.io>"
],
"license": "MIT",
"private": true,
"dependencies": {
"bootstrap": "git://github.com/twbs/bootstrap.git#v4-dev",
"tether": "^1.1.1"
}
}

30
dev.js Normal file
View File

@@ -0,0 +1,30 @@
/* eslint no-console: 0 */
var webpack = require('webpack'),
webpackDevMiddleware = require('webpack-dev-middleware'),
webpackHotMiddleware = require('webpack-hot-middleware'),
browserSync = require('browser-sync');
var devBuildConfig = require('./webpack.config'),
compiler = webpack(devBuildConfig);
browserSync.init({
proxy: {
target: 'http://example.dev', // change to dev server
middleware: [
webpackDevMiddleware(compiler, {
publicPath: devBuildConfig.output.publicPath,
stats: {
colors: true
},
}),
webpackHotMiddleware(compiler, {
log: browserSync.notify
})
]
},
files: [
'templates/**/*.php',
'src/**/*.php'
]
});

View File

@@ -1,288 +0,0 @@
// ## Globals
var argv = require('minimist')(process.argv.slice(2));
var autoprefixer = require('gulp-autoprefixer');
var browserSync = require('browser-sync').create();
var changed = require('gulp-changed');
var concat = require('gulp-concat');
var flatten = require('gulp-flatten');
var gulp = require('gulp');
var gulpif = require('gulp-if');
var imagemin = require('gulp-imagemin');
var eslint = require('gulp-eslint');
var lazypipe = require('lazypipe');
var less = require('gulp-less');
var merge = require('merge-stream');
var cssNano = require('gulp-cssnano');
var plumber = require('gulp-plumber');
var rev = require('gulp-rev');
var runSequence = require('run-sequence');
var sass = require('gulp-sass');
var sourcemaps = require('gulp-sourcemaps');
var uglify = require('gulp-uglify');
// See https://github.com/austinpray/asset-builder
var manifest = require('asset-builder')('./assets/manifest.json');
// `path` - Paths to base asset directories. With trailing slashes.
// - `path.source` - Path to the source files. Default: `assets/`
// - `path.dist` - Path to the build directory. Default: `dist/`
var path = manifest.paths;
// `config` - Store arbitrary configuration values here.
var config = manifest.config || {};
// `globs` - These ultimately end up in their respective `gulp.src`.
// - `globs.js` - Array of asset-builder JS dependency objects. Example:
// ```
// {type: 'js', name: 'main.js', globs: []}
// ```
// - `globs.css` - Array of asset-builder CSS dependency objects. Example:
// ```
// {type: 'css', name: 'main.css', globs: []}
// ```
// - `globs.fonts` - Array of font path globs.
// - `globs.images` - Array of image path globs.
// - `globs.bower` - Array of all the main Bower files.
var globs = manifest.globs;
// `project` - paths to first-party assets.
// - `project.js` - Array of first-party JS assets.
// - `project.css` - Array of first-party CSS assets.
var project = manifest.getProjectGlobs();
// CLI options
var enabled = {
// Enable static asset revisioning when `--production`
rev: argv.production,
// Disable source maps when `--production`
maps: !argv.production,
// Fail styles task on error when `--production`
failStyleTask: argv.production,
// Fail due to ESLint warnings only when `--production`
failESLint: argv.production,
// Strip debug statments from javascript when `--production`
stripJSDebug: argv.production
};
// Path to the compiled assets manifest in the dist directory
var revManifest = path.dist + 'assets.json';
// ## Reusable Pipelines
// See https://github.com/OverZealous/lazypipe
// ### CSS processing pipeline
// Example
// ```
// gulp.src(cssFiles)
// .pipe(cssTasks('main.css')
// .pipe(gulp.dest(path.dist + 'styles'))
// ```
var cssTasks = function(filename) {
return lazypipe()
.pipe(function() {
return gulpif(!enabled.failStyleTask, plumber());
})
.pipe(function() {
return gulpif(enabled.maps, sourcemaps.init());
})
.pipe(function() {
return gulpif('*.less', less());
})
.pipe(function() {
return gulpif('*.scss', sass({
outputStyle: 'nested', // libsass doesn't support expanded yet
precision: 10,
includePaths: ['.'],
errLogToConsole: !enabled.failStyleTask
}));
})
.pipe(concat, filename)
.pipe(autoprefixer, {
browsers: [
'last 2 versions',
'android 4',
'opera 12'
]
})
.pipe(cssNano, {
safe: true
})
.pipe(function() {
return gulpif(enabled.rev, rev());
})
.pipe(function() {
return gulpif(enabled.maps, sourcemaps.write('.', {
sourceRoot: 'assets/styles/'
}));
})();
};
// ### JS processing pipeline
// Example
// ```
// gulp.src(jsFiles)
// .pipe(jsTasks('main.js')
// .pipe(gulp.dest(path.dist + 'scripts'))
// ```
var jsTasks = function(filename) {
return lazypipe()
.pipe(function() {
return gulpif(enabled.maps, sourcemaps.init());
})
.pipe(concat, filename)
.pipe(uglify, {
compress: {
'drop_debugger': enabled.stripJSDebug
}
})
.pipe(function() {
return gulpif(enabled.rev, rev());
})
.pipe(function() {
return gulpif(enabled.maps, sourcemaps.write('.', {
sourceRoot: 'assets/scripts/'
}));
})();
};
// ### Write to rev manifest
// If there are any revved files then write them to the rev manifest.
// See https://github.com/sindresorhus/gulp-rev
var writeToManifest = function(directory) {
return lazypipe()
.pipe(gulp.dest, path.dist + directory)
.pipe(browserSync.stream, {match: '**/*.{js,css}'})
.pipe(rev.manifest, revManifest, {
base: path.dist,
merge: true
})
.pipe(gulp.dest, path.dist)();
};
// ## Gulp tasks
// Run `gulp -T` for a task summary
// ### Styles
// `gulp styles` - Compiles, combines, and optimizes Bower CSS and project CSS.
// By default this task will only log a warning if a precompiler error is
// raised. If the `--production` flag is set: this task will fail outright.
gulp.task('styles', ['wiredep'], function() {
var merged = merge();
manifest.forEachDependency('css', function(dep) {
var cssTasksInstance = cssTasks(dep.name);
if (!enabled.failStyleTask) {
cssTasksInstance.on('error', function(err) {
console.error(err.message);
this.emit('end');
});
}
merged.add(gulp.src(dep.globs, {base: 'styles'})
.pipe(cssTasksInstance));
});
return merged
.pipe(writeToManifest('styles'));
});
// ### Scripts
// `gulp scripts` - Runs ESLint then compiles, combines, and optimizes Bower JS
// and project JS.
gulp.task('scripts', ['lint'], function() {
var merged = merge();
manifest.forEachDependency('js', function(dep) {
merged.add(
gulp.src(dep.globs, {base: 'scripts'})
.pipe(jsTasks(dep.name))
);
});
return merged
.pipe(writeToManifest('scripts'));
});
// ### Fonts
// `gulp fonts` - Grabs all the fonts and outputs them in a flattened directory
// structure. See: https://github.com/armed/gulp-flatten
gulp.task('fonts', function() {
return gulp.src(globs.fonts)
.pipe(flatten())
.pipe(gulp.dest(path.dist + 'fonts'))
.pipe(browserSync.stream());
});
// ### Images
// `gulp images` - Run lossless compression on all the images.
gulp.task('images', function() {
return gulp.src(globs.images)
.pipe(imagemin({
progressive: true,
interlaced: true,
svgoPlugins: [{removeUnknownsAndDefaults: false}, {cleanupIDs: false}]
}))
.pipe(gulp.dest(path.dist + 'images'))
.pipe(browserSync.stream());
});
// ### ESLint
// `gulp lint` - Lints configuration JSON and project JS.
gulp.task('lint', function() {
return gulp.src([
'gulpfile.js'
].concat(project.js))
.pipe(eslint())
.pipe(eslint.format())
.pipe(gulpif(enabled.failESLint, eslint.failAfterError()));
});
// ### Clean
// `gulp clean` - Deletes the build folder entirely.
gulp.task('clean', require('del').bind(null, [path.dist]));
// ### Watch
// `gulp watch` - Use BrowserSync to proxy your dev server and synchronize code
// changes across devices. Specify the hostname of your dev server at
// `manifest.config.devUrl`. When a modification is made to an asset, run the
// build step for that asset and inject the changes into the page.
// See: http://www.browsersync.io
gulp.task('watch', function() {
browserSync.init({
files: ['{src,templates}/**/*.php'],
proxy: config.devUrl,
snippetOptions: {
whitelist: ['/wp-admin/admin-ajax.php'],
blacklist: ['/wp-admin/**']
}
});
gulp.watch([path.source + 'styles/**/*'], ['styles']);
gulp.watch([path.source + 'scripts/**/*'], ['lint', 'scripts']);
gulp.watch([path.source + 'fonts/**/*'], ['fonts']);
gulp.watch([path.source + 'images/**/*'], ['images']);
gulp.watch(['bower.json', 'assets/manifest.json'], ['build']);
});
// ### Build
// `gulp build` - Run all the build tasks but don't clean up beforehand.
// Generally you should be running `gulp` instead of `gulp build`.
gulp.task('build', function(callback) {
runSequence('styles',
'scripts',
['fonts', 'images'],
callback);
});
// ### Wiredep
// `gulp wiredep` - Automatically inject Less and Sass Bower dependencies. See
// https://github.com/taptapship/wiredep
gulp.task('wiredep', function() {
var wiredep = require('wiredep').stream;
return gulp.src(project.css)
.pipe(wiredep())
.pipe(changed(path.source + 'styles', {
hasChanged: changed.compareSha1Digest
}))
.pipe(gulp.dest(path.source + 'styles'));
});
// ### Gulp
// `gulp` - Run a complete build. To compile for production run `gulp --production`.
gulp.task('default', ['clean'], function() {
gulp.start('build');
});

View File

@@ -18,45 +18,52 @@
}
],
"scripts": {
"build": "bower install && gulp",
"lint": "gulp lint"
"build:production": "export SAGE_ENV=production && webpack",
"build": "export SAGE_ENV=development && webpack",
"watch": "export SAGE_ENV=development && node dev.js"
},
"engines": {
"node": ">= 0.12.0",
"npm": ">=2.1.5"
},
"devDependencies": {
"asset-builder": "^1.1.0",
"browser-sync": "^2.10.1",
"del": "^2.2.0",
"gulp": "^3.9.0",
"gulp-autoprefixer": "^3.1.0",
"gulp-changed": "^1.3.0",
"gulp-concat": "^2.6.0",
"gulp-cssnano": "^2.1.0",
"gulp-eslint": "^1.1.1",
"gulp-flatten": "0.2.0",
"gulp-if": "^2.0.0",
"gulp-imagemin": "^2.4.0",
"gulp-less": "^3.0.5",
"gulp-cssnano": "^2.1.0",
"gulp-eslint": "^1.1.1",
"gulp-flatten": "0.2.0",
"gulp-if": "^2.0.0",
"gulp-imagemin": "^2.4.0",
"gulp-less": "^3.0.5",
"gulp-plumber": "^1.0.1",
"gulp-rename": "^1.2.2",
"gulp-rev": "^6.0.1",
"gulp-sass": "^2.1.1",
"gulp-sourcemaps": "^1.6.0",
"gulp-uglify": "^1.5.1",
"assets-webpack-plugin": "^3.3.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-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",
"cssnano": "^3.5.2",
"eslint": "^1.9.0",
"eslint-config-airbnb": "^1.0.0",
"eslint-loader": "^1.3.0",
"extract-text-webpack-plugin": "^0.9.1",
"file-loader": "^0.8.5",
"imagemin-pngcrush": "^4.1.0",
"lazypipe": "^1.0.1",
"merge-stream": "^1.0.0",
"minimist": "^1.2.0",
"run-sequence": "^1.1.5",
"traverse": "^0.6.6",
"wiredep": "^3.0.0"
"imports-loader": "^0.6.5",
"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",
"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"
},
"dependencies": {
"babel-runtime": "^6.5.0",
"bootstrap": "github:twbs/bootstrap#v4.0.0-alpha.2",
"jquery": "^2.1.4",
"tether": "^1.2.0"
}
}

View File

@@ -12,5 +12,5 @@ add_action('customize_register', function (\WP_Customize_Manager $wp_customize)
* Customizer JS
*/
add_action('customize_preview_init', function () {
wp_enqueue_script('sage/customizer.js', asset_path('scripts/customizer.js'), ['customize-preview'], null, true);
wp_enqueue_script('sage/customizer.js', asset_path('customizer.js'), ['customize-preview'], null, true);
});

6
src/setup.php Normal file → Executable file
View File

@@ -6,8 +6,8 @@ use Roots\Sage\Template;
* Theme assets
*/
add_action('wp_enqueue_scripts', function () {
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);
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);
}, 100);
/**
@@ -62,7 +62,7 @@ add_action('after_setup_theme', function () {
* Use main stylesheet for visual editor
* @see assets/styles/layouts/_tinymce.scss
*/
add_editor_style(asset_path('styles/main.css'));
add_editor_style(asset_path('main.css'));
});
/**

142
webpack.config.js Normal file
View File

@@ -0,0 +1,142 @@
var webpack = require('webpack'),
path = require('path'),
autoprefixer = require('autoprefixer'),
Clean = require("clean-webpack-plugin"),
AssetsPlugin = require('assets-webpack-plugin'),
ExtractTextPlugin = require('extract-text-webpack-plugin'),
OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin'),
cssnano = require('cssnano');
var SAGE_ENV = process.env.SAGE_ENV || 'development',
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" }
var assetsPluginProcessOutput = function (assets) {
var results = {},
name,
ext;
for (name in assets) {
if (assets.hasOwnProperty(name)) {
for (ext in assets[name]) {
if (assets[name].hasOwnProperty(ext)) {
results[name + '.' + ext] = assets[name][ext];
}
}
}
}
return JSON.stringify(results);
}
webpackConfig = {
entry: {
main: [
'./assets/scripts/main'
],
customizer: [
'./assets/scripts/customizer'
]
},
output: {
path: sage.dist,
publicPath: sage.publicPath
},
module: {
preLoaders: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'eslint'
}
],
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loaders: ['monkey-hot', 'babel']
},
{
test: /\.css$/,
exclude: /node_modules/,
loader: ExtractTextPlugin.extract('style', 'css?sourceMap!postcss')
},
{
test: /\.scss$/,
exclude: /node_modules/,
loader: ExtractTextPlugin.extract('style', 'css?sourceMap!postcss!sass?sourceMap')
},
{
test: /\.(ttf|eot|svg)$/,
loader: 'url?limit=10000'
},
{
test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
loader: 'url?limit=10000&mimetype=application/font-woff'
},
{
test: /\.(png|jpg|jpeg|gif)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
loader: 'file-loader'
}
],
},
resolve: { extensions: [ '', '.js', '.json' ] },
externals: {
jquery: 'jQuery'
},
plugins: [
new Clean([sage.dist]),
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
'window.jQuery': 'jquery',
'window.Tether': 'tether'
}),
new AssetsPlugin({
path: sage.dist,
filename: sage.manifest,
fullPath: false,
processOutput: assetsPluginProcessOutput,
})
],
postcss: [ autoprefixer ],
eslint: {
failOnWarning: false,
failOnError: true,
}
};
if ( SAGE_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.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 }));
} else {
// production
webpackConfig.output.filename = '[name].[hash].js';
webpackConfig.output.sourceMapFilename = '[file].[hash].map';
webpackConfig.plugins.push(new ExtractTextPlugin('[name].[hash].css'));
webpackConfig.plugins.push(new webpack.optimize.UglifyJsPlugin());
webpackConfig.plugins.push(new OptimizeCssAssetsPlugin({
cssProcessor: cssnano,
cssProcessorOptions: { discardComments: { removeAll: true } },
canPrint: true
}));
}
module.exports = webpackConfig;