Closed101

EJSをやめてReactでHTMLを書く

HishoHisho
$ npm init -y

npmの雛形を作る

{
  "name": "react-stati-generator",
  "version": "1.0.0",
  "license": "MIT",
  "private": true,
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  }
}

いらない記述を削除して"private": true,を追記する

HishoHisho

webpackは4.x系を使うので4.4を入れるcliは最新でも問題無さそう

$ npm i -D webpack@4.44.2 webpack-cli

webpackのconfigファイルを作成する

$ touch webpack.config.js

忘れないうちにコマンドを登録しておく

{
  "name": "react-stati-generator",
  "version": "1.0.0",
  "license": "MIT",
  "private": true,
  "scripts": {
-    "test": "echo \"Error: no test specified\" && exit 1",
+    "webpack-watch": "webpack -w",
+    "webpack-build": "webpack"
  },
  "devDependencies": {
    "webpack": "^4.44.2",
    "webpack-cli": "^4.2.0"
  }
}
HishoHisho

普段はGUIから作るけど今回はコマンドでファイルを作ってみる

$ mkdir src && mkdir src/pages && touch src/pages/index.jsx

一応普通のjsも作っておく

$ mkdir src/js && touch src/js/main.js

適当にmain.jsに書いとく

main.js
console.log('test');
HishoHisho

とりあえずいつも使ってる設定ファイルからいらないところを消して、webpack.config.jsの中身を書く

webpack.config.js
const webpack = require('webpack');
const path = require('path');

module.exports = () => {
  const MODE = process.env.NODE_ENV;
  const IS_DEVELOPMENT = process.env.NODE_ENV === 'development';
  const IS_PRODUCTION = process.env.NODE_ENV === 'production';

  return {
    mode: MODE,
    devtool: IS_DEVELOPMENT ? 'inline-source-map' : false,
    entry: {
      "dist/assets/js/main": "./src/js/main",
    },
    resolve: {
      extensions: ['.ts', '.tsx', '.jsx', '.js'],
    },
    output: {
      filename: '[name].js',
      path: path.join(__dirname),
    },
    module: {},
    plugins: [
      new webpack.ProgressPlugin(),
    ],
  }
};

HishoHisho

赤いびよびよが出てくるので@typesを入れる

$ npx typesync
HishoHisho

@types/nodeもいるな?🤔

$ npm i -D @types/node
HishoHisho

process.env.NODE_ENVを使うのでnpm scriptsを増やす

npm run allを入れる

$ npm i -D npm-run-all

とりあえずstartとbuildを作る

pakage.json
{
  "name": "react-stati-generator",
  "version": "1.0.0",
  "license": "MIT",
  "private": true,
  "scripts": {
+    "start": "NODE_ENV=development run-p _start",
+    "build": "NODE_ENV=production run-p _build",
    "webpack-watch": "webpack -w",
    "webpack-build": "webpack",
+    "_start": "run-s webpack-watch",
+    "_build": "run-s webpack-build"
  },
  "devDependencies": {
    "@types/node": "^14.14.10",
    "@types/webpack": "^4.41.25",
    "npm-run-all": "^4.1.5",
    "webpack": "^4.44.2",
    "webpack-cli": "^4.2.0"
  }
}

HishoHisho

react-static-generator

Command

Install

$ npm ci

Watch

$ npm start

Build

$ npm run build
HishoHisho

コマンドが動くか確認する

npm start
[webpack-cli] Compilation starting...
98% after emitting[webpack-cli] Compilation finished
Hash: af324363234bc97f2776
Version: webpack 4.44.2
Time: 60ms
Built at: 2020/12/06 16:30:02
                 Asset      Size               Chunks             Chunk Names
dist/assets/js/main.js  8.62 KiB  dist/assets/js/main  [emitted]  dist/assets/js/main
Entrypoint dist/assets/js/main = dist/assets/js/main.js
[./src/js/main.js] 20 bytes {dist/assets/js/main} [built]
[webpack-cli] watching files for updates...
npm run build
$ NODE_ENV=production run-p _build
$ run-s webpack-build
$ webpack
98% after emitting SizeLimitsPlugin[webpack-cli] Compilation finished
Hash: b1ec4be4a855a7482c28
Version: webpack 4.44.2
Time: 188ms
Built at: 2020/12/06 16:30:12
                 Asset       Size  Chunks             Chunk Names
dist/assets/js/main.js  949 bytes       0  [emitted]  dist/assets/js/main
Entrypoint dist/assets/js/main = dist/assets/js/main.js
[0] ./src/js/main.js 20 bytes {0} [built]
✨  Done in 2.70s.
HishoHisho

コミットしておく

gitignore作るの忘れたから危うくnode_modules上げるところだった😂

$ touch .gitignore
.gitignore
node_modules
HishoHisho

一応distもignoreしておこう🤔

.gitignore
node_modules
+ dist
HishoHisho

tsxでやりたいから全部リネームするか
まずはts-loaderの設定をちゃちゃっと

HishoHisho
$ npm i -D ts-loader fork-ts-checker-webpack-plugin

めんどくさいので設定は普段使ってるやつを持ってくる

$ touch tsconfig.json
HishoHisho
tsconfig.json
{
  "compilerOptions": {
    "target": "es5",
    "module": "esnext",
    "jsx": "react",
    "declaration": true,
    "declarationMap": true,
    "sourceMap": true,
    "incremental": true,
    "tsBuildInfoFile": "./.cache/ts/.tsbuildinfo",
    "importHelpers": true,
    "downlevelIteration": true,
    "lib": ["dom", "es2015", "dom.iterable"],
    "typeRoots": ["src/@types", "node_modules/@types"],
    "baseUrl": ".",
    "strict": true,
    "moduleResolution": "node",
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "forceConsistentCasingInFileNames": true,
    "allowSyntheticDefaultImports": true,
    "experimentalDecorators": true
  }
}

HishoHisho
webpack.config.js
const webpack = require('webpack');
const path = require('path');
+const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');

module.exports = () => {
  const MODE = process.env.NODE_ENV;
  const IS_DEVELOPMENT = process.env.NODE_ENV === 'development';
  const IS_PRODUCTION = process.env.NODE_ENV === 'production';

  return {
    mode: MODE,
    devtool: IS_DEVELOPMENT ? 'inline-source-map' : false,
    entry: {
      "dist/assets/js/main": "./src/js/main",
    },
    resolve: {
      extensions: ['.ts', '.tsx', '.jsx', '.js'],
    },
    output: {
      filename: '[name].js',
      path: path.join(__dirname),
    },
    module: {
+      rules: [
+        {
+          test: /\.(tsx?|jsx?)$/,
+          use: [
+            {
+              loader: 'ts-loader',
+              options: {
+                transpileOnly: true,
+                experimentalWatchApi: true,
+              }
+            }
+          ],
+        }
+      ]
    },
    plugins: [
+      new ForkTsCheckerWebpackPlugin(),
      new webpack.ProgressPlugin(),
    ],
  }
};

HishoHisho

ここも普段はGUIだけどCUIで拡張子を変えてみる

$ mv src/js/main.js src/js/main.ts && mv src/pages/index.jsx src/pages/index.tsx
HishoHisho

ちゃんとtypescriptかどうか適当にテストしてみる

main.ts
function test(args: number): number {
  return args;
}

console.log(test(3));
console.log(test('3'));
$ NODE_ENV=production run-p _build
$ run-s webpack-build
$ webpack
94% after seal[webpack-cli] Compilation finished
Hash: c4ee805885803dff91a5
Version: webpack 4.44.2
Time: 1038ms
Built at: 2020/12/06 16:56:39
                 Asset       Size  Chunks  Chunk Names
dist/assets/js/main.js  976 bytes       0  dist/assets/js/main
Entrypoint dist/assets/js/main = dist/assets/js/main.js
[0] ./src/js/main.ts 101 bytes {0} [built]

ERROR in src/js/main.ts:6:18
TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.
    4 |
    5 | console.log(test(3));
  > 6 | console.log(test('3'));
      |                  ^^^
HishoHisho

うまくいったのでmain.tsの内容をエラーが起きないようにする

main.ts
console.log('ああああああああ');
HishoHisho

なんかこのスクラップハンズオンでなにかするのに良さそう(?)

HishoHisho

.cache/ts/.tsbuildinfoはgitで共有する必要が無いのでignoreする

.gitignore
node_modules
dist
+.cache
HishoHisho

html-webpack-pluginとReactを入れる

$ npm i -D html-webpack-plugin

Reactは普通に使うかもしれないので、dependenciesに入れる

$ npm i react react-dom

@typesはめんどくさいのでnpx typesyncで一気に入れる

$ npx typesync
HishoHisho

webpacckとindex.tsxを書き換える

webpack.config.js
const webpack = require('webpack');
const path = require('path');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
+const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = () => {
  const MODE = process.env.NODE_ENV;
  const IS_DEVELOPMENT = process.env.NODE_ENV === 'development';
  const IS_PRODUCTION = process.env.NODE_ENV === 'production';

  return {
    mode: MODE,
    devtool: IS_DEVELOPMENT ? 'inline-source-map' : false,
    entry: {
      "dist/assets/js/main": "./src/js/main",
    },
    resolve: {
      extensions: ['.ts', '.tsx', '.jsx', '.js'],
    },
    output: {
      filename: '[name].js',
      path: path.join(__dirname),
    },
    module: {
      rules: [
        {
          test: /\.(tsx?|jsx?)$/,
          use: [
            {
              loader: 'ts-loader',
              options: {
                transpileOnly: true,
                experimentalWatchApi: true,
              }
            }
          ],
        }
      ]
    },
    plugins: [
+      new HtmlWebpackPlugin({
+        title: "My App",
+        template: "src/pages/index.tsx",
+      }),
      new ForkTsCheckerWebpackPlugin(),
      new webpack.ProgressPlugin(),
    ],
  }
};

index.tsx
import React from "react";
import { renderToStaticMarkup } from "react-dom/server";

const App = () => <div>Use React !!</div>;

export default ({ htmlWebpackPlugin }) => `
  <!DOCTYPE html>
  <html lang="ja">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>${htmlWebpackPlugin.options.title}</title>
  </head>
  <body>
    ${renderToStaticMarkup(<App />)}
  </body>
  </html>
`;
HishoHisho
$ NODE_ENV=production run-p _build
$ run-s webpack-build
$ webpack
94% after seal[webpack-cli] Compilation finished
Hash: 85dd6deab4f9bfb87e91
Version: webpack 4.44.2
Time: 1330ms
Built at: 2020/12/06 17:31:55
                 Asset       Size  Chunks  Chunk Names
dist/assets/js/main.js  984 bytes       0  dist/assets/js/main
Entrypoint dist/assets/js/main = dist/assets/js/main.js
[0] ./src/js/main.ts 55 bytes {0} [built]

ERROR in src/pages/index.tsx:1:19
TS7016: Could not find a declaration file for module 'react'. '/Users/name/github/react-static-generator/node_modules/react/index.js' implicitly has an 'any' type.
  Try `npm install @types/react` if it exists or add a new declaration (.d.ts) file containing `declare module 'react';`
  > 1 | import React from "react";
      |                   ^^^^^^^
    2 | import { renderToStaticMarkup } from "react-dom/server";
    3 |
    4 | const App = () => <div>Use React !!</div>;

ERROR in src/pages/index.tsx:2:38
TS7016: Could not find a declaration file for module 'react-dom/server'. '/Users/name/github/react-static-generator/node_modules/react-dom/server.js' implicitly has an 'any' type.
  Try `npm install @types/react-dom` if it exists or add a new declaration (.d.ts) file containing `declare module 'react-dom/server';`
    1 | import React from "react";
  > 2 | import { renderToStaticMarkup } from "react-dom/server";
      |                                      ^^^^^^^^^^^^^^^^^^
    3 |
    4 | const App = () => <div>Use React !!</div>;
    5 |

ERROR in src/pages/index.tsx:4:19
TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
    2 | import { renderToStaticMarkup } from "react-dom/server";
    3 |
  > 4 | const App = () => <div>Use React !!</div>;
      |                   ^^^^^
    5 |
    6 | export default ({ htmlWebpackPlugin }) => `
    7 |   <!DOCTYPE html>

ERROR in src/pages/index.tsx:4:36
TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
    2 | import { renderToStaticMarkup } from "react-dom/server";
    3 |
  > 4 | const App = () => <div>Use React !!</div>;
      |                                    ^^^^^^
    5 |
    6 | export default ({ htmlWebpackPlugin }) => `
    7 |   <!DOCTYPE html>

ERROR in src/pages/index.tsx:6:19
TS7031: Binding element 'htmlWebpackPlugin' implicitly has an 'any' type.
    4 | const App = () => <div>Use React !!</div>;
    5 |
  > 6 | export default ({ htmlWebpackPlugin }) => `
      |                   ^^^^^^^^^^^^^^^^^
    7 |   <!DOCTYPE html>
    8 |   <html lang="ja">
    9 |   <head>
Child HtmlWebpackCompiler:
                          Asset      Size  Chunks  Chunk Names
    __child-HtmlWebpackPlugin_0  33.5 KiB       0  HtmlWebpackPlugin_0
    Entrypoint HtmlWebpackPlugin_0 = __child-HtmlWebpackPlugin_0
    [0] ./node_modules/react/index.js 190 bytes {0} [built]
    [1] ./node_modules/object-assign/index.js 2.06 KiB {0} [built]
    [2] ./node_modules/react-dom/server.browser.js 228 bytes {0} [built]
    [3] ./node_modules/html-webpack-plugin/lib/loader.js!./src/pages/index.tsx 601 bytes {0} [built]
    [4] ./node_modules/react/cjs/react.production.min.js 6.3 KiB {0} [built]
    [5] ./node_modules/react-dom/cjs/react-dom-server.browser.production.min.js 19.8 KiB {0} [built]

めちゃくちゃ怒られた

HishoHisho
Try `npm install @types/react` if it exists or add a new declaration (.d.ts) file containing `declare module 'react';

typesyncしたのに@types/reactが無いらしいのでもう一度npm installしてみる

HishoHisho

とりあえずエラーを消していく
FC付ける必要あるのかわからないけどとりあえず

index.tsx
+import React, {FC} from "react";
import {renderToStaticMarkup} from "react-dom/server";

+const App: FC = () => <div>Use React !!</div>;

export default ({htmlWebpackPlugin}) => `
  <!DOCTYPE html>
  <html lang="ja">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>${htmlWebpackPlugin.options.title}</title>
  </head>
  <body>
    ${renderToStaticMarkup(<App/>)}
  </body>
  </html>
`;
HishoHisho

export default ({htmlWebpackPlugin}) => の型がわからないけど、html-webpack-pluginの型定義を見に行くと

html-webpack-plugin
  /**
   * The plugin options after adding default values
   */
  interface ProcessedOptions {
    ...
  }

この部分っぽい

HishoHisho
index.tsx
import React, {FC} from "react";
import {renderToStaticMarkup} from "react-dom/server";
+import htmlWebpackPlugin from 'html-webpack-plugin';

const App: FC = () => <div>Use React !!</div>;

+export default ({htmlWebpackPlugin}: { htmlWebpackPlugin: htmlWebpackPlugin.ProcessedOptions }) => `
  <!DOCTYPE html>
  <html lang="ja">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>${htmlWebpackPlugin.options.title}</title>
  </head>
  <body>
    ${renderToStaticMarkup(<App/>)}
  </body>
  </html>
`;

とりあえず、エラーは全部消えた

$ NODE_ENV=production run-p _build
$ run-s webpack-build
$ webpack
98% after emitting SizeLimitsPlugin[webpack-cli] Compilation finished
Hash: 87c7784d007b2445c334
Version: webpack 4.44.2
Time: 1765ms
Built at: 2020/12/06 17:41:55
                 Asset       Size  Chunks             Chunk Names
dist/assets/js/main.js  984 bytes       0  [emitted]  dist/assets/js/main
            index.html  243 bytes          [emitted]
Entrypoint dist/assets/js/main = dist/assets/js/main.js
[0] ./src/js/main.ts 55 bytes {0} [built]
Child HtmlWebpackCompiler:
                          Asset      Size  Chunks  Chunk Names
    __child-HtmlWebpackPlugin_0  46.1 KiB       0  HtmlWebpackPlugin_0
    Entrypoint HtmlWebpackPlugin_0 = __child-HtmlWebpackPlugin_0
    [0] ./node_modules/react/index.js 193 bytes {0} [built]
    [1] ./node_modules/object-assign/index.js 2.43 KiB {0} [built]
    [2] ./node_modules/react-dom/server.browser.js 231 bytes {0} [built]
    [3] ./node_modules/html-webpack-plugin/lib/loader.js!./src/pages/index.tsx 601 bytes {0} [built]
    [4] ./node_modules/react/cjs/react.production.min.js 8.12 KiB {0} [built]
    [5] ./node_modules/react-dom/cjs/react-dom-server.browser.production.min.js 30.2 KiB {0} [built]
✨  Done in 4.78s.
HishoHisho

index.htmlをdistに吐き出したいので設定を変える

HishoHisho

タイトルいらないので消す
scriptの自動挿入もいらないので消す

webpack.config.js
const webpack = require('webpack');
const path = require('path');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = () => {
  const MODE = process.env.NODE_ENV;
  const IS_DEVELOPMENT = process.env.NODE_ENV === 'development';
  const IS_PRODUCTION = process.env.NODE_ENV === 'production';

  return {
    mode: MODE,
    devtool: IS_DEVELOPMENT ? 'inline-source-map' : false,
    entry: {
      "dist/assets/js/main": "./src/js/main",
    },
    resolve: {
      extensions: ['.ts', '.tsx', '.jsx', '.js'],
    },
    output: {
      filename: '[name].js',
      path: path.join(__dirname),
    },
    module: {
      rules: [
        {
          test: /\.(tsx?|jsx?)$/,
          use: [
            {
              loader: 'ts-loader',
              options: {
                transpileOnly: true,
                experimentalWatchApi: true,
              }
            }
          ],
        }
      ]
    },
    plugins: [
      new HtmlWebpackPlugin({
-        title: "My App",
        template: "src/pages/index.tsx",
+        filename: 'dist/index.html',
+        inject: false,
      }),
      new ForkTsCheckerWebpackPlugin(),
      new webpack.ProgressPlugin(),
    ],
  }
};

HishoHisho
$ NODE_ENV=production run-p _build
$ run-s webpack-build
$ webpack
98% after emitting SizeLimitsPlugin[webpack-cli] Compilation finished
Hash: 87c7784d007b2445c334
Version: webpack 4.44.2
Time: 1357ms
Built at: 2020/12/06 17:52:24
                 Asset       Size  Chunks             Chunk Names
dist/assets/js/main.js  984 bytes       0  [emitted]  dist/assets/js/main
       dist/index.html  202 bytes          [emitted]
Entrypoint dist/assets/js/main = dist/assets/js/main.js
[0] ./src/js/main.ts 55 bytes {0} [built]
Child HtmlWebpackCompiler:
                          Asset      Size  Chunks  Chunk Names
    __child-HtmlWebpackPlugin_0  46.1 KiB       0  HtmlWebpackPlugin_0
    Entrypoint HtmlWebpackPlugin_0 = __child-HtmlWebpackPlugin_0
    [0] ./node_modules/react/index.js 193 bytes {0} [built]
    [1] ./node_modules/object-assign/index.js 2.43 KiB {0} [built]
    [2] ./node_modules/react-dom/server.browser.js 231 bytes {0} [built]
    [3] ./node_modules/html-webpack-plugin/lib/loader.js!./src/pages/index.tsx 601 bytes {0} [built]
    [4] ./node_modules/react/cjs/react.production.min.js 8.12 KiB {0} [built]
    [5] ./node_modules/react-dom/cjs/react-dom-server.browser.production.min.js 30.2 KiB {0} [built]
✨  Done in 3.40s.

distに出てきた!

HishoHisho

pages/の中でheadを書きたくないのでLayoutsを作る

HishoHisho

先にさっきの吐き出し先を変えた変更をpushしとこう

HishoHisho

とりあえずレイアウトに切り出し

src/layouts/index.tsx
import React, {FC, ReactNode} from 'react'

type LayoutType = {
  children: ReactNode
}

export const Layout: FC<LayoutType> = ({children}) => {
  return (
    <>
      <html lang="ja">
      <head>
        <meta charSet="UTF-8"/>
        <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
        <title>タイトル</title>
      </head>
      <body>
      {children}
      </body>
      </html>
    </>
  )
}

残ったもの

src/pages/index.tsx
import React from 'react';
import {renderToStaticMarkup} from 'react-dom/server';
import {Layout} from "../layouts";

export default () => {
  return '<!DOCTYPE html>' + renderToStaticMarkup(
    <Layout>
      テスト
    </Layout>
  )
};
HishoHisho

Reactで<!DOCTYPE html>できない?よくわからないから

return '<!DOCTYPE html>' + renderToStaticMarkup(
    <Layout>
      テスト
    </Layout>
  )

こうしたけど、ダサいな🤔

HishoHisho

renderToStaticMarkup()をwrapした関数を作る事で(いいかどうかは置いといて)とりあえず、解決だ😎

HishoHisho

名前も思いつかないしhooksフォルダに入れても良いかわからないけどとりあえず作る

$ mkdir src/hooks && touch src/hooks/index.ts && touch src/hooks/staticRender.ts
HishoHisho

renderToStaticMarkupなのでmyRenderToStaticMarkupにする

HishoHisho
$ mv src/hooks/staticRender.ts src/hooks/myRenderToStaticMarkup.ts
HishoHisho

renderToStaticMarkupのwrapper

myRenderToStaticMarkup
import {renderToStaticMarkup} from 'react-dom/server';
import {ReactElement} from "react";

export function myRenderToStaticMarkup(element: ReactElement): string {
  return '<!DOCTYPE html>' + renderToStaticMarkup(element);
}

hooksのエントリーポイント

src/hooks/index.ts
export {myRenderToStaticMarkup} from './myRenderToStaticMarkup';

pagesのindex.tsx

src/pages/index.tsx
import React from 'react';
-import {renderToStaticMarkup} from 'react-dom/server';
+import {myRenderToStaticMarkup} from "../hooks";
import {Layout} from "../layouts";

export default () => {
+  return myRenderToStaticMarkup(
    <Layout>
      テスト
    </Layout>
+  )
};
HishoHisho

myRenderToStaticMarkupの型はここから取ってきた

node_modules/@types/react-dom/server/index.d.ts
/**
 * Similar to `renderToString`, except this doesn't create extra DOM attributes
 * such as `data-reactid`, that React uses internally. This is useful if you want
 * to use React as a simple static page generator, as stripping away the extra
 * attributes can save lots of bytes.
 */
export function renderToStaticMarkup(element: ReactElement): string;
HishoHisho

他のページ今回はabout pageも出てくるようにする

HishoHisho

名前が思いつかないので一旦作る

$ mkdir .config && mkdir .config/webpack && touch .config/webpack/glob.js
HishoHisho

目標

webpack.config.js
    plugins: [
-      new HtmlWebpackPlugin({
-        template: "src/pages/index.tsx",
-        filename: 'dist/index.html',
-        inject: false,
-      }),
+      pages.forEach((page) => {
+        new HtmlWebpackPlugin(page)
+      }),
      new ForkTsCheckerWebpackPlugin(),
      new webpack.ProgressPlugin(),
    ],
HishoHisho

とりあえず名前はpagesでいいので変更

$ mv .config/webpack/glob.js .config/webpack/pages.js

中身
globを使ってsrc/pages/**/.tsxをすべて取得しmapで加工して返す

.config/webpack/pages.js
const glob = require('glob');

const pages = glob.sync(`**/*.tsx`, {
  ignore: `**/_*.tsx`,
  cwd: `./src/pages`
}).map((n) => {
  const ext = n.replace(/tsx$/,'html')
  return {
    template: "src/pages/" + n,
    filename: 'dist/' + ext,
    inject: false,
  }
});

module.exports = pages;

変数名とか雑だけど、とりあえずこれでいいっしょ

webpack.config.js
const webpack = require('webpack');
const path = require('path');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
const HtmlWebpackPlugin = require("html-webpack-plugin");
+const Pages = require(path.resolve('.config/webpack/pages.js'));

module.exports = () => {
  console.log(Pages);
  const MODE = process.env.NODE_ENV;
  const IS_DEVELOPMENT = process.env.NODE_ENV === 'development';
  const IS_PRODUCTION = process.env.NODE_ENV === 'production';

  return {
    mode: MODE,
    devtool: IS_DEVELOPMENT ? 'inline-source-map' : false,
    entry: {
      "dist/assets/js/main": "./src/js/main",
    },
    resolve: {
      extensions: ['.ts', '.tsx', '.jsx', '.js'],
    },
    output: {
      filename: '[name].js',
      path: path.join(__dirname),
    },
    module: {
      rules: [
        {
          test: /\.(tsx?|jsx?)$/,
          use: [
            {
              loader: 'ts-loader',
              options: {
                transpileOnly: true,
                experimentalWatchApi: true,
              }
            }
          ],
        }
      ]
    },
    plugins: [
-        new HtmlWebpackPlugin({
-        template: "src/pages/index.tsx",
-        filename: 'dist/index.html',
-        inject: false,
-      }),
+      Pages.forEach((page) => {
+        new HtmlWebpackPlugin(page);
+      }),
      new ForkTsCheckerWebpackPlugin(),
      new webpack.ProgressPlugin(),
    ],
  }
};

