create-react-app
create-react-app copied to clipboard
Add more entry points
In addition to src/index.js, I have other entry points in my app. How do I add them?
You could render to multiple html elements in index.js
, is that what you mean?
reactdom.render(<App1/>, div1)
reactdom.render(<App2/>, div2)
reactdom.render(<App3/>, div3)
@riceyeh I think you asked this already? is there anything here different from https://github.com/facebookincubator/create-react-app/issues/1079
Can you explain your use case in more detail? While you asked this earlier, I wonder if I misunderstood your use case.
For example, when preparing multiple apps I might want to do index.html for web and touch-kiosk.html for other devices. Basically using components and composition to do multiple apps. For this I need not just few html files but multiple entry points for webpack.
I would like to be able to create multiple bundles.
For example:
admin.bundle.js # for admin section in my site
client.bundle.js # for client section
Now I am forced to create two applications:
create-react-app admin
create-react-app client
This approach seem to be excessive. It would be great if it was possible to separate the bundles within a single application.
Thanks for sharing the use cases. We might support this eventually but for now I recommend either separate apps or ejecting and manually configuring Webpack.
You could do something funky with shell scripting. Not sure how practical this is but it will solve your problem (from how I interpret it).
Have two entry point js files, lets go with user.js
and admin.js
.
Have two build
commands in package.json
; build-user
, build-admin
.
When you go to run build-user
, before running react-scripts build
, have some shell code that copies user.js
to index.js
in your source code. Same for build-admin
, just copy it's entry point to index.js
before running the actual build
command. This way if you're working on the same entry point consistently, you can still use build
.
package.json
"build": "react-scripts build",
"build-user": "cp src/user.js src/index.js && react-scripts build",
"build-admin": "cp src/admin.js src/index.js && react-scripts build"
Maybe we could pass entry point to webpack instead? Then add instructions of shell scripting it.
I ran in to a similar issue today, and there appears to be an easier and more elegant workaround to this, similar to what @tbillington had mentioned. Keep in mind, I am pretty new to React/Webpack/Babel/etc, so forgive me any nube mistakes.
You can simulate having multiple HTML files by using URL parameters on the Index.html file. In index.js, you simply parse out the URL param and serve the appropriate React Component into the root
div based on that parameter. In this case, the default would be App
, but if you load it with
http://www.myhost.com/index.html?startPage=SecondApp
... it will serve up the SecondApp
React component.
The App.js and index.html are the boilerplate created by create-react-app.
Index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import SecondApp from './SecondApp';
import './index.css';
// Copied from http:jquery-howto.blogspot.com/2009/09/get-url-parameters-values-with-jquery.html
function getUrlVars() {
var vars = [], hash;
var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
for (var i = 0; i < hashes.length; i++) {
hash = hashes[i].split('=');
vars.push(hash[0]);
vars[hash[0]] = hash[1];
}
return vars;
}
var urlParams = getUrlVars();
switch (urlParams["startPage"]) {
case "SecondApp":
ReactDOM.render(<SecondApp />, document.getElementById('root'));
break;
case undefined:
default:
ReactDOM.render(<App />, document.getElementById('root'));
break;
}
SecondApp.js
import React, { Component } from 'react';
class SecondApp extends Component {
constructor() {
super();
}
render() {
return <div>MY SECOND APP!</div>;
}
}
export default SecondApp;
This way, everything will get packaged, minified, transmorgrifacated, etc. It seems a lot more palatable to do it this way than ejecting entirely. If you have legacy HTML/Javascript that isn't part of the module system, you put that in the public directory (and subdirectories), and Webpack will just copy it untouched.
Let me know if this is helpful.
Nick
I'm also trying to implement a User / Admin scenario.
@carruthe, I believe your approach is not working for me because the User part is simple and should be as light weight (code size wise) as possible as it's expected to be accessed mainly on mobile. The Admin part is more complex with more dependencies and expected to be accessed predominantly on desktop. I'd therefore prefer separate JS bundles for Client and Admin.
@tbillington, I think your suggestion should work with a bit of extra scripting to merge the separate build outputs into one, or am I passed duct-tape-strength?
For admin/user use case you can use require.ensure(dependencies, callback)
to load module async.
more detail
but I like create-react-app support multiple entry/bundle.
I agree it's something we'd like to see in the future.
I've implemented this as proof of concept, so you can try it already by installing [email protected]
on an existed CRA project:
-
npm uninstall --save-dev react-scripts
-
npm install --save-dev [email protected]
To add a new entry point:
- Create the
./pages
folder in the root of project and put in them a*.js
file (for example./pages/admin.js
) -
npm run start
(npm run build
also works) - Open
http://localhost:3000/admin.html
🎉
Also I think it's a good first step for prerendred pages. All we need now is to generate *.html
pages differently (see static-site-generator-webpack-plugin). What do think about the API?
Changes: https://github.com/stockspiking/create-react-app/commit/fced96efe4d2c717ec95f48548998b5da7e03dcc
Known issues of this POC
- Dev server doesn't recompile after adding new entries
- New separated chunk
/static/js/polyfills.js
may be added after other scripts. - It's not possible to change html template
@kohver Do you think it would be best to have a separate html file or using react-router
to lazy load those extra js required. Something like this http://moduscreate.com/code-splitting-for-react-router-with-es6-imports/
My use case of having multiple entry points is that i'm developing plugins for cloud solutions of atlassian (jira, confluence) and all plugins injected into the system by iframe, so in different places like admin panel, main page panel, sections inside issues e.t.c. i need html page, since i want to use react i need to split my app into multiple pieces. It's quite inconvinient to create different folders for each section of the whole app bcs they could be very small and it would be easier to split them into folders by page or section. So the only way to me (for now) is to create my own build process.
I also have a similar use case as Alexter-progs. I am developing a chrome extension which requires both background.js and inject.js files. At present, there isn't a way for me to generate separate bundles to be able to make a chrome extension.
If you ask me, application should be separated into logical bundles and modules. It may sound a bit complicated, but let me explain.
For example, we want to build large application, with frontend (landing page), customer dashboard and admin dashboard.
We'll have 3 bundles, let's call them frontend
, customer
, admin
. They are nothing but entry-points, only containing routes. Bundles should be able to reuse code, e.g. frontend
will display plans for customers, which is edited/maintained in admin
dashboard. Now we're approaching modules.
Module - set of components/reducers/middlewares/actions/etc grouped by logic, e.g. Users, Plans, Notifications, UIKit. Each module could communicate with each other and also be used by bundles only via single index.js
containing all module's exports. Each module should be loaded on-demand, so we separate each bundle and module into it's own chunk.
I would be really happy if we can have current create-react-app structure as simple
application and smth called advanced
similar to I've described above. E.g. create-react-app foo
creates simple app by default, but if we use create-react-app foo --advanced
it would switch to advanced mode.
/cc @gaearon
Hi, using webpack built-in MultiCompiler it is quite easy to run different builds at the same time (including shared cache and quick rebuilding when watching).
In the most basic setup it would use the same index.html template and generate index.html, index2.html and indexanything.html from index.js, index2.js and indexanything.js, with minimal page-specific bundles, but using the same output dirs for assets. That would necessitate changing only a few lines in create-react-app react-scripts. I could send a PR as a discussion point, so we could see how it affects testing, routing etc? Somebody more knowledgeable in create-react-app customs could continue from there.
It might also be possible to configure source templates, output paths, etc, from package.json fields or file system structure (such as src/pages/indexname/index.js), but I would guess that outputting properly to different paths would need more involved changes depending on how paths are handled in build scripts. I could look into this briefly too, if you have opinions on how it should work...
I met the same problem, here is my way, it works :
Four steps to add multiple entry points to create-react-app
(say we add an admin.html):
1. Eject the project
npm run eject
2. Add multiple entry to webpack.config.dev.js
entry: {
index: [
require.resolve('react-dev-utils/webpackHotDevClient'),
require.resolve('./polyfills'),
require.resolve('react-error-overlay'),
paths.appIndexJs,
],
admin:[
require.resolve('react-dev-utils/webpackHotDevClient'),
require.resolve('./polyfills'),
require.resolve('react-error-overlay'),
paths.appSrc + "/admin.js",
]
},
output: {
path: paths.appBuild,
pathinfo: true,
filename: 'static/js/[name].bundle.js',
chunkFilename: 'static/js/[name].chunk.js',
publicPath: publicPath,
devtoolModuleFilenameTemplate: info =>
path.resolve(info.absoluteResourcePath),
},
3. Modify HtmlWebpackPlugin
add a new plugin node:
new HtmlWebpackPlugin({
inject: true,
chunks: ["index"],
template: paths.appHtml,
}),
new HtmlWebpackPlugin({
inject: true,
chunks: ["admin"],
template: paths.appHtml,
filename: 'admin.html',
}),
4. webpack Dev Server
rewrite urls /config/webpackDevServer.config.js:
historyApiFallback: {
disableDotRule: true,
// 指明哪些路径映射到哪个html
rewrites: [
{ from: /^\/admin.html/, to: '/build/admin.html' },
]
}
detail: http://imshuai.com/create-react-app-multiple-entry-points/
Thanks @maoshuai. Works perfectly!
@maoshuai, Worked out well, thanks. Though I had to change the value of filename to 'admin.html', without the build/ like this...
new HtmlWebpackPlugin({
inject: true,
chunks: ["admin"],
template: paths.appHtml,
filename: 'admin.html',
}),
@Sowed You are right, I had amended it.
Have some effects when change the output file name? The filename is "bundle.js" definitely in react-dev-utils/webpackHotDevClient.js. https://github.com/facebookincubator/create-react-app/blob/master/packages/react-dev-utils/webpackHotDevClient.js#L36
HI guys. I'm just wonder how you feel about extending webpack.config? Same or similar way as Storybook did it https://storybook.js.org/configurations/custom-webpack-config/? In that way you'll give more power to the end user. I can imagine something like this will kick off creating plugins for react-create-app and it could became quite conflicting. However, I believe this could be handled by some conflict message which warn user whenever overwriting property trying to overwrite already overwritten property. Thanks
@maoshuai What changes might one need to make if using a library
and libraryTarget
in the webpack output
?
Hi guys.
My use case is this:
When I create a new component and I want to actually release it standalone, I still need an app as a testbed/dev environment. One solution would be to use storybook, another - what I do - is create a separate project with create-react-app, and then I have two projects, e.g. my-component
and my-component-examples
. It works, but it's clumsy.
I'd prefer having src/examples
and src/my-component
with the ability to build e.g. just the component á la "build": "react-scripts build src/my-component"
.
By the way, the electron-webpack
project has an interesting approach. It's not perfect yet, and it would require quite some hard-wiring inside create-react-app
, but: As a user we define a custom webpack config, e.g. in package.json, and that's justthe path to a .js file that simply exports a partial configuration, and under the hood it gets merged via webpack-merge
.
What's missing in the electron-webpack
approach (as of now) is the abiity to specify the merge strategy from outside, it's using merge.smart
automatically so it's easy to add stuff (loaders, entry points), but you cant replace anything.
Anyways, just a suggestion. The ergonomy of that approach is great: As a user, you just use what's provided and don't care, until you do care and add some modifications to the default config.
I also want this since I have multiple applications in my single CRA setup (since I have a lot of shared components). I tried this approach:
import AsyncLoad from './components/AsyncComponent' // this is basically react-loadable
const App = AsyncLoad({ loader: () => import(`${condition? './App1' : './App2'}`) });
ReactDOM.render(<App />,
document.getElementById('root')
)
The problem is that webpack is bundling this stuff a lot of time. If I do simple imports, it bundles in below a minute.
@themre You could improve that by using compile-time conditional switches. Then webpack won't bundle all possibilities. E.g:
importApp() {
switch (process.env.MY_CONDITION) {
case 'App1': return import('./App1');
case 'App2': return import('./App2');
}
}
The trick is to have explicit and static imports. I think this should work because Uglify pluin removes dead code after static analysis.
@loopmode you mean the webpackConfig
option (as for the custom webpack config file)? I could imagine that the custom webpack file could either export a plain object or a function.
In case of a plain object, that could be merged. In case of a function, that function could receive the current webpack configuration and is expected to return a new webpack configuration. Then you could do whatever you want with your webpack configuration. (webpack itself allows for exporting a function too)
@rmoorman Yes absolutely, that works too. Just receive the config, modify it and return it again. In that case, webpack-merge
doesn't even have to be known to create-react-app
- we "users" could decide whether we need it or not, as in many cases it would be enough to just modify the given config a little.
Any update on this? Using a similar approach to @tbillington at the moment
What i've done differently, is make sure that the original index.js
file remains untouched. This way, it's business as usual, unless you want to build your alternate
app.
"build:app2": "cp src/index.js src/index-default.js && cp src/app2.js src/index.js && react-scripts build && cp src/index-default.js src/index.js",
What this does:
- Step 1: copy
index.js
toindex-default.js
- Step 2: copy
app2.js
toindex.js
- Step 3: run the build script
- Step 4: copy
index-default.js
back toindex.js
Then, you can have an app2.js
file that only builds when you run npm run build:app2
Does the job for now!
@GioLogist
My opinion is this is incredibly convoluted. You are trying to use a simple tool (CRA) for a more complex use case (multiple entry points). It's just not supported.
Doing something like https://github.com/facebookincubator/create-react-app/issues/1084#issuecomment-349846916 means you lose the simplicity. You might at that point eject and/or maintain your own fork.
Hey, I'm hoping I can get some advice. I'm successfully following this technique outlined by @maoshuai 👍 but when it comes to the npm run build
I'm a little stuck.
I've been tweaking the webpack.config.prod.js
. So far I've added the following into the entry
object:
entry: {
polyfills: require.resolve('./polyfills'),
vendors: require.resolve('./vendor'),
runner: paths.appSrc + '/admin.js', // this is my new bundle
main: paths.appIndexJs,
}
…which generates the right bundle file, but when it comes to the resulting index.html
and admin.html
- the index contains the admin
bundle - and doesn't boot, and the admin.html
matches the index.html
file - obviously not the desired result.
How do I configure the production build of admin.html
to use the correct bundle?
@remy , I haven't tried your setup of the entry object, but a I curated a similar setup in prod, like the one @maoshuai presented in webpack.config.dev.js. By stripping out the hot reloader and error overlays and using multiple html webpack plugins, it just works.
in webpack.config.prod.js
entry: {
index: [
require.resolve('./polyfills'),
paths.publicJs, //in paths.js publicJs: resolveApp('src/index.js'),
],
admin: [
require.resolve('./polyfills'),
paths.adminJs, //in paths.js adminJs: resolveApp('src/admin.js'),
],
},
output: {
path: paths.appBuild,
filename: 'static/js/[name].[chunkhash:8].js',
chunkFilename: 'static/js/[name].[chunkhash:8].chunk.js',
publicPath: publicPath,
devtoolModuleFilenameTemplate: info =>
path.relative(paths.appSrc, info.absoluteResourcePath).replace(/\\/g, '/'),
},
and also remember to split the HtmlWebpackPlugins
module: {
...
},
plugins: [
...
// Generates an `index.html` file with the <script> injected.
new HtmlWebpackPlugin({
inject: true,
chunks: ['index'],
template: paths.publicHtml, // paths.js publicHtml: resolveApp('public/index.html')
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
}),
// Generates an `admin.html` file with the <script> injected.
new HtmlWebpackPlugin({
inject: true,
chunks: ['admin'],
template: paths.adminHtml, // paths.js adminHtml: resolveApp('public/admin.html')
filename: 'admin.html',
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
}),
...
]
#PS, it's still painful though upgrading react and cra after the eject, having to retouch files to make some things work. I just wish inbuilt support for multiple entry points ships soon.
@gaearon adding a feature to pass entry point as param to 'react-scripts build' will be a good feature right.
package.json
"build": "react-scripts build",
"build-admin": "react-scripts build src/admin.js",
"build-kiosk": "react-scripts build src/kiosk.js"
or
"build": "react-scripts build",
"build-admin": "react-scripts build --entry src/admin.js",
"build-kiosk": "react-scripts build --entry src/kiosk.js"
build directory
build
├── asset-manifest.json
├── favicon.ico
├── images
├── static
├── index.html
├── admin
│ ├── index.html
├── kiosk
│ ├── index.html
In the meantime, a way around this is to pass through environment variables that determine which file to include for your app and then specify multiple builders in your package.json. eg:
package.json
...
"start": "react-scripts start",
"start-mobile": "REACT_APP_WHICH_APP=mobile npm start",
"build": "react-scripts build",
"build-all": "npm run build-mobile && npm run build",
"build-mobile": "REACT_APP_WHICH_APP=mobile npm run build && mv build build-mobile",
App.js
switch (process.env. REACT_APP_WHICH_APP) {
case 'mobile':
RootScreen = require('./screens/mobile/RootScreen').default;
break;
default:
RootScreen = require('./screens/desktop/RootScreen').default;
break;
}
export default RootScreen;
Remember your environment variables must begin with REACT_APP_ in order for them to be picked up.
That seems like a totally valid solution, not just a workaround. One could have a file entry.js
and use REACT_APP_ENTRY
in the switch-case there.
I have a general purpose "App Variants" feature in my fork. It works like react-native's .ios vs .android, but is configurable, like this:
app/
package.json
"targets": { // configure variants
"ios": { // configure ios variant
"jsExts": [".ios.js", ".cor.js"],
"appHtml": "index.cor.html"
},
"android": { // configure android variant
"jsExts": [".android.js", ".cor.js"],
"appHtml": "index.cor.html"
},
},
"scripts": {
"build": "react-scripts build", // standard build
"build:android": "TARGET=android react-scripts build", // build android
"build:ios": "TARGET=ios react-scripts build" // build ios
}
src/
App.js
import comp1 from './comp1';
import comp2 from './comp2';
comp1.js // use in standard build
comp1.android.js // use in TARGET=android build
comp1.ios.js // use in TARGET=ios build
comp2.js // use in standard build
comp2.cor.js // use in both ios and android builds
public/
index.html // use in standard build
index.cor.html // use in TARGET=ios and TARGET=android build
build/ // prod output for standard build
build_android/ // prod output for TARGET=android build
build_ios/ // prod output for TARGET=ios build
I'd PR that here if it has any support.
I had the same problem developing Chrome extensions with React, and there is my workaround.
-
Add additional
html
file to/public
directory. It has to be almost the same asindex.html
except following changes- Change
id
of root element so for every entry point it has to be uniq
<div id="options-root"></div>
- Manually add
- Change
-
Last change in
index.js
file. I added condition based on root element and load modules dynamically.
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import registerServiceWorker from './registerServiceWorker';
const rootApp = document.getElementById('root');
const optionsApp = document.getElementById('options-root');
if (rootApp) {
import('./App').then(({ App }) =>
ReactDOM.render(<App />, rootApp));
} else if (optionsApp) {
import('./Options').then(({ Options }) =>
ReactDOM.render(<Options />, optionsApp));
}
registerServiceWorker();
@avorobiov This is my favorite approach so far :) This will only work in dev, though, right? (Since /static/js/bundle.js
doesn't have a fingerprint in the name.)
For one possible approach - Neutrino has the concept of a mains
pref:
https://neutrino.js.org/api/#optionsmains
Which is then used for:
- entry point definitions, including pre-pending
webpack-dev-server/client
- configuring multiple instances of html-webpack-plugin ...and so on.
Whilst we are reluctant to add too many options to Neutrino, making users manually override every case where the entry point is referenced was pretty burdensome, so it seemed worth the trade-off.
Also, neutrino is working on a web-extension preset (module).
The purpose is to build a new work-flow (files organization, start
script...) for Web Extension.
There is a (IMHO) very elegant solution to this problem that doesn't require ejecting by making use of react-app-rewired.
If you want to include an entry for an "admin" page for instance, then simply add a config-overrides.js file to your project root with the following contents:
// config-overrides.js
const paths = require('react-scripts/config/paths');
const path = require('path');
const publicPath = paths.servedPath
// Define the paths to your javascript & HTML files
// admin.js should be in your src folder
// admin.html should be in your public folder
paths.adminJs = paths.appSrc + '/admin.js';
paths.adminHtml = paths.appPublic + '/admin.html';
// Utility function to replace plugins in the webpack config files used by react-scripts
const replacePlugin = (plugins, nameMatcher, newPlugin) => {
const pluginIndex = plugins.findIndex((plugin) => {
return plugin.constructor && plugin.constructor.name && nameMatcher(plugin.constructor.name);
});
if (pluginIndex === -1) {
return plugins;
}
const nextPlugins = plugins.slice(0, pluginIndex).concat(newPlugin).concat(plugins.slice(pluginIndex + 1));
return nextPlugins;
};
module.exports = function override(config, env) {
// Define the entries for index & admin
config.entry = {
index: [
require.resolve('react-scripts/config/polyfills'),
paths.appIndexJs,
],
admin: [
require.resolve('react-scripts/config/polyfills'),
paths.adminJs,
]
};
// Split code into chuncks for index & admin
config.output = {
path: paths.appBuild,
pathinfo: true,
filename: 'static/js/[name].bundle.js',
chunkFilename: 'static/js/[name].chunk.js',
publicPath: publicPath,
devtoolModuleFilenameTemplate: info =>
path.resolve(info.absoluteResourcePath),
};
// Create a HTML entry for index that only includes code for the index bundle
const indexHtmlPlugin = new HtmlWebpackPlugin({
inject: true,
chunks: ["index"],
template: paths.appHtml,
});
// Create a HTML entry for admin that only includes code for the admin bundle
const adminHtmlPlugin = new HtmlWebpackPlugin({
inject: true,
title: 'Admin page',
chunks: ["admin"],
template: paths.adminHtml,
});
// Replace the default HTML entry in the webpack config used by react-scripts with the index HTML entry
replacePlugin(config.plugins, (name) => /HtmlWebpackPlugin/i.test(name), indexHtmlPlugin);
// And finally add the second HTML plugin for your Admin page.
config.plugins.push(adminHtmlPlugin);
return config;
};
These overrides create an index.html and an admin.html file in your build folder containing just the code chuncks they need, without needing to eject, so you retain all the conveniences provided by create-react-app.
The replacePlugin function was written by @ngotchac => see his comment
@poupapaa I like your solution, so I followed your instruction however in my build/, my index.html is including the css and js for the other page instead of the chunks I specified in the HTMLWebPackPlugin config.
Can you help me? Here's my config-override.js
// config-overrides.js
const paths = require('react-scripts/config/paths');
const path = require('path');
const publicPath = paths.servedPath;
const HtmlWebpackPlugin = require('html-webpack-plugin');
// Define the paths to your javascript & HTML files
// glrl.js should be in your src folder
// glrl.html should be in your public folder
paths.glrlJs = paths.appSrc + '/glrl.js';
paths.glrlHtml = paths.appPublic + '/glrl.html';
// Utility function to replace plugins in the webpack config files used by react-scripts
const replacePlugin = (plugins, nameMatcher, newPlugin) => {
const pluginIndex = plugins.findIndex((plugin) => {
return plugin.constructor && plugin.constructor.name && nameMatcher(plugin.constructor.name);
});
if (pluginIndex === -1) {
return plugins;
}
const nextPlugins = plugins.slice(0, pluginIndex).concat(newPlugin).concat(plugins.slice(pluginIndex + 1));
return nextPlugins;
};
module.exports = function override(config, env) {
// Define the entries for index & glrl
config.entry = {
index: [
require.resolve('react-scripts/config/polyfills'),
paths.appIndexJs,
],
glrl: [
require.resolve('react-scripts/config/polyfills'),
paths.glrlJs,
]
};
// Split code into chuncks for index & glrl
config.output = {
path: paths.appBuild,
pathinfo: true,
filename: 'static/js/[name].bundle.js',
chunkFilename: 'static/js/[name].chunk.js',
publicPath: publicPath,
devtoolModuleFilenameTemplate: info =>
path.resolve(info.absoluteResourcePath),
};
// Create a HTML entry for index that only includes code for the index bundle
const indexHtmlPlugin = new HtmlWebpackPlugin({
inject: true,
title: 'Double Up',
chunks: ["index"],
template: paths.appHtml,
});
// Create a HTML entry for glrl that only includes code for the glrl bundle
const glrlHtmlPlugin = new HtmlWebpackPlugin({
inject: true,
title: 'Green Light Red Light',
chunks: ["glrl"],
template: paths.glrlHtml,
});
// Replace the default HTML entry in the webpack config used by react-scripts with the index HTML entry
replacePlugin(config.plugins, (name) => /HtmlWebpackPlugin/i.test(name), indexHtmlPlugin);
// And finally add the second HTML plugin for your glrl page.
config.plugins.push(glrlHtmlPlugin);
return config;
};
I did some digging and it looks like replacePlugin function was not able to successfully replace the original config for index.html. Still looking for a solution to override/replace it.
Update: Fixed now. Updated the replacePlugin line to be:
config.plugins = replacePlugin(config.plugins, (name) => /HtmlWebpackPlugin/i.test(name), indexHtmlPlugin);
to make sure the new HTML config are used.
Sorry @carolozar, just now noticed your comment. Well spotted & nice fix, kudos! :+1:
@gaearon
My opinion is this is incredibly convoluted.
Wouldn't enabling custom webpack configuration, maybe using something like webpack-merge be a lot less convoluted way of enabling this (without ejecting or forking CRA)
@poupapaa @carolozar
Hi, can I ask for help? seems I have a problem when building my application, it does not add a code "JS" and "CSS" in admin.html or index.html inside the build folder, Please see below my code:
const paths = require('react-scripts/config/paths');
const path = require('path');
const publicPath = paths.servedPath;
const HtmlWebpackPlugin = require('html-webpack-plugin');
// Define the paths to your javascript & HTML files
// overriding index to member and add admin
// [name].js should be in your src folder
// [name].html should be in your public folder
paths.adminJs = paths.appSrc + '/admin/index.js';
paths.adminHtml = paths.appPublic + '/admin.html';
paths.appIndexJs = paths.appSrc + '/member/index.js';
// Utility function to replace plugins in the webpack config files used by react-scripts
const replacePlugin = (plugins, nameMatcher, newPlugin) => {
const pluginIndex = plugins.findIndex((plugin) => {
return plugin.constructor && plugin.constructor.name && nameMatcher(plugin.constructor.name);
});
if (pluginIndex === -1)
return plugins;
const nextPlugins = plugins.slice(0, pluginIndex).concat(newPlugin).concat(plugins.slice(pluginIndex + 1));
return nextPlugins;
};
function rewire(config, env) {
// check and get portal value: member or admin
const portal = process.env.REACT_APP_PORTAL || 'member'
// Define the entries for index & admin
config.entry = {
index: [
require.resolve('react-scripts/config/polyfills'),
paths.appIndexJs,
],
admin: [
require.resolve('react-scripts/config/polyfills'),
paths.adminJs,
]
};
// Split code into chuncks for index & admin
config.output = {
path: paths.appBuild,
pathinfo: true,
filename: 'static/js/[name].bundle.js',
chunkFilename: 'static/js/[name].chunk.js',
publicPath: publicPath,
devtoolModuleFilenameTemplate: info =>
path.resolve(info.absoluteResourcePath),
};
const htmlTemplates = {
member: paths.appHtml,
admin: paths.adminHtml,
}
// Create a HTML entry for index that only includes code for the index amd admin bundle
const htmlPluginConfig = new HtmlWebpackPlugin({
inject: true,
chunks: [portal],
template: htmlTemplates[portal],
});
// Replace the default HTML entry in the webpack config used by react-scripts with the index HTML entry
config.plugins = replacePlugin(config.plugins, (name) => /HtmlWebpackPlugin/i.test(name), htmlPluginConfig);
return config
}
module.exports = rewire
`
Thank you @poupapaa for the perfect solution, but I found that the devServer config was not rewritten. By reading the document of react-app-rewired, I found the way to modify devServer config by config-override.js.
Here is my config-override.js
const paths = require('react-scripts/config/paths');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const rewireLess = require('react-app-rewire-less-modules');
// 多页配置:增加入口
paths.appAdminJs = paths.appSrc + '/admin.js';
const replacePlugin = (plugins, nameMatcher, newPlugin) => {
const pluginIndex = plugins.findIndex((plugin) => {
return plugin.constructor && plugin.constructor.name && nameMatcher(plugin.constructor.name);
});
if (-1 === pluginIndex) {
return plugins;
}
const nextPlugins = plugins.slice(0, pluginIndex).concat(newPlugin).concat(plugins.slice(pluginIndex + 1));
return nextPlugins;
};
module.exports = {
webpack: (config, env) => {
if ('development' === env) {
config.entry = {
index: [
require.resolve('react-scripts/config/polyfills'),
require.resolve('react-dev-utils/webpackHotDevClient'),
paths.appIndexJs,
],
admin: [
require.resolve('react-scripts/config/polyfills'),
require.resolve('react-dev-utils/webpackHotDevClient'),
paths.appAdminJs,
]
};
// 多页配置:区分开入口文件
config.output.filename = 'static/js/[name].bundle.js';
const indexHtmlPlugin = new HtmlWebpackPlugin({
inject: true,
template: paths.appHtml,
// 多页配置:2.x 版本默认开启 optimization ,chunks 中需要增加 'vendors', 'runtime~admin'
chunks: ['vendors', 'runtime~index', 'index'],
filename: 'index.html',
});
const adminHtmlPlugin = new HtmlWebpackPlugin({
inject: true,
template: paths.appHtml,
chunks: ['vendors', 'runtime~admin', 'admin'],
filename: 'admin.html',
});
// 多页配置:
config.plugins = replacePlugin(config.plugins, (name) => /HtmlWebpackPlugin/i.test(name), indexHtmlPlugin);
config.plugins.push(adminHtmlPlugin);
} else {
config.entry = {
index: [require.resolve('react-scripts/config/polyfills'), paths.appIndexJs],
admin: [require.resolve('react-scripts/config/polyfills'), paths.appAdminJs],
};
const indexHtmlPlugin = new HtmlWebpackPlugin({
inject: true,
template: paths.appHtml,
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
chunks: ['vendors', 'runtime~index', 'index'],
filename: 'index.html',
});
const adminHtmlPlugin = new HtmlWebpackPlugin({
inject: true,
template: paths.appHtml,
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
chunks: ['vendors', 'runtime~admin', 'admin'],
filename: 'admin.html',
});
// 多页配置:替换 index.html 生成规则
config.plugins = replacePlugin(config.plugins, (name) => /HtmlWebpackPlugin/i.test(name), indexHtmlPlugin);
// 多页配置:添加 plugin 生成 admin.html
config.plugins.push(adminHtmlPlugin);
}
return config;
},
devServer: (configFunction) => {
return (proxy, allowedHost) => {
const config = configFunction(proxy, allowedHost);
// 多页配置: 指明哪些路径映射到哪个html
config.historyApiFallback.rewrites = [
{ from: /^\/admin.html/, to: '/build/admin.html' },
];
return config;
};
},
};
Hi! I created a package to make it a bit more convenient: https://github.com/tadatuta/rewire-entry
@gaearon, isn't this a stupid simple problem to solve or am I missing something? Willing to submit a PR if this looks about right to you, but I understand if there are some concerns about introducing a vendor.js alongside n number of entry points. Either way, we should talk it out. Looking for some guidance before committing too much time into this.
This also opens up a whole new topic/concern about bundle optimization and if we want CRA to have any of that at all. If we did, I might suggest a similar setup to what we use at Nordstrom Rack | HauteLook, which provides the following bundles:
-
vendor.js
all 3rd-party code from thenode_modules
folder. Large file, but cached and very rarely changes. -
common.js
contains common chunks of business code between multiple entry points. Changes pretty much every deploy, but not likely to be near as big asvendor.js
. -
<entry-point>.js
contains mostly code specific only to that entry point. Should be a pretty small file and only changes if that entry point has been changed.
The webpack configuration should look something like this:
// webpack.config.babel.js
import { compact, keys } from 'lodash';
const isDev = NODE_ENV === 'development';
export default {
plugins: compact([
isDev && new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: 3,
}),
!isDev && new webpack.optimize.CommonsChunkPlugin({
name: 'common',
chunks: keys(browserMainEntries),
minChunks: 3,
}),
!isDev && new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
chunks: ['common'],
minChunks: isExternal,
}),
]),
}
function isExternal({ context }) {
return context && context.indexOf('node_modules') !== -1;
}
Edit: browserMainEntries
represents the list of entry points (e.g., home.js
, catalog.js
, about.js
). You can get this by scanning a directory in which you only place files for each entry point.
I also believe that adding this could add way too much complexity to create-react-app.
Recently I needed different entry points to export an app to both electron and the web. Since the electron variant has access to node.js APIs that aren't available on browsers it was impossible to have the same bundle on both platforms. I can't put require statements inside if
blocks (I think these get hoisted by webpack), so all of the workarounds posted here aren't useful for me.
Eventually I just decided to fork react-scripts
and implement this feature for myself.
This is actually really easy to achieve if you decide to override the entry point using environment variables.
At the top of packages/react-scripts/config/paths.js
I declare:
const entryPoint = process.env.ENTRY || 'index.js';
Then in that same file I replaced every "index.js" occurrence with "entrypoint like this:
- appIndexJs: resolveApp('src/index.js'),
+ appIndexJs: resolveApp(`src/${entryPoint}`)
If I run yarn start
I get the same behavior as usual, index.js
is the entry point. But, if run ENTRY=custom-index.js yarn start
the the entry point is now custom-index.js
. Easily switching between variants just by changing a variable is very nice. I haven't tested this exhaustively yet, but it start
and build
work pretty great so far.
If this feature were to be added into create-react-app
I think that this is probably the best approach. The change is small and it doesn't break things for anyone.
+1 for #5671 , this issue almost two years old, is it possible to merge this one?
@GAumala @lepture since we also want to change the index html can we change like this?
20a21
> const envEntryPoint = process.env.ENTRY_POINT || 'index';
82,83c83,84
< appHtml: resolveApp('public/index.html'),
< appIndexJs: resolveModule(resolveApp, 'src/index'),
---
> appHtml: resolveApp('public/' + envEntryPoint + '.html'),
> appIndexJs: resolveModule(resolveApp, 'src/' + envEntryPoint),
104,105c105,106
< appHtml: resolveApp('public/index.html'),
< appIndexJs: resolveModule(resolveApp, 'src/index'),
---
> appHtml: resolveApp('public/' + envEntryPoint + '.html'),
> appIndexJs: resolveModule(resolveApp, 'src/' + envEntryPoint),
If you go this way, I'd also like to change the .env location. Maybe it is more simple to change the appDirectory value used by resolveApp ? https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/config/paths.js#L17
This could work with naming convention, as it does now. index.js --> Normal single entry *.entry.js -> Multiple entrypoints.
In case you run into this problem trying to build a chrome extension (and need eg. an options.html and a index.html), here is what worked for me:
I just appended a hash to the index.html and "route" to the correct view in App.js. Something like:
"manifest_version": 2,
"name": "Random",
"version": "1.0.0",
"options_page": "index.html#options",
...
"browser_action": {
"default_popup": "index.html#popup"
},```
Happy New Year! (and soon Chinese New Year)
I've been watching this issue for a while, and I can see there's a couple workaround. Which one is the "recommended" way (as in, future proofing in line with CRA direction)?
@wiradikusuma I do think there is no "recommended" way as long as the feature is not provided natively. The solution you chose, depends always on your requirement and the purpose of the app.
There are a lot of good solutions in this discussion here. Thanks to all of you!
A different use case than what was described here before: we need (and forked to get) a second entrypoint for our service worker.
The current SW approaches (with workbox and plugins) is seriously crippled for any real service worker logic (BackgroundSync, push notifications, etc). By having an additional entrypoint you gain the same Typescript stack, code sharing, etc.
@gaearon I agree that it's a complicated use case, but the point of great tools is to be simple at first and let you access more expert-y features when you need them. I really wished there was an other way than forking, SW are an advanced feature, but fiddling with react-scripts and maintaining your fork afterwards is a whole different game.
Since your fork is now up and running, maybe Facebook maintainers will accept a pull request?
Our fork is pretty much "make it work" for our use case, definitely not "make it right". There would be multiple other concerns for inclusion in a project like CRA (docs, tests, ), I'm not sure a PR with our minimal modifications would be needed.
That being said, feel free to use it, or be inspired from it, it's available at https://github.com/ouihelp/create-react-app/pull/4/files - and you can also try it with:
yarn create react-app sw-entrypoint-test-app --typescript --scripts-version @ouihelp/react-scripts
Just wanted to post an alternate requirement for multiple entry-points that isn't the normal "User" app and "Admin" app thing.
Implementing "Silent Authentication" with an identity provider such as Auth0: https://auth0.com/docs/api-auth/tutorials/silent-authentication
You definitely don't want the silent-auth redirect loading a lot of code. But at the same time there are definitely a few things you do (or at least I do) want to share at a code level (post target, etc.) without having to go all the way to defining those shared parts as configuration or even factoring out a whole shared library.
I'm fine with ejecting being the solution to this - just wanted to mention it as a use-case of this feature for future consideration.
I think it's obvious that most people want a config file at project root to overwrite specific keys of react-scripts
' Webpack configuration (and leave leave the rest as they are) - without ejecting or forking the whole entity of react-scripts
.
I mean... This could also be in your (Create React App team) interest, as 1/3 of all issues are related to Webpack: https://github.com/facebook/create-react-app/issues?utf8=%E2%9C%93&q=is%3Aissue+webpack
We had to build an admin / portal apps and did not want to come up with two different projects as this requires extracting common components into third project, which complicates the entire stack.
As create-react-app is a single app build system, we came up with the following solution:
Assuming you have two entry points admin & portal, we simply maintain two entry point files (instead of the default index.js), so we have:
-
src/admin.js
-
src/portal.js
then we we have a simple switch-entrypoint
command that does:
# replace "homepage" property in package.json
sed -i '' -e 's/\("homepage": \)\(.*\)/\1"\/${ENTRYPOINT}",/' package.json
# override index.js with the correct entry point
cp src/${ENTRYPOINT}.js src/index.js
for those who's using Makefile, here's an example
Advantages:
- No eject
- Simple and clean
"Disadvantages":
- Two build folders
- Cannot start single dev server for both projects
I personally see the "Disadvantages" as a positive thing as IMO we should treat our different entry points as isolated apps.
Thanks
Here is one more example CRA with multiple entries without ejecting.
Basic idea is to symlink desired entrypoint to index.js
on start and build, and you can have how many you want, in a single app.
https://github.com/iamandrewluca/example-cra-multiple-entries/pull/3
Notes:
- You can't have multiple entries started at same time
- You can't build multiple entries at the same time
- Each entry will have it's own build folder.
- For
PUBLIC_URL
just append it beforebuild-*
command https://github.com/iamandrewluca/example-cra-multiple-entries/pull/4
Looking at PR 5671 (which was closed because nobody reviewed it) seemed like small change to support output to multiple html files.
I'm admittedly new to the CRA stack. But I'm surprised to see this issue about multiple entry points limping along for over two years. Is this not a common problem, or, are most folks only building small-sized apps with this framework?
Isnt there just some sort of a "safety valve" placeholder one can add into webpack to support modifications/overrides to support something like 5671
Does anyone have an example working for CRA v3.2.0
?
This package https://github.com/Derek-Hu/react-app-rewire-multiple-entry worked flawlessly for me and we have a non standard CRA setup.
I successfully workaround this using dynamic import and React lazy loading of main component, might want to see in this gist
This way, while the entry point is still the unswitchable index.js, main component loaded by it can still be switched using environment variable.
Just so everyone's clear, switching the entry point via environment variables or otherwise does not actually solve the problem of not having multiple entry points. With actual multiple entry points, you can optimize the bundles via webpack such that scripts are shared between bundles in an efficient way. You get none of this with only a single entry point, because webpack doesn't have the whole picture.
@jedmao I get your point, I even have an example where this would be useful. We have a project where it has 5 frontend modules. Each module share same library also created internaly as living style guide. Would be great when building all 5 modules, they would share, common dependencies like, react, react-dom, bootstrap, reactstrap, our living style guide, and more dependencies.
And you can do exactly that. You just need to eject and tweak your webpack configuration. It works beautifully.
And you can do exactly that. You just need to eject and tweak your webpack configuration. It works beautifully.
The supposed benefit of CRA is having the easy path of React development without needing to tweak configurations. Using CRA is very streamlined in that it allows seamless upgrade of try-and-tested best practice configuration. Most boilerplate is clone-once-then-orphan from its upstream repo, that cannot be easily upgraded with NPM. Ejecting CRA and tweak sounds great, except when you have a lot of repo/app you need to maintain, each of them might need update every once in a while. Allowing customization without ejecting is opening CRA for much wider use cases. If you're suggesting ejecting, everything becomes possible but you're defeating the purpose of easy maintenance of CRA bootstrapped app. If ejecting is already desired, I think we won't be here asking for these features.
I agree, but considering the complexity of just blindly adding more entry points without accounting for your specific bundling needs, which will 100% need tweaking, I think the real solution here is to make this aspect and the ability to tune bundles both configurable in a CRA.
Understanding that making CRA configurable is not yet within the design goals of CRA, ejecting is the only viable option.
I recently encountered a similar scenario where I'd like multiple entries points and stumbled onto this issue. I'm glad to see it's still active and under discussion. I've been hesitant to eject, but I understand there is a lot of complexity with CRA managing user defined entry points.
My use-case is needing to load an iframe from CRA that uses a separate entry point.
Reading through the comments I was able to hack together a solution that should code split and bundle assets by combining the solution from @carruthe and @benedictjohannes.
Disclaimer I will be ejecting and not using this solution, but I wanted to go ahead and post it for others if they wanted to discuss the advantages and disadvantages of this approach. I've enjoyed reading through all of the creative solutions on this thread.
Note: This works with iframes.
By combining dynamic imports and url parsing here is the solution I have working locally without ejecting.
import React, { lazy, Suspense } from 'react';
import ReactDOM from 'react-dom';
import Parser from './parser'; // Parser is a URL parser to extract the example parameter.
// Parses the browser url for the example parameter value.
const { example } = Parser.parse();
const Example = lazy(() => import(`./Example${example || 3}`));
const App = () => (
<Suspense fallback={<div>Loading...</div>}>
<Example />
</Suspense>
);
ReactDOM.render(<App />, document.getElementById('root'));
Source code here: https://github.com/StephenEsser/example-code-splitting
Example demo here: https://stephenesser.github.io/example-code-splitting/
The url parser algorithm is from here
Dynamic Imports information can be found here.
Hi @gaearon
Could you please share you thought on what could be the best way for doing this on CRA 3.3.0?
I met the same problem, here is my way, it works :
Four steps to add multiple entry points to create-react-app
(say we add an admin.html):
1. Eject the project
npm run eject
2. Add multiple entry to webpack.config.dev.js
entry: { index: [ require.resolve('react-dev-utils/webpackHotDevClient'), require.resolve('./polyfills'), require.resolve('react-error-overlay'), paths.appIndexJs, ], admin:[ require.resolve('react-dev-utils/webpackHotDevClient'), require.resolve('./polyfills'), require.resolve('react-error-overlay'), paths.appSrc + "/admin.js", ] }, output: { path: paths.appBuild, pathinfo: true, filename: 'static/js/[name].bundle.js', chunkFilename: 'static/js/[name].chunk.js', publicPath: publicPath, devtoolModuleFilenameTemplate: info => path.resolve(info.absoluteResourcePath), },
3. Modify HtmlWebpackPlugin
add a new plugin node:
new HtmlWebpackPlugin({ inject: true, chunks: ["index"], template: paths.appHtml, }), new HtmlWebpackPlugin({ inject: true, chunks: ["admin"], template: paths.appHtml, filename: 'admin.html', }),
4. webpack Dev Server
rewrite urls /config/webpackDevServer.config.js:
historyApiFallback: { disableDotRule: true, // 指明哪些路径映射到哪个html rewrites: [ { from: /^\/admin.html/, to: '/build/admin.html' }, ] }
detail: http://imshuai.com/create-react-app-multiple-entry-points/
When I try this approach, I'm getting the following error:
Cannot read property 'filter' of undefined
@DanZeuss Customize-cra changed its configuration after upgrading to 2.x. Here is an example you can refer to the following:https://github.com/goblin-laboratory/cra-multi-page-template
A functional example of create-react-app
V3 working with multiple entry points: https://github.com/DanZeuss/create-react-app-multiple-entry-points
@DanZeuss ~~can you split changes into different commits, so we can see what you changed?~~
- ~~Initial app commit~~
- ~~App ejected~~
- ~~Your chagnes~~
I splitted it https://github.com/iamandrewluca/example-cra-multi-entry/pull/1/files
To check if works well with:
- webpack-dev-server
- service worker
- public assets
- PUBLIC_URL / homepage
- build folder
@DanZeuss
When I try this approach, I'm getting the following error:
Cannot read property 'filter' of undefined
the 'filter' error is caused by:
new ManifestPlugin({ // ......original code }),
modify them:
new ManifestPlugin({
fileName: "asset-manifest.json",
publicPath: publicPath,
generate: (seed, files, entrypoints) => {
const manifestFiles = files.reduce((manifest, file) => {
manifest[file.name] = file.path;
return manifest;
}, seed);
const _entrypoints = Object.values(entrypoints).reduce(
(total, cur) => [...total, ...cur],
[]
);
const entrypointFiles = _entrypoints.filter(
fileName => !fileName.endsWith(".map")
);
return {
files: manifestFiles,
entrypoints: entrypointFiles
};
}
}),
and it works well.
Hi, any update on this four year issue? Multiple entry-points are an absolute must in security perspective.
security perspective.
What do you mean?
Maybe we need an RFC to discuss possible solutions/implementations.
As @DanZeuss mentioned in #8249
My intention with this implementation is to allow the user add multiple entry points assuming that it’s going to generate separate bundles for each entry point.
Why? Because security. Let’s me introduce a scenario for you to understand my point. There is an application that is defined to work with many security best practices. One for example is to not allow the user have access to the application and all content related to it (JavaScript files, images, html, etc) without being authorized through login. The user may have access only after logged in. It’s to avoid the user have access to (included) JavaScript file/app and try to get some way to perform something the the user shouldn’t (understanding the code and injecting something, for example)
With this approach implemented, we can serve different apps in order to not have the whole application in a single file. It also avoids the user to have 2 separate (cra) applications for this purpose. This also avoid the user to eject cra and lose all benefits of it, including th futures one.
So, I hope we can reach a deal, but, independent our deal, it also needs the approvals from the reviewers, which maybe are offline for now. But I really hope to get a feedback from them.
Stay blessed
Hello,
I wanted to implement multiple entry points in my application, Do we have some clear example/link which will be helpful, I am new to webpack and do not have thorough knowledge of it.
Actually react-scripts
configures bundler to support automatic code splitting, which is multiple entry points in regard to central entry. It splits bundle for chunks even when you do nothing special. You seen 0.chunk.js
and so on? This is splittings for small chunks, it is already here (with some automatic splittings criteria).
If you want to provide some help to bundler to make it better understand the structure of your application, - then you can directly use asynchronous chunk loading for some components with something like react-loadable.
Multiple entry points allow you fully separate some code from anther to make two different app with different exclusive parts. Usually this is significantly different applications. For such case I use different create-react-app
projects. Shared (library) code is in common project and multiple src dir with alias is used to its integration.
@harshalpandey, I know this is a really long thread, so maybe you missed it in the collapsed comments, but I hope this comment is what you're looking for.
I spent a lot of time with the webpack bundle analyzer to ensure it was a highly optimized configuration; however, it does require ejecting a CRA. You might have to play with the minChunks
values until the bundle analyzer shows you what you're looking for, which should be:
- A big vendor.js file (contains mostly node_modules).
- A smaller common.js file (contains mostly header, nav and footer code, shared between most pages on your site).
- A bunch of other smaller files, ranging from small to super tiny (should be just page-specific code at this point).
According to the image, it is noticeable to the naked eye that the largest area is occupied by the localization of moment
and lodash
.
This indicates an incorrect assembly. Localizations (including moment) are definitely not needed in budnle, only one is used in the application. They must be assebled separately, here is an example how to to do that with intl-webpack-plugin
I have not met a situation where lodash would be needed at all. Neither in my projects or in which I had to work. The es
allows you to make a lot of things. Someones even struggle to avoid lodash. Nevertheless, if you really want to take something from lodash, you should use individual imports and not drag all existing code to the client.
Regarding everything else, based on these two examples above, it should be more interesting not how the bundle is assembled, but the app coverage. That is, how much code in the bundle really executes in the application (and recuired to be in bundle).
That image is from 2015, so take it for what it's worth 😄
The mentioned questions is not related to the year and bundle version. It is application configuration and with same errors it will be bundled same way today (as before).
@oklas his point was to show how app bundle can be splitted, not what actually is included in bundle
This weekend I experimented with angular-cli. They have a config, where you can add multiple projects
e.g.
For each project you can configure a lot of options
e.g
Maybe react-scripts
should start moving to use this aproach maybe, to have a config file.
Hello Guys,
Thanks a lot for your reply, But I am still not clear about the implementation with react application.
Let me try to tell you the exact use case of what exactly I want to achieve in my application.
Basically I need two entry points in the application one for the mail application and another is for the admin panel.
For Ex. localhost:3000/app - it should run the main application. localhost::3000/adminApp - it should run the application which will only be having menus that are related to admin.
Please let me know what is the exact solution I can use which will resolve my problem.
if something other then multiple entry points we can do to achieve this without ejecting the application that will also be fine.
import Loadable from 'react-loadable'
import Loading from './my-loading-component'
import {BrowserRouter as Router, Switch, Route} from "react-router-dom"
const LoadableApp = Loadable({
loader: () => import('./my-app'),
loading: Loading,
});
const LoadableAdmin = Loadable({
loader: () => import('./my-admin'),
loading: Loading,
});
const App = () => (
<Router>
<Switch>
<Route path="/app"><LoadableApp/></Route>
<Route path="/admin"><LoadableAdmin/></Route>
</Switch>
</Router>
)
In my situation, I have my main App.js using index.html but I also have chromecast-receiver.html for Google Chromecast application, I can't figure out a way to build and bundle my Chromecast App with CRA
@oklas Thanks for your solution.
I have one doubt/question. The solution which you have mentioned in which we are anyway using React Routes.
So after deploying build of my application on the server.
if I run my application and hit URL https://example.com it will initiate the route and then I can hit any of the URL (/app , /admin), and accordingly, it will load the related component.
But If I directly hit the URL https://example.com/app or https://example.com/admin , I think it will search for app or admin directory in my build folder and it will give me an error that
Not Found The requested URL /app was not found on this server.
Do we have any solution for this?
@harshalpandey when having a SPA, in most of cases server should return index.html
when no request handler found.
You could do something funky with shell scripting. Not sure how practical this is but it will solve your problem (from how I interpret it).
Have two entry point js files, lets go with
user.js
andadmin.js
.Have two
build
commands inpackage.json
;build-user
,build-admin
.When you go to run
build-user
, before runningreact-scripts build
, have some shell code that copiesuser.js
toindex.js
in your source code. Same forbuild-admin
, just copy it's entry point toindex.js
before running the actualbuild
command. This way if you're working on the same entry point consistently, you can still usebuild
.package.json
"build": "react-scripts build", "build-user": "cp src/user.js src/index.js && react-scripts build", "build-admin": "cp src/admin.js src/index.js && react-scripts build"
this sadly is still the best solution to avoid to eject
maybe is possible to use https://github.com/timarney/react-app-rewired/ and use some webpack plugin / configuration?
any recommendation for me?