rekt-theme/webpack.config.babel.js

410 lines
11 KiB
JavaScript
Raw Normal View History

2018-07-13 09:11:16 +00:00
import {
DefinePlugin,
NamedModulesPlugin,
HashedModuleIdsPlugin,
LoaderOptionsPlugin,
HotModuleReplacementPlugin,
NoEmitOnErrorsPlugin,
ProvidePlugin,
optimize,
} from 'webpack';
import path from 'path';
import { isatty } from 'tty';
import chalk from 'chalk';
import _debug from 'debug';
// import slash from 'slash';
2018-07-13 11:55:46 +00:00
import cssnano from 'cssnano';
2018-07-13 09:11:16 +00:00
import CaseSensitivePathsPlugin from 'case-sensitive-paths-webpack-plugin';
// import ExtractTextPlugin from 'extract-text-webpack-plugin';
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
import HtmlPlugin from 'html-webpack-plugin';
import ProgressBarPlugin from 'progress-bar-webpack-plugin';
import UglifyJsPlugin from 'uglifyjs-webpack-plugin';
2018-07-13 11:55:46 +00:00
import OptimizeCssAssetsPlugin from 'optimize-css-assets-webpack-plugin';
import postcssSafeParser from 'postcss-safe-parser';
2018-07-13 09:11:16 +00:00
import Environment from './config/webpack/environment';
const debug = _debug('webpack:config');
debug.generated = _debug('webpack:config:generated');
debug.generated('filename:', __filename);
debug.generated('dirname:', __dirname);
const {
ModuleConcatenationPlugin,
} = optimize;
2018-07-22 21:26:43 +00:00
const dj = 'icedream';
export default (options, { mode }) => {
2018-07-13 09:11:16 +00:00
const environment = new Environment({
// @HACK
// ExtractTextPlugin,
MiniCssExtractPlugin,
});
environment.input(options);
environment.input(mode);
2018-07-13 09:11:16 +00:00
debug.generated(environment);
const {
development,
production,
server,
docker,
} = environment;
2018-07-22 21:26:43 +00:00
const baseOutputFilepath = 'static/theme/[type]/[filename]';
const filenames = {
css: '[name].[ext]',
default: development
? '[name].dev.[ext]'
// Always use a hash (in production) to prevent files with the same name causing issues
: '[name].[chunkhash:8].[ext]',
};
2018-07-13 09:11:16 +00:00
2018-07-22 21:26:43 +00:00
function replaceField(string, name, value) {
const retval = string.replace(new RegExp(`\\[${name}(.*?)\\]`), value);
debug('replaceField:', { args: { string, name, value } }, string, '=>', retval);
return retval;
}
function getOutputFilename(type) {
let filename = baseOutputFilepath;
switch (type) {
case 'css':
filename = replaceField(filename, 'type', type);
break;
default:
filename = replaceField(filename, 'type', `${type}/${dj}`);
break;
}
filename = replaceField(filename, 'filename', filenames[type] || filenames.default);
return filename;
}
function getAssetOutputFilename(type) {
let filename = getOutputFilename(type);
filename = replaceField(filename, 'chunkhash', '[hash$1]');
return filename;
}
const cssOutputFileName = getOutputFilename('css')
2018-07-13 09:11:16 +00:00
.replace(/\[ext(.*?)\]/g, 'css');
// .replace(/\[chunkhash(.*?)\]/g, '[contenthash$1]');
2018-07-22 21:26:43 +00:00
const cssChunkOutputFileName = getOutputFilename('css')
2018-07-13 09:11:16 +00:00
.replace(/\[chunkhash(.*?)\]/g, '[id$1]')
.replace(/\[ext(.*?)\]/g, 'css');
// const cssOutputRebasePath = `${slash(path.relative(path.dirname(cssOutputFileName), ''))}/`;
2018-07-22 21:39:45 +00:00
const cssOutputRebasePath = '../../../';
2018-07-13 09:11:16 +00:00
// Default options for file-loader
2018-07-22 21:26:43 +00:00
const getFileLoaderOptions = type => ({
name: getAssetOutputFilename(type),
2018-07-13 09:11:16 +00:00
publicPath: cssOutputRebasePath,
2018-07-22 21:26:43 +00:00
});
2018-07-13 09:11:16 +00:00
// Default options for url-loader
2018-07-22 21:26:43 +00:00
const getUrlLoaderOptions = type => ({
...getFileLoaderOptions(type),
2018-07-13 09:11:16 +00:00
// limit: 1, // Don't inline anything (but empty files) by default
limit: 4 * 1024,
2018-07-22 21:26:43 +00:00
});
2018-07-13 09:11:16 +00:00
const config = {
name: 'frontend',
target: 'web',
devServer: {
// inline: true,
// contentBase: path.join(__dirname, 'public'),
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET',
},
historyApiFallback: {
index: '/',
},
hot: true,
https: !docker,
noInfo: false,
overlay: true,
publicPath: '',
quiet: false,
disableHostCheck: docker,
watchOptions: {
ignored: /node_modules/,
},
},
module: {
rules: [
{
test: /\.jsx?/i,
include: [
path.resolve(__dirname, 'src'),
],
use: [
{
loader: 'babel-loader',
options: {
// Look for babel configuration in project directory
babelrc: false,
// Cache transformations to the filesystem (in default temp dir)
cacheDirectory: true,
presets: [
['babel-preset-env', {
targets: {
browsers: {},
uglify: false,
},
// spec: true,
// debug: development,
useBuiltIns: true,
modules: false, // do not transpile modules, webpack 2+ does that
}],
],
plugins: [
'babel-plugin-transform-class-properties',
'babel-plugin-transform-object-rest-spread',
['babel-plugin-transform-runtime', {
helpers: false,
polyfill: false,
regenerator: true,
}],
'babel-plugin-dynamic-import-webpack',
],
},
},
],
},
...[
/\.(gif|png|webp)$/i, // graphics
/\.svg$/i, // svg
/\.jpe?g$/i, // jpeg
].map(test => ({
test,
2018-07-22 21:26:43 +00:00
use: [
{
loader: 'url-loader',
options: {
...getUrlLoaderOptions('img'),
// fallback: 'responsive-loader',
},
},
{
loader: 'image-webpack-loader',
options: {
mozjpeg: {
progressive: true,
quality: 80,
},
optipng: {
enabled: true,
optimizationLevel: 7,
},
pngquant: {
enabled: false,
quality: '65-85',
speed: 2,
strip: true,
},
gifsicle: {
interlaced: false,
},
disable: development,
},
},
2018-07-22 21:26:43 +00:00
],
2018-07-13 09:11:16 +00:00
})),
...[
/\.(mp4|ogg|webm)$/i, // video
/\.(wav|mp3|m4a|aac|oga)$/i, // audio
].map(test => ({
test,
loader: 'url-loader',
2018-07-22 21:26:43 +00:00
options: getUrlLoaderOptions('media'),
})),
...[
/\.(eot|otf|ttf|woff|woff2)$/i, // fonts
].map(test => ({
test,
loader: 'url-loader',
options: getUrlLoaderOptions('font'),
2018-07-13 09:11:16 +00:00
})),
2018-07-22 21:26:43 +00:00
2018-07-13 09:11:16 +00:00
{
test: /\.css$/,
use: environment.styleLoaders(),
},
{
test: /\.s[ac]ss$/,
use: environment.styleLoaders({
loader: 'sass-loader',
}),
},
// {
// test: /\.styl$/,
// use: environment.styleLoaders({
// loader: 'stylus-loader',
// }),
// },
],
strictExportPresence: true,
},
output: {
2018-07-22 21:26:43 +00:00
filename: replaceField(getOutputFilename('js'), 'ext', 'js'),
chunkFilename: replaceField(getOutputFilename('js'), 'ext', 'js'),
2018-07-13 09:11:16 +00:00
path: path.join(__dirname, 'dist'),
publicPath: '',
globalObject: 'this', // https://github.com/webpack-contrib/worker-loader/issues/142#issuecomment-385764803
},
plugins: [
// Show progress as a bar during build
isatty(1) && new ProgressBarPlugin({
complete: chalk.white('\u2588'),
incomplete: chalk.grey('\u2591'),
format: `:bar ${chalk.cyan.bold(':percent')} Webpack build: ${chalk.grey(':msg')}`,
}),
// Enforce case-sensitive import paths
new CaseSensitivePathsPlugin(),
// Replace specified expressions with values
new DefinePlugin({
'process.env.__DEV__': JSON.stringify(development),
'process.env.__PROD__': JSON.stringify(production),
'process.env.__SERVER__': JSON.stringify(server),
'process.env.NODE_ENV': JSON.stringify(production ? 'production' : 'development'),
}),
// Publish modules that are expected by other dependencies to be
// registered on `window` global in respective expected way.
new ProvidePlugin({
jQuery: 'jquery',
$: 'jquery',
jquery: 'jquery',
}),
// Dev server build
...[
// Hot module reloading
new HotModuleReplacementPlugin(),
new NoEmitOnErrorsPlugin(),
// Use paths as names when serving
new NamedModulesPlugin(),
].filter(() => server),
// If we're not serving, we're creating a static build
...[// Extract imported stylesheets out into .css files
new MiniCssExtractPlugin({
filename: cssOutputFileName,
chunkFileName: cssChunkOutputFileName,
}),
].filter(() => !server),
// If we're generating an HTML file, we must be building a web app, so
// configure deterministic hashing for long-term caching.
// Generate stable module ids instead of having Webpack assign integers.
// NamedModulesPlugin allows for easier debugging and
// HashedModuleIdsPlugin does this without adding too much to bundle
// size.
development
? new NamedModulesPlugin()
: new HashedModuleIdsPlugin(),
// Production builds
...[
new LoaderOptionsPlugin({ debug: false, minimize: true }),
// Hoisting
new ModuleConcatenationPlugin(),
].filter(() => production),
new HtmlPlugin({
template: path.join(__dirname, 'src', 'index.html'),
filename: 'index.html',
hash: false,
inject: true,
compile: true,
favicon: false,
minify: production ? {
removeComments: true,
removeRedundantAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
useShortDoctype: true,
// includeAutoGeneratedTags: false,
collapseWhitespace: true,
// conservativeCollapse: true,
} : false,
cache: true,
showErrors: true,
chunks: 'all',
excludeChunks: [],
title: 'REKT Network',
xhtml: false,
chunksSortMode: 'dependency',
}),
].filter(plugin => plugin !== false),
resolve: {
extensions: [
'.js', '.jsx',
],
},
resolveLoader: {
modules: ['node_modules'],
},
optimization: {
minimize: production,
minimizer: [
new UglifyJsPlugin({
parallel: true,
uglifyOptions: {
compress: {
warnings: false,
},
output: {
comments: false,
},
},
sourceMap: true,
}),
2018-07-13 11:55:46 +00:00
new OptimizeCssAssetsPlugin({
cssProcessor: cssnano,
cssProcessorOptions: {
2018-07-22 22:02:24 +00:00
preset: 'advanced',
2018-07-13 11:55:46 +00:00
parser: postcssSafeParser,
discardComments: { removeAll: true },
},
canPrint: true,
}),
2018-07-13 09:11:16 +00:00
],
},
devtool: server ? 'cheap-module-source-map' : 'source-map',
entry: {
2018-07-22 21:26:43 +00:00
[dj]: [
2018-07-13 09:11:16 +00:00
path.join(__dirname, 'src'),
],
},
};
debug.generated(config);
return config;
};