9
9

More than 5 years have passed since last update.

【React + TypeScript + Pug】JSXをPugに置き換える際のワークアラウンド

Posted at

概要

Pugを覚えてReactコンポーネント内のJSXをPugに置き換えようと思い立ったが、TypeScriptとの連携でつまづいた。最終的にワークアラウンドにて対処したため、仔細を覚書として書き残す。

再現

再現用のリポジトリをcloneして yarn start を実行すると、以下のようなエラー画面が表示される。

Screen Shot 2019-07-05 at 3.47.43.png

検証

エラーメッセージ ReferenceError: React is not defined の詳細を調べるため、トランスパイル後のソースコードを確認する。なお、先述のリポジトリはCRA@3.0.1で作成したテンプレートをejectしたものを基にしている。

設定、および検証手順


package.jsonにおける検証用の設定は以下の通りである。

package.json
{
  "dependencies": {
    "@babel/cli": "^7.4.4",
    "babel-plugin-transform-react-jsx": "^6.24.1",
    "babel-plugin-transform-react-pug": "^7.0.1",
    "pug": "^2.0.4"
  },
  "scripts": {
    "transpile": "babel src/App.tsx --out-file App.js"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "plugin:react-pug/all"
    ],
    "plugins": [
      "react-pug"
    ]
  },
  "babel": {
    "presets": [
      "@babel/preset-typescript"
    ],
    "plugins": [
      "transform-react-pug",
      "transform-react-jsx"
    ]
  }
}


トランスパイル前のソースコードは以下の通り、JSXをPugに書き直しただけのものである。

src/App.tsx
import React from 'react';
import logo from './logo.svg';
import './App.css';

const App: React.FC = () => {
  return pug`
    div.App
      header.App-header
        img.App-logo(
          src=logo
          alt="logo"
        )
        p
          | Edit 
          code src/App.js
          |  and save to reload.

        a.App-link(
          href="https://reactjs.org" 
          target="_blank" 
          rel="noopener noreferrer"
        ) Learn React
  `;
}

export default App;


以下のコマンドにて、トランスパイルを実行する。

$ yarn transpile

結果

ファイル冒頭にて、Reactlogo に対するimport文が存在しないことに注目する。宣言されていない変数の関数を呼び出そうとしたため、ReferenceError: React is not defined が引き起こされている。

Screen Shot 2019-07-05 at 3.46.25.png

原因

@babel/plugin-transform-typescript removes regular imports #9723によると、TypeScriptはトランスパイルする際に使用されていないimport文を削除する仕様となっている。そしてBabelプラグインの@babel/plugin-transform-typescriptもその仕様を踏襲している。

今回の例では、JSXがPugに置き換わったことで React が使われなくなったため、これに対するimport文が削除された。また、logo はPugの中に存在していることが認識されていないため、同様に使われていないものとしてimport文が削除されている。

対策(ワークアラウンド)

使われていないimport文を削除しないよう、Babelプラグインにおける当該のコードを以下のようにコメントアウトする。

node_modules/@babel/plugin-transform-typescript/lib/index.js
        // for (const stmt of path.get("body")) {
        //   if (_core().types.isImportDeclaration(stmt)) {
        //     if (stmt.node.specifiers.length === 0) {
        //       continue;
        //     }

        //     let allElided = true;
        //     const importsToRemove = [];

        //     for (const specifier of stmt.node.specifiers) {
        //       const binding = stmt.scope.getBinding(specifier.local.name);

        //       if (binding && isImportTypeOnly(file, binding, state.programPath)) {
        //         importsToRemove.push(binding.path);
        //       } else {
        //         allElided = false;
        //       }
        //     }

        //     if (allElided) {
        //       stmt.remove();
        //     } else {
        //       for (const importPath of importsToRemove) {
        //         importPath.remove();
        //       }
        //     }
        //   }
        // }

検証

再度トランスパイルを行うと、Reactlogo に対するimport文が残っていることが確認できる。この状態で yarn start を実行すると、ページが正常に表示される。

Screen Shot 2019-07-05 at 3.50.26.png

考察

ワークアラウンドによって辛くもReact、TypeScript、Pugを連携させたが、不要なimport文を削除してくれる恩恵は失われてしまった。これを失わずに済む方法を考察したい。

BabelのプラグインはほとんどがJavaScript向けに書かれているため、TypeScriptからJavaScriptへの変換は最初に行われなければならない。ここでimport文の削除はして欲しくないため、前述のプラグインを「import文の削除をする機能」と「それ以外の機能」に分割し、Webpackにて

import文の削除以外の処理 ⇨ PugをJSXに変換 ⇨ import文の削除

という流れでトランスパイルを行うと、各ツールの恩恵を失うことなく連携が出来るものと推察される。

9
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
9