HishoHisho

😇😇😇😇😇😇

$ NODE_ENV=production run-p _build
$ run-s webpack-build
$ webpack
[
  {
    template: 'src/pages/about/index.tsx',
    filename: 'dist/about/index.html',
    inject: false
  },
  {
    template: 'src/pages/index.tsx',
    filename: 'dist/index.html',
    inject: false
  }
]
[webpack-cli] Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
 - configuration.plugins[1] should be one of these:
   object { apply, … } | function
   -> Plugin of type object or instanceof Function
   Details:
    * configuration.plugins[1] should be an object.
      -> Plugin instance
    * configuration.plugins[1] should be an instance of function
      -> Function acting as plugin
HishoHisho

forEachじゃだめみたいなので、mapでnew HtmlWebpackPluginを返してpluginsにマージさせればいいので

webpack.config.js
const webpack = require('webpack');
const path = require('path');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
const HtmlWebpackPlugin = require("html-webpack-plugin");
const Pages = require(path.resolve('.config/webpack/pages.js'));

module.exports = () => {
-  const test = Pages.map((page) => new HtmlWebpackPlugin(page));←debugした時の残骸
-  console.log(test);←debugした時の残骸
  const MODE = process.env.NODE_ENV;
  const IS_DEVELOPMENT = process.env.NODE_ENV === 'development';
  const IS_PRODUCTION = process.env.NODE_ENV === 'production';

  return {
    mode: MODE,
    devtool: IS_DEVELOPMENT ? 'inline-source-map' : false,
    entry: {
      "dist/assets/js/main": "./src/js/main",
    },
    resolve: {
      extensions: ['.ts', '.tsx', '.jsx', '.js'],
    },
    output: {
      filename: '[name].js',
      path: path.join(__dirname),
    },
    module: {
      rules: [
        {
          test: /\.(tsx?|jsx?)$/,
          use: [
            {
              loader: 'ts-loader',
              options: {
                transpileOnly: true,
                experimentalWatchApi: true,
              }
            }
          ],
        }
      ]
    },
    plugins: [
-      Pages.forEach((page) => {
-        new HtmlWebpackPlugin(page);
-      }),
+      ...Pages.map((page) => new HtmlWebpackPlugin(page)),
+      ...[
        new ForkTsCheckerWebpackPlugin(),
        new webpack.ProgressPlugin(),
+      ]
    ],
  }
};

HishoHisho

無事about pageも出るようになりました😤

$ NODE_ENV=production run-p _build
$ run-s webpack-build
$ webpack
98% after emitting SizeLimitsPlugin[webpack-cli] Compilation finished
Hash: f18a6300809936ad70c8
Version: webpack 4.44.2
Time: 1403ms
Built at: 2020/12/06 19:20:02
                 Asset       Size  Chunks             Chunk Names
 dist/about/index.html  179 bytes          [emitted]
dist/assets/js/main.js  984 bytes       0  [emitted]  dist/assets/js/main
       dist/index.html  177 bytes          [emitted]
Entrypoint dist/assets/js/main = dist/assets/js/main.js
[0] ./src/js/main.ts 55 bytes {0} [built]
Child HtmlWebpackCompiler:
                          Asset      Size  Chunks  Chunk Names
    __child-HtmlWebpackPlugin_0  47.5 KiB       0  HtmlWebpackPlugin_0
    __child-HtmlWebpackPlugin_1  47.5 KiB       1  HtmlWebpackPlugin_1
    Entrypoint HtmlWebpackPlugin_0 = __child-HtmlWebpackPlugin_0
    Entrypoint HtmlWebpackPlugin_1 = __child-HtmlWebpackPlugin_1
    [0] ./node_modules/react/index.js 193 bytes {0} {1} [built]
    [1] ./node_modules/object-assign/index.js 2.43 KiB {0} {1} [built]
    [2] ./node_modules/react-dom/server.browser.js 231 bytes {0} {1} [built]
    [3] ./src/layouts/index.tsx 575 bytes {0} {1} [built]
    [4] ./src/hooks/index.ts + 1 modules 243 bytes {0} {1} [built]
        | ./src/hooks/index.ts 67 bytes [built]
        | ./src/hooks/myRenderToStaticMarkup.ts 171 bytes [built]
    [5] ./node_modules/react/cjs/react.production.min.js 8.12 KiB {0} {1} [built]
    [6] ./node_modules/react-dom/cjs/react-dom-server.browser.production.min.js 30.2 KiB {0} {1} [built]
    [7] ./node_modules/html-webpack-plugin/lib/loader.js!./src/pages/about/index.tsx 234 bytes {0} [built]
    [8] ./node_modules/html-webpack-plugin/lib/loader.js!./src/pages/index.tsx 241 bytes {1} [built]
✨  Done in 3.16s.
HishoHisho

というかglobってビルドインだったっけ?間違えてグローバルに入れちゃっててそれ参照してるのかも?

HishoHisho

ちょっと分割代入に変えたほうがオプション渡しやすいから変えよう

HishoHisho
.config/webpack/pages.js
const glob = require('glob');

const pages = glob.sync(`**/*.tsx`, {
  ignore: `**/_*.tsx`,
  cwd: `./src/pages`
}).map((n) => {
  const ext = n.replace(/tsx$/,'html')
  return {
    template: "src/pages/" + n,
    filename: 'dist/' + ext,
-    inject: false,
  }
});

module.exports = pages;

webpack.config.js
const webpack = require('webpack');
const path = require('path');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
const HtmlWebpackPlugin = require("html-webpack-plugin");
const Pages = require(path.resolve('.config/webpack/pages.js'));

