vizon-countdown-website/packages/frontend/webpack.config.babel.js

310 lines
8.9 KiB
JavaScript
Raw Permalink Normal View History

2017-08-25 06:28:44 +00:00
import {
DefinePlugin,
NamedModulesPlugin,
HashedModuleIdsPlugin,
LoaderOptionsPlugin,
ContextReplacementPlugin,
HotModuleReplacementPlugin,
NoEmitOnErrorsPlugin,
optimize,
} from 'webpack';
import path from 'path';
import { isatty } from 'tty';
import chalk from 'chalk';
import _debug from 'debug';
2017-08-25 06:28:44 +00:00
import slash from 'slash';
import CaseSensitivePathsPlugin from 'case-sensitive-paths-webpack-plugin';
import ExtractTextPlugin from 'extract-text-webpack-plugin';
import HtmlPlugin from 'html-webpack-plugin';
import ProgressBarPlugin from 'progress-bar-webpack-plugin';
import Environment from '../../config/webpack/environment';
import injectManifestPlugin from '../../config/webpack/injectManifestPlugin';
const debug = _debug('webpack:config');
debug.generated = _debug('webpack:config:generated');
debug.generated('filename:', __filename);
debug.generated('dirname:', __dirname);
2017-08-25 06:28:44 +00:00
const {
CommonsChunkPlugin,
UglifyJsPlugin,
ModuleConcatenationPlugin,
} = optimize;
const websiteSubfolder = 'website';
2017-08-25 06:28:44 +00:00
export default (options) => {
const environment = new Environment({
ExtractTextPlugin, // @HACK
});
2017-08-25 06:28:44 +00:00
environment.input(options);
2017-08-25 06:28:44 +00:00
debug.generated(environment);
2017-08-25 06:28:44 +00:00
const {
development,
production,
server,
locales,
autoprefixerTargets,
2017-08-25 06:28:44 +00:00
} = environment;
const baseOutputFilename = development
? 'assets/dev/[name].dev.[ext]'
// Always use a hash (in production) to prevent files with the same name causing issues
: 'assets/prod/[chunkhash:2]/[name].[chunkhash:8].[ext]';
const webpackChunkFilename = baseOutputFilename
.replace(/\[ext(.*?)\]/g, 'js');
const webpackOutputFilename = webpackChunkFilename;
const assetOutputFilename = baseOutputFilename
.replace(/\[chunkhash(.*?)\]/g, '[hash$1]');
const cssOutputFileName = baseOutputFilename
.replace(/\[ext(.*?)\]/g, 'css')
.replace(/\[chunkhash(.*?)\]/g, '[contenthash$1]');
const cssOutputRebasePath = `${slash(path.relative(path.dirname(cssOutputFileName), ''))}/`;
// Default options for url-loader
const urlLoaderOptions = {
limit: 1, // Don't inline anything (but empty files) by default
name: assetOutputFilename,
publicPath: cssOutputRebasePath,
};
const config = {
name: 'frontend',
target: 'web',
2017-08-25 06:28:44 +00:00
devServer: {
// inline: true,
headers: {
'Access-Control-Allow-Origin': '*',
},
historyApiFallback: true,
hot: true,
noInfo: true,
overlay: true,
publicPath: '',
quiet: false,
2017-08-25 06:28:44 +00:00
watchOptions: {
ignored: /node_modules/,
},
},
2017-08-25 06:28:44 +00:00
module: {
rules: [
{
test: /\.jsx?/,
loader: 'babel-loader',
exclude: /node_modules/,
options: {
2017-08-25 21:35:33 +00:00
// Look for babel configuration in project directory
babelrc: false,
2017-08-25 06:28:44 +00:00
// Cache transformations to the filesystem (in default temp dir)
cacheDirectory: true,
presets: [
['babel-preset-env', {
targets: {
browsers: autoprefixerTargets,
uglify: false,
},
// spec: true,
// debug: development,
modules: false, // do not transpile modules, webpack 2+ does that
}],
'babel-preset-react',
],
plugins: [
'babel-plugin-transform-react-constant-elements',
'babel-plugin-transform-class-properties',
['babel-plugin-transform-runtime', {
helpers: false,
polyfill: false,
regenerator: true,
}],
'babel-plugin-dynamic-import-webpack',
],
2017-08-25 06:28:44 +00:00
},
},
...[
/\.(gif|png|webp)$/i, // graphics
/\.svg$/i, // svg
/\.jpe?g$/i, // jpeg
/\.(mp4|ogg|webm)$/i, // video
/\.(eot|otf|ttf|woff|woff2)$/i, // fonts
/\.(wav|mp3|m4a|aac|oga)$/i, // audio
].map(test => ({
test,
loader: 'url-loader',
options: urlLoaderOptions,
})),
{
test: /\.css$/,
use: environment.styleLoaders(),
2017-08-25 06:28:44 +00:00
},
{
test: /\.s[ac]ss$/,
use: environment.styleLoaders(
2017-08-25 06:28:44 +00:00
{
loader: 'sass-loader',
},
),
},
],
strictExportPresence: true,
},
2017-08-25 06:28:44 +00:00
output: {
filename: webpackOutputFilename,
chunkFilename: webpackChunkFilename,
path: path.join(__dirname, 'lib', websiteSubfolder),
2017-08-25 06:28:44 +00:00
publicPath: '',
},
2017-08-25 06:28:44 +00:00
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'),
}),
// 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 ExtractTextPlugin({
allChunks: true,
filename: cssOutputFileName,
}),
].filter(() => !server),
// Move modules imported from node_modules/ into a vendor chunk when enabled
new CommonsChunkPlugin({
name: 'vendor',
minChunks(module) {
return (module.resource && module.resource.includes('node_modules'));
},
}),
// 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(),
// The Webpack manifest is normally folded into the last chunk, changing
// its hash - prevent this by extracting the manifest into its own
// chunk - also essential for deterministic hashing.
new CommonsChunkPlugin({ name: 'manifest' }),
// Inject the Webpack manifest into the generated HTML as a <script>
injectManifestPlugin,
// Production builds
...[
// JavaScript minification
new LoaderOptionsPlugin({ debug: false, minimize: true }),
new UglifyJsPlugin({
compress: {
warnings: false,
},
output: {
comments: false,
},
sourceMap: true,
}),
// Hoisting
new ModuleConcatenationPlugin(),
].filter(() => production),
new HtmlPlugin({
template: path.join(__dirname, 'src', websiteSubfolder, 'index.html'),
2017-08-25 06:28:44 +00:00
filename: 'index.html',
hash: false,
inject: true,
compile: true,
favicon: false,
minify: false,
cache: true,
showErrors: true,
chunks: 'all',
excludeChunks: [],
2017-08-25 23:50:32 +00:00
title: 'VIzon Countdown',
2017-08-25 06:28:44 +00:00
xhtml: false,
chunksSortMode: 'dependency',
}),
// Only include selected locales for moment.js
new ContextReplacementPlugin(/moment[/\\]locale$/, new RegExp(`^\\.\\/(${locales.join('|')})$`)),
].filter(plugin => plugin !== false),
resolve: {
extensions: [
'.js', '.json', '.jsx',
],
alias: {
'moment-timezone': 'moment-timezone/builds/moment-timezone-with-data-2012-2022.js',
},
},
resolveLoader: {
modules: ['node_modules', 'nwb'],
},
devtool: server ? 'cheap-module-source-map' : 'source-map',
entry: {
app: [
...[
'eventsource-polyfill',
'react-hot-loader/patch',
'webpack-hot-middleware/client',
].filter(() => server).map(require.resolve),
path.join(__dirname, 'src', websiteSubfolder),
2017-08-25 06:28:44 +00:00
],
},
};
if (!server) {
// Use preact instead of react (only on static build)
Object.assign(config.resolve.alias, {
react: !server ? 'preact-compat\\dist\\preact-compat' : undefined,
'react-dom': !server ? 'preact-compat\\dist\\preact-compat' : undefined,
'create-react-class': !server ? 'preact-compat/lib/create-react-class' : undefined,
});
}
debug.generated(config);
return config;
2017-08-25 06:28:44 +00:00
};