UNPKG

varan

Version:

A webpack starter kit for offline-first bring-your-own-code apps with server side rendering

260 lines 12.2 kB
"use strict"; 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