openapi-ts-json-schema
Version:
OpenAPI to JSON schema generator with TypeScript in mind
147 lines (146 loc) • 6.36 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.openapiToTsJsonSchema = void 0;
const fs_1 = require("fs");
const node_path_1 = __importDefault(require("node:path"));
const json_schema_ref_parser_1 = __importDefault(require("@apidevtools/json-schema-ref-parser"));
const lodash_get_1 = __importDefault(require("lodash.get"));
const utils_1 = require("./utils");
async function openapiToTsJsonSchema(options) {
const { plugins = [] } = options;
// Execute plugins onInit method
for (const { onInit } of plugins) {
if (onInit) {
await onInit({
options,
});
}
}
const { openApiSchema: openApiSchemaRelative, definitionPathsToGenerateFrom, schemaPatcher, outputPath: providedOutputPath, silent, refHandling = 'import', $idMapper = ({ id }) => id, } = options;
if (definitionPathsToGenerateFrom.length === 0 && !silent) {
console.log(`[openapi-ts-json-schema] ⚠️ No schemas will be generated since definitionPathsToGenerateFrom option is empty`);
}
definitionPathsToGenerateFrom.forEach((defPath) => {
if (node_path_1.default.isAbsolute(defPath)) {
throw new Error(`[openapi-ts-json-schema] "definitionPathsToGenerateFrom" must be an array of relative paths. "${defPath}" found.`);
}
});
const openApiSchemaPath = node_path_1.default.resolve(openApiSchemaRelative);
if (!(0, fs_1.existsSync)(openApiSchemaPath)) {
throw new Error(`[openapi-ts-json-schema] Provided OpenAPI definition path doesn't exist: ${openApiSchemaPath}`);
}
const outputPath = providedOutputPath ??
node_path_1.default.resolve(node_path_1.default.dirname(openApiSchemaPath), 'schemas-autogenerated');
await (0, utils_1.clearFolder)(outputPath);
const openApiParser = new json_schema_ref_parser_1.default();
const jsonSchemaParser = new json_schema_ref_parser_1.default();
// Resolve and inline external $ref definitions
const bundledOpenApiSchema = await openApiParser.bundle(openApiSchemaPath);
// Convert oas definitions to JSON schema
const initialJsonSchema = (0, utils_1.convertOpenApiToJsonSchema)(bundledOpenApiSchema);
const inlinedRefs = new Map();
// Inline and collect internal $ref definitions
const dereferencedJsonSchema = await jsonSchemaParser.dereference(initialJsonSchema, {
dereference: {
// @ts-expect-error onDereference seems not to be properly typed
onDereference: (ref, inlinedSchema) => {
const id = (0, utils_1.refToId)(ref);
// Keep track of inlined refs
if (!inlinedRefs.has(id)) {
// Shallow copy the ref schema to avoid the mutations below
inlinedRefs.set(id, {
// @ts-expect-error Spread types may only be created from object types
...jsonSchemaParser.$refs.get(ref),
});
}
/**
* mark inlined ref objects with a "SCHEMA_ID_SYMBOL"
* to retrieve their id once inlined
*/
inlinedSchema[utils_1.SCHEMA_ID_SYMBOL] = id;
/**
* "inline" refHandling support:
* add a $ref comment to each inlined schema with the original ref value.
* See: https://github.com/kaelzhang/node-comment-json
*/
if (refHandling === 'inline') {
inlinedSchema[Symbol.for('before')] = [
{
type: 'LineComment',
value: ` $ref: "${ref}"`,
},
];
}
},
},
});
const jsonSchema = (0, utils_1.convertOpenApiPathsParameters)(dereferencedJsonSchema);
const schemaMetaDataMap = new Map();
/**
* Create meta data for $ref schemas which have been previously dereferenced.
* It happens only with "import" and "keep" refHandling since they expect
* $ref schemas to be generated no matter of
*/
if (refHandling === 'import' || refHandling === 'keep') {
for (const [id, schema] of inlinedRefs) {
(0, utils_1.addSchemaToMetaData)({
id,
$id: $idMapper({ id }),
schemaMetaDataMap,
schema,
outputPath,
isRef: true,
});
}
}
/**
* Create meta data for each output schema
*/
for (const definitionPath of definitionPathsToGenerateFrom) {
const definitionSchemas = (0, lodash_get_1.default)(jsonSchema, definitionPath);
for (const schemaName in definitionSchemas) {
// Create expected OpenAPI ref
const id = (0, utils_1.makeId)({
schemaRelativeDirName: definitionPath,
schemaName,
});
(0, utils_1.addSchemaToMetaData)({
id,
$id: $idMapper({ id }),
schemaMetaDataMap,
schema: definitionSchemas[schemaName],
outputPath,
isRef: inlinedRefs.has(id),
});
}
}
const returnPayload = {
outputPath,
metaData: { schemas: schemaMetaDataMap },
};
// Execute plugins onBeforeGeneration method
for (const { onBeforeGeneration } of plugins) {
if (onBeforeGeneration) {
await onBeforeGeneration({
...returnPayload,
options,
utils: { makeRelativeModulePath: utils_1.makeRelativeModulePath, formatTypeScript: utils_1.formatTypeScript, saveFile: utils_1.saveFile },
});
}
}
// Generate schemas
await (0, utils_1.makeTsJsonSchemaFiles)({
refHandling,
schemaMetaDataMap,
schemaPatcher,
$idMapper,
});
if (!silent) {
console.log(`[openapi-ts-json-schema] ✅ JSON schema models generated at ${outputPath}`);
}
return returnPayload;
}
exports.openapiToTsJsonSchema = openapiToTsJsonSchema;