Created
October 14, 2021 10:22
-
-
Save viktorbezdek/155634ca0f3e4ccd6f7afc1628d112aa to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// @remove-on-eject-end | |
'use strict' | |
const fs = require('fs') | |
const isWsl = require('is-wsl') | |
const path = require('path') | |
const PnpWebpackPlugin = require('pnp-webpack-plugin') | |
const TerserPlugin = require('terser-webpack-plugin') | |
const HtmlWebpackPlugin = require('html-webpack-plugin') | |
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin') | |
const safePostCssParser = require('postcss-safe-parser') | |
const ManifestPlugin = require('webpack-manifest-plugin') | |
const ModuleScopePlugin = require('@siteone/react-ssr-dev-utils/ModuleScopePlugin') | |
const InlineChunkHtmlPlugin = require('@siteone/react-ssr-dev-utils/InlineChunkHtmlPlugin') | |
const InterpolateHtmlPlugin = require('@siteone/react-ssr-dev-utils/InterpolateHtmlPlugin') | |
const paths = require('../paths') | |
const modules = require('../modules') | |
const getClientEnvironment = require('../env') | |
// Source maps are resource heavy and can cause out of memory issue for large source files. | |
const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false' | |
// Some apps do not need the benefits of saving a web request, so not inlining the chunk | |
// makes for a smoother build process. | |
const shouldInlineRuntimeChunk = process.env.INLINE_RUNTIME_CHUNK !== 'false' | |
// Check if TypeScript is setup | |
const useTypeScript = fs.existsSync(paths.appTsConfig) | |
// This is the production and development configuration for client. | |
// It is focused on developer experience, fast rebuilds, and a minimal bundle. | |
const clientFactory = function(webpackEnv) { | |
const isEnvDevelopment = webpackEnv === 'development' | |
const isEnvProduction = webpackEnv === 'production' | |
const { client: clientLoaders } = require('./loaders')(webpackEnv, 'client') | |
const clientPlugins = require('./plugins')(webpackEnv, 'client') | |
const env = getClientEnvironment() | |
const publicPath = isEnvProduction | |
? `${env.raw.ASSETS_PATH}/` | |
: isEnvDevelopment && '/' | |
return { | |
name: 'client', | |
target: 'web', | |
mode: isEnvProduction ? 'production' : isEnvDevelopment && 'development', | |
// Stop compilation early in production | |
bail: isEnvProduction, | |
devtool: isEnvProduction | |
? shouldUseSourceMap | |
? 'source-map' | |
: false | |
: isEnvDevelopment && 'eval-source-map', | |
entry: [paths.appClientIndexJs], | |
devServer: { | |
filename: isEnvProduction | |
? 'static/js/[name].[chunkhash:8].js' | |
: isEnvDevelopment && 'static/js/bundle.js', | |
}, | |
output: { | |
// The build folder. | |
path: isEnvDevelopment ? paths.appDistPublic : paths.appBuildPublic, | |
// Add /* filename */ comments to generated require()s in the output. | |
pathinfo: isEnvDevelopment, | |
// There will be one main bundle, and one file per asynchronous chunk. | |
// In development, it does not produce real files. | |
filename: isEnvProduction | |
? 'static/js/[name].[chunkhash:8].js' | |
: isEnvDevelopment && 'static/js/bundle.js', | |
// TODO: remove this when upgrading to webpack 5 | |
futureEmitAssets: false, | |
// There are also additional JS chunk files if you use code splitting. | |
chunkFilename: isEnvProduction | |
? 'static/js/[name].[chunkhash:8].chunk.js' | |
: isEnvDevelopment && 'static/js/[name].chunk.js', | |
// We use "/" in development, can be configured in production | |
publicPath: publicPath, | |
// Point sourcemap entries to original disk location (format as URL on Windows) | |
devtoolModuleFilenameTemplate: isEnvProduction | |
? info => | |
path | |
.relative(paths.appSrc, info.absoluteResourcePath) | |
.replace(/\\/g, '/') | |
: isEnvDevelopment && | |
(info => path.resolve(info.absoluteResourcePath).replace(/\\/g, '/')), | |
}, | |
optimization: { | |
minimize: isEnvProduction, | |
minimizer: [ | |
// This is only used in production mode | |
new TerserPlugin({ | |
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, | |
// 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 | |
// Disabled on WSL (Windows Subsystem for Linux) due to an issue with Terser | |
// https://github.com/webpack-contrib/terser-webpack-plugin/issues/21 | |
parallel: !isWsl, | |
// Enable file caching | |
cache: true, | |
sourceMap: shouldUseSourceMap, | |
}), | |
// This is only used in production mode | |
new OptimizeCSSAssetsPlugin({ | |
cssProcessorOptions: { | |
parser: safePostCssParser, | |
map: shouldUseSourceMap | |
? { | |
// `inline: false` forces the sourcemap to be output into a | |
// separate file | |
inline: false, | |
// `annotation: true` appends the sourceMappingURL to the end of | |
// the css file, helping the browser find the sourcemap | |
annotation: true, | |
} | |
: false, | |
}, | |
}), | |
], | |
// Automatically split vendor and commons | |
// https://twitter.com/wSokra/status/969633336732905474 | |
// https://medium.com/webpack/webpack-4-code-splitting-chunk-graph-and-the-splitchunks-optimization-be739a861366 | |
splitChunks: { | |
chunks: 'all', | |
name: false, | |
}, | |
// Keep the runtime chunk separated to enable long term caching | |
// https://twitter.com/wSokra/status/969679223278505985 | |
runtimeChunk: true, | |
moduleIds: 'hashed', | |
}, | |
resolve: { | |
// This allows you to set a fallback for where Webpack should look for modules. | |
// We placed these paths second because we want `node_modules` to "win" | |
// if there are any conflicts. This matches Node resolution mechanism. | |
// https://github.com/facebook/create-react-app/issues/253 | |
modules: ['node_modules', paths.appNodeModules].concat( | |
modules.additionalModulePaths || [] | |
), | |
// These are the reasonable defaults supported by the Node ecosystem. | |
// We also include JSX as a common component filename nsion to support | |
// some tools, although we do not recommend using it, see: | |
// https://github.com/facebook/create-react-app/issues/290 | |
// `web` extension prefixes have been added for better support | |
// for React Native Web. | |
extensions: paths.moduleFileExtensions | |
.map(ext => `.${ext}`) | |
.filter(ext => useTypeScript || !ext.includes('ts')), | |
alias: { | |
// Support React Native Web | |
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/ | |
'react-native': 'react-native-web', | |
}, | |
plugins: [ | |
// Adds support for installing with Plug'n'Play, leading to faster installs and adding | |
// guards against forgotten dependencies and such. | |
PnpWebpackPlugin, | |
// Prevents users from importing files from outside of src/ (or node_modules/). | |
// This often causes confusion because we only process files within src/ with babel. | |
// To fix this, we prevent you from importing files out of src/ -- if you'd like to, | |
// please link the files into your node_modules/ and let module-resolution kick in. | |
// Make sure your source files are compiled, as they will not be processed in any way. | |
new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]), | |
], | |
}, | |
resolveLoader: { | |
plugins: [ | |
// Also related to Plug'n'Play, but this time it tells Webpack to load its loaders | |
// from the current package. | |
PnpWebpackPlugin.moduleLoader(module), | |
], | |
}, | |
module: { | |
strictExportPresence: true, | |
rules: [ | |
// Disable require.ensure as it's not a standard language feature. | |
{ parser: { requireEnsure: false } }, | |
// First, run the linter. | |
// It's important to do this before Babel processes the JS. | |
{ | |
test: /\.(js|mjs|jsx)$/, | |
enforce: 'pre', | |
use: [ | |
{ | |
options: { | |
formatter: require.resolve( | |
'@siteone/react-ssr-dev-utils/eslintFormatter' | |
), | |
eslintPath: require.resolve('eslint'), | |
// @remove-on-eject-begin | |
baseConfig: { | |
extends: [require.resolve('eslint-config-react-app')], | |
}, | |
ignore: false, | |
useEslintrc: false, | |
// @remove-on-eject-end | |
}, | |
loader: require.resolve('eslint-loader'), | |
}, | |
], | |
include: paths.appSrc, | |
}, | |
{ | |
// "oneOf" will traverse all following loaders until one will | |
// match the requirements. When no loader matches it will fall | |
// back to the "file" loader at the end of the loader list. | |
oneOf: clientLoaders, | |
}, | |
], | |
}, | |
plugins: [ | |
// Generates an `app.html` file with the <script> injected. | |
new HtmlWebpackPlugin( | |
Object.assign( | |
{}, | |
{ | |
inject: true, | |
filename: 'app.html', | |
template: paths.appHtml, | |
}, | |
isEnvProduction | |
? { | |
minify: { | |
removeComments: true, | |
collapseWhitespace: true, | |
removeRedundantAttributes: true, | |
useShortDoctype: true, | |
removeEmptyAttributes: true, | |
removeStyleLinkTypeAttributes: true, | |
keepClosingSlash: true, | |
minifyJS: true, | |
minifyCSS: true, | |
minifyURLs: true, | |
}, | |
} | |
: undefined | |
) | |
), | |
// Inlines the webpack runtime script. This script is too small to warrant | |
// a network request. | |
isEnvProduction && | |
shouldInlineRuntimeChunk && | |
new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime~.+[.]js/]), | |
// Makes some environment variables available in index.html. | |
// The public URL is available as %PUBLIC_URL% in index.html, e.g.: | |
// <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico"> | |
// In production, it will be an empty string unless you specify "homepage" | |
// in `package.json`, in which case it will be the pathname of that URL. | |
// In development, this will be an empty string. | |
new InterpolateHtmlPlugin(HtmlWebpackPlugin, env.raw), | |
// Generate a manifest file which contains a mapping of all asset filenames | |
// to their corresponding output file so that tools can pick it up without | |
// having to parse `app.html`. | |
new ManifestPlugin({ | |
fileName: 'asset-manifest.json', | |
publicPath: publicPath, | |
generate: (seed, files) => { | |
const manifestFiles = files.reduce(function(manifest, file) { | |
manifest[file.name] = file.path | |
return manifest | |
}, seed) | |
return { | |
files: manifestFiles, | |
} | |
}, | |
}), | |
...clientPlugins, | |
].filter(Boolean), | |
// Some libraries import Node modules but don't use them in the browser. | |
// Tell Webpack to provide empty mocks for them so importing them works. | |
node: { | |
module: 'empty', | |
dgram: 'empty', | |
dns: 'mock', | |
fs: 'empty', | |
net: 'empty', | |
tls: 'empty', | |
child_process: 'empty', | |
}, | |
// Turn off performance processing because we utilize | |
// our own hints via the FileSizeReporter | |
performance: false, | |
} | |
} | |
module.exports = function(webpackEnv) { | |
const clientConfig = clientFactory(webpackEnv) | |
try { | |
require(process.cwd() + '/bldr.config.js').webpack(clientConfig, { | |
production: webpackEnv === 'production', | |
webpack: 'client', | |
}) | |
} catch (e) { | |
console.log('No custom config applied') | |
} | |
return clientConfig | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment