Closed17

gulpでtsを触る

HishoHisho

Q. なぜgulpでやるのか

A. 単純にwebpackを使う必要が無いから
css-loaderなどを使う必要が無いので多分gulpでやった方が良いとの判断
pageを増やす度にwebpackを止めないといけないけどめんどくさいgulpなら止めなくても大丈夫

HishoHisho

とりあえず、gulpが動くまで設定

最初の目標

src/pages/index.tsxをdist/index.tsxとして出力

HishoHisho
  1. npmの初期設定をする
$ npm init -y
  1. いらないフィールドを消す
package.json
{
  "name": "gulp-react-static-generate",
  "version": "1.0.0",
  "description": "",
  "license": "MIT",
  "author": "hisho",
  "scripts": {
    "start": "echo \"Error: no test specified\" && exit 1"
  }
}

  1. src/pages/index.tsxを作成する
$ mkdir src && mkdir src/pages && touch src/pages/index.tsx
  1. gulp周りをインストールする
$ npm i -D gulp npm-run-all
  1. gulpのtaskを作成する
$ mkdir gulpfile.js && touch gulpfile.js/index.js && touch gulpfile.js/reactRenderStaticMarkup.js
  1. node.js周りをインストールする
$ npm i -D @types/node
  1. gulpの設定をする
gulpfile.js/reactRenderStaticMarkup.js
const gulp = require('gulp');

reactRenderStaticMarkup = (done) => {
  gulp
    .src('src/pages/**/*.tsx')
    .pipe(gulp.dest('dist'))
  done();
}

module.exports = reactRenderStaticMarkup;
gulpfile.js/index.js
const gulp = require('gulp');

const reactRenderStaticMarkup = require('./reactRenderStaticMarkup');

exports.renderReact = reactRenderStaticMarkup;
exports.watch = () => {
  gulp.watch('src/pages/**/*.tsx', reactRenderStaticMarkup);
}
package.json
{
  "name": "gulp-react-static-generate",
  "version": "1.0.0",
  "description": "",
  "license": "MIT",
  "author": "hisho",
  "scripts": {
    "start": "run-s gulp-render-react gulp-watch",
    "build": "run-p gulp-render-react",
    "gulp-render-react": "gulp renderReact",
    "gulp-watch": "gulp watch"
  },
  "devDependencies": {
    "@types/node": "^15.0.2",
    "gulp": "^4.0.2",
    "npm-run-all": "^4.1.5"
  }
}
  1. LICENCEを作る
HishoHisho

コマンドをREADME.mdに追記する

動作環境

  • OS: macOS
  • Node: v14.16.1
  • npm: 7.9.0

コマンド

インストール

$ npm ci

ウォッチ

$ npm start

ビルド

$ npm run build

License

MIT © hisho

HishoHisho

やるべきこと

  • tsをトランスパイル
  • throughで受け取りhtmlに変換
  • 吐き出し
    こんな感じかな?
HishoHisho

まずはReactをインストールする

$ npm i react react-dom

typesを入れる

$ npx typesync
$ npm i
HishoHisho

とりあえずtsxをjsxに変換する

$ npm i -D gulp-typescript typescript
$ touch tsconfig.json
tsconfig.json
{
  "compilerOptions": {
    "target": "esnext",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve",
  },
  "include": [
    "src/**/*.ts",
    "src/**/*.tsx"
  ],
  "exclude": [
    "node_modules"
  ]
}
gulpfile.js/reactRenderStaticMarkup.js
const gulp = require('gulp');
const ts = require('gulp-typescript');
const path = require('path');

const tsProject = ts.createProject(path.resolve(process.cwd(),'tsconfig.json'));

reactRenderStaticMarkup = (done) => {
  gulp
    .src('src/pages/**/*.tsx')
    .pipe(tsProject())
    .pipe(gulp.dest('dist'))
  done();
}

module.exports = reactRenderStaticMarkup;
$ npm run gulp-render-react
HishoHisho

トランスパイル前

src/pages/index.tsx
import React from "react";

const IndexPage: React.VFC = () => {
  return (
    <div>
      ハロー
    </div>
  )
}

トランスパイル後

dist/index.jsx
import React from "react";
const IndexPage = () => {
    return (<div>
      ハロー
    </div>);
};
HishoHisho

.tsxからjsxにした後の処理一番めんどくさそうなので先に.tsx.htmlとして出力する

$ npm i -D through2
$ npx typesync
$ npm i
gulpfile.js/reactRenderStaticMarkup.js
const gulp = require('gulp');
const ts = require('gulp-typescript');
const path = require('path');
const through = require("through2");
const gutil = require('gulp-util');

const tsProject = ts.createProject(path.resolve(process.cwd(), 'tsconfig.json'));

reactRenderStaticMarkup = (done) => {
  gulp
    .src('src/pages/**/*.tsx')
    .pipe(tsProject())
    .pipe(through.obj((file, _, callback) => {
      //何かしらのプラグインがファイルの中身を stream にして次に渡して来た時
      if (file.isStream()) {
        callback(new gutil.PluginError('react render static', 'Streams not supported'));
        return callback(null, file);
      }

      //ファイルの中身がBufferとしてわたってきた場合
      if (file.isBuffer()) {
        try {
          //現在処理しているfileのpath
          const modulePath = path.resolve(process.cwd(), file.path);
          //現在処理しているfileのext(拡張子)
          const moduleExt = path.extname(modulePath);

          //拡張子を.htmlに変換する
          file.path = file.path.replace(new RegExp(`${moduleExt}$`), '.html');

          //変換後のfileを返す
          return callback(null, file);

          //エラーの処理
        } catch (e) {
          console.error("ERROR RENDERING TSX PAGE: ", e.stack);
          return callback(null, file);
        }
      }
    }))
    .pipe(gulp.dest('dist'))
  done();
}

module.exports = reactRenderStaticMarkup;

とりあえず、.tsx.htmlに吐き出すのはできた

HishoHisho

元のプラグインではjsxをそのままnodeでimportしてReact.createElement()してReact.renderToStaticMarkup()をしているけど、tsxからだとそのパターンが使えない

HishoHisho

くぅ〜1時間調べても進展が無いので諦めます😂

  • tsx
  • jsx
  • React.ReactNodeを取得する
  • renderToStaticMarkupで文字列に変換する
  • 文字列をfile.contetnsに渡す
  • 拡張子を.htmlに変換する

この手順で行けるはずなんだけど、React.ReactNodeを取得するができない
後、webpackと違って

import React from "react";
import {Layout} from "../layouts/Layout";

export default function () {
  return (
    <Layout>
      <h1>タイトル</h1>
      ハロー
    </Layout>
  )
}

これが

import React from "react";
import {Layout} from "../layouts/Layout";

export default function () {
  return (
    <div>
      レイアウト
      <h1>タイトル</h1>
      ハロー
    </div>
  )
}

こうならない

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