at backyard

Color my life with the chaos of trouble.

create-react-appで作成したアプリに別のエンドポイント(html)を生やす方法

はじめに

自分用の備忘録として書いているので説明が簡素なのと、細かなところまで確認していないので確認が漏れている箇所がある。
よって間違いが含まれている可能性がある。

また、この修正自体はちょっとした検証を行う際に必要だったという理由で行ったものであり、この設定を施したアプリをProduction環境にデプロイして確認したわけではないので、その点は注意する必要がある。

特に後半の rewrite の設定などはアプリの環境やデプロイするホスティングサービスによって設定を行う必要も出てくると思うので、参考程度にとどめてほしい。

目次

Create react appで作成したアプリに別のエンドポイントを生やす

Create react appで作成したアプリではエンドポイントが / のみとなる。

今回 /about.html というエンドポイントを生やし、about.html にアクセスした場合はそちらはそちらで別のSPAとしてアプリを機能させたいと考えた。

このポストはそれを実現するまでのメモとなる。

基本的にgit の diff を貼り付けてメモを残す。

ejectする

まず eject する。
ejectしないで済む方法はわからない。

yarn eject

一旦 eject した状態で問題なくアプリが動くかを確認し、一旦コミットする。

以降の diff はこのコミットした状態からの diff となる。

別のエンドポイントを追加するまでの修正内容

まずは config/paths.js を以下のように変更する。

--- a/config/paths.js
+++ b/config/paths.js
@@ -57,7 +57,9 @@ module.exports = {
   appBuild: resolveApp(buildPath),
   appPublic: resolveApp('public'),
   appHtml: resolveApp('public/index.html'),
+  appAboutHtml: resolveApp('public/about.html'),
   appIndexJs: resolveModule(resolveApp, 'src/index'),
+  appAboutJs: resolveModule(resolveApp, 'src/about'),
   appPackageJson: resolveApp('package.json'),
   appSrc: resolveApp('src'),
   appTsConfig: resolveApp('tsconfig.json'),

次に config/webpack.config.js を以下のように変更する。

--- a/config/webpack.config.js
+++ b/config/webpack.config.js
@@ -200,7 +200,10 @@ 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: paths.appIndexJs,
+    entry: {
+      index:[paths.appIndexJs],
+      about:[paths.appAboutJs],
+    },
     output: {
       // The build folder.
       path: paths.appBuild,
@@ -210,7 +213,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].bundle.js',
       // There are also additional JS chunk files if you use code splitting.
       chunkFilename: isEnvProduction
         ? 'static/js/[name].[contenthash:8].chunk.js'
@@ -569,7 +572,36 @@ module.exports = function (webpackEnv) {
           {},
           {
             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
+        )
+      ),
+      new HtmlWebpackPlugin(
+        Object.assign(
+          {},
+          {
+            inject: true,
+            chunks: ["about"],
+            template: paths.appAboutHtml,
+            filename: "about.html"
           },
           isEnvProduction
             ? {
@@ -642,9 +674,12 @@ module.exports = function (webpackEnv) {
             manifest[file.name] = file.path;
             return manifest;
           }, seed);
-          const entrypointFiles = entrypoints.main.filter(
-            fileName => !fileName.endsWith('.map')
-          );
+
+          let entrypointFiles = [];
+          for(let [entryFile, fileName] of Object.entries(entrypoints)) {
+            let notMapFiles = fileName.filter(fieldName => !fieldName.endsWith(".map"))
+            entrypointFiles = entrypointFiles.concat(notMapFiles);
+          }
 
           return {
             files: manifestFiles,

この時点で /about.html にアクセスして意図したページが表示されることを確認。

rewriteの設定

ただし、/about.html/foo みたいなURLをReact Routerなどでアクセスできるようにしている場合、/about.html/foo の状態でリロードすると意図した画面が表示されない。

ここでは config/webpackDevServer.config.js を編集し、下記のようなリダイレクト処理を挟むことにする。

ちなみに verbose: true にすると、 Rewriting 時のログをコンソール上で確認できる。

--- a/config/webpackDevServer.config.js
+++ b/config/webpackDevServer.config.js
@@ -98,6 +98,10 @@ module.exports = function (proxy, allowedHost) {
       // See https://github.com/facebook/create-react-app/issues/387.
       disableDotRule: true,
       index: paths.publicUrlOrPath,
+      verbose: true,
+      rewrites: [
+        { from : /^\/about/, to: "/about.html" }
+      ]
     },
     // `proxy` is run between `before` and `after` `webpack-dev-server` hooks
     proxy,

これで /about.html/foo の状態でリロードしても、意図したページの状態で描画されることが確認できた。