File: /var/www/innodrive/webpack.config.js
const path = require('path');
const glob = require('glob');
const webpack = require('webpack');
const merge = require('webpack-merge');
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin');
const ManifestPlugin = require('webpack-manifest-plugin');
const StylelintPlugin = require('stylelint-webpack-plugin');
const CleanPlugin = require('clean-webpack-plugin');
const SpriteLoaderPlugin = require('svg-sprite-loader/plugin');
const ProvidePlugin = webpack.ProvidePlugin;
const parts = require('./webpack.parts');
const pagesOptions = require('./webpack.pages');
const lintJSOptions = {
emitWarning: true,
failOnWarning: false,
failOnError: false,
quiet: true,
// Toggle autofix
fix: false,
cache: true,
formatter: require('eslint-friendly-formatter')
};
const pugOptions = {
pretty: true // Depricated, use with cautions
};
/*
To move all assets to some static folder
getPaths({ staticDir: 'some-name' })
To rename asset build folder
getPaths({ js: 'some-name' })
To move assets to the root build folder
getPaths({ css: '' })
Defaults values:
sourceDir - 'app',
buildDir - 'build',
staticDir - '',
images - 'images',
fonts - 'fonts',
css - 'styles',
js - 'scripts'
*/
const paths = getPaths({
sourceDir: 'src',
buildDir: 'public/wp-content/themes/innodrive',
images: 'assets/img',
js: 'js',
fonts: 'assets/fonts',
icons: 'assets/icons'
});
const pages = pagesOptions.map((options) => {
return parts.page({
title: options.title,
path: options.path,
template: path.join(paths.app, `templates/pages/${options.pathToFile}`)
});
});
const lintStylesOptions = {
context: path.resolve(__dirname, `${paths.app}/scss`),
syntax: 'scss',
emitErrors: false,
fix: true
};
const svgSpriteOptions = {
options: {
symbolId: filePath => path.basename(filePath, '.svg'),
extract: true,
spriteFilename: svgPath => `assets/sprite/sprite.[hash]${svgPath.substr(-4)}`,
esModule: false
},
include: path.join(paths.app, paths.icons)
};
const cssPreprocessorLoader = { loader: 'fast-sass-loader' };
const commonConfig = merge([
{
context: paths.app,
resolve: {
unsafeCache: true,
symlinks: false,
alias: {
'@utils': path.resolve(__dirname, `src/js/utils/utils.js`)
}
},
entry: {
main: `${paths.app}/index.js`,
sprite: `${paths.app}/js/sprite/sprite.js`
},
output: {
path: paths.build,
publicPath: parts.publicPath
},
devtool: process.env.NODE_ENV !== 'production' ? 'none' : 'source-map',
plugins: [
new ProvidePlugin({
jQuery: 'jquery'
}),
new FriendlyErrorsPlugin(),
new StylelintPlugin(lintStylesOptions),
new SpriteLoaderPlugin({
plainSprite: true
})
],
module: {
noParse: /\.min\.js/
}
},
parts.loadPug(pugOptions),
// parts.lintJS({ include: paths.app, exclude: path.resolve(__dirname, 'src/js/polyfills'), options: lintJSOptions }),
parts.lintJS({ include: paths.app, exclude: path.resolve(__dirname, 'src'), options: lintJSOptions }),
parts.loadFonts({
include: paths.app,
options: {
name: `${paths.fonts}/[name].[ext]`
}
}),
parts.svgSprite(svgSpriteOptions)
]);
const productionHot = merge([
{
mode: 'development',
output: {
filename: `${paths.js}/[name].bundle.dev.js`
},
plugins: [
new webpack.HashedModuleIdsPlugin(),
new CleanPlugin({
cleanOnceBeforeBuildPatterns: ['js/main.*.js', 'js/sprite.*.js', 'styles/style.*.css', 'manifest.json']
})
]
},
parts.loadJS(),
parts.loadImages({
exclude: path.join(paths.app, paths.icons),
options: {
limit: 15000,
name: `${paths.images}/[name].[ext]`
}
}),
parts.extractCSS({
include: paths.app,
use: [cssPreprocessorLoader],
options: {
filename: `${paths.css}/style.dev.css`
}
})
]);
const productionConfig = merge([
{
mode: 'development',
output: {
filename: `${paths.js}/[name].[hash].js`
},
performance: {
hints: 'warning', // 'error' or false are valid too
maxEntrypointSize: 100000, // in bytes
maxAssetSize: 450000 // in bytes
},
plugins: [
new webpack.HashedModuleIdsPlugin(),
new CleanPlugin({
cleanOnceBeforeBuildPatterns: ['js/main.*.js', 'js/sprite.*.js', 'styles/style.*.css', 'html/*.html', 'manifest.json']
}),
new ManifestPlugin({
map: (f) => {
if (String(f.name).includes('assets/sprite/sprite')) {
f.name = 'assets/sprite/sprite.svg';
}
return f;
},
fileName: 'manifest.json'
})
]
},
parts.minifyJS({
terserOptions: {
parse: {
// we want terser to parse ecma 8 code. However, we don't want it
// to apply any minfication steps that turns valid ecma 5 code
// into invalid ecma 5 code. This is why the 'compress' and 'output'
// sections only apply transformations that are ecma 5 safe
// https://github.com/facebook/create-react-app/pull/4234
ecma: 8
},
compress: {
ecma: 5,
warnings: false,
// Disabled because of an issue with Uglify breaking seemingly valid code:
// https://github.com/facebook/create-react-app/issues/2376
// Pending further investigation:
// https://github.com/mishoo/UglifyJS2/issues/2011
comparisons: false
},
mangle: {
safari10: true
},
output: {
ecma: 5,
comments: false,
// Turned on because emoji and regex is not minified properly using default
// https://github.com/facebook/create-react-app/issues/2488
ascii_only: true
}
},
// Use multi-process parallel running to improve the build speed
// Default number of concurrent runs: os.cpus().length - 1
parallel: true,
// Enable file caching
cache: true
}),
parts.loadJS(),
parts.extractCSS({
include: paths.app,
use: [parts.autoprefix(), cssPreprocessorLoader],
options: {
filename: `${paths.css}/style.[hash].css`
}
}),
parts.purifyCSS({
paths: glob.sync(`${paths.app}/**/*.+(pug|js)`, { nodir: true }),
styleExtensions: ['.scss']
}),
parts.minifyCSS({
options: {
discardComments: {
removeAll: true
}
}
}),
parts.loadImages({
exclude: path.join(paths.app, paths.icons),
options: {
limit: 15000,
name: `${paths.images}/[name].[ext]`
}
}),
// should go after loading images
parts.optimizeImages()
]);
const developmentConfig = merge([
{
mode: 'development'
},
parts.devServer({
host: '0.0.0.0', // to access from mobile
port: process.env.PORT || 3000
}),
parts.loadCSS({ include: paths.app, use: [cssPreprocessorLoader] }),
parts.loadImages({
include: paths.app,
exclude: path.join(paths.app, paths.icons)
}),
parts.loadJS()
]);
module.exports = env => {
process.env.NODE_ENV = env;
if (env === 'production-watch') {
return merge(
commonConfig,
productionHot
);
}
return merge(
commonConfig,
env === 'production' ? productionConfig : developmentConfig,
...pages
);
};
function getPaths ({
sourceDir = 'app',
buildDir = 'build',
staticDir = '',
icons = 'icons',
images = 'images',
fonts = 'fonts',
js = 'scripts',
css = 'styles'
} = {}) {
const assets = { images, fonts, js, css };
return Object.keys(assets).reduce((obj, assetName) => {
const assetPath = assets[assetName];
obj[assetName] = !staticDir ? assetPath : `${staticDir}/${assetPath}`;
return obj;
}, {
app: path.join(__dirname, sourceDir),
build: path.join(__dirname, buildDir),
staticDir,
icons
});
}