varan
Version:
A webpack starter kit for offline-first bring-your-own-code apps with server side rendering
260 lines • 12.2 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const webpack_1 = require("webpack");
const zlib_1 = __importDefault(require("zlib"));
const webpack_merge_1 = __importDefault(require("webpack-merge"));
const mini_css_extract_plugin_1 = __importDefault(require("mini-css-extract-plugin"));
const terser_webpack_plugin_1 = __importDefault(require("terser-webpack-plugin"));
const sw_precache_webpack_plugin_1 = __importDefault(require("sw-precache-webpack-plugin"));
const webpack_bundle_analyzer_1 = require("webpack-bundle-analyzer");
const webpack_stats_plugin_1 = require("webpack-stats-plugin");
const compression_webpack_plugin_1 = __importDefault(require("compression-webpack-plugin"));
const imagemin_webpack_plugin_1 = __importDefault(require("imagemin-webpack-plugin"));
const imagemin_mozjpeg_1 = __importDefault(require("imagemin-mozjpeg"));
const lodash_1 = require("lodash");
const errorOverlayMiddleware_1 = __importDefault(require("react-dev-utils/errorOverlayMiddleware"));
const noopServiceWorkerMiddleware_1 = __importDefault(require("react-dev-utils/noopServiceWorkerMiddleware"));
const ignoredFiles_1 = __importDefault(require("react-dev-utils/ignoredFiles"));
const path_1 = __importDefault(require("path"));
const WebpackVaranAssetsManifest_1 = __importDefault(require("../lib/WebpackVaranAssetsManifest"));
const createCommonConfig_1 = __importDefault(require("./createCommonConfig"));
// Init
const getOpts = (options) => {
const appDir = options.appDir || process.cwd();
const resolve = (relativePath) => path_1.default.resolve(appDir, relativePath);
return lodash_1.defaults({}, options, {
analyze: false,
appDir: resolve('./'),
buildVars: {},
entry: 'index',
env: process.env.NODE_ENV,
target: 'web',
name: undefined,
targetDir: resolve('dist/client'),
sourceDir: resolve('src/client'),
devServerPort: process.env.DEV_PORT || 3000,
serverPort: process.env.PORT || 3001,
});
};
/**
* Create a webpack configuration optimized for client (browser) applications
*
* @param {{ analyze: boolean=, appDir: string=, buildVars: object=, entry: string=, env: 'development' | 'test' | 'production'=, target: 'web' | 'node'=, name: string=, pwaManifest: object=, targetDir: string=, sourceDir: string=, devServerPort: number=, serverPort: number= }=} options
* @returns {webpack.Configuration}
*/
exports.default = (options = {}) => {
const opts = getOpts(options);
const isDev = opts.env !== 'production';
const publicPath = isDev ? `http://localhost:${opts.devServerPort}/` : `/${path_1.default.dirname(opts.entry).substr(2)}`;
const outputPath = path_1.default.resolve(opts.targetDir);
const name = opts.name || path_1.default.basename(opts.entry);
return webpack_merge_1.default.smart(createCommonConfig_1.default(opts), {
name,
devtool: isDev ? 'cheap-module-source-map' : false,
devServer: {
proxy: {
'/': `http://localhost:${opts.serverPort}/`,
},
historyApiFallback: true,
compress: true,
clientLogLevel: 'warn',
quiet: true,
noInfo: true,
stats: 'errors-only',
overlay: true,
contentBase: opts.targetDir,
watchContentBase: false,
publicPath,
progress: false,
liveReload: false,
hot: true,
injectHot: true,
writeToDisk: (p) => /^(?!.*(\.hot-update\.)).*/.test(p),
lazy: false,
watchOptions: {
ignored: ignoredFiles_1.default(opts.sourceDir),
},
headers: {
'Access-Control-Allow-Origin': '*',
},
before(app) {
app.use(errorOverlayMiddleware_1.default());
app.use(noopServiceWorkerMiddleware_1.default('/'));
},
},
performance: false,
entry: [require.resolve('react-app-polyfill/ie11'), path_1.default.resolve(opts.sourceDir, opts.entry)].filter(Boolean),
output: {
path: outputPath,
filename: isDev ? 'dev-bundle.js' : 'static/js/[name].[contenthash:8].js',
chunkFilename: isDev ? '[name].[contenthash:8].chunk.js' : 'static/js/[name].[contenthash:8].chunk.js',
pathinfo: isDev,
publicPath,
libraryTarget: 'var',
crossOriginLoading: 'anonymous',
},
plugins: [
new webpack_1.DefinePlugin(Object.assign(Object.assign({ BUILD_TARGET: JSON.stringify('client'), 'process.env.BABEL_ENV': JSON.stringify(opts.env), 'process.env.NODE_ENV': JSON.stringify(opts.env), 'process.env.browser': JSON.stringify(true) }, Object.entries(process.env)
.filter(([key]) => key.startsWith('APP_BUILD_VAR_') || key.startsWith('REACT_APP_'))
.reduce((acc, [key, value]) => {
acc[`process.env.${key}`] = JSON.stringify(value);
return acc;
}, {})), opts.buildVars)),
new webpack_1.EnvironmentPlugin({
DEBUG: false,
}),
!isDev &&
new compression_webpack_plugin_1.default({
filename: '[path].gz[query]',
algorithm: 'gzip',
test: /(\.js|\.json|\.html|\.css|\.svg|\.eot)$/,
threshold: 3 * 1024,
minRatio: 0.8,
}),
!isDev &&
zlib_1.default.brotliCompress &&
new compression_webpack_plugin_1.default({
filename: '[path].br[query]',
algorithm: 'brotliCompress',
test: /(\.js|\.json|\.html|\.css|\.svg|\.eot)$/,
threshold: 3 * 1024,
minRatio: 0.8,
}),
!isDev &&
new mini_css_extract_plugin_1.default({
filename: 'static/css/[name].[contenthash:8].css',
chunkFilename: 'static/css/[name].[contenthash:8].chunk.css',
}),
!isDev &&
new imagemin_webpack_plugin_1.default({
test: /\.(jpe?g|png|gif|svg)$/i,
minFileSize: 10 * 1024,
pngquant: { quality: '90' },
optipng: null,
jpegtran: null,
plugins: [
imagemin_mozjpeg_1.default({
quality: 90,
progressive: true,
}),
],
}),
new webpack_stats_plugin_1.StatsWriterPlugin({
filename: 'stats-manifest.json',
fields: ['assetsByChunkName', 'assets'],
}),
new WebpackVaranAssetsManifest_1.default({
output: 'asset-manifest.json',
integrity: true,
integrityHashes: ['sha512'],
}),
!isDev &&
new sw_precache_webpack_plugin_1.default({
cacheId: name,
dontCacheBustUrlsMatching: /(\.\w{8}\.)/,
filename: 'service-worker.js',
minify: !isDev,
mergeStaticsConfig: true,
skipWaiting: true,
clientsClaim: true,
directoryIndex: false,
dynamicUrlToDependencies: {
[publicPath]: [`${outputPath}/stats-manifest.json`],
},
navigateFallback: publicPath,
navigateFallbackWhitelist: [/^(?!\/__).*/],
staticFileGlobsIgnorePatterns: [/\.map$/, /asset-manifest\.json$/, /stats-manifest\.json$/, /\.gz$/, /\.br$/],
runtimeCaching: [
{
handler: 'fastest',
urlPattern: /\/*/,
},
],
logger(message) {
if (message.startsWith('Total precache size is') || message.startsWith('Skipping static resource'))
return;
// eslint-disable-next-line no-console
console.log(message);
},
}),
opts.analyze && new webpack_bundle_analyzer_1.BundleAnalyzerPlugin(),
].filter(Boolean),
optimization: isDev
? {
namedModules: true,
noEmitOnErrors: true,
splitChunks: {
chunks: 'all',
},
}
: {
noEmitOnErrors: true,
minimizer: [
new terser_webpack_plugin_1.default({
cache: true,
parallel: true,
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,
// Disabled because of an issue with Terser breaking valid code:
// https://github.com/facebook/create-react-app/issues/5250
// Pending futher investigation:
// https://github.com/terser-js/terser/issues/120
inline: 2,
},
mangle: {
safari10: true,
},
output: {
ecma: 5,
comments: false,
// eslint-disable-next-line @typescript-eslint/camelcase
ascii_only: true,
},
},
sourceMap: true,
}),
],
splitChunks: {
minSize: 30 * 1024,
maxSize: 1024 * 1024,
automaticNameDelimiter: '.',
cacheGroups: {
// Don't split css in vendor chunks by default due to potential ordering issues
commons: {
test: /[\\/]node_modules[\\/](.*)\.(?!(css|sass|scss|less)$)([^.]+$)/,
name: 'vendor',
chunks: 'all',
priority: -5,
},
},
},
},
node: {
dgram: 'empty',
fs: 'empty',
net: 'empty',
tls: 'empty',
// eslint-disable-next-line @typescript-eslint/camelcase
child_process: 'empty',
},
});
};
//# sourceMappingURL=createClientConfig.js.map