diff --git a/scripts/cache.ts b/scripts/cache.ts index e0a6d7d4262..84d08bff704 100644 --- a/scripts/cache.ts +++ b/scripts/cache.ts @@ -7,23 +7,23 @@ import { exists } from './common.ts'; export class Cache { folder: string; generatedFiles: string[]; - filesToCache: string[]; + dependencies: string[]; cacheFile: string; constructor({ folder, generatedFiles, - filesToCache, + dependencies, cacheFile, }: { folder: string; generatedFiles: string[]; - filesToCache: string[]; + dependencies: string[]; cacheFile: string; }) { this.folder = folder; this.generatedFiles = generatedFiles; - this.filesToCache = filesToCache; + this.dependencies = dependencies; this.cacheFile = cacheFile; } @@ -34,14 +34,14 @@ export class Cache { hash += (await hashElement(`${this.folder}/${generatedFile}`)).hash; } - for (const fileToCache of this.filesToCache) { - hash += (await hashElement(`${this.folder}/${fileToCache}`)).hash; + for (const dependency of this.dependencies) { + hash += (await hashElement(`${this.folder}/${dependency}`)).hash; } return hash; } - async isValid(): Promise { + async hit(): Promise { if (!(await exists(this.cacheFile))) { return false; } diff --git a/scripts/common.ts b/scripts/common.ts index 91d5e751891..647b4b05d39 100644 --- a/scripts/common.ts +++ b/scripts/common.ts @@ -33,6 +33,7 @@ export const ROOT_DIR = path.resolve(process.cwd(), '..'); export const GENERATORS = Object.entries(clientsConfig).reduce( (current, [language, opts]) => { if (typeof opts === 'string') { + // skip $schema return current; } @@ -51,7 +52,6 @@ export const GENERATORS = Object.entries(clientsConfig).reduce( } current[key] = { - additionalProperties: {}, ...opts, output, client: clientName, @@ -61,7 +61,7 @@ export const GENERATORS = Object.entries(clientsConfig).reduce( // guess the package name for js from the output folder variable if (language === 'javascript') { - current[key].additionalProperties.packageName = output.substring(output.lastIndexOf('/') + 1); + current[key].packageName = output.substring(output.lastIndexOf('/') + 1); } } @@ -153,11 +153,11 @@ async function buildCustomGenerators(): Promise { const cache = new Cache({ folder: toAbsolutePath('generators/'), generatedFiles: ['build/classes'], - filesToCache: ['src', 'build.gradle', 'settings.gradle', '../config/.java-version'], + dependencies: ['src', 'build.gradle', 'settings.gradle', '../config/.java-version'], cacheFile: toAbsolutePath('generators/.cache'), }); - const cacheExists = await cache.isValid(); + const cacheExists = await cache.hit(); if (cacheExists) { spinner.succeed('job skipped, cache found for custom generators'); diff --git a/scripts/config.ts b/scripts/config.ts index 6d87518f70f..e8d704c3bb9 100644 --- a/scripts/config.ts +++ b/scripts/config.ts @@ -1,7 +1,7 @@ import clientsConfig from '../config/clients.config.json' with { type: 'json' }; -import { CI } from './common.ts'; -import type { Language, LanguageConfig } from './types.ts'; +import { CI, createClientName } from './common.ts'; +import type { Generator, Language, LanguageConfig } from './types.ts'; export function getClientsConfigField( language: Language, @@ -45,6 +45,10 @@ export function getTestOutputFolder(language: Language): string { return getClientsConfigField(language, ['tests', 'outputFolder']); } +export function getSnippetFile(gen: Generator): string { + return `docs/snippets/${gen.language}/${gen.snippets.outputFolder}/${createClientName(gen.client, gen.language)}${gen.snippets.extension}`; +} + export function getDockerImage(language?: Language): string | undefined { if (CI || !language) { return undefined; diff --git a/scripts/release/dart.ts b/scripts/release/dart.ts index 204da58d904..58ced0a2e57 100644 --- a/scripts/release/dart.ts +++ b/scripts/release/dart.ts @@ -18,7 +18,7 @@ export async function updateDartPackages(changelog: string, nextVersion: string) } if (!nextVersion) { - throw new Error(`Failed to bump '${gen.packageName}'.`); + throw new Error(`Failed to bump '${gen.client}'.`); } let currentVersion = await getPubspecField(gen.output, 'version'); diff --git a/scripts/specs/format.ts b/scripts/specs/format.ts index 5d33c045492..83f2ae4bec9 100644 --- a/scripts/specs/format.ts +++ b/scripts/specs/format.ts @@ -18,11 +18,11 @@ export async function lintCommon(useCache: boolean): Promise { const cache = new Cache({ folder: toAbsolutePath('specs/'), generatedFiles: [], - filesToCache: ['common'], + dependencies: ['common'], cacheFile: toAbsolutePath('specs/dist/common.cache'), }); - if (useCache && (await cache.isValid())) { + if (useCache && (await cache.hit())) { spinner.succeed("job skipped, cache found for 'common' spec"); return; } diff --git a/scripts/specs/index.ts b/scripts/specs/index.ts index f4f38f3a9a2..85869f9547a 100644 --- a/scripts/specs/index.ts +++ b/scripts/specs/index.ts @@ -3,10 +3,11 @@ import fsp from 'fs/promises'; import yaml from 'js-yaml'; import { Cache } from '../cache.ts'; -import { exists, run, toAbsolutePath } from '../common.ts'; +import { exists, GENERATORS, run, toAbsolutePath } from '../common.ts'; import { createSpinner } from '../spinners.ts'; import type { Spec } from '../types.ts'; +import { getSnippetFile } from '../config.ts'; import { bundleSpecsForClient, bundleSpecsForDoc, lintCommon } from './format.ts'; import type { BaseBuildSpecsOptions } from './types.ts'; @@ -73,26 +74,39 @@ async function buildSpec({ // In case of lite we use a the `search` spec as a base because only its bundled form exists. const specBase = isLiteSpec ? 'search' : spec; const logSuffix = `${outputFormat} ${docs ? 'doc spec' : 'spec'}`; - const basePath = docs ? 'docs/' : 'specs/'; - const deps = isLiteSpec ? ['search', 'recommend'] : [spec]; + const basePath = docs ? 'docs' : 'specs'; + const deps = isLiteSpec ? ['specs/search', 'specs/recommend'] : [`specs/${spec}`]; + const generatedFile = `${basePath}/bundled/${spec}.${outputFormat}`; + + const generatedFiles = [generatedFile]; + if (docs && outputFormat === 'yml') { + for (const gen of Object.values(GENERATORS)) { + if (gen.client === spec) { + deps.push(getSnippetFile(gen)); + } + } + + generatedFiles.push(`docs/bundled/${spec}-snippets.json`); + } + const cache = new Cache({ - folder: toAbsolutePath('specs/'), - generatedFiles: [`bundled/${spec}.yml`], - filesToCache: [...deps, 'common'], - cacheFile: toAbsolutePath(`specs/dist/${spec}.cache`), + folder: toAbsolutePath('.'), + generatedFiles, + dependencies: [...deps, 'specs/common'], + cacheFile: toAbsolutePath(`specs/dist/${spec}-${basePath}-${outputFormat}.cache`), }); const spinner = createSpinner(`starting '${spec}' ${logSuffix}`); if (useCache) { - spinner.text = `checking cache for '${specBase}'`; + spinner.text = `checking cache for '${specBase}' ${logSuffix}`; - if (await cache.isValid()) { - spinner.succeed(`job skipped, cache found for '${specBase}'`); + if (await cache.hit()) { + spinner.succeed(`job skipped, cache found for '${specBase}' ${logSuffix}`); return; } - spinner.text = `cache not found for '${specBase}'`; + spinner.text = `cache not found for '${specBase}' ${logSuffix}`; } // First linting the base @@ -100,7 +114,7 @@ async function buildSpec({ await run(`yarn specs:fix specs/${specBase}`); // Then bundle the file - const bundledPath = toAbsolutePath(`${basePath}/bundled/${spec}.${outputFormat}`); + const bundledPath = toAbsolutePath(generatedFile); await run(`yarn redocly bundle specs/${specBase}/spec.yml -o ${bundledPath} --ext ${outputFormat} `); if (!(await exists(bundledPath))) { @@ -108,9 +122,7 @@ async function buildSpec({ } // Add the correct tags to be able to generate the proper client - if (!isLiteSpec) { - docs ? await bundleSpecsForDoc(bundledPath, spec) : await bundleSpecsForClient(bundledPath, spec); - } else { + if (isLiteSpec) { await buildLiteSpec({ spec, bundledPath: toAbsolutePath(bundledPath), @@ -118,6 +130,8 @@ async function buildSpec({ docs, useCache, }); + } else if (outputFormat === 'yml') { + docs ? await bundleSpecsForDoc(bundledPath, spec) : await bundleSpecsForClient(bundledPath, spec); } spinner.text = `validating '${spec}' ${logSuffix}`; diff --git a/scripts/specs/snippets.ts b/scripts/specs/snippets.ts index 53b49469969..e5362443cc2 100644 --- a/scripts/specs/snippets.ts +++ b/scripts/specs/snippets.ts @@ -1,8 +1,9 @@ import fsp from 'fs/promises'; -import { GENERATORS, capitalize, createClientName, exists, toAbsolutePath } from '../common.ts'; +import { GENERATORS, capitalize, exists, toAbsolutePath } from '../common.ts'; import type { Language } from '../types.ts'; +import { getSnippetFile } from '../config.ts'; import type { CodeSamples, OpenAPICodeSample, SampleForOperation } from './types.ts'; export function getCodeSampleLabel(language: Language): OpenAPICodeSample['label'] { @@ -63,16 +64,14 @@ export async function transformGeneratedSnippetsToCodeSamples(clientName: string continue; } - const ppath = toAbsolutePath( - `docs/snippets/${gen.language}/${gen.snippets.outputFolder}/${createClientName(clientName, gen.language)}${gen.snippets.extension}`, - ); + const snippetPath = toAbsolutePath(getSnippetFile(gen)); - if (!(await exists(ppath))) { + if (!(await exists(snippetPath))) { continue; } // find snippets for each operationId in the current gen.language + clientName combo - const snippetFileContent = await fsp.readFile(ppath, 'utf8'); + const snippetFileContent = await fsp.readFile(snippetPath, 'utf8'); const importMatch = snippetFileContent.match(/>IMPORT\n([\s\S]*?)\n.*IMPORT & - Record; - -export type Generator = Record & { +export type Generator = { language: Language; client: string; key: string; output: string; - additionalProperties: AdditionalProperties; + snippets: GeneratedFile; + packageName?: string; }; export type GeneratorMode = 'client' | 'guides' | 'snippets' | 'tests';