Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion packages/react-scripts/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
# `backpack-react-scripts` Change Log

## 8.1.0 (Pending)

- Added support for loadable components.
- Added `start-ssr` command, to produce Node.js-compatible watched output. Several changes to SSR Webpack config to support.
- Defined `typeof window` for browser and SSR environments, enabling dead code elimination (https://webpack.js.org/plugins/define-plugin/#usage)
- SSR output always includes hash as part of filename
- Added experimental support for https://github.com/mzgoddard/hard-source-webpack-plugin/, enabled by `USE_HARD_SOURCE_WEBPACK_PLUGIN=true` environment variable
- `web` and `ssr` subpaths for each build's output
- Output build 'status files' (`.build-status`, one for web, one for SSR), which can be watched by a Node.js server to know when builds are in progress or completed.
- Added `ignoreCssWarnings` config item to allow the ability to supress CSS ordering issues when its safe to allow mixed order when it has not effect on output. https://github.com/webpack-contrib/mini-css-extract-plugin#remove-order-warnings

## 8.0.6

- BACKPORT: Updated webpack paths to include the new `@skyscanner/bpk` foundations packages.

## 8.0.4

- Updated `bpk-mixins` to latest version `20.1.5`
Expand All @@ -11,7 +26,8 @@
- Downgraded `sass-loader` to `7.3.1` due to issues with `node-sass` and functional changes to options.

Rebased onto `upstream/master` v3.4.4 (6c009edface3ed63d0c7046f088c675a63c82fdb)
- Update `resolve-url-loader` to `3.1.2` to resolve security vunerability

- Update `resolve-url-loader` to `3.1.2` to resolve security vunerability

## 8.0.2

Expand Down
3 changes: 2 additions & 1 deletion packages/react-scripts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ npm start
- **`css.html` & `js.html`**: New files in the `build/` output folder. These are html partials that include `<script />` and `<link />` references to the various static assets output by webpack. Useful if automatic chunking is turned on and you don't want to worry about order.
- A bunch of configuration options via `"backpack-react-scripts"` field in `package.json`:
- `crossOriginLoading`: Modify the default behaviour, see [docs](https://webpack.js.org/configuration/output/#output-crossoriginloading).
- `babelIncludePrefixes`: An array of module name prefixes to opt into babel compilation. Includes `["bpk-", "saddlebag-"]` by default.
- `babelIncludePrefixes`: An array of module name prefixes to opt into babel compilation. Includes `["@skyscanner/bpk-", "bpk-", "saddlebag-"]` by default.
- `enableAutomaticChunking`: Boolean, opt in to automatic chunking of vendor, common and app code.
- `vendorsChunkRegex`: String, Regex for picking what goes into the `vendors` chunk. See `cacheGroups` in webpack docs. Dependent on `enableAutomaticChunking` being enabled
- `amdExcludes`: Array of module names to exclude from AMD parsing. Incldues `["lodash"]` by default.
Expand All @@ -32,6 +32,7 @@ npm start
- `cssModules`: Boolean, true by default.
- `sriEnabled`: Sets if SRI is to be used during build to add integrity hash for files, see [docs](https://github.com/waysact/webpack-subresource-integrity/blob/master/README.md).
- **Note** if this is enabled, `crossOriginLoading` value is overriden with `anonymous` in order for it to output with the integrity value.
- `ignoreCssWarnings`: Boolean, false by default. Allows the ability to supress CSS ordering issues when its safe to allow mixed order when it has not effect on output, see [docs](https://github.com/webpack-contrib/mini-css-extract-plugin#remove-order-warnings). False by default

## Releasing a new version of `backpack-react-scripts`

Expand Down
9 changes: 7 additions & 2 deletions packages/react-scripts/bin/react-scripts.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,17 @@ const spawn = require('react-dev-utils/crossSpawn');
const args = process.argv.slice(2);

const scriptIndex = args.findIndex(
x => x === 'build' || x === 'eject' || x === 'start' || x === 'test'
x =>
x === 'build' ||
x === 'eject' ||
x === 'start' ||
x === 'start-ssr' ||
x === 'test'
);
const script = scriptIndex === -1 ? args[0] : args[scriptIndex];
const nodeArgs = scriptIndex > 0 ? args.slice(0, scriptIndex) : [];

if (['build', 'eject', 'start', 'test'].includes(script)) {
if (['build', 'eject', 'start', 'start-ssr', 'test'].includes(script)) {
const result = spawn.sync(
'node',
nodeArgs
Expand Down
15 changes: 15 additions & 0 deletions packages/react-scripts/config/environmentHash.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
'use strict';

const fs = require('fs');
const path = require('path');

const reactScriptsRoot = path.resolve(__dirname, '..');
const haveIsolatedDependencies =
fs.existsSync(path.join(reactScriptsRoot, 'package-lock.json')) ||
fs.existsSync(path.join(reactScriptsRoot, 'yarn.lock'));

module.exports = {
root: haveIsolatedDependencies ? reactScriptsRoot : process.cwd(),
directories: [],
files: ['package-lock.json', 'yarn.lock'],
};
9 changes: 6 additions & 3 deletions packages/react-scripts/config/paths.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ const resolveModule = (resolveFn, filePath) => {
module.exports = {
dotenv: resolveApp('.env'),
appPath: resolveApp('.'),
appBuild: resolveApp('build'),
appBuildWeb: resolveApp('build/web'),
appBuildSsr: resolveApp('build/ssr'),
appPublic: resolveApp('public'),
appHtml: resolveApp('public/index.html'),
appIndexJs: resolveModule(resolveApp, 'src/index'),
Expand All @@ -83,7 +84,8 @@ const resolveOwn = relativePath => path.resolve(__dirname, '..', relativePath);
module.exports = {
dotenv: resolveApp('.env'),
appPath: resolveApp('.'),
appBuild: resolveApp('build'),
appBuildWeb: resolveApp('build/web'),
appBuildSsr: resolveApp('build/ssr'),
appPublic: resolveApp('public'),
appHtml: resolveApp('public/index.html'),
appIndexJs: resolveModule(resolveApp, 'src/index'),
Expand Down Expand Up @@ -119,7 +121,8 @@ if (
module.exports = {
dotenv: resolveOwn(`${templatePath}/.env`),
appPath: resolveApp('.'),
appBuild: resolveOwn('../../build'),
appBuildWeb: resolveOwn('../../build/web'),
appBuildSsr: resolveOwn('../../build/ssr'),
appPublic: resolveOwn(`${templatePath}/public`),
appHtml: resolveOwn(`${templatePath}/public/index.html`),
appIndexJs: resolveModule(resolveOwn, `${templatePath}/src/index`),
Expand Down
68 changes: 54 additions & 14 deletions packages/react-scripts/config/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,24 +40,33 @@ const postcssNormalize = require('postcss-normalize');

const appPackageJson = require(paths.appPackageJson);

const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
const LoadablePlugin = require('@loadable/webpack-plugin');

const sassFunctions = require('bpk-mixins/sass-functions');
const camelCase = require('lodash/camelCase');
const bpkReactScriptsConfig = appPackageJson['backpack-react-scripts'] || {};
const customModuleRegexes = bpkReactScriptsConfig.babelIncludePrefixes
? bpkReactScriptsConfig.babelIncludePrefixes.map(
prefix => new RegExp(`node_modules[\\/]${prefix}`)
(prefix) => new RegExp(`node_modules[\\/]${prefix}`)
)
: [];
const cssModulesEnabled = bpkReactScriptsConfig.cssModules !== false;
const crossOriginLoading = bpkReactScriptsConfig.crossOriginLoading || false;
const sriEnabled = bpkReactScriptsConfig.sriEnabled || false;
const supressCssWarnings = bpkReactScriptsConfig.ignoreCssWarnings || false;

// 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';

// We might not want to use the hard source plugin on environments that won't persist the cache for later
const useHardSourceWebpackPlugin =
process.env.USE_HARD_SOURCE_WEBPACK_PLUGIN === 'true';
const environmentHash = require('./environmentHash');

// const isExtendingEslintConfig = process.env.EXTEND_ESLINT === 'true';

const imageInlineSizeLimit = parseInt(
Expand All @@ -76,10 +85,11 @@ const sassModuleRegex = /\.module\.(scss|sass)$/;
// Backpack / saddlebag node module regexes
const backpackModulesRegex = /node_modules[\\/]bpk-/;
const saddlebagModulesRegex = /node_modules[\\/]saddlebag-/;
const scopedBackpackModulesRegex = /node_modules[\\/]@skyscanner[\\/]bpk-/;

// This is the production and development configuration.
// It is focused on developer experience, fast rebuilds, and a minimal bundle.
module.exports = function(webpackEnv) {
module.exports = function (webpackEnv) {
const isEnvDevelopment = webpackEnv === 'development';
const isEnvProduction = webpackEnv === 'production';

Expand Down Expand Up @@ -195,7 +205,7 @@ module.exports = function(webpackEnv) {
output: {
crossOriginLoading: sriEnabled ? 'anonymous' : crossOriginLoading,
// The build folder.
path: isEnvProduction ? paths.appBuild : undefined,
path: paths.appBuildWeb,
// Add /* filename */ comments to generated require()s in the output.
pathinfo: isEnvDevelopment,
// There will be one main bundle, and one file per asynchronous chunk.
Expand All @@ -215,12 +225,13 @@ module.exports = function(webpackEnv) {
publicPath: paths.publicUrlOrPath,
// Point sourcemap entries to original disk location (format as URL on Windows)
devtoolModuleFilenameTemplate: isEnvProduction
? info =>
? (info) =>
path
.relative(paths.appSrc, info.absoluteResourcePath)
.replace(/\\/g, '/')
: isEnvDevelopment &&
(info => path.resolve(info.absoluteResourcePath).replace(/\\/g, '/')),
((info) =>
path.resolve(info.absoluteResourcePath).replace(/\\/g, '/')),
// Prevents conflicts when multiple webpack runtimes (from different apps)
// are used on the same page.
// jsonpFunction: `webpackJsonp${appPackageJson.name}`,
Expand Down Expand Up @@ -321,7 +332,7 @@ module.exports = function(webpackEnv) {
// },
runtimeChunk: bpkReactScriptsConfig.enableAutomaticChunking
? {
name: entrypoint => `runtime-${entrypoint.name}`,
name: (entrypoint) => `runtime-${entrypoint.name}`,
}
: false,
},
Expand All @@ -341,8 +352,8 @@ module.exports = function(webpackEnv) {
// `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')),
.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/
Expand Down Expand Up @@ -374,6 +385,7 @@ module.exports = function(webpackEnv) {
],
},
module: {
noParse: /iconv-loader\.js$/, // https://github.com/webpack/webpack/issues/3078#issuecomment-400697407
strictExportPresence: true,
rules: [
// Disable require.ensure as it's not a standard language feature.
Expand Down Expand Up @@ -440,6 +452,7 @@ module.exports = function(webpackEnv) {
paths.appSrc,
backpackModulesRegex,
saddlebagModulesRegex,
scopedBackpackModulesRegex,
...customModuleRegexes,
],
loader: require.resolve('babel-loader'),
Expand Down Expand Up @@ -469,6 +482,7 @@ module.exports = function(webpackEnv) {
),
// @remove-on-eject-end
plugins: [
require.resolve('@loadable/babel-plugin'),
[
require.resolve('babel-plugin-named-asset-import'),
{
Expand Down Expand Up @@ -539,7 +553,7 @@ module.exports = function(webpackEnv) {
{
test: {
and: [cssRegex, () => !cssModulesEnabled],
exclude: [backpackModulesRegex],
exclude: [backpackModulesRegex, scopedBackpackModulesRegex],
},
exclude: cssModuleRegex,
use: getStyleLoaders({
Expand All @@ -561,7 +575,11 @@ module.exports = function(webpackEnv) {
and: [cssRegex, () => cssModulesEnabled],
},
{
and: [cssRegex, backpackModulesRegex],
and: [
cssRegex,
backpackModulesRegex,
scopedBackpackModulesRegex,
],
},
],
use: getStyleLoaders({
Expand All @@ -578,7 +596,7 @@ module.exports = function(webpackEnv) {
{
test: {
and: [sassRegex, () => !cssModulesEnabled],
exclude: [backpackModulesRegex],
exclude: [backpackModulesRegex, scopedBackpackModulesRegex],
},
exclude: sassModuleRegex,
use: getStyleLoaders(
Expand Down Expand Up @@ -606,7 +624,11 @@ module.exports = function(webpackEnv) {
and: [sassRegex, () => cssModulesEnabled],
},
{
and: [sassRegex, backpackModulesRegex],
and: [
sassRegex,
backpackModulesRegex,
scopedBackpackModulesRegex,
],
},
],
use: getStyleLoaders(
Expand Down Expand Up @@ -646,6 +668,20 @@ module.exports = function(webpackEnv) {
],
},
plugins: [
useHardSourceWebpackPlugin &&
new HardSourceWebpackPlugin({ environmentHash }),
useHardSourceWebpackPlugin &&
new HardSourceWebpackPlugin.ExcludeModulePlugin([
{
// HardSource works with mini-css-extract-plugin but due to how
// mini-css emits assets, assets are not emitted on repeated builds with
// mini-css and hard-source together. Ignoring the mini-css loader
// modules, but not the other css loader modules, excludes the modules
// that mini-css needs rebuilt to output assets every time.
test: /mini-css-extract-plugin[\\/]dist[\\/]loader/,
},
]),
new LoadablePlugin(),
// Generates an `index.html` file with the <script> injected.
new HtmlWebpackPlugin(
Object.assign(
Expand Down Expand Up @@ -712,7 +748,10 @@ module.exports = function(webpackEnv) {
// It is absolutely essential that NODE_ENV is set to production
// during a production build.
// Otherwise React will be compiled in the very slow development mode.
new webpack.DefinePlugin(env.stringified),
new webpack.DefinePlugin({
...env.stringified,
'typeof window': '"object"',
}),
// This is necessary to emit hot updates (currently CSS only):
isEnvDevelopment && new webpack.HotModuleReplacementPlugin(),
// Watcher doesn't work well if you mistype casing in a path so we use
Expand All @@ -731,6 +770,7 @@ module.exports = function(webpackEnv) {
// both options are optional
filename: 'static/css/[name].[contenthash:8].css',
chunkFilename: 'static/css/[name].[contenthash:8].chunk.css',
ignoreOrder: supressCssWarnings,
}),
// Generate an asset manifest file with the following content:
// - "files" key: Mapping of all asset filenames to their corresponding
Expand All @@ -747,7 +787,7 @@ module.exports = function(webpackEnv) {
return manifest;
}, seed);
const entrypointFiles = entrypoints.main.filter(
fileName => !fileName.endsWith('.map')
(fileName) => !fileName.endsWith('.map')
);

return {
Expand Down
Loading