Skip to content

Commit 0a962d5

Browse files
committed
perf: cache generated openapiTS type templates
Now when we generate type template we also save it inside `.nuxt/cache`, and if nothing changes (options, template) the cached version is reused to avoid calling `openapiTS`. Closes: #87
1 parent fdab485 commit 0a962d5

File tree

1 file changed

+68
-5
lines changed

1 file changed

+68
-5
lines changed

src/module.ts

Lines changed: 68 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { Readable } from 'node:stream'
22
import type { FetchOptions } from 'ofetch'
33
import type { OpenAPI3, OpenAPITSOptions } from 'openapi-typescript'
44
import { existsSync } from 'node:fs'
5+
import { mkdir, readdir, readFile, unlink, writeFile } from 'node:fs/promises'
56
import {
67
addImportsSources,
78
addPlugin,
@@ -12,10 +13,11 @@ import {
1213
createResolver,
1314
defineNuxtModule,
1415
} from '@nuxt/kit'
16+
import { hash } from 'ohash'
1517
import openapiTS, { astToString } from 'openapi-typescript'
1618
import { join } from 'pathe'
17-
import { kebabCase, pascalCase } from 'scule'
1819

20+
import { kebabCase, pascalCase } from 'scule'
1921
import { name, version } from '../package.json'
2022

2123
type OpenAPI3Schema = string | URL | OpenAPI3 | Readable
@@ -130,10 +132,15 @@ export default defineNuxtModule<ModuleOptions>({
130132
schemas.forEach(({ name, schema, openAPITS }) => {
131133
addTemplate({
132134
filename: `types/${moduleName}/schemas/${kebabCase(name)}.ts`,
133-
getContents: async () => {
134-
const ast = await openapiTS(schema, openAPITS)
135-
return astToString(ast)
136-
},
135+
getContents: () =>
136+
addCachedSchemaTemplate({
137+
name,
138+
schema,
139+
openAPITS,
140+
moduleName,
141+
nuxtBuildDir: nuxt.options.buildDir || '.nuxt',
142+
resolvePath: resolve,
143+
}),
137144
write: true,
138145
})
139146
})
@@ -319,3 +326,59 @@ function isValidUrl(url: string) {
319326
return false
320327
}
321328
}
329+
330+
interface SchemaOptions {
331+
name: string
332+
schema: string | URL | OpenAPI3 | Readable
333+
openAPITS?: object
334+
moduleName: string
335+
nuxtBuildDir: string
336+
resolvePath: (...paths: string[]) => string
337+
}
338+
339+
export async function addCachedSchemaTemplate({
340+
name,
341+
schema,
342+
openAPITS,
343+
moduleName,
344+
nuxtBuildDir,
345+
resolvePath,
346+
}: SchemaOptions) {
347+
const shortName = kebabCase(name)
348+
const cacheDir = resolvePath(nuxtBuildDir || '.nuxt', `cache/${moduleName}`)
349+
await mkdir(cacheDir, { recursive: true })
350+
351+
let fileBody = ''
352+
const filePath = schema instanceof URL ? schema.pathname : schema
353+
if (typeof filePath === 'string' && existsSync(filePath)) {
354+
fileBody = await readFile(filePath, 'utf-8')
355+
}
356+
else {
357+
// Currently we can only cache local files
358+
const ast = await openapiTS(schema, openAPITS)
359+
return astToString(ast)
360+
}
361+
362+
const key = hash([schema, openAPITS, moduleName, shortName, fileBody])
363+
const cachedPath = resolvePath(cacheDir, `${shortName}-${key}.ts`)
364+
365+
if (existsSync(cachedPath)) {
366+
return await readFile(cachedPath, 'utf-8')
367+
}
368+
369+
const ast = await openapiTS(schema, openAPITS)
370+
const contents = astToString(ast)
371+
372+
await writeFile(cachedPath, contents, 'utf-8')
373+
374+
for (const file of await readdir(cacheDir)) {
375+
if (file.startsWith(`${shortName}-`) && file !== `${shortName}-${key}.ts`) {
376+
try {
377+
await unlink(resolvePath(cacheDir, file))
378+
}
379+
catch {}
380+
}
381+
}
382+
383+
return contents
384+
}

0 commit comments

Comments
 (0)