From 13ad45083ef69784c61ff0e7446bc7dc80a7a229 Mon Sep 17 00:00:00 2001 From: BatLeDev Date: Thu, 2 Jul 2026 10:31:51 +0200 Subject: [PATCH] perf(types-builder): reuse vjsf layout for compiledLayout export A schema exporting both `vjsf` and `compiledLayout` compiled and serialized its layout twice. The vjsf compiler now exposes the serialized compiled layout via compileParts(), so cache it per locale during the vjsf export and reuse it for the compiledLayout export instead of compiling again. Output is byte-identical. Requires @koumoul/vjsf-compiler >=1.5.0 (compileParts); peer dependency bumped. --- packages/types-builder/build.ts | 56 +++++++++++++++++------------ packages/types-builder/package.json | 2 +- 2 files changed, 35 insertions(+), 23 deletions(-) diff --git a/packages/types-builder/build.ts b/packages/types-builder/build.ts index e2e00dc..31007d7 100755 --- a/packages/types-builder/build.ts +++ b/packages/types-builder/build.ts @@ -127,6 +127,9 @@ const main = async (dir: string, options: TypesBuilderOptions) => { let importsCode = '/* eslint-disable */\n\n' let code = '' let dtsCode = '' + // serialized compiled layouts produced by the vjsf export (keyed by locale), so the + // compiledLayout export can reuse them instead of compiling+serializing a second time + const builtVjsfLayoutCodes: Record = {} if (existsSync(path.join(dir, '.type'))) rmSync(path.join(dir, '.type'), { recursive: true }) mkdirSync(path.join(dir, '.type')) @@ -282,7 +285,7 @@ export declare function returnValid(data: any, options?: import('${validationImp let compName = key const vjsfDir = path.resolve(options.vjsfDir) - const compileVjsf = (await import('@koumoul/vjsf-compiler')).compile + const { compileParts } = await import('@koumoul/vjsf-compiler') const { resolveXI18n } = await import('@json-layout/core') for (const locale of vjsfLocales) { @@ -301,7 +304,8 @@ export declare function returnValid(data: any, options?: import('${validationImp if (schema.$id) delete otherSchemas[schema.$id] schemaVjsfOpts.ajvOptions = { schemas: otherSchemas } - const vjsfCode = await compileVjsf(schema, { locale, ...schemaVjsfOpts }) + const { code: vjsfCode, compiledLayoutCode } = await compileParts(schema, { locale, ...schemaVjsfOpts }) + builtVjsfLayoutCodes[locale] = compiledLayoutCode const vjsfFilePath = path.join(vjsfDir, vjsfLocales.length > 1 ? `vjsf-${compName}-${locale}.vue` : `vjsf-${compName}.vue`) console.log(` vjsf ${locale} component path : ${vjsfFilePath}`) writeFileSync(vjsfFilePath, vjsfCode) @@ -359,27 +363,35 @@ const emit = defineEmits(emits) const { resolveXI18n } = await import('@json-layout/core') for (const locale of vjsfLocales) { - const schemaVjsfOpts = { ...schema['x-vjsf'] } - delete schemaVjsfOpts.compName - console.log(` compiledLayout ${locale} options: ${JSON.stringify(schemaVjsfOpts)}`) - const otherSchemas = { ...schemas } - for (const [key, otherSchema] of Object.entries(schemas)) { - if (key === schema.$id) continue - otherSchemas[key] = clone(otherSchema) - resolveXI18n(otherSchemas[key], locale) - } - if (schema.$id) delete otherSchemas[schema.$id] - schemaVjsfOpts.ajvOptions = { schemas: otherSchemas } - - const fullOptions = { pluginsImports: [] as string[], webmcp: false, locale, ...schemaVjsfOpts } - for (const pluginImport of fullOptions.pluginsImports) { - const componentInfo = (await import(pluginImport + '/info.js')).default - fullOptions.components = fullOptions.components ?? {} - fullOptions.components[componentInfo.name] = componentInfo - } + // reuse the layout already compiled+serialized by the vjsf export when present + // (the vjsf compiler embeds the very same serialized compiled layout), otherwise + // compile it now + let compiledLayoutCode = builtVjsfLayoutCodes[locale] + if (compiledLayoutCode) { + console.log(` compiledLayout ${locale}: reusing the layout compiled for the vjsf export`) + } else { + const schemaVjsfOpts = { ...schema['x-vjsf'] } + delete schemaVjsfOpts.compName + console.log(` compiledLayout ${locale} options: ${JSON.stringify(schemaVjsfOpts)}`) + const otherSchemas = { ...schemas } + for (const [key, otherSchema] of Object.entries(schemas)) { + if (key === schema.$id) continue + otherSchemas[key] = clone(otherSchema) + resolveXI18n(otherSchemas[key], locale) + } + if (schema.$id) delete otherSchemas[schema.$id] + schemaVjsfOpts.ajvOptions = { schemas: otherSchemas } - const compiledLayout = compileLayout(schema, fullOptions) - let compiledLayoutCode = await serializeCompiledLayout(compiledLayout) + const fullOptions = { pluginsImports: [] as string[], webmcp: false, locale, ...schemaVjsfOpts } + for (const pluginImport of fullOptions.pluginsImports) { + const componentInfo = (await import(pluginImport + '/info.js')).default + fullOptions.components = fullOptions.components ?? {} + fullOptions.components[componentInfo.name] = componentInfo + } + + const compiledLayout = compileLayout(schema, fullOptions) + compiledLayoutCode = await serializeCompiledLayout(compiledLayout) + } // The serialized code declares `const compiledLayout = {...}`. // Make it an export. compiledLayoutCode = compiledLayoutCode.replace('const compiledLayout =', 'export const compiledLayout =') diff --git a/packages/types-builder/package.json b/packages/types-builder/package.json index 54e44be..2c2102d 100644 --- a/packages/types-builder/package.json +++ b/packages/types-builder/package.json @@ -9,7 +9,7 @@ "build": "cd .. && npm run build" }, "peerDependencies": { - "@koumoul/vjsf-compiler": "1" + "@koumoul/vjsf-compiler": "^1.5.0" }, "peerDependenciesMeta": { "@koumoul/vjsf-compiler": {