From ea6f91b3c4cb30d315be6a246cb91049d686d73a Mon Sep 17 00:00:00 2001 From: Andrea Carraro Date: Sat, 9 Sep 2023 12:50:37 +0200 Subject: [PATCH] feat: expose metaData return prop --- .changeset/little-rats-join.md | 5 +++ README.md | 33 +++++++++++++++++++ src/openapiToTsJsonSchema.ts | 21 ++++++------ ...hemaMetaInfo.ts => addSchemaToMetaData.ts} | 18 +++++----- src/utils/index.ts | 6 ++-- src/utils/jsonSchemaToTsConst/index.ts | 14 ++++---- .../replacePlaceholdersWithImportedSchemas.ts | 10 +++--- src/utils/makeJsonSchemaFiles.ts | 14 ++++---- src/utils/types.ts | 6 ++-- test/metaData.test.ts | 31 +++++++++++++++++ 10 files changed, 114 insertions(+), 44 deletions(-) create mode 100644 .changeset/little-rats-join.md rename src/utils/{addSchemaMetaInfo.ts => addSchemaToMetaData.ts} (81%) create mode 100644 test/metaData.test.ts diff --git a/.changeset/little-rats-join.md b/.changeset/little-rats-join.md new file mode 100644 index 0000000..207b595 --- /dev/null +++ b/.changeset/little-rats-join.md @@ -0,0 +1,5 @@ +--- +'openapi-ts-json-schema': minor +--- + +Expose `metaData` return property holding generated schemas meta data diff --git a/README.md b/README.md index 54a2bbf..966fdec 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,39 @@ Circular `$ref`s can be technically resolved with `experimentalImportRefs` optio Take a look at the [Developer's notes](./docs/developer-notes.md) for a few more in-depth explanations. +## Return values + +Beside generating the expected schema files under `outputPath`, `openapiToTsJsonSchema` returns the following data: + +```ts +{ + // The path where the schemas are generated + outputPath: string; + metaData: { + // Meta data of the generated schemas + schemas: Map< + string, + { + // Valid filename for given schema (without extension). + schemaFileName: string; + // Absolute path pointing to schema folder + schemaAbsoluteDirName: string; + // Absolute path pointing to schema file + schemaAbsolutePath: string; + // Absolute import path (without extension) + schemaAbsoluteImportPath: string; + // Unique JavaScript identifier used as import name + schemaUniqueName: string; + // The actual JSON schema + schema: JSONSchema; + // True is schemas is used as a `$ref` + isRef: boolean; + } + >; + } +} +``` + ## Todo - Consider merging "operation" and "path" parameters definition diff --git a/src/openapiToTsJsonSchema.ts b/src/openapiToTsJsonSchema.ts index 0b4be0a..ad542e8 100644 --- a/src/openapiToTsJsonSchema.ts +++ b/src/openapiToTsJsonSchema.ts @@ -11,9 +11,9 @@ import { SchemaPatcher, convertOpenApiToJsonSchema, convertOpenApiParameters, - SchemaMetaInfoMap, + SchemaMetaDataMap, JSONSchema, - addSchemaMetaInfo, + addSchemaToMetaData, pathToRef, } from './utils'; @@ -31,7 +31,7 @@ export async function openapiToTsJsonSchema({ outputPath?: string; silent?: boolean; experimentalImportRefs?: boolean; -}) { +}): Promise<{ outputPath: string; metaData: { schemas: SchemaMetaDataMap } }> { if (definitionPathsToGenerateFrom.length === 0 && !silent) { console.log( `[openapi-ts-json-schema] ⚠️ No schemas will be generated since definitionPathsToGenerateFrom option is empty`, @@ -97,14 +97,14 @@ export async function openapiToTsJsonSchema({ ); const jsonSchema = convertOpenApiParameters(dereferencedJsonSchema); - const schemas: SchemaMetaInfoMap = new Map(); + const schemaMetaDataMap: SchemaMetaDataMap = new Map(); // Generate schema meta info for inlined refs, first if (experimentalImportRefs) { for (const [ref, schema] of inlinedRefs) { - addSchemaMetaInfo({ + addSchemaToMetaData({ id: ref, - schemas, + schemaMetaDataMap, schema, outputPath, schemaPatcher, @@ -124,9 +124,9 @@ export async function openapiToTsJsonSchema({ schemaName, }); - addSchemaMetaInfo({ + addSchemaToMetaData({ id, - schemas, + schemaMetaDataMap, schema: definitionSchemas[schemaName], outputPath, schemaPatcher, @@ -137,7 +137,7 @@ export async function openapiToTsJsonSchema({ } await makeJsonSchemaFiles({ - schemas, + schemaMetaDataMap, }); if (!silent) { @@ -145,5 +145,6 @@ export async function openapiToTsJsonSchema({ `[openapi-ts-json-schema] ✅ JSON schema models generated at ${outputPath}`, ); } - return { outputPath }; + + return { outputPath, metaData: { schemas: schemaMetaDataMap } }; } diff --git a/src/utils/addSchemaMetaInfo.ts b/src/utils/addSchemaToMetaData.ts similarity index 81% rename from src/utils/addSchemaMetaInfo.ts rename to src/utils/addSchemaToMetaData.ts index 8a3633c..bc620e6 100644 --- a/src/utils/addSchemaMetaInfo.ts +++ b/src/utils/addSchemaToMetaData.ts @@ -3,8 +3,8 @@ import path from 'path'; import namify from 'namify'; import filenamify from 'filenamify'; import { - SchemaMetaInfoMap, - SchemaMetaInfo, + SchemaMetaDataMap, + SchemaMetaData, JSONSchema, replaceInlinedRefsWithStringPlaceholder, patchJsonSchema, @@ -13,11 +13,11 @@ import { } from '.'; /* - * Just an utility function to add entries to SchemaMetaInfoMap Map keyed by ref + * Just an utility function to add entries to SchemaMetaDataMap Map keyed by ref */ -export function addSchemaMetaInfo({ +export function addSchemaToMetaData({ id, - schemas, + schemaMetaDataMap, schema, isRef, // Options @@ -26,7 +26,7 @@ export function addSchemaMetaInfo({ experimentalImportRefs, }: { id: string; - schemas: SchemaMetaInfoMap; + schemaMetaDataMap: SchemaMetaDataMap; schema: JSONSchema; isRef: boolean; outputPath: string; @@ -34,7 +34,7 @@ export function addSchemaMetaInfo({ experimentalImportRefs: boolean; }): void { // Do not override existing meta info of inlined schemas - if (schemas.has(id) === false) { + if (schemaMetaDataMap.has(id) === false) { const { schemaRelativeDirName, schemaName } = refToPath(id); const schemaRelativePath = path.join(schemaRelativeDirName, schemaName); const originalSchema = experimentalImportRefs @@ -49,7 +49,7 @@ export function addSchemaMetaInfo({ schemaFileName, ); - const metaInfo: SchemaMetaInfo = { + const metaInfo: SchemaMetaData = { schemaFileName, schemaAbsoluteDirName, schemaAbsoluteImportPath, @@ -58,6 +58,6 @@ export function addSchemaMetaInfo({ schema: patchedSchema, isRef, }; - schemas.set(id, metaInfo); + schemaMetaDataMap.set(id, metaInfo); } } diff --git a/src/utils/index.ts b/src/utils/index.ts index fb09c78..38a7783 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -17,8 +17,8 @@ export type { JSONSchema, OpenApiSchema, SchemaPatcher, - SchemaMetaInfo, - SchemaMetaInfoMap, + SchemaMetaData, + SchemaMetaDataMap, } from './types'; -export { addSchemaMetaInfo } from './addSchemaMetaInfo'; +export { addSchemaToMetaData } from './addSchemaToMetaData'; export { makeRelativePath } from './makeRelativePath'; diff --git a/src/utils/jsonSchemaToTsConst/index.ts b/src/utils/jsonSchemaToTsConst/index.ts index 9384783..240f298 100644 --- a/src/utils/jsonSchemaToTsConst/index.ts +++ b/src/utils/jsonSchemaToTsConst/index.ts @@ -1,16 +1,16 @@ import prettier from 'prettier'; import { stringify } from 'comment-json'; import { replacePlaceholdersWithImportedSchemas } from './replacePlaceholdersWithImportedSchemas'; -import type { SchemaMetaInfoMap, SchemaMetaInfo } from '../'; +import type { SchemaMetaDataMap, SchemaMetaData } from '../'; export async function jsonSchemaToTsConst({ - schemaMetaInfo, - schemas, + metaData, + schemaMetaDataMap, }: { - schemaMetaInfo: SchemaMetaInfo; - schemas: SchemaMetaInfoMap; + metaData: SchemaMetaData; + schemaMetaDataMap: SchemaMetaDataMap; }): Promise { - const { schema, schemaAbsoluteDirName } = schemaMetaInfo; + const { schema, schemaAbsoluteDirName } = metaData; // Stringify schema with "node-comment-json" to generate inline comments const stringifiedSchema = stringify(schema, null, 2); @@ -20,7 +20,7 @@ export async function jsonSchemaToTsConst({ tsSchema = replacePlaceholdersWithImportedSchemas({ schemaAsText: tsSchema, schemaAbsoluteDirName, - schemas, + schemaMetaDataMap, }); const formattedSchema = await prettier.format(tsSchema, { diff --git a/src/utils/jsonSchemaToTsConst/replacePlaceholdersWithImportedSchemas.ts b/src/utils/jsonSchemaToTsConst/replacePlaceholdersWithImportedSchemas.ts index 3ebc53a..e060161 100644 --- a/src/utils/jsonSchemaToTsConst/replacePlaceholdersWithImportedSchemas.ts +++ b/src/utils/jsonSchemaToTsConst/replacePlaceholdersWithImportedSchemas.ts @@ -1,4 +1,4 @@ -import { SchemaMetaInfoMap, makeRelativePath, PLACEHOLDER_REGEX } from '..'; +import { SchemaMetaDataMap, makeRelativePath, PLACEHOLDER_REGEX } from '..'; /** * Replace Refs placeholders with imported schemas @@ -6,22 +6,22 @@ import { SchemaMetaInfoMap, makeRelativePath, PLACEHOLDER_REGEX } from '..'; export function replacePlaceholdersWithImportedSchemas({ schemaAsText, schemaAbsoluteDirName, - schemas, + schemaMetaDataMap, }: { schemaAsText: string; schemaAbsoluteDirName: string; - schemas: SchemaMetaInfoMap; + schemaMetaDataMap: SchemaMetaDataMap; }): string { const importStatements = new Set(); // Replace placeholder occurrences with the relevant imported schema name let schema = schemaAsText.replaceAll(PLACEHOLDER_REGEX, (_match, ref) => { - const importedSchema = schemas.get(ref); + const importedSchema = schemaMetaDataMap.get(ref); /* istanbul ignore if: It should not be possible to hit this condition -- @preserve */ if (!importedSchema) { throw new Error( - '[openapi-ts-json-schema] No matching schema found in "schemas"', + '[openapi-ts-json-schema] No matching schema found in "schemaMetaDataMap"', ); } diff --git a/src/utils/makeJsonSchemaFiles.ts b/src/utils/makeJsonSchemaFiles.ts index b68b427..5a17663 100644 --- a/src/utils/makeJsonSchemaFiles.ts +++ b/src/utils/makeJsonSchemaFiles.ts @@ -1,21 +1,21 @@ import fs from 'fs/promises'; -import { jsonSchemaToTsConst, SchemaMetaInfoMap } from '.'; +import { jsonSchemaToTsConst, SchemaMetaDataMap } from '.'; /** * Save TS JSON schema with the expected naming conventions */ export async function makeJsonSchemaFiles({ - schemas, + schemaMetaDataMap, }: { - schemas: SchemaMetaInfoMap; + schemaMetaDataMap: SchemaMetaDataMap; }) { - for (const [_, schemaMetaInfo] of schemas) { + for (const [_, metaData] of schemaMetaDataMap) { const tsSchema = await jsonSchemaToTsConst({ - schemaMetaInfo, - schemas, + metaData, + schemaMetaDataMap, }); - const { schemaAbsoluteDirName, schemaAbsolutePath } = schemaMetaInfo; + const { schemaAbsoluteDirName, schemaAbsolutePath } = metaData; await fs.mkdir(schemaAbsoluteDirName, { recursive: true }); await fs.writeFile(schemaAbsolutePath, tsSchema); } diff --git a/src/utils/types.ts b/src/utils/types.ts index 6e760b6..8aa469e 100644 --- a/src/utils/types.ts +++ b/src/utils/types.ts @@ -12,7 +12,7 @@ export type SchemaPatcher = (params: { schema: JSONSchema }) => void; * @prop `schema` - The actual JSON schema * @prop `isRef` - Mark schemas used as `$ref` */ -export type SchemaMetaInfo = { +export type SchemaMetaData = { schemaFileName: string; schemaAbsoluteDirName: string; schemaAbsolutePath: string; @@ -21,7 +21,7 @@ export type SchemaMetaInfo = { schema: JSONSchema; isRef: boolean; }; -export type SchemaMetaInfoMap = Map< +export type SchemaMetaDataMap = Map< string, // Schema file relative path - SchemaMetaInfo + SchemaMetaData >; diff --git a/test/metaData.test.ts b/test/metaData.test.ts new file mode 100644 index 0000000..3f89b20 --- /dev/null +++ b/test/metaData.test.ts @@ -0,0 +1,31 @@ +import path from 'path'; +import fs from 'fs'; +import { describe, it, expect } from 'vitest'; +import { openapiToTsJsonSchema } from '../src'; + +const fixtures = path.resolve(__dirname, 'fixtures'); + +describe('Returned "metaData"', async () => { + it('returns expected data', async () => { + const { metaData } = await openapiToTsJsonSchema({ + openApiSchema: path.resolve(fixtures, 'mini-referenced/specs.yaml'), + definitionPathsToGenerateFrom: ['components.months'], + experimentalImportRefs: true, + silent: false, + }); + + const answerMeta = metaData.schemas.get('#/components/schemas/Answer'); + const januaryMeta = metaData.schemas.get('#/components/months/January'); + + expect(answerMeta).toBeDefined(); + expect(januaryMeta).toBeDefined(); + expect(metaData.schemas.size).toBe(2); + + if (!answerMeta || !januaryMeta) { + throw 'Unexpected undefined meta data'; + } + + expect(fs.existsSync(answerMeta.schemaAbsolutePath)).toBe(true); + expect(fs.existsSync(januaryMeta.schemaAbsolutePath)).toBe(true); + }); +});