tsoaでNitro/H3に対応したいと思って、
routeファイルのテンプレートをカスタマイズする方法について調べたときの備忘録(*´ω`*)
ドキュメントにちょっと書かれているけど、詳細はほぼ無い。。
{ // ... "routes": { "routesDir": "...", "middleware": "express", "middlewareTemplate": "custom-template.ts" ... } }
tsoaのテンプレートの仕組み
expressのテンプレート自体はこれっぽい。
Handlebarsで書かれているよう。
デフォルトのRouteGenerator
はこれっぽい。
コンストラクタを見ると、tsoa.json
のroutes.middleware
やroutes.middlewareTemplate
で、
テンプレートファイル(.hbs
)を設定しているよう。
tsoa routes
で呼ばれる本体は、これっぽい。
ここでRouteGenerator
を取得して、実行している感じ。
// packages/cli/src/module/generate-routes.ts const routeGenerator = await getRouteGenerator(metadata, routesConfig); await fsMkDir(routesConfig.routesDir, { recursive: true }); await routeGenerator.GenerateCustomRoutes();
defaultRouteGenerator.ts
でhandlebars
を呼び出しているところはこんな感じ。
// packages/cli/src/routeGeneration/defaultRouteGenerator.ts public buildContent(middlewareTemplate: string) { handlebars.registerHelper('json', (context: any) => { return JSON.stringify(context); }); const additionalPropsHelper = (additionalProperties: TsoaRoute.RefObjectModelSchema['additionalProperties']) => { if (additionalProperties) { // Then the model for this type explicitly allows additional properties and thus we should assign that return JSON.stringify(additionalProperties); } else if (this.options.noImplicitAdditionalProperties === 'silently-remove-extras') { return JSON.stringify(false); } else if (this.options.noImplicitAdditionalProperties === 'throw-on-extras') { return JSON.stringify(false); } else if (this.options.noImplicitAdditionalProperties === 'ignore') { return JSON.stringify(true); } else { return assertNever(this.options.noImplicitAdditionalProperties); } }; handlebars.registerHelper('additionalPropsHelper', additionalPropsHelper); const routesTemplate = handlebars.compile(middlewareTemplate, { noEscape: true }); return routesTemplate(this.buildContext()); }
handlebars
で使われるContextはAbstractRouteGenerator.buildContext()
で生成されているよう。
protected buildContext() { // ... return { authenticationModule, basePath: normalisedBasePath, canImportByAlias, controllers: this.metadata.controllers.map(controller => { return { actions: controller.methods.map(method => { // ... return { fullPath: normalisedFullPath, method: method.method.toLowerCase(), name: method.name, parameters: parameterObjs, path: normalisedMethodPath, uploadFile: !!uploadFileParameter, uploadFileName: uploadFileParameter?.name, uploadFiles: !!uploadFilesParameter, uploadFilesName: uploadFilesParameter?.name, security: method.security, successStatus: method.successStatus ? method.successStatus : 'undefined', }; }), modulePath: this.getRelativeImportPath(controller.location), name: controller.name, path: normalisedControllerPath, }; }), environment: process.env, iocModule, minimalSwaggerConfig: { noImplicitAdditionalProperties: this.options.noImplicitAdditionalProperties }, models: this.buildModels(), useFileUploads: //... multerOpts: this.options.multerOpts, useSecurity: this.metadata.controllers.some(controller => controller.methods.some(method => !!method.security.length)), esm: this.options.esm, }; }
かなり大きいけど、以下あたりがメインどこぽい?
controllers
/models
authenticationModule
/useSecurity
useFileUploads
/multerOpts
カスタムテンプレートのHello world
まずは、コンテキストを全部表示するサンプル。
こんな感じのテンプレートを
{{!-- ./context-all.hbs --}} {{json @root}}
利用するように設定を変更して、
// tsoa.json { // ... "routes": { "middlewareTemplate": "./context-all.hbs" ... } }
pnpm tsoa routes
を実行すると、
こんな感じのJSONが出力される。
{ "basePath": "", "canImportByAlias": true, "controllers": [ { "actions": [ { "fullPath": "/auth/me", "method": "post", "name": "me", "parameters": { "event": { "in": "request", "name": "event", "required": true, "dataType": "object" } }, "path": "/me", "uploadFile": false, "uploadFiles": false, "security": [ { "jwt": [] } ], "successStatus": "undefined" } ], "modulePath": "./../../controllers/1.authController", "name": "AuthController", "path": "/auth" }, // ... "models": { "PagingParams": { "dataType": "refObject", "properties": { "page": { "dataType": "integer" }, "pageSize": { "dataType": "integer" } }, "additionalProperties": false }, // ... }, "useFileUploads": false, "useSecurity": true }
あとは、これを参照しながらテンプレートを組み立てていけばよさそう!
とりあえず、カスタマイズ方法がわかった気がする(*´ω`*)
これでnitroでもtsoa使えそうかも(*´ω`*)