TypeSpecでscaffdogを利用する
背景
TypeSpecを利用する際に、SchemaとModelを分離したい気持ちが生じており、複数ファイルのテンプレートをVSCなどのテンプレートで利用せずにscaffdogを利用してファイルの生成速度をあげるための対応。
0. 想定しているdirectory構成
tree -L . -I node_modules
.
├── dockerfile
├── package-lock.json
├── package.json
├── scripts
│ └── sync
│ └── main.ts
├── src
│ ├── main.tsp // monosilic dist用のファイル
│ ├── tspconfig.yaml // dist用config file
│ ├── blog // model package
│ │ ├── model.tsp // Model
│ │ └── rotue.tsp // Open API Schema
│ ├── rest-util
│ │ └── page.tsp
│ └── util
│ └── uuid.tsp
├── tsconfig.json
└── tsp-output
└── @typespec
└── openapi3
構成としては、src/{model_name}/model.tspにModelの内容を記載。
src/{model_name}/route.tspにOpenAPIのSchemaを記載
model Blog {
@visibility("read")
@key
id: uint64;
@visibility("create")
title: blogTitle;
}
@minLength(1)
@maxLength(255)
scalar blogTitle extends string;
@withVisibility("create")
model CreateBlogContent {
...Blog;
}
op createBlogCotnent(dto: CreateBlogContent): Blog;
import "@typespec/http";
import "./model.tsp";
import "../rest-util/page.tsp";
using TypeSpec.Http;
@tag("blog")
@route("/blogs")
interface Blogs {
create is createBlogCotnent;
list(): Page<Blog>;
}
この記事で、ある程度の内容を書いてます。
1. scaffdogのinstallとinitialise
1-1. scaff dogのinstall
% npm install --save-dev scaffdog
1-2. scaff dogのinit
% npx scaffdog init
? Please enter a document name. scaff
Setup of scaffdog 🐶 is complete!
✔ .scaffdog/config.js
✔ .scaffdog/scaff.md
Now you can do scaffold by running `$ scaffdog generate`.
Please refer to the following documents and customize it.
https://scaff.dog/docs/templates
上記を実施すると、.scaffdogのdirectoryが作成され、2つのファイル(config.jsとscaff.md)が生成されていることが確認できます。
tree .scaffdog
.scaffdog
├── config.js
└── scaff.md
1 directory, 2 files
2. tspファイル用に生成ファイルをいじる
scaffdogでは、pascal caseやsnake caseなどのhelperなども準備されているため、
scaholdingのdocumentを生成します。
markdownの開始と終了については、qiitaの記事では省いておりますが、つけてください。
---
name: 'scaff'
root: './src'
output: '**/*'
ignore: []
questions:
name: 'Please enter name of model'
---
# Variables
- featurename: `{{ inputs.name | kebab }}`
- modelname: `{{ inputs.name | pascal }}`
# `{{ featurename }}/model.tsp`
markdown
/**
* Generated by scaffdog
* feature name is {{ featurename }}
* created at {{ date "YYYY/MM/DD" }}
**/
model {{ modelname }} {
}
# `{{ featurename }}/route.tsp`
/**
* Generated by scaffdog
* feature name is {{ featurename }}
* created at {{ date "YYYY/MM/DD" }}
**/
import "@typespec/http";
import "./model.tsp";
using TypeSpec.Http;
@tag("{{ inputs.name }}")
@route("/{{ inputs.name | kebab | plur }}")
interface {{ modelname | plur }} {
}
variablesに変数を置いています。
#
{{ inputs.name | kebab | define 'filename' }}のようにした場合、defineしたファイルが正常にdistされないので注意してください。
(多分、内部の噛み合わせなのですが、中身まで見てない)
3. ファイルを生成してみる
% npx scaffdog generate
? Please select a document. scaff
ℹ Output destination directory: "./src"
? Please enter name of model user
🐶 Generated 2 files!
✔ src/user/model.tsp
✔ src/user/route.tsp
/**
* Generated by scaffdog
* feature name is user
* created at 2024/10/28
**/
model User {
}
/**
* Generated by scaffdog
* feature name is user
* created at 2024/10/28
**/
import "@typespec/http";
import "./model.tsp";
using TypeSpec.Http;
@tag("user")
@route("/users")
interface Users {
}
4. Templateを拡張して、distファイルにも追加する
4-0. 現状のmain.tsp
現状のsrc/main.tspについては、以下となっています。
import "@typespec/http";
import "@typespec/rest";
import "@typespec/openapi3";
import "./blog/route.tsp";
手動で、import "./user/route.tsp"
を追加してもいいのですが、scaffdogを利用してもいいのですが、変更していきます。
4-1. main.tspのtemplateを作成
---
name: 'scaff'
root: './src'
output: 'src/*' // <- 変更
ignore: []
questions:
name: 'Please enter name of model'
---
## 省略
# `main.tsp`
|```
{{ read output.abs }}
import "./{{ featurename }}/route.tsp";
|```
read output.absを利用してファイルの内部を読み込み最終行に追加していきます。
4-2. 生成
scaff dogはoverrideをする場合には、terminalで確認が可能となります。
% npx scaffdog generate
? Please select a document. scaff
ℹ Output destination directory: "./src"
? Please enter name of model user
⚠ Would you like to overwrite it?
("src/user/model.tsp") No
⚠ Would you like to overwrite it?
("src/user/route.tsp") No
⚠ Would you like to overwrite it? ("src/main.tsp") Yes
🐶 Generated 1 file! (2 skipped)
✔ src/main.tsp
⚠ src/user/model.tsp (skipped)
⚠ src/user/route.tsp (skipped)
import "@typespec/http";
import "@typespec/rest";
import "@typespec/openapi3";
import "./blog/route.tsp";
import "./user/route.tsp";