module.exports = () => {
  const MODE = process.env.NODE_ENV;
  const IS_DEVELOPMENT = process.env.NODE_ENV === 'development';
  const IS_PRODUCTION = process.env.NODE_ENV === 'production';

  return {
    mode: MODE,
    devtool: IS_DEVELOPMENT ? 'inline-source-map' : false,
    entry: {
      "dist/assets/js/main": "./src/js/main",
    },
    resolve: {
      extensions: ['.ts', '.tsx', '.jsx', '.js'],
    },
    output: {
      filename: '[name].js',
      path: path.join(__dirname),
    },
    module: {
      rules: [
        {
          test: /\.(tsx?|jsx?)$/,
          use: [
            {
              loader: 'ts-loader',
              options: {
                transpileOnly: true,
                experimentalWatchApi: true,
              }
            }
          ],
        }
      ]
    },
    plugins: [
+      ...Pages.map(({template, filename}) => new HtmlWebpackPlugin({
+        template,
+        filename,
+        inject: false,
+      })),
      ...[
        new ForkTsCheckerWebpackPlugin(),
        new webpack.ProgressPlugin(),
      ]
    ],
  }
};

HishoHisho

そういえばせっかく作ったmain.jsを読み込んでなかった

HishoHisho
$ npm i -D browser-sync
$ touch .config/bs-config.js
.config/bs-config.js
const BROWSER_SYNC = {
  files: [
    `dist/assets/**/*`,
    `dist/**/*.html`,
  ],
  ghostMode: {
    clicks: false,
    scroll: false,
    forms: false
  },
  server: {
    baseDir: 'dist',
    middleware: [],
  },
  logFileChanges: false
};

module.exports = BROWSER_SYNC;
pakage.json
{
  "name": "react-stati-generator",
  "version": "1.0.0",
  "license": "MIT",
  "private": true,
  "scripts": {
+    "start": "NODE_ENV=development run-p _start server",
    "build": "NODE_ENV=production run-p _build",
+    "server": "browser-sync start --config ./.config/bs-config.js",
    "webpack-watch": "webpack -w",
    "webpack-build": "webpack",
    "_start": "run-s webpack-watch",
    "_build": "run-s webpack-build"
  }
}

HishoHisho

typescriptが無いっぽいので入れる

$ npm i typescript -D
HishoHisho

pathの情報をpage全体で使用したのでuseContextを使う

HishoHisho
$ touch src/config.ts
src/config.ts
import React from "react";

type currentPageValueType = {
  path: '/' | './' | '../' | '../../',
}

const currentPageValue: currentPageValueType = {
  path: '/',
}

export const CurrentPage = React.createContext(currentPageValue);
HishoHisho

とりあえずpagesに反映させるためにProviderで囲う

src/pages/index.tsx
 import React from 'react';
 import {myRenderToStaticMarkup} from "../hooks";
 import {Layout} from "../layouts";
+import {CurrentPage} from "../config";
 
 export default () => {
   return myRenderToStaticMarkup(
-    <Layout>
-      テスト
-    </Layout>
+    <CurrentPage.Provider value={{path: './'}}>
+      <Layout>
+        テスト
+      </Layout>
+    </CurrentPage.Provider>
   )
 };

src/pages/about/index.tsx
 import React from 'react';
 import {myRenderToStaticMarkup} from "../../hooks";
 import {Layout} from "../../layouts";
+import {CurrentPage} from "../../config";
 
 export default () => {
   return myRenderToStaticMarkup(
-    <Layout>
-      About
-    </Layout>
+    <CurrentPage.Provider value={{path: '../'} as const}>
+      <Layout>
+        about
+      </Layout>
+    </CurrentPage.Provider>
   )
 };

HishoHisho

ちゃんとできているか確認するためにダミーのコンポーネントを作成する

$ mkdir src/components && touch src/components/index.ts && touch src/components/App.tsx
src/components/App.tsx
import React, {useContext} from "react";
import {CurrentPage} from "../config";

export const App = () => {
  const {path} = useContext(CurrentPage);
  return (
    <div>
      {path}
    </div>
  )
}
src/components/index.ts
export {App} from './App';
src/pages/index.tsx
import {myRenderToStaticMarkup} from "../hooks";
import {Layout} from "../layouts";
import {CurrentPage} from "../config";
+import {App} from "../components";

export default () => {
  return myRenderToStaticMarkup(
    <CurrentPage.Provider value={{path: './'}}>
      <Layout>
        テスト
+        <App/>
      </Layout>
    </CurrentPage.Provider>
  )

src/pages/about/index.tsx
import {myRenderToStaticMarkup} from "../../hooks";
import {Layout} from "../../layouts";
import {CurrentPage} from "../../config";
+import {App} from "../../components";

export default () => {
  return myRenderToStaticMarkup(
    <CurrentPage.Provider value={{path: '../'} as const}>
      <Layout>
        about
+        <App/>
      </Layout>
    </CurrentPage.Provider>
  )
HishoHisho

pagesを起点に現在のページの相対pathを割り出す

HishoHisho

フォルダから相対パスを出すのは履修済みなので実装する

pathをrequireしてpath.relative()してやればいい

.config/webpacl/page
 const glob = require('glob');
+const path = require('path');
+
+const PAGE_ROOT = 'src/pages/';
 
 const pages = glob.sync(`**/*.tsx`, {
   ignore: `**/_*.tsx`,
-  cwd: `./src/pages`
-}).map((n) => {
-  const ext = n.replace(/tsx$/,'html')
+  cwd: PAGE_ROOT
+}).map((currentPagePath) => {
+  const pageCurrentPath = PAGE_ROOT + currentPagePath.replace(/index\.tsx$/, '');
+  const relativePath = `${path.relative(pageCurrentPath, PAGE_ROOT) || '.'}/`;
+  const currentPageHTMLPath = currentPagePath.replace(/tsx$/, 'html')
   return {
-    template: "src/pages/" + n,
-    filename: 'dist/' + ext,
+    template: PAGE_ROOT,
+    filename: 'dist/' + currentPageHTMLPath,
+    relativePath
   }
 });
 

HishoHisho

currentPagePath.replace(/index\.tsx$/, '')の部分が/index\.tsx$/なので、index以外もできるように今後変更したい
すぐに正規表現が思いつかない

HishoHisho

というかこれどっかで一回やったことあるので探してみる

HishoHisho

Gatsbyですべての画像から特定の画像を抜き出す時に使った((?!.*\/).+\.(png|jpe?g))$これを((?!.*\/).+\.tsx)$こうじゃ

HishoHisho

余計な括弧もいらないからこうじゃ!(?!.*\/).+\.tsx$

HishoHisho
.config/webpack/pages.js
   ignore: `**/_*.tsx`,
   cwd: PAGE_ROOT
 }).map((currentPagePath) => {
-  const pageCurrentPath = PAGE_ROOT + currentPagePath.replace(/index\.tsx$/, '');
+  const pageCurrentPath = PAGE_ROOT + currentPagePath.replace(/(?!.*\/).+\.tsx$/, '');
   const relativePath = `${path.relative(pageCurrentPath, PAGE_ROOT) || '.'}/`;
   const currentPageHTMLPath = currentPagePath.replace(/tsx$/, 'html')
   return {

HishoHisho

とりあえず、適当に変数名も名前も読みやすく変えるのと
pathTypeを別の所でも使うので、抜き出せるようにする

src/confit.ts
 import React from "react";
 
-type currentPageValueType = {
-  path: '/' | './' | '../' | '../../',
+export type pathType = '/' | './' | '../' | '../../';
+
+type initialPageValueType = {
+  path: pathType,
 }
 
-const currentPageValue: currentPageValueType = {
+const initialPageValue: initialPageValueType = {
   path: '/',
 }
 
-export const CurrentPage = React.createContext(currentPageValue);
+export const CurrentPage = React.createContext(initialPageValue);
HishoHisho

webpackで先程のrelativePathを受け取れるり、HtmlWebpackPluginに投げる

webpack.config.js
     plugins: [
-      ...Pages.map(({template, filename}) => new HtmlWebpackPlugin({
+      ...Pages.map(({template, filename, relativePath}) => new HtmlWebpackPlugin({
         template,
         filename,
+        relativePath,
         inject: false,
       })),
       ...[

投げ方的にtemplateParametersもあるみたいだけど、普通に投げても受け取れるみたいなので普通に投げる

HishoHisho

一旦pages/index.tsxに読み込ませる

型とか分割代入で汚いのでなんとかしたい

src/pages/index.tsx
 import React from 'react';
+import htmlWebpackPlugin from 'html-webpack-plugin';
+import {pathType} from "../config";
 import {myRenderToStaticMarkup} from "../hooks";
 import {Layout} from "../layouts";
 import {CurrentPage} from "../config";
 import {App} from "../components";
 
-export default () => {
+export default ({htmlWebpackPlugin}: { htmlWebpackPlugin: htmlWebpackPlugin.ProcessedOptions }) => {
+  const relativePath: pathType = htmlWebpackPlugin.options.relativePath;
   return myRenderToStaticMarkup(
-    <CurrentPage.Provider value={{path: './'}}>
+    <CurrentPage.Provider value={{path: relativePath}}>
       <Layout>
         テスト
         <App/>

HishoHisho

aboutにも読み込ませる

src/about/index.tsx
 import React from 'react';
+import htmlWebpackPlugin from 'html-webpack-plugin';
+import {pathType} from "../../config";
 import {myRenderToStaticMarkup} from "../../hooks";
 import {Layout} from "../../layouts";
 import {CurrentPage} from "../../config";
 import {App} from "../../components";
 
-export default () => {
+export default ({htmlWebpackPlugin}: { htmlWebpackPlugin: htmlWebpackPlugin.ProcessedOptions }) => {
+  const relativePath: pathType = htmlWebpackPlugin.options.relativePath;
   return myRenderToStaticMarkup(
-    <CurrentPage.Provider value={{path: '../'} as const}>
+    <CurrentPage.Provider value={{path: relativePath}}>
       <Layout>
         about
         <App/>

HishoHisho

Providerまでは絶対共通なのでProviderまでを切り出す。

HishoHisho
src/hooks/myRenderToStaticMarkup.ts
  import {renderToStaticMarkup} from 'react-dom/server';
  import React, {ReactElement} from "react";
  import {ProcessedOptions} from 'html-webpack-plugin';
  import {pathType} from "../config";
  import {CurrentPage} from "../config";
  
  function myRenderToStaticMarkup(element: ReactElement): string {
    return '<!DOCTYPE html>' + renderToStaticMarkup(element);
  }
  
  export function newRenderToStaticMarkup(element: ReactElement) {
    return (htmlWebpackPlugin: ProcessedOptions) => {
      const relativePath: pathType = htmlWebpackPlugin.options.relativePath;
      return myRenderToStaticMarkup(
        <CurrentPage.Provider value={{path: relativePath}}>
          {element}
        </CurrentPage.Provider>
      )
    }
  }

拡張子と名前が変わったので変える

$ mv src/hooks/myRenderToStaticMarkup.ts src/hooks/newRenderToStaticMarkup.tsx
HishoHisho

なんとなくかっこよさでカリー化してみたけど、他にいい方法があるかもしれない。
ファイル分割とかその辺も

HishoHisho

共通部分を切り出したのでだいぶスッキリした😎

src/pages/index.tsx
 import React from 'react';
-import htmlWebpackPlugin from 'html-webpack-plugin';
-import {pathType} from "../config";
-import {myRenderToStaticMarkup} from "../hooks";
+import {ProcessedOptions} from 'html-webpack-plugin';
+import {newRenderToStaticMarkup} from "../hooks";
 import {Layout} from "../layouts";
-import {CurrentPage} from "../config";
 import {App} from "../components";
 
-export default ({htmlWebpackPlugin}: { htmlWebpackPlugin: htmlWebpackPlugin.ProcessedOptions }) => {
-  const relativePath: pathType = htmlWebpackPlugin.options.relativePath;
-  return myRenderToStaticMarkup(
-    <CurrentPage.Provider value={{path: relativePath}}>
-      <Layout>
-        テスト
-        <App/>
-      </Layout>
-    </CurrentPage.Provider>
-  )
+export default ({htmlWebpackPlugin}: ProcessedOptions) => {
+  return newRenderToStaticMarkup(
+    <Layout>
+      テスト
+      <App/>
+    </Layout>
+  )(htmlWebpackPlugin);
 };

src/pages/about/index.tsx
 import React from 'react';
-import htmlWebpackPlugin from 'html-webpack-plugin';
-import {pathType} from "../../config";
-import {myRenderToStaticMarkup} from "../../hooks";
+import {ProcessedOptions} from 'html-webpack-plugin';
+import {newRenderToStaticMarkup} from "../../hooks";
 import {Layout} from "../../layouts";
-import {CurrentPage} from "../../config";
 import {App} from "../../components";
 
-export default ({htmlWebpackPlugin}: { htmlWebpackPlugin: htmlWebpackPlugin.ProcessedOptions }) => {
-  const relativePath: pathType = htmlWebpackPlugin.options.relativePath;
-  return myRenderToStaticMarkup(
-    <CurrentPage.Provider value={{path: relativePath}}>
-      <Layout>
-        about
-        <App/>
-      </Layout>
-    </CurrentPage.Provider>
-  )
+export default ({htmlWebpackPlugin}: ProcessedOptions) => {
+  return newRenderToStaticMarkup(
+    <Layout>
+      about
+      <App/>
+    </Layout>
+  )(htmlWebpackPlugin);
 };

HishoHisho

もしかしてこれ引数の順番を変えると一発でいけるのでは?

HishoHisho

この形にしないといけなさそうなので、だめかも?
export default ({htmlWebpackPlugin}) => test(htmlWebpackPlugin)(jsx);

HishoHisho

いい加減pathを書くのがめんどくさいのでaliasを設定する

HishoHisho
webpack.config.js
    },
    resolve: {
      extensions: ['.ts', '.tsx', '.jsx', '.js'],
+      alias: {
+        'src': path.resolve('./src'),
+      },
    },
    output: {
      filename: '[name].js',
tsconfig.json
    "lib": ["dom", "es2015", "dom.iterable"],
    "typeRoots": ["src/@types", "node_modules/@types"],
    "baseUrl": ".",
+    "paths": {
+      "src/*": ["src/*"]
+    },
    "strict": true,
    "moduleResolution": "node",
    "noUnusedLocals": true,

HishoHisho

headタグを書くページで追記したい時の事を考えてreact-helmetを採用する

HishoHisho
renderToStaticMarkup(element);

の後に

const helmet = Helmet.renderStatic();

を追記してください!

const html = `
    <!doctype html>
    <html ${helmet.htmlAttributes.toString()}>
        <head>
            ${helmet.title.toString()}
            ${helmet.meta.toString()}
            ${helmet.link.toString()}
        </head>
        <body ${helmet.bodyAttributes.toString()}>
            <div id="content">
                // React stuff here
            </div>
        </body>
    </html>
`;
HishoHisho

つまりこうじゃ!

src/layouts/index.tsx
+import {Helmet} from "react-helmet";

 export const Layout: FC<LayoutType> = ({children}) => {
   return (
     <>
-      <html lang="ja">
-      <head>
+      <Helmet>
+        <html lang="ja" />
         <meta charSet="UTF-8"/>
-        <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
         <title>タイトル</title>
-      </head>
+        <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
+        <meta name="description" content="説明文"/>
+      </Helmet>
       <body>
       {children}
       </body>
-      </html>
     </>
   )
 }


src/hooks/newRenderToStaticMarkup.tsx
import {renderToStaticMarkup} from 'react-dom/server';
import React, {ReactElement} from "react";
import {Helmet} from "react-helmet";
import {ProcessedOptions} from 'html-webpack-plugin';
import {CurrentPage, pathType} from "src/config";

function myRenderToStaticMarkup(element: ReactElement): string {
  const staticMarkup = renderToStaticMarkup(element);
  const helmet = Helmet.renderStatic();

  function replaceDataHelmet(helmet: string) {
    return helmet.replace(/data-react-helmet="true"/g, '')
  }

  return `
    <!doctype html>
     <html ${helmet.htmlAttributes.toString()}>
      <head>
       ${replaceDataHelmet(helmet.title.toString())}
       ${replaceDataHelmet(helmet.meta.toString())}
       ${replaceDataHelmet(helmet.link.toString())}
      </head>
      ${staticMarkup}
    </html>
  `;
}

export function newRenderToStaticMarkup(element: ReactElement) {
  return (htmlWebpackPlugin: ProcessedOptions) => {
    const relativePath: pathType = htmlWebpackPlugin.options.relativePath;
    return myRenderToStaticMarkup(
      <CurrentPage.Provider value={{path: relativePath}}>
        {element}
      </CurrentPage.Provider>
    )
  }
}

renderToStaticMarkup(element);を返してた部分を変数に打ち込んで、doc通りにするbodyにclassは付けないのでbodyはLayoutの中のまま

HishoHisho

data-react-helmet="true"はいらないので削除する

function replaceDataHelmet(helmet: string) {
  return helmet.replace(/data-react-helmet="true"/g, '')
}
HishoHisho
$ npm install -g npm-check-updates
$ ncu
Upgrading /Users/hisho/github/react-static-generator/package.json
[====================] 19/19 100%

 @types/node                     ^14.14.10  →  ^14.14.17
 fork-ts-checker-webpack-plugin     ^6.0.5  →     ^6.0.8
 ts-loader                         ^8.0.11  →    ^8.0.13
 typescript                         ^4.1.2  →     ^4.1.3
 webpack                           ^4.44.2  →    ^5.11.1
 webpack-cli                        ^4.2.0  →     ^4.3.1

Run npm install to install new versions.
$ ncu -u
$ npm i
HishoHisho
webpack.config.js
      filename: '[name].js',
      path: path.join(__dirname),
    },
+    target: ['web', 'es5'],
    module: {
      rules: [
        {

HishoHisho

TODO

  • 各ページを一言管理するjsonの読み込み
  • ESlintの設定
  • htmlhintの設定
  • prettierの設定
HishoHisho

pages.jsがバグっていたので、修正する

.config/webpack/pages.js
const glob = require('glob');
const path = require('path');

const PAGE_ROOT = 'src/pages/';

const pages = glob.sync(`**/*.tsx`, {
  ignore: `**/_*.tsx`,
  cwd: PAGE_ROOT
}).map((currentPagePath) => {
  const pageCurrentPath = PAGE_ROOT + currentPagePath.replace(/(?!.*\/).+\.tsx$/, '');
  const relativePath = `${path.relative(pageCurrentPath, PAGE_ROOT) || '.'}/`;
  const currentPageHTMLPath = currentPagePath.replace(/tsx$/, 'html')
  return {
-    template: PAGE_ROOT,
+    template: PAGE_ROOT + currentPagePath,
    filename: 'dist/' + currentPageHTMLPath,
    relativePath
  }
});

module.exports = pages;

このスクラップは2021/04/26にクローズされました