React(JSX)で書けるコーディング用SSG - minista v0
はじめに
こんにちは!クラクです。コーディングの仕事用にminista(ミニスタ)という、React(JSX)で書ける静的サイトジェネレーターを作りましたので、よかったら試してみてください!
ministaを作った経緯
SaaSの仕事では綺麗なHTMLを納品することが多いです。開発者が別途アサインされているので。ただ、EJS等はもう辛い。Next.jsやGatsby.jsで書くJSXの快適さに慣れすぎたのでしょう。
とはいえ、Reactフレームワークの出力するコードはSPAっぽい動作を前提としているため、納品用としては微妙です。機械的な命名でコード分割されるし、そもそもReact自体が不要...。
色々と試していたときにotsukayuhiさんとHishoさんの記事を見かけ、renderToStaticMarkup()
を使った構成が良さそうだと思いました!応用で機能をパッケージ化したものがministaです。
ministaの使い方
セットアップ
コーディング用のプロジェクトを作ったら minista
を devDependencies
に加えます。
$ npm install --save-dev minista react react-dom
package.json
にnpm scriptsを追加しておきます。minista
または minista dev
でライブリロードサーバーを立ち上げてコーディングできます。minista build
で納品用データを生成。
{
"scripts": {
"dev": "minista",
"build": "minista build"
}
}
必須要素 src/assets/index.js
を作成して npm run build
、空の dist/assets/scripts.js
が生成されたらセットアップは成功です。
$ mkdir -p src/assets && touch src/assets/index.js
$ npm run build
HTML
src/pages/**/*.js
がHTMLになります。試しに src/pages/index.js
を作成してみましょう。metaタグ用に react-helmet
、コメントタグ用に専用のコンポーネントが使えます。全体をministaの render()
で囲むのを忘れずに。
$ mkdir -p src/pages && touch src/pages/index.js
import React from "react"
import { Helmet } from "react-helmet"
import { render, Comment } from "minista"
const Home = () => {
return render(
<main>
<Helmet>
<title>HTML coding with minista</title>
</Helmet>
<Comment text="Comment Test" />
<h1>Hello</h1>
</main>
)
}
export default Home
npm run build
で生成。dist/assets/scripts.js
も読み込まれています。
<!doctype html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>HTML coding with minista</title>
<script defer="defer" src="/assets/scripts.js"></script>
</head>
<body>
<main>
<!-- Comment Test -->
<h1>Hello</h1>
</main>
</body>
</html>
HTML Components
コンポーネントを分割して管理できます。React(JSX)なので children
が使えて便利。以下は共通レイアウトをコンポーネント化した例です。
import React from "react"
const AppLayout = ({ children }) => {
return <main>{children}</main>
}
export default AppLayout
CSS
CSSは src/assets/scripts.js
からインポートします。
$ mkdir -p src/assets/css && touch src/assets/css/index.css
h1 {
color: red;
}
import "./css/index.css"
npm run build
でminifyされた dist/assets/styles.css
が追加で生成。HTMLファイルにはリンクが自動的に付与されるようになります。
h1{color:red}
+ <link href="/assets/styles.css" rel="stylesheet">
JavaScript
JavaScriptも src/assets/scripts.js
を起点として書きます。
$ mkdir -p src/assets/js && touch src/assets/js/hello.js
console.log("Hello JavaScript")
import "./js/hello"
npm run build
を実行すると dist/assets/scripts.js
にwebpack/Babelを通しつつminifyしたJavaScriptが出力されます。
+ (()=>{var e={27:()=>{console.log("Hello JavaScript")}},r={};function o(t){var a=r[t];if(void 0!==a)return a.exports;var n=r[t]={exports:{}};return e[t](n,n.exports,o),n.exports}o.n=e=>{var r=e&&e.__esModule?()=>e.default:()=>e;return o.d(r,{a:r}),r},o.d=(e,r)=>{for(var t in r)o.o(r,t)&&!o.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},o.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),(()=>{"use strict";o(27)})()})();
Other
画像などの処理はministaで行いません。その代わり、public
ディレクトリ内のファイルをそのまま dist
にコピーする機能を使います。
public
└── favicon.png
+ <link rel="icon" href="/favicon.png" />
ministaの全体図
最終的にプロジェクトの構成を見ると、以下のようになります。src
と public
はNext.js風で、dist
の中身はgulpやwebpackで納品していたものに近い形です。
.
├── dist
│ ├── assets
│ │ ├── images
│ │ │ └── demo-icon.png
│ │ ├── scripts.js
│ │ └── styles.css
│ ├── favicon.png
│ └── index.html
├── package-lock.json
├── package.json
├── public
│ ├── assets
│ │ └── images
│ │ └── demo-icon.png
│ └── favicon.png
└── src
├── assets
│ ├── css
│ │ └── index.css
│ ├── index.js
│ └── js
│ └── hello.js
├── components
│ └── app-layout.js
└── pages
└── index.js
上手くいかない点があった場合は、サンプルリポジトリを参考にしてみてください。
最後に
なんとか、React(JSX)で快適に書きつつ納品向きなコードを生成するSSGができました!
動かすとわかると思いますが、内部的にはwebpackメインです。ただ、ユーザー設定をマージさせる方法がわからず、結果的に全部入りのゼロコンフィグSSGとなってしまいました...。もし詳しい方がいれば、実装方法を共有いただけるとありがたいです。
余談ですが、以前にはHugoで近しい構成を作ったりもしました。ビルドが早く省エネなものの、テンプレ構造が融通効かないんですよねー。後発のJSバンドラーやDenoで新たな構成も考えられそうですが、現状はwebpackかなーというところです。
Discussion
webpack configの合成であれば素直にwebpack-merge でいいような気がします。
プレーンオブジェクトなので上書きしていいものだけ抜き出してマージする感じ。
引数で渡したファイルパスなどからFSで取得とかは別途必要と思いますが。
おー!ありがとうございます!!
便利なライブラリがあるんですね。見てみます!
教えていただいたライブラリでうまくいったので、v0.8に導入してみました!
ローカルに
webpack.config.js
があればマージされます。こちらってcharsetって変更可能なんですか?
いいえ、v2.7.4時点では実装していません。必要な場面がありますか?
あーそうなんですね、、shift-jisで大量のページを製造しないといけない案件があるんですよね。
現状だと、rect-helmetでhead内の情報をいじれるかなーとおもっていたのですが、無理でした
回答ありがとうございました。
めちゃくちゃ便利な気がするので、アップデート期待しております。