Skip to content

Commit

Permalink
Support multiple entry points from src/entries/*
Browse files Browse the repository at this point in the history
Based on facebook@fd9952c

Other changes:
1. Allow customizing the path to node_modules using ENV var
2. Added instructions for updating this fork
3. Attempt to minimize MaterialUI bundle size
4. Fix "path is undefined" error in webpackDevServer
5. Support multiple entrypoints in ManifestPlugin output
  • Loading branch information
cberkom committed Mar 30, 2023
1 parent 6c009ed commit 6767c56
Show file tree
Hide file tree
Showing 9 changed files with 153 additions and 53 deletions.
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Here are a few examples of them in action.

<!--alex disable easy-->

Instead of letting the user specify the entry filename, we always assume it to be `src/index.js`. Rather than letting the user specify the output bundle name, we generate it, but make sure to include the content hash in it. Whenever possible, we want to leverage convention to make good choices for the user, especially in cases where it’s easy to misconfigure something.
We accept one or more entry points in `src/entries/*.js`. Rather than letting the user specify the output bundle name, we generate it, but make sure to include the content hash in it. Whenever possible, we want to leverage convention to make good choices for the user, especially in cases where it’s easy to misconfigure something.

### Heuristics

Expand Down
53 changes: 53 additions & 0 deletions UPDATING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Instructions for Updating this Fork

[Based on this article](https://medium.com/@denis.zhbankov/maintaining-a-fork-of-create-react-app-as-an-alternative-to-ejecting-c555e8eb2b63)

```sh
# Indicate the version we're on now, and which one we're upgrading to:
VERSION_FROM="3.3.1"
VERSION_TO="3.4.4"

# In Github, first sync our fork with the parent facebook/create-react-app repository

# Clone down our fork if you haven't already
git clone [email protected]:LeadSimple/create-react-app.git
# Might not be necessary:
git remote add upstream [email protected]/facebook/create-react-app.git

# Pull upstream changes into main (master) branch
git checkout main
git pull upstream main

# Check out the last custom branch we updated (ex custom-react-scripts-3.1.1)
git checkout custom-react-scripts-$VERSION_FROM
# Create a new branch from it
git checkout -b custom-react-scripts-$VERSION_TO

# Rebase our custom branch onto the desired tag from the main branch
# During the rebase, edit packages/react-scripts/package.json to have an rc version, like 3.3.1-rc.1
git rebase --onto v$VERSION_TO main

# TODO: find out how to test the package at this point, before publishing rc to NPM

# Publish package to NPM
cd packages/react-scripts
npm publish --access public

# Update package.json in main app, and test locally
# Once we confirm everything is working, we could update this package's version to drop the -rc.1, but that isn't really necessary

# If something is not working:
# 1. Debug and fix, updating the package version to a new rc.
# 2. Commit and tag
# 3. Publish to NPM
# 4. Test again

# Tag our local branch with the new version
git tag -a v$VERSION_TO-rc1 -m "Customized v$VERSION_TO"

# IMPORTANT: Make sure everything is working first, then:
# Push our rebased branch up to Github
cd ../..
git status
git push origin
```
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import '../index.css';
import App from '../App';
import * as serviceWorker from '../serviceWorker';

ReactDOM.render(
<React.StrictMode>
Expand Down
22 changes: 13 additions & 9 deletions packages/react-scripts/config/paths.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,17 @@

const path = require('path');
const fs = require('fs');
const globby = require('globby');
const getPublicUrlOrPath = require('react-dev-utils/getPublicUrlOrPath');

// Make sure any symlinks in the project folder are resolved:
// https://github.com/facebook/create-react-app/issues/637
const appDirectory = fs.realpathSync(process.cwd());
const resolveApp = relativePath => path.resolve(appDirectory, relativePath);

const envBuildPath = process.env.BUILD_PATH; // expecting relative path
const nodeModulesPath = process.env.NODE_MODULES_PATH; // expecting absolute path

// We use `PUBLIC_URL` environment variable or "homepage" field to infer
// "public path" at which the app is served.
// webpack needs to know it to put the right <script> hrefs into HTML even in
Expand Down Expand Up @@ -60,18 +64,18 @@ const resolveModule = (resolveFn, filePath) => {
module.exports = {
dotenv: resolveApp('.env'),
appPath: resolveApp('.'),
appBuild: resolveApp('build'),
appBuild: resolveApp(envBuildPath || 'build'),
appPublic: resolveApp('public'),
appHtml: resolveApp('public/index.html'),
appIndexJs: resolveModule(resolveApp, 'src/index'),
appEntryPoints: globby.sync(resolveApp('src/entries/*')),
appPackageJson: resolveApp('package.json'),
appSrc: resolveApp('src'),
appTsConfig: resolveApp('tsconfig.json'),
appJsConfig: resolveApp('jsconfig.json'),
yarnLockFile: resolveApp('yarn.lock'),
testsSetup: resolveModule(resolveApp, 'src/setupTests'),
proxySetup: resolveApp('src/setupProxy.js'),
appNodeModules: resolveApp('node_modules'),
appNodeModules: nodeModulesPath || resolveApp('node_modules'),
publicUrlOrPath,
};

Expand All @@ -82,18 +86,18 @@ const resolveOwn = relativePath => path.resolve(__dirname, '..', relativePath);
module.exports = {
dotenv: resolveApp('.env'),
appPath: resolveApp('.'),
appBuild: resolveApp('build'),
appBuild: resolveApp(envBuildPath || 'build'),
appPublic: resolveApp('public'),
appHtml: resolveApp('public/index.html'),
appIndexJs: resolveModule(resolveApp, 'src/index'),
appEntryPoints: globby.sync(resolveApp('src/entries/*')),
appPackageJson: resolveApp('package.json'),
appSrc: resolveApp('src'),
appTsConfig: resolveApp('tsconfig.json'),
appJsConfig: resolveApp('jsconfig.json'),
yarnLockFile: resolveApp('yarn.lock'),
testsSetup: resolveModule(resolveApp, 'src/setupTests'),
proxySetup: resolveApp('src/setupProxy.js'),
appNodeModules: resolveApp('node_modules'),
appNodeModules: nodeModulesPath || resolveApp('node_modules'),
publicUrlOrPath,
// These properties only exist before ejecting:
ownPath: resolveOwn('.'),
Expand All @@ -117,18 +121,18 @@ if (
module.exports = {
dotenv: resolveOwn(`${templatePath}/.env`),
appPath: resolveApp('.'),
appBuild: resolveOwn('../../build'),
appBuild: resolveOwn(envBuildPath || '../../build'),
appPublic: resolveOwn(`${templatePath}/public`),
appHtml: resolveOwn(`${templatePath}/public/index.html`),
appIndexJs: resolveModule(resolveOwn, `${templatePath}/src/index`),
appEntryPoints: globby.sync(resolveApp(`${templatePath}/src/entries/*`)),
appPackageJson: resolveOwn('package.json'),
appSrc: resolveOwn(`${templatePath}/src`),
appTsConfig: resolveOwn(`${templatePath}/tsconfig.json`),
appJsConfig: resolveOwn(`${templatePath}/jsconfig.json`),
yarnLockFile: resolveOwn(`${templatePath}/yarn.lock`),
testsSetup: resolveModule(resolveOwn, `${templatePath}/src/setupTests`),
proxySetup: resolveOwn(`${templatePath}/src/setupProxy.js`),
appNodeModules: resolveOwn('node_modules'),
appNodeModules: nodeModulesPath || resolveOwn('node_modules'),
publicUrlOrPath,
// These properties only exist before ejecting:
ownPath: resolveOwn('.'),
Expand Down
101 changes: 71 additions & 30 deletions packages/react-scripts/config/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,13 @@ module.exports = function(webpackEnv) {
const isEnvDevelopment = webpackEnv === 'development';
const isEnvProduction = webpackEnv === 'production';

// Specify the host and port, so hot reloading will work when js is hosted on another server
// NOTE: any changes here need to be mirrored in start.js
const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3000;
const HOST = process.env.HOST || '0.0.0.0';
const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
var devServer = protocol + '://' + HOST + ':' + DEFAULT_PORT;

// Variable used for enabling profiling in Production
// passed into alias object. Uses a flag if passed into the build command
const isEnvProductionProfile =
Expand Down Expand Up @@ -149,25 +156,31 @@ module.exports = function(webpackEnv) {
: isEnvDevelopment && 'cheap-module-source-map',
// These are the "entry points" to our application.
// This means they will be the "root" imports that are included in JS bundle.
entry: [
// Include an alternative client for WebpackDevServer. A client's job is to
// connect to WebpackDevServer by a socket and get notified about changes.
// When you save a file, the client will either apply hot updates (in case
// of CSS changes), or refresh the page (in case of JS changes). When you
// make a syntax error, this client will display a syntax error overlay.
// Note: instead of the default WebpackDevServer client, we use a custom one
// to bring better experience for Create React App users. You can replace
// the line below with these two lines if you prefer the stock client:
// require.resolve('webpack-dev-server/client') + '?/',
// require.resolve('webpack/hot/dev-server'),
isEnvDevelopment &&
require.resolve('react-dev-utils/webpackHotDevClient'),
// Finally, this is your app's code:
paths.appIndexJs,
entry: {
// Load multiple entry points, from ../src/entries
...paths.appEntryPoints.reduce((result, entrypoint) => {
const entry_name = path.basename(entrypoint).replace(/\.[^/.]+$/, "") // trim file extensions off the end
result[entry_name] = [
// Include an alternative client for WebpackDevServer. A client's job is to
// connect to WebpackDevServer by a socket and get notified about changes.
// When you save a file, the client will either apply hot updates (in case
// of CSS changes), or refresh the page (in case of JS changes). When you
// make a syntax error, this client will display a syntax error overlay.
// Note: instead of the default WebpackDevServer client, we use a custom one
// to bring better experience for Create React App users. You can replace
// the line below with these two lines if you prefer the stock client:
// isEnvDevelopment && require.resolve('react-dev-utils/webpackHotDevClient'),
isEnvDevelopment && require.resolve('webpack-dev-server/client') + '?' + devServer + '/sockjs-node',
isEnvDevelopment && require.resolve('webpack/hot/dev-server'),
// Finally, this is your app's code:
entrypoint,
].filter(Boolean); // Remove undefined lines
return result;
}, {}),
// We include the app code last so that if there is a runtime error during
// initialization, it doesn't blow up the WebpackDevServer client, and
// changing JS code would still trigger a refresh.
].filter(Boolean),
},
output: {
// The build folder.
path: isEnvProduction ? paths.appBuild : undefined,
Expand All @@ -177,7 +190,7 @@ module.exports = function(webpackEnv) {
// In development, it does not produce real files.
filename: isEnvProduction
? 'static/js/[name].[contenthash:8].js'
: isEnvDevelopment && 'static/js/bundle.js',
: isEnvDevelopment && 'static/js/[name].js',
// TODO: remove this when upgrading to webpack 5
futureEmitAssets: true,
// There are also additional JS chunk files if you use code splitting.
Expand Down Expand Up @@ -270,16 +283,13 @@ module.exports = function(webpackEnv) {
// 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,
},
// splitChunks: {
// chunks: 'all',
// name: false,
// },
// Keep the runtime chunk separated to enable long term caching
// https://twitter.com/wSokra/status/969679223278505985
// https://github.com/facebook/create-react-app/issues/5358
runtimeChunk: {
name: entrypoint => `runtime-${entrypoint.name}`,
},
// runtimeChunk: true,
},
resolve: {
// This allows you to set a fallback for where webpack should look for modules.
Expand Down Expand Up @@ -420,6 +430,23 @@ module.exports = function(webpackEnv) {
},
},
],
// Attempt to minimize bundle-size while still allowing root-level import statements
// https://material-ui.com/guides/minimizing-bundle-size/
[
'babel-plugin-transform-imports',
{
'@material-ui/core': {
// Use "transform: '@material-ui/core/${member}'," if your bundler does not support ES modules
'transform': '@material-ui/core/${member}',
'preventFullImport': true
},
'@material-ui/icons': {
// Use "transform: '@material-ui/icons/${member}'," if your bundler does not support ES modules
'transform': '@material-ui/icons/${member}',
'preventFullImport': true
}
}
]
],
// This is a feature of `babel-loader` for webpack (not Babel itself).
// It enables caching results in ./node_modules/.cache/babel-loader/
Expand Down Expand Up @@ -630,22 +657,36 @@ module.exports = function(webpackEnv) {
// `index.html`
// - "entrypoints" key: Array of files which are included in `index.html`,
// can be used to reconstruct the HTML if necessary
// Updated to support multiple entrypoints. Output is in format of:
// {
// files: {
// 'entry1.js': '/path/entry1.js',
// 'entry1.js.map': '/path/entry1.js.map',
// 'entry2.js': '/path/entry2.js',
// 'entry2.js.map': '/path/entry2.js.map',
// },
// entrypoints: {
// entry1: [ 'path/entry1.js' ],
// entry2: [ 'path/entry2.js' ],
// }
// }
new ManifestPlugin({
fileName: 'asset-manifest.json',
publicPath: paths.publicUrlOrPath,
generate: (seed, files, entrypoints) => {
// console.log("ManifestPlugin entrypoints", entrypoints)
const manifestFiles = files.reduce((manifest, file) => {
manifest[file.name] = file.path;
return manifest;
}, seed);
const entrypointFiles = entrypoints.main.filter(
fileName => !fileName.endsWith('.map')
);

const entrypointFiles = {}
for (const entry in entrypoints) {
entrypointFiles[entry] = entrypoints[entry].filter(fileName => !fileName.endsWith('.map'));
}
return {
files: manifestFiles,
entrypoints: entrypointFiles,
};
}
},
}),
// Moment.js is an extremely popular library that bundles large locale files
Expand Down
4 changes: 4 additions & 0 deletions packages/react-scripts/config/webpackDevServer.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ const sockPort = process.env.WDS_SOCKET_PORT;

module.exports = function(proxy, allowedHost) {
return {
// CUSTOM PATCH: allow hot reload requests to work (#2479)
headers: {
'Access-Control-Allow-Origin': '*'
},
// WebpackDevServer 2.4.3 introduced a security fix that prevents remote
// websites from potentially accessing local content through DNS rebinding:
// https://github.com/webpack/webpack-dev-server/issues/887
Expand Down
14 changes: 6 additions & 8 deletions packages/react-scripts/package.json
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
{
"name": "react-scripts",
"version": "3.4.4",
"name": "@leadsimple/react-scripts",
"version": "3.4.4-rc.1",
"description": "Configuration and scripts for Create React App.",
"repository": {
"type": "git",
"url": "https://github.com/facebook/create-react-app.git",
"directory": "packages/react-scripts"
},
"repository": "leadsimple/create-react-app",
"license": "MIT",
"engines": {
"node": ">=8.10"
},
"bugs": {
"url": "https://github.com/facebook/create-react-app/issues"
"url": "https://github.com/leadsimple/create-react-app/issues"
},
"files": [
"bin",
Expand All @@ -36,6 +32,7 @@
"babel-jest": "^24.9.0",
"babel-loader": "8.1.0",
"babel-plugin-named-asset-import": "^0.3.6",
"babel-plugin-transform-imports": "2.0.0",
"babel-preset-react-app": "^9.1.2",
"camelcase": "^5.3.1",
"case-sensitive-paths-webpack-plugin": "2.3.0",
Expand All @@ -53,6 +50,7 @@
"file-loader": "4.3.0",
"fs-extra": "^8.1.0",
"html-webpack-plugin": "4.0.0-beta.11",
"globby": "^8.0.1",
"identity-obj-proxy": "3.0.0",
"jest": "24.9.0",
"jest-environment-jsdom-fourteen": "1.0.1",
Expand Down
2 changes: 1 addition & 1 deletion packages/react-scripts/scripts/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ const WARN_AFTER_CHUNK_GZIP_SIZE = 1024 * 1024;
const isInteractive = process.stdout.isTTY;

// Warn and crash if required files are missing
if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
if (!checkRequiredFiles([paths.appHtml, ...paths.appEntryPoints])) {
process.exit(1);
}

Expand Down
2 changes: 1 addition & 1 deletion packages/react-scripts/scripts/start.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ const useYarn = fs.existsSync(paths.yarnLockFile);
const isInteractive = process.stdout.isTTY;

// Warn and crash if required files are missing
if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
if (!checkRequiredFiles([paths.appHtml, ...paths.appEntryPoints])) {
process.exit(1);
}

Expand Down

0 comments on commit 6767c56

Please sign in to comment.