craco
craco copied to clipboard
Multiple entry points
Is it possible to use craco to set multiple entry points?
Similar to
module.exports = {
devtool: "source-map",
entry: {
app: ["./src/index.js"],
silentRenew: ["./silent_renew/index.js"]
I doubt it will work.
Does craco in any way support multiple entries?
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
For those getting here from Google, I believe this config works to give you multiple entrypoints. It was ported from the much-appreciated react-app-rewire-multiple-entry.
const path = require('path');
const rewireEntries = [
{
name: 'background',
entry: path.resolve(__dirname, './src/background/index.ts'),
template: path.resolve(__dirname, './src/background/index.html'),
outPath: 'background.html',
},
{
name: 'hud',
entry: path.resolve(__dirname, './src/hud/index.tsx'),
template: path.resolve(__dirname, './src/hud/index.html'),
outPath: 'hud.html',
},
];
const defaultEntryName = 'main';
const appIndexes = ['js', 'tsx', 'ts', 'jsx'].map((ext) =>
path.resolve(__dirname, `src/index.${ext}`)
);
function webpackMultipleEntries(config) {
// Multiple Entry JS
const defaultEntryHTMLPlugin = config.plugins.filter((plugin) => {
return plugin.constructor.name === 'HtmlWebpackPlugin';
})[0];
defaultEntryHTMLPlugin.options.chunks = [defaultEntryName];
// config.entry is not an array in Create React App 4
if (!Array.isArray(config.entry)) {
config.entry = [config.entry];
}
// If there is only one entry file then it should not be necessary for the rest of the entries
const necessaryEntry =
config.entry.length === 1
? []
: config.entry.filter((file) => !appIndexes.includes(file));
const multipleEntry = {};
multipleEntry[defaultEntryName] = config.entry;
rewireEntries.forEach((entry) => {
multipleEntry[entry.name] = necessaryEntry.concat(entry.entry);
// Multiple Entry HTML Plugin
config.plugins.unshift(
new defaultEntryHTMLPlugin.constructor(
Object.assign({}, defaultEntryHTMLPlugin.options, {
filename: entry.outPath,
template: entry.template,
chunks: [entry.name],
})
)
);
});
config.entry = multipleEntry;
// Multiple Entry Output File
let names = config.output.filename.split('/').reverse();
if (names[0].indexOf('[name]') === -1) {
names[0] = '[name].' + names[0];
config.output.filename = names.reverse().join('/');
}
return config;
}
module.exports = {
webpack: {
configure: webpackMultipleEntries,
},
};
I created a multi entry demo with craco
and react-app-rewired
. This demo project is simply shifted from react-app-rewired
into craco
accroding to bkrausz's comment.
Note: This method doesn't work with [email protected]
now. I've tested it with [email protected]
. Here's a workaround.
const path = require('path');
const rewireEntries = [
{
name: 'albuminfo',
entry: path.resolve(__dirname, './src/albuminfo/index.tsx'),
template: path.resolve(__dirname, './src/albuminfo/index.html'),
outPath: 'albuminfo/index.html',
},
{
name: 'sound',
entry: path.resolve(__dirname, './src/sound/index.tsx'),
template: path.resolve(__dirname, './src/sound/index.html'),
outPath: 'sound/index.html',
},
];
const defaultEntryName = 'main';
const appIndexes = ['js', 'tsx', 'ts', 'jsx'].map((ext) =>
path.resolve(__dirname, `src/index.${ext}`)
);
function webpackMultipleEntries(config) {
// Multiple Entry JS
const defaultEntryHTMLPlugin = config.plugins.filter((plugin) => {
return plugin.constructor.name === 'HtmlWebpackPlugin';
})[0];
defaultEntryHTMLPlugin.userOptions.chunks = [defaultEntryName];
// config.entry is not an array in Create React App 4
if (!Array.isArray(config.entry)) {
config.entry = [config.entry];
}
// If there is only one entry file then it should not be necessary for the rest of the entries
const necessaryEntry =
config.entry.length === 1
? []
: config.entry.filter((file) => !appIndexes.includes(file));
const multipleEntry = {};
multipleEntry[defaultEntryName] = config.entry;
rewireEntries.forEach((entry) => {
multipleEntry[entry.name] = necessaryEntry.concat(entry.entry);
// Multiple Entry HTML Plugin
config.plugins.unshift(
new defaultEntryHTMLPlugin.constructor(
Object.assign({}, defaultEntryHTMLPlugin.userOptions, {
filename: entry.outPath,
template: entry.template,
chunks: [entry.name],
})
)
);
});
config.entry = multipleEntry;
// Multiple Entry Output File
let names = config.output.filename.split('/').reverse();
if (names[0].indexOf('[name]') === -1) {
names[0] = '[name].' + names[0];
config.output.filename = names.reverse().join('/');
}
return config;
}
module.exports = {
webpack: {
configure: webpackMultipleEntries,
},
};
It looks like the demo by @helsonxiao works as of v7.0.0-alpha.3 (probably because the dependencies for craco have been updated, specifically react-scripts) but maybe we can build this functionality directly into craco :eyes:
For those still searching for a solution just switch to Vite.js and all problems are solved.
@dilanx I'm facing an issue where the entry points work fine when building the app (craco build
) but not when running the app locally (craco start
). For some reason the webpack-web-server is hanging forever and not serving any files.
I face the same issue if I set configure.optimization.runtimeChunk
to true
or multiple
- it builds fine, but it hangs when trying to serve the app.
I'm using CRA 5 and [email protected]
@brunano21 weird. Could you share your craco config?
Sure thing!
{
webpack: {
configure: (config, { env, paths }) => {
// This makes craco (/webpack-dev-server) hanging forever if running craco start. Works fine with craco build.
config.optimization.runtimeChunk = 'single';
config.module.rules.unshift({
test: /\.js(x)?$/,
resolve: {
fullySpecified: false, // disable the behavior
},
});
// ATTEMPT #1
// config.entry = {
// main: config.entry,
// vendor: './src/vendor.js',
// };
// ATTEMPT #2
config.entry = {
main: config.entry,
vendor: {
import: './src/vendor.js',
filename: '[name][ext]',
// chunkLoading: false,
// asyncChunks: false,
},
};
return config;
},
},
style: {
css: {
loaderOptions: () =>
// Prevents Webpack from trying to load URLs as modules
// which is incompatible with public content.
({ url: false }),
},
},
jest: {
configure: (config) => {
// some stuff here
return config;
},
},
devServer: {
// NOTE: I have verified that even without a devServer configuration, the issue persists.
setupMiddlewares: (middlewares, devServer) => {
// some stuff here
return middlewares;
},
onListening(devServer) {
// some stuff here
},
},
};
Console output:
> BROWSER=none PORT=3005 craco start
(node:54736) [DEP_WEBPACK_DEV_SERVER_ON_AFTER_SETUP_MIDDLEWARE] DeprecationWarning: 'onAfterSetupMiddleware' option is deprecated. Please use the 'setupMiddlewares' option.
(Use `node --trace-deprecation ...` to show where the warning was created)
(node:54736) [DEP_WEBPACK_DEV_SERVER_ON_BEFORE_SETUP_MIDDLEWARE] DeprecationWarning: 'onBeforeSetupMiddleware' option is deprecated. Please use the 'setupMiddlewares' option.
Starting the development server...
@brunano21 okay I think I figured it out. It looks like by default in the dev environment, CRA compiles all of the entries into a single bundle, which is an issue because vendor
ends up overwriting main
which might be causing the hang. I was reading about entry points in the webpack docs.
Adding this to your webpack configure function in your craco config fixed it for me:
if (env === 'development') {
config.output = {
...config.output,
filename: 'static/js/[name].bundle.js',
};
}
I console logged the default output and the filename was static/js/bundle.js
(no reference to the name of the entry), so I think that's why adding the name makes it work.
@dilanx that did the trick, indeed! Thanks a lot for the quick turnaround. As a side effect, that also fixed the issue with the optimization.runtimeChunk = 'multiple' which was causing webpack-dev-server to hang indefinitely.
@brunano21
There is now Vite.js I would use it over CRACO any day.
@brunano21 okay I think I figured it out. It looks like by default in the dev environment, CRA compiles all of the entries into a single bundle, which is an issue because
vendor
ends up overwritingmain
which might be causing the hang. I was reading about entry points in the webpack docs.Adding this to your webpack configure function in your craco config fixed it for me:
if (env === 'development') { config.output = { ...config.output, filename: 'static/js/[name].bundle.js', }; }
I console logged the default output and the filename was
static/js/bundle.js
(no reference to the name of the entry), so I think that's why adding the name makes it work.
It fixed for me
The above point led me to this as well. By having atleast one "main" as an entry point also we could resolve this but this approach might not be as expected for many
entry: {
main: ...,
entry1: ....,
entry2: ....
}
In my use case, I wanted to keep a simple single-page react application, but additionally compile and provide a few statically available script files. This thread helped me in figuring the details out, and here is my final craco.config.ts
, in case it helps anybody else (be sure to update CRA to version 5 first, so you have webpack 5 and craco 7):
const path = require('path');
type RewireEntry = {
name: string
entry: string
outPath: string
}
const rewireEntries: RewireEntry[] = [
{
name: 'my-static-scripts',
entry: path.resolve(__dirname, './src/static/my-static-scripts.ts'),
outPath: 'static/my-static-scripts.js',
},
];
const defaultEntryName = 'main';
/** adapted from: https://github.com/dilanx/craco/issues/298#issue-906476295 */
function webpackMultipleEntries(config) {
const defaultEntryHTMLPlugin = config.plugins.filter((plugin) => plugin.constructor.name === 'HtmlWebpackPlugin')[0];
defaultEntryHTMLPlugin.userOptions.chunks = [defaultEntryName];
if (!Array.isArray(config.entry)) {
config.entry = [config.entry];
}
const multipleEntry = {};
multipleEntry[defaultEntryName] = config.entry;
// see https://webpack.js.org/concepts/entry-points/#entrydescription-object
for (const entry of rewireEntries) {
multipleEntry[entry.name] = {
import: entry.entry,
filename: entry.outPath,
runtime: false,
};
}
config.entry = multipleEntry;
// Multiple Entry Output File
const names = config.output.filename.split('/').reverse();
if (names[0].indexOf('[name]') === -1) {
names[0] = `[name].${names[0]}`;
config.output.filename = names.reverse().join('/');
}
return config;
}
module.exports = {
webpack: {
configure: webpackMultipleEntries,
},
};
export {};
Really grateful for the work here! saved me ~6months of trauma lol.
I wrote my own version based of the comments here which I hope makes it even easier for anyone finding this thread in desperation:
// craco.config.ts
import { CracoConfig, WebpackContext } from '@craco/types';
import { Configuration as WebpackConfiguration } from 'webpack';
const config: CracoConfig = {
webpack: {
configure: (webpackConfig, { env, paths }) => {
return getMultiEntryWebpackConfig(
{
main: './src/index.tsx',
anotherEntry: './src/another-entry.tsx',
},
webpackConfig,
{ env, paths }
);
},
},
};
const getMultiEntryWebpackConfig = (
entries: { main: string; [name: string]: string },
config: WebpackConfiguration,
{ env }: WebpackContext
): WebpackConfiguration => {
const defaultHTMLPlugin = config.plugins!.find((plugin) => {
return plugin?.constructor.name === 'HtmlWebpackPlugin';
});
if(!defaultHTMLPlugin) {
throw new Error('HtmlWebpackPlugin not found!');
}
const plugins = config.plugins!.filter(plugin => plugin !== defaultHTMLPlugin);
plugins.push(...Object.entries(entries).map(([entryName, entry]) => {
const filename = `${entryName === 'main' ? 'index' : entryName}.html`
// @ts-ignore
return new defaultHTMLPlugin.constructor(
// @ts-ignore
Object.assign({}, defaultHTMLPlugin.userOptions, {
filename,
// @ts-ignore
template: defaultHTMLPlugin.userOptions.template,
chunks: [entryName],
})
)
}));
config.entry = entries;
config.plugins = plugins;
if (env === 'development') {
config.output = {
...config.output,
filename: 'static/js/[name].bundle.js',
};
}
return config;
};
export default config;
Note:
- resuse default template for every entry point
- dev server compatible thanks to @dilanx
- min config requires a
main
entrypoint ... namedmain