各種JSフレームワークでgeneratorがどう実装されてるか比較

Blitz@0.35.0
この配下のコードがCLI系
require("v8-compile-cache")
const cacheFile = require("path").join(__dirname, ".blitzjs-cli-cache")
const lazyLoad = require("@salesforce/lazy-require").default.create(cacheFile)
lazyLoad.start()
import {buildConfig} from "@blitzjs/config"
import {run as oclifRun} from "@oclif/command"
// Load the .env environment variable so it's available for all commands
require("dotenv-expand")(require("dotenv-flow").config({silent: true}))
export function run() {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
buildConfig().then(() => {
oclifRun()
.then(require("@oclif/command/flush"))
// @ts-ignore (TS complains about using `catch`)
.catch(require("@oclif/errors/handle"))
})
}
CLI Frameworkのoclifが使われてる。開発元はHerokuで信頼できる。
oclifからは、なんとYeomanが呼ばれているっぽい!
yoコマンドは入ってないので、yo相当の機能はこの辺でラップしてるのかな。
でもYeomanが使われるのは、一番最初にアプリをgenerateするときだけで、
npx oclif single mynewcli
とかで生成されたアプリのpackage.jsonには@oclif/oclifは含まれない。
@oclif/command あたりの各パッケージは、Yeomanに依存していない。
・・・と色々書いたけども、上記はあくまで「CLI」であって、
ジェネレータ本体はまた別パッケージだった。こちら。
EJSとかでもなく、かなり独自実装されてますね。

他に調べたいもの、このあたり。
- vue-cli
- firebase-cli
- create-react-app
- create-next-app
- create-nuxt-app
- create-frourio-app
- create-bison-app
create-xxx-app という名前がデファクトスタンダードな感じですね。

create-react-app@4.0.3
この1本でほぼ全て完結。
かなりゴリゴリ独自実装されている印象。
Commander.jsを使ってるくらいで、scaffold用のフレームワークは特になし。
dependenciesのインストール以外は、
こんな感じのテンプレート一式をそのままコピーしているっぽい。
テンプレート1つずつが独自packageになってる。
デフォルト
TypeScript版

create-next-app@10.2.2
全体的にcreate-react-appと似たような印象。
一番メインがこのコードで、Commander.jsを使ってるのも一緒。
テンプレートのpackageは分かれていないので、
全体のディレクトリ構成はとても分かりやすかった。
default / typescriptの2種類が用意されてる。

全体的に、最近JS/TS界隈はMonorepoが多いなーという印象ですね。

create-nuxt-app@3.6.0
// See https://sao.vercel.app/api.html#standalone-cli
sao({ generator, outDir, logLevel, answers, cliOptions })
.run()
.catch((err) => {
console.trace(err)
process.exit(1)
})
})
saoなるscaffoldライブラリが使われている。これ初めて知った。
Why not use Yeoman?
Yeoman is great except that writing a Yeoman generator is time-consuming, I created SAO to simplify the way we write and use generators.
「Yeomanは素晴らしいんだけど、もうちょっとサクッとgenerator作りたかったからSAOを作った」とのこと。やはりYeomanはちょいちょい触れられますね。
sao
というコマンドを使っても良いし、自前コマンドからrequireしてsao()
関数を走らせても良いというハイブリッド仕様。Yeomanではyo
コマンドが必須で辛かったので、saoのこの仕様は好きかも!もちろんcreate-nuxt-appでもrequireして使ってる。
デフォルトのテンプレートはこちら。
Monorepoで単独packageになっている。
nuxt@2.15.7のサブジェネレータ
nuxt generate xxx
で走行するサブジェネレーターはSAOは使っておらず、独自実装ですね。SAOのサブジェネレーターは最低限の機能しかなさそうなので、こうなってるのかな。

vue-cli@4.5.13
boxen, commander, inquirer, minimist,... などなど、
CLI用のライブラリが沢山インストールされていますが、
どれも軽めの奴で、割と独自実装が多めな印象。
自作ヘルパーライブラリがこんなに沢山。
DIY派ですねぇ。
GitはこれもMonorepoで、サブコマンドごとに、色々なpackageが用意されてる。
vue-cliはジェネレータ以外にも、ビルドだとかサーバ起動だとか、色々な種類の操作ができるので、上に上げたcreate-xxx-app
系とは毛色が違いますよね。
generatorで使われるテンプレートはこのあたり。
EJS形式ですね。
<template>
<%_ if (rootOptions.vueVersion === '3') { _%>
<img alt="Vue logo" src="./assets/logo.png">
<%_ if (!rootOptions.bare) { _%>
<HelloWorld msg="Welcome to Your Vue.js App"/>
<%_ } else { _%>
<h1>Welcome to Your Vue.js App</h1>
<%_ } _%>
<%_ } else { _%>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<%_ if (!rootOptions.bare) { _%>
<HelloWorld msg="Welcome to Your Vue.js App"/>
<%_ } else { _%>
<h1>Welcome to Your Vue.js App</h1>
<%_ } _%>
</div>
<%_ } _%>
</template>
<%_ if (!rootOptions.bare) { _%>

create-bison-app@1.11.0-canary.11
Hygenというツールを使っている。
Bison自体はあんまり興味ないのだけど、
Hygenはこの辺読んで気になっていた。
最初にプロジェクトを生成するcreate-bison-app
コマンドでは、Hygenは使われていない。inquirer
, yargs
あたりの軽めのライブラリだけが使われてる感じ。
実際にHygenが活躍し始めるのは、プロジェクト生成後。
Yeomanでいうと「サブジェネレーター」の役割をHygenが担ってる感じですね。
これは生成されるpackage.jsonのテンプレートですが、devDependenciesにhygenが含まれてる。scriptsがかなり沢山定義されていて、g:xxx
という名前のものがすべてHygenを使ってますね。
"scripts": {
// 中略
"g:cell": "hygen cell new --name",
"g:component": "hygen component new --name",
"g:graphql": "hygen graphql new --name",
"g:page": "hygen page new --name",
"g:migration": "yarn -s prisma migrate dev",
"g:test:component": "hygen test component --name",
"g:test:factory": "hygen test factory --name",
"g:test:request": "hygen test request --name",
"g:test:util": "hygen test util --name",
cell
component
などは何処にあるかというと、_templates
ディレクトリ。
hygen test factory
であれば、_templates/test/factory/
内のテンプレートが使われるわけですね。
テンプレートのパスは、デフォルトでは${__dirname}/_templates
だけど、.hygen.js
にtemplates: xxx
と書くことで独自定義できる。
ローカルに置くのが基本だけど、
templates: `${__dirname}/.hygen`
こうすれば、外部ライブラリのtemplatesも普通に参照できた。
templates: `${__dirname}/node_modules/create-bison-app/template/_templates`
まだ本当の良さは分らんけど、仕組みは何となくわかった。

create-frourio-app@0.27.2
ジェネレータのためのWebサービスがlocalhostで立ち上がるというシャレオツ仕様。イマドキな感じですねぇ。CLIアプリとしての参考にはあまりならなかった。
ジェネレータはejs
で独自にやってる感じかな。
テンプレートは相当沢山バリエーション用意されてる。