Evgeny  Pushkar
Evgeny Pushkar

Reputation: 31

multiple entry points by using create-react-app

I have a page with 20% of React components and 80% of normal html content. It is necessary to place several react components at different points of the page, and therefore I need several entry points in index.html. I want to use create-react-app, but I don’t want to use redux, since the page is rather small. Are there ways it’s relatively easy to make 2 entry points in index.html so that ALL react components on the page have access to the single parent's props? .. Or is it possible to use global variables with an event listener for their changes that would triger update the react components in different entry points? .. Please tell me the best practice for such tasks, because I don't want to develop a whole page by using jsx from a single entry point.

Upvotes: 3

Views: 3826

Answers (3)

David Lin
David Lin

Reputation: 13353

Adding multiple entry points require modifying the default react-scripts configuration. Ejecting (i.e. extracting all configuration from react-scripts and manage them yourself) is one way to do so.

Ejecting lets you customize anything, but from that point on you have to maintain the configuration and scripts yourself. This can be daunting if you have many similar projects. In such cases instead of ejecting we recommend to fork react-scripts and any other packages you need

Please visit https://create-react-app.dev/docs/alternatives-to-ejecting for details.

When I came across this problem, I created a fork of the script and made it available at https://www.npmjs.com/package/@linsight/react-scripts. Please try it out.

Do remember updating the react-app-env.d.ts file to:

/// <reference types="@linsight/react-scripts" />

Upvotes: 0

Sapan Diwakar
Sapan Diwakar

Reputation: 10966

I know it's a delayed answer, but just for future searches, the steps are:

  1. Eject (yarn eject)
  2. Edit paths.js and add the second entry point html file under the entry for appHtml
appAdminHtml: resolveApp('public/admin.html'),
  1. Update entry inside webpack.config.js to include one entry per entry point.
entry: {
  index: [
    isEnvDevelopment &&
    require.resolve('react-dev-utils/webpackHotDevClient'),
    paths.appIndexJs,
  ].filter(Boolean),
  admin: [
    isEnvDevelopment &&
    require.resolve('react-dev-utils/webpackHotDevClient'),
    paths.appSrc + '/admin/index.js',
  ].filter(Boolean)
},
  1. Change the generated output JS file to the name of the entry (inside webpack.config.js)
output: {
  path: isEnvProduction ? paths.appBuild : undefined,
  pathinfo: isEnvDevelopment,
  // This is the important entry
  filename: isEnvProduction
    ? 'static/js/[name].[contenthash:8].js'
    : isEnvDevelopment && 'static/js/[name].bundle.js',
  futureEmitAssets: true,
  chunkFilename: isEnvProduction
    ? 'static/js/[name].[contenthash:8].chunk.js'
    : isEnvDevelopment && 'static/js/[name].chunk.js',
  publicPath: publicPath,
  devtoolModuleFilenameTemplate: isEnvProduction
    ? info =>
        path
          .relative(paths.appSrc, info.absoluteResourcePath)
          .replace(/\\/g, '/')
    : isEnvDevelopment &&
      (info => path.resolve(info.absoluteResourcePath).replace(/\\/g, '/')),
  jsonpFunction: `webpackJsonp${appPackageJson.name}`,
  globalObject: 'this',
},
  1. Update the plugins to generate the second file with the injected js script (also inside webpack.config.js).
// Generates an `index.html` file with the <script> injected.
new HtmlWebpackPlugin(
  Object.assign(
    {},
    {
      inject: true,
      chunks: ['index'],
      template: paths.appHtml,
      filename: 'index.html'
    },
    isEnvProduction
      ? {
          minify: {
            removeComments: true,
            collapseWhitespace: true,
            removeRedundantAttributes: true,
            useShortDoctype: true,
            removeEmptyAttributes: true,
            removeStyleLinkTypeAttributes: true,
            keepClosingSlash: true,
            minifyJS: true,
            minifyCSS: true,
            minifyURLs: true,
          },
        }
      : undefined
  )
),
// Generates an `admin.html` file with the <script> injected.
new HtmlWebpackPlugin(
  Object.assign(
    {},
    {
      inject: true,
      chunks: ['admin'],
      template: paths.appAdminHtml,
      filename: 'admin.html',
    },
    isEnvProduction
      ? {
          minify: {
            removeComments: true,
            collapseWhitespace: true,
            removeRedundantAttributes: true,
            useShortDoctype: true,
            removeEmptyAttributes: true,
            removeStyleLinkTypeAttributes: true,
            keepClosingSlash: true,
            minifyJS: true,
            minifyCSS: true,
            minifyURLs: true,
          },
        }
      : undefined
  )
),
  1. Update the ManifestPlugin configuration to include the new entry point (also insidewebpack.config.js`):
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);
    let entrypointFiles = [];
    for (let [entryFile, fileName] of Object.entries(entrypoints)) {
      let notMapFiles = fileName.filter(fileName => !fileName.endsWith('.map'));
      entrypointFiles = entrypointFiles.concat(notMapFiles);
    };
    return {
      files: manifestFiles,
      entrypoints: entrypointFiles,
    };
  },
}),
  1. Update your server (both dev and prod) to rewrite paths.
    • For the dev server, you need to update webpackDevServer.config.js.
historyApiFallback: {
  disableDotRule: true,
  verbose: true,
  rewrites: [
    { from: /^\/admin/, to: '/admin.html' },
  ]
},

Since Prod server settings can be quite different, I'll let you figure it out.

This post describes everything in more detail.

Upvotes: 1

K41F4r
K41F4r

Reputation: 1551

To avoid ejecting you might want to check rescripts, you can add entry points to be added to index.html like so:

create .rescriptsrc.js file in projects main directory:

module.exports = [
  config => {
      config.entry = {
         app: ["./src/index.js"],
         content: ["./src/content.js"],
      };
  }
];

Upvotes: 1

Related Questions