json-schema-typescript-generator
Version:
Generate typescript types from json schemas
825 lines (795 loc) • 27.9 kB
JavaScript
// src/generate/file-generator.ts
import * as path from "path";
// src/util/filter.ts
var isDefined = (_) => _ !== void 0;
var filtered = (values) => values.filter(isDefined);
var filteredJoin = (values, joiner) => values.filter(isDefined).join(joiner ? joiner : "");
// src/generate/OneOfN-generator.ts
var SuppressNGenerator = (suppressCount) => {
const suppressTypeArgs = [];
for (let i = 0; i < suppressCount; i++) {
suppressTypeArgs.push(`S${i}`);
}
const suppressType = `Suppress_${suppressCount}<T, ${suppressTypeArgs.join(", ")}>`;
const excludeType = `Exclude<(keyof ${suppressTypeArgs.join(" | keyof ")}), keyof T>`;
return `type ${suppressType} = T & { [P in ${excludeType}]?: never };`;
};
var OneOfNGenerator = (typeCount) => {
if (!Number.isInteger(typeCount) || typeCount < 2) {
return;
}
const typeArgs = [];
for (let i = 0; i < typeCount; i++) {
typeArgs.push(`T${i}`);
}
const pipeSepTypes = typeArgs.join(" | ");
const firstLine = `type OneOf_${typeCount}<${typeArgs.join(", ")}> = (${pipeSepTypes}) extends object`;
const middleLines = [];
const suppressCount = typeCount - 1;
for (let i = 0; i < typeCount; i++) {
const temp = typeArgs[i];
typeArgs[i] = typeArgs[0];
typeArgs[0] = temp;
middleLines.push(`Suppress_${suppressCount}<${typeArgs.join(", ")}>`);
}
const middle = `? ${middleLines.join("\n| ")}`;
const lastLine = `: ${pipeSepTypes};`;
const suppressType = SuppressNGenerator(suppressCount);
return [firstLine, middle, lastLine, suppressType].join("\n");
};
// src/generate/name-generator.ts
var nameGenerator = (name) => {
const usableName = name.replace(/[^a-zA-Z0-9_]/g, "");
return usableName.match(/^[a-zA-Z_]/) ? usableName : "_" + usableName;
};
// src/generate/TypeGenerator.ts
var located = (schema, located2) => ({
fileLocation: located2.fileLocation,
schema
});
// src/generate/all-of-generator.ts
var allOfGenerator = (locatedSchema, gatheredInfo, inputInfo) => {
const schema = locatedSchema.schema;
if (!schema.allOf || schema.allOf.length === 0) {
return;
}
const lines = [];
schema.allOf.forEach((elementSchema) => {
const elementLocatedSchema = located(elementSchema, locatedSchema);
const elementContent = typeGenerator(elementLocatedSchema, gatheredInfo, inputInfo);
lines.push(elementContent);
});
const filteredLines = filtered(lines);
if (filteredLines.length === 0) {
return;
}
return filteredLines.join("\n& ");
};
// src/generate/any-of-generator.ts
var anyOfGenerator = (locatedSchema, gatheredInfo, inputInfo) => {
const schema = locatedSchema.schema;
if (!schema.anyOf || schema.anyOf.length === 0) {
return;
}
const lines = [];
schema.anyOf.forEach((elementSchema) => {
const elementLocatedSchema = located(elementSchema, locatedSchema);
const elementContent = typeGenerator(elementLocatedSchema, gatheredInfo, inputInfo);
lines.push(elementContent);
});
const filteredLines = filtered(lines);
if (filteredLines.length === 0) {
return;
} else if (filteredLines.length === 1) {
return filteredLines[0];
} else {
return "(" + filteredLines.join("\n| ") + ")";
}
};
// src/generate/basic-type-generator.ts
var PRIMITIVE_TYPES = /* @__PURE__ */ new Map();
PRIMITIVE_TYPES.set("null", "null");
PRIMITIVE_TYPES.set("boolean", "boolean");
PRIMITIVE_TYPES.set("integer", "number");
PRIMITIVE_TYPES.set("number", "number");
PRIMITIVE_TYPES.set("string", "string");
var basicTypeGenerator = (locatedSchema, _gatheredInfo, _inputInfo) => {
const schemaTypes = locatedSchema.schema.type;
if (!schemaTypes || schemaTypes.size === 0) {
return;
}
const tsTypesSet = /* @__PURE__ */ new Set();
Array.from(PRIMITIVE_TYPES.entries()).filter(([schemaType, _]) => schemaTypes.has(schemaType)).map(([_, tsType]) => tsType).forEach((tsType) => tsTypesSet.add(tsType));
const tsTypes = Array.from(tsTypesSet);
if (tsTypes.length === 0) {
return;
}
if (tsTypes.length === 1) {
return tsTypes[0];
}
return `(${tsTypes.join(" | ")})`;
};
// src/generate/collection-generator.ts
var collectionGenerator = (locatedSchema, gatheredInfo, inputInfo) => {
const schema = locatedSchema.schema;
if (!schema.type || !schema.type.has("array") || !schema.collection) {
return;
}
const collection = schema.collection;
const collectionItems = collection.items;
if (Array.isArray(collectionItems)) {
return tupleGenerator(collectionItems, collection.additionalItems, locatedSchema, gatheredInfo, inputInfo);
} else {
if (collection.uniqueItems) {
const collectionProperties = collectionItems.object?.properties;
const key = collectionProperties?.get("key");
const value = collectionProperties?.get("value");
if (isMap(collection) && key && value) {
return mapGenerator(key, value, locatedSchema, gatheredInfo, inputInfo);
} else {
return setGenerator(collectionItems, locatedSchema, gatheredInfo, inputInfo);
}
} else {
return arrayGenerator(collectionItems, locatedSchema, gatheredInfo, inputInfo);
}
}
};
var isMap = (collection) => {
if (!collection.uniqueItems || Array.isArray(collection.items) || collection.additionalItems) {
return false;
}
const element = collection.items;
if (!element.object || element.const || element.$ref || element.enum || element.collection || element.allOf || element.anyOf || element.oneOf) {
return false;
}
const object = element.object;
return object.properties.size === 2 && object.properties.has("key") && object.properties.has("value") && object.additionalProperties === void 0 && object.required.size === 2 && object.required.has("key") && object.required.has("value");
};
var arrayGenerator = (items, locatedSchema, gatheredInfo, inputInfo) => {
const itemsLocatedSchema = located(items, locatedSchema);
const itemsType = typeGenerator(itemsLocatedSchema, gatheredInfo, inputInfo);
return `${itemsType}[]`;
};
var setGenerator = (items, locatedSchema, gatheredInfo, inputInfo) => {
const itemsLocatedSchema = located(items, locatedSchema);
const itemsType = typeGenerator(itemsLocatedSchema, gatheredInfo, inputInfo);
return `Set<${itemsType}>`;
};
var mapGenerator = (key, value, locatedSchema, gatheredInfo, inputInfo) => {
const keyLocatedSchema = located(key, locatedSchema);
const valueLocatedSchema = located(value, locatedSchema);
const keyType = typeGenerator(keyLocatedSchema, gatheredInfo, inputInfo);
const valueType = typeGenerator(valueLocatedSchema, gatheredInfo, inputInfo);
return `Map<${keyType}, ${valueType}>`;
};
var tupleGenerator = (items, additionalItems, locatedSchema, gatheredInfo, inputInfo) => {
const itemTypes = items.map((item) => located(item, locatedSchema)).map((itemLocatedSchema) => typeGenerator(itemLocatedSchema, gatheredInfo, inputInfo));
const additionalType = additionalItems ? typeGenerator(located(additionalItems, locatedSchema), gatheredInfo, inputInfo) : void 0;
const itemsCsv = filteredJoin(itemTypes, ", ");
const combinedItemsCsv = additionalType ? `${itemsCsv}, ...${additionalType}[]` : `${itemsCsv}`;
return `[${combinedItemsCsv}]`;
};
// src/generate/constant-generator.ts
var constantGenerator = (locatedSchema) => {
const constant = locatedSchema.schema.const;
if (constant === void 0) {
return;
}
return typeof constant === "string" ? `'${constant}'` : `${constant}`;
};
// src/generate/enum-generator.ts
var enumGenerator = (locatedSchema) => {
const _enum = locatedSchema.schema.enum;
if (!_enum || _enum.size === 0) {
return;
}
const enumTypes = [];
_enum.forEach((primitive) => {
const value = typeof primitive === "string" ? `'${primitive}'` : `${primitive}`;
enumTypes.push(value);
});
const combined = enumTypes.join(" | ");
return _enum.size === 1 ? combined : `(${combined})`;
};
// src/options.ts
var DEFAULT_OPTIONS = {
files: {
source: {
dir: "src/schemas",
encoding: "utf-8",
recursive: true
},
destination: {
dir: "src/generated",
preClean: false,
indexFiles: true
}
},
ts: {
optionalFields: "fieldName?" /* QUESTION */,
untyped: "unknown" /* UNKNOWN */
}
};
var createOptions = (options) => {
return {
files: {
...DEFAULT_OPTIONS.files,
...options.files,
source: {
...DEFAULT_OPTIONS.files.source,
...options.files?.source
},
destination: {
...DEFAULT_OPTIONS.files.destination,
...options.files?.destination
}
},
ts: {
...DEFAULT_OPTIONS.ts,
...options.ts
}
};
};
// src/generate/object-generator.ts
var objectGenerator = (locatedSchema, gatheredInfo, inputInfo) => {
const schema = locatedSchema.schema;
if (!schema.type || !schema.type.has("object") || !schema.object) {
return;
}
const { properties, required, additionalProperties } = schema.object;
const lines = [];
lines.push("{");
if (properties) {
Array.from(properties.entries()).forEach(([propertyName, propertySchema]) => {
const propertyLocatedSchema = located(propertySchema, locatedSchema);
const type = typeGenerator(propertyLocatedSchema, gatheredInfo, inputInfo);
if (type) {
const isRequired = required && required.has(propertyName);
const isQuestion = !isRequired && inputInfo.options.ts.optionalFields == "fieldName?" /* QUESTION */;
const isPipeUndefined = !isRequired && inputInfo.options.ts.optionalFields == "Type | undefined" /* PIPE_UNDEFINED */;
const lineParts = [];
lineParts.push(propertyName);
if (isQuestion) {
lineParts.push("?");
}
lineParts.push(`: ${type}`);
if (isPipeUndefined) {
lineParts.push(" | undefined");
}
lineParts.push(";");
lines.push(lineParts.join(""));
}
});
}
if (!additionalProperties) {
lines.push("}");
} else {
const lastLineParts = [];
lastLineParts.push("} & Record<string, ");
const valueLocatedSchema = located(additionalProperties, locatedSchema);
const valueType = typeGenerator(valueLocatedSchema, gatheredInfo, inputInfo) || inputInfo.options.ts.untyped;
lastLineParts.push(valueType);
lastLineParts.push(">");
lines.push(lastLineParts.join(""));
}
return lines.join("\n");
};
// src/generate/one-of-generator.ts
var oneOfGenerator = (locatedSchema, gatheredInfo, inputInfo) => {
const schema = locatedSchema.schema;
if (!schema.oneOf || schema.oneOf.length === 0) {
return;
}
const lines = [];
schema.oneOf.forEach((elementSchema) => {
const elementLocatedSchema = located(elementSchema, locatedSchema);
const elementContent = typeGenerator(elementLocatedSchema, gatheredInfo, inputInfo);
lines.push(elementContent);
});
const filteredLines = filtered(lines);
if (filteredLines.length === 0) {
return;
} else if (filteredLines.length === 1) {
return filteredLines[0];
} else {
gatheredInfo.oneOfTypes.add(filteredLines.length);
const typeName = `OneOf_${filteredLines.length}`;
const combinedTypeNames = filteredLines.join(", ");
return `${typeName}<${combinedTypeNames}>`;
}
};
// src/ids/parse.ts
var parseAuthority = (url) => {
const protocolMatch = url.match(/^([^:/]+:\/\/)/);
if (!protocolMatch) {
return;
}
const protocol = protocolMatch[1];
const withoutProtocol = url.substring(protocol.length);
const endpointStartMatch = withoutProtocol.match(/^[^/#]+([/#].*)/);
const endpoint = endpointStartMatch ? endpointStartMatch[1] : void 0;
const authority = endpoint ? url.substring(0, url.length - endpoint.length) : url;
return authority;
};
var parsePath = (endpoint) => {
const pathMatch = endpoint.match(/^\/([^#]+)/);
if (!pathMatch) {
return;
}
const match = pathMatch[1];
const lastSlashIndex = match.lastIndexOf("/");
const folder = lastSlashIndex <= 0 ? "." : match.substring(0, lastSlashIndex);
const name = lastSlashIndex === -1 ? match : match.substring(lastSlashIndex + 1);
return {
folder,
name
};
};
var parseFragment = (url) => {
const pathMatch = url.match(/^[^#]*#\/?(?:(?:\$defs|definitions)\/)?(.*)$/);
if (!pathMatch) {
return;
}
return pathMatch[1];
};
var parseSchemaId = (id) => {
if (!id) {
return;
}
const authority = parseAuthority(id);
if (authority === void 0 && !id.startsWith("/")) {
return;
}
const endpoint = authority === void 0 ? id : id.substring(authority.length);
const path6 = parsePath(endpoint);
if (!path6) {
return;
}
return {
authority,
folder: path6.folder,
name: path6.name
};
};
var parseSchemaRef = (ref) => {
if (!ref) {
return;
}
const authority = parseAuthority(ref);
const endpoint = authority === void 0 ? ref : ref.substring(authority.length);
const path6 = parsePath(endpoint);
const fragment = parseFragment(ref);
if (!path6) {
return fragment ? { fragment } : void 0;
}
return {
authority,
folder: path6.folder,
name: path6.name,
fragment
};
};
// src/ids/schema-ids.ts
var isAbsoluteSchemaId = (schemaId) => schemaId.authority !== void 0;
// src/ids/schema-refs.ts
var isAbsolute = (schemaRef) => hasProperties(schemaRef, true, true, true);
var isRelative = (schemaRef) => hasProperties(schemaRef, false, true, true);
var isLocal = (schemaRef) => hasProperties(schemaRef, false, false, false, true);
var hasProperties = (schemaRef, authority, folder, name, fragment) => {
const test = schemaRef;
return authority === (test.authority !== void 0) && folder === (test.folder !== void 0) && name === (test.name !== void 0) && (fragment === void 0 || fragment === (test.fragment !== void 0));
};
// src/generate/reference-generator.ts
var referenceGenerator = (locatedSchema, gatheredInfo, inputInfo) => {
const schema = locatedSchema.schema;
const id = schema.$id;
const ref = schema.$ref;
if (!ref) {
return;
}
if (isLocal(ref)) {
return ref.fragment;
}
const references = gatheredInfo.references;
const idFileLocations = inputInfo.idFileLocations;
if (isAbsolute(ref)) {
return createFromAbsoluteRef(references, idFileLocations, ref);
}
if (isRelative(ref)) {
if (id && isAbsoluteSchemaId(id)) {
const absoluteRef = { authority: id.authority, ...ref };
return createFromAbsoluteRef(references, idFileLocations, absoluteRef);
} else {
return createFromRelativeRef(references, idFileLocations, ref);
}
}
return;
};
var createFromAbsoluteRef = (references, idFileLocations, ref) => {
const fileLocation = idFileLocations.get(ref);
if (fileLocation) {
return addExternalReference(
references,
fileLocation,
ref.fragment
);
}
return createFromRelativeRef(references, idFileLocations, ref);
};
var createFromRelativeRef = (references, idFileLocations, ref) => {
const foundFileLocations = Array.from(idFileLocations.entries()).filter(
([schemaId, _]) => !isAbsoluteSchemaId(schemaId) || !isAbsolute(ref) || schemaId.authority === ref.authority
).filter(([schemaId, _]) => schemaId.folder === ref.folder).filter(([schemaId, _]) => schemaId.name === ref.name).map(([_, fileLocation]) => fileLocation);
return foundFileLocations && foundFileLocations.length === 1 ? addExternalReference(
references,
foundFileLocations[0],
ref.fragment
) : void 0;
};
var addExternalReference = (references, fileLocation, importName) => {
let importNames = references.schema.get(fileLocation);
if (!importNames) {
importNames = /* @__PURE__ */ new Set();
references.schema.set(fileLocation, importNames);
}
const name = importName || fileLocation.fileName;
importNames.add(name);
return name;
};
// src/generate/type-generator.ts
var typeGenerator = (locatedSchema, gatheredInfo, inputInfo) => {
const types = [];
types.push(constantGenerator(locatedSchema, gatheredInfo, inputInfo));
types.push(referenceGenerator(locatedSchema, gatheredInfo, inputInfo));
types.push(enumGenerator(locatedSchema, gatheredInfo, inputInfo));
types.push(basicTypeGenerator(locatedSchema, gatheredInfo, inputInfo));
types.push(objectGenerator(locatedSchema, gatheredInfo, inputInfo));
types.push(collectionGenerator(locatedSchema, gatheredInfo, inputInfo));
types.push(allOfGenerator(locatedSchema, gatheredInfo, inputInfo));
types.push(anyOfGenerator(locatedSchema, gatheredInfo, inputInfo));
types.push(oneOfGenerator(locatedSchema, gatheredInfo, inputInfo));
const filteredLines = filtered(types);
if (filteredLines.length === 0) {
return inputInfo.options.ts.untyped;
}
return filteredLines.join("\n& ");
};
// src/generate/file-generator.ts
var fileGenerator = (locatedSchema, inputInfo) => {
const references = {
schema: /* @__PURE__ */ new Map()
};
const gatheredInfo = {
namedSchemas: /* @__PURE__ */ new Map(),
references,
oneOfTypes: /* @__PURE__ */ new Set()
};
const schemaContent = schemaContentGenerator(locatedSchema, gatheredInfo, inputInfo);
const definitions = schemaMapGenerator(
locatedSchema.fileLocation,
locatedSchema.schema.definitions,
gatheredInfo,
inputInfo
);
const named = namedGenerator(locatedSchema.fileLocation, gatheredInfo, inputInfo);
const imports = importsGenerator(locatedSchema.fileLocation, references);
const oneOfs = oneOfTypesGenerator(gatheredInfo.oneOfTypes);
return filteredJoin([imports, schemaContent, named, definitions, oneOfs], "\n\n") + "\n";
};
var schemaContentGenerator = (locatedSchema, gatheredInfo, inputInfo, schemaName) => {
const typeName = nameGenerator(schemaName || locatedSchema.fileLocation.fileName);
const typeContent = typeGenerator(locatedSchema, gatheredInfo, inputInfo);
return typeContent ? `export type ${typeName} = ${typeContent};` : void 0;
};
var importsGenerator = (fileLocation, references) => {
if (references.schema.size === 0) {
return;
}
const content = [];
content.push(importMapGenerator(fileLocation, references.schema));
const defined = filtered(content);
return defined.join("\n");
};
var importMapGenerator = (fileLocation, references) => {
if (references.size === 0) {
return;
}
const imports = [];
references.forEach((names, referenceFileLocation) => {
if (names.size > 0) {
const combinedNames = Array.from(names).sort().join(", ");
const importPath = tsPathGenerator(
path.normalize(path.relative(fileLocation.dir, referenceFileLocation.dir))
);
const file = referenceFileLocation.fileName.length === 0 ? "" : `/${referenceFileLocation.fileName}`;
imports.push(`import { ${combinedNames} } from '${importPath}${file}';`);
}
});
return imports.join("\n");
};
var namedGenerator = (fileLocation, gatheredInfo, inputInfo) => {
if (gatheredInfo.namedSchemas.size === 0) {
return;
}
const content = [];
while (true) {
const map = gatheredInfo.namedSchemas;
gatheredInfo = {
...gatheredInfo,
namedSchemas: /* @__PURE__ */ new Map()
};
const schemaMapContent = schemaMapGenerator(fileLocation, map, gatheredInfo, inputInfo);
if (schemaMapContent) {
content.push(schemaMapContent);
} else {
return content.length === 0 ? void 0 : content.join("\n");
}
}
};
var oneOfTypesGenerator = (typeCounts) => {
if (typeCounts.size === 0) {
return;
}
const oneOfTypeLines = [];
typeCounts.forEach((typeCount) => {
const oneOfType = OneOfNGenerator(typeCount);
if (oneOfType) {
oneOfTypeLines.push(oneOfType);
}
});
return oneOfTypeLines.join("\n");
};
var schemaMapGenerator = (fileLocation, map, gatheredInfo, inputInfo) => {
if (!map || map.size === 0) {
return;
}
const content = [];
map.forEach((namedSchema, name) => {
const namedLocatedSchema = {
fileLocation,
schema: namedSchema
};
const schemaContent = schemaContentGenerator(namedLocatedSchema, gatheredInfo, inputInfo, name);
if (schemaContent) {
content.push(schemaContent);
}
});
return content.join("\n");
};
var tsPathGenerator = (relativePath) => relativePath.startsWith(".") ? relativePath : "." + path.sep + relativePath;
// src/generate/id-locations.ts
var idLocations = (fileSchemas) => {
let idLocations2 = /* @__PURE__ */ new Map();
fileSchemas.forEach((schema, fileLocation) => {
const id = schema.$id;
if (id) {
idLocations2 = idLocations2.set(id, fileLocation);
}
});
return idLocations2;
};
// src/generate/file-contents-generator.ts
var generateFileContents = (fileSchemas, options) => {
const idFileLocations = idLocations(fileSchemas);
const inputInfo = {
idFileLocations,
options
};
const fileContents = /* @__PURE__ */ new Map();
fileSchemas.forEach((schema, fileLocation) => {
const locatedSchema = {
fileLocation,
schema
};
const generated = fileGenerator(locatedSchema, inputInfo);
fileContents.set(fileLocation, generated);
});
return fileContents;
};
// src/files/clean.ts
import * as path2 from "path";
import { rimraf } from "rimraf";
var clean = async (options) => {
if (!options.files.destination.preClean) {
return;
}
const cwd = options.files.cwd || process.cwd();
const absoluteDir = path2.resolve(cwd, options.files.destination.dir);
await rimraf(absoluteDir);
};
// src/files/read.ts
import * as fs2 from "fs/promises";
import * as path4 from "path";
// src/files/walk.ts
import * as fs from "fs/promises";
import * as path3 from "path";
var READ_OPTIONS = { withFileTypes: true };
var files = async function* (dir, recursive) {
const dirents = await fs.readdir(dir, READ_OPTIONS);
for (const dirent of dirents) {
const filePath = path3.resolve(dir, dirent.name);
if (dirent.isDirectory()) {
if (recursive) {
yield* files(filePath, recursive);
}
} else {
yield filePath;
}
}
};
// src/files/read.ts
var read = async (options) => {
const sourceDir = options.files.source.dir;
const encoding = options.files.source.encoding;
const recursive = options.files.source.recursive;
const cwd = options.files.cwd || process.cwd();
const absoluteDir = path4.isAbsolute(sourceDir) ? sourceDir : path4.resolve(cwd, sourceDir);
const filesContent = /* @__PURE__ */ new Map();
for await (const file of files(absoluteDir, recursive)) {
const fileLocation = toFileLocation(file);
const content = await fs2.readFile(file, encoding);
filesContent.set(fileLocation, content);
}
return filesContent;
};
var toFileLocation = (file) => {
const dir = path4.dirname(file);
const fileNameWithExt = path4.basename(file);
const fileName = fileNameWithExt.substring(0, fileNameWithExt.indexOf("."));
return {
dir,
fileName,
fileNameWithExt
};
};
// src/files/write.ts
import * as fs3 from "fs/promises";
import * as path5 from "path";
var write = async (filesContent, options) => {
const promises = [];
const cwd = options.files.cwd || process.cwd();
const rootSourceDir = path5.resolve(cwd, options.files.source.dir);
const rootDestinationDir = path5.resolve(cwd, options.files.destination.dir);
const folderFiles = /* @__PURE__ */ new Map();
filesContent.forEach((content, fileLocation) => {
const relativeDir = path5.relative(rootSourceDir, fileLocation.dir);
const absoluteDir = path5.resolve(rootDestinationDir, relativeDir);
const absoluteFile = path5.resolve(absoluteDir, fileLocation.fileName) + ".ts";
const promise = writeContent(content, absoluteFile);
let files2 = folderFiles.get(absoluteDir);
if (!files2) {
files2 = /* @__PURE__ */ new Set();
folderFiles.set(absoluteDir, files2);
}
files2.add(fileLocation.fileName);
promises.push(promise);
});
if (options.files.destination.indexFiles) {
promises.push(createIndexFiles(folderFiles));
}
await Promise.all(promises);
};
var createIndexFiles = async (folderFiles) => {
const promises = [];
Array.from(folderFiles.entries()).forEach(([folder, files2]) => {
const indexFileName = `${folder}/index.ts`;
const content = Array.from(files2).sort().map((file) => `export * from './${file}';`).join("\n");
promises.push(writeContent(content, indexFileName));
});
await Promise.all(promises);
};
var writeContent = async (content, absoluteFile) => {
await mkdirs(absoluteFile);
await fs3.writeFile(absoluteFile, content);
};
var mkdirs = async (absoluteFile) => {
const parentDir = absoluteFile.substring(0, absoluteFile.lastIndexOf(path5.sep));
await fs3.mkdir(parentDir, { recursive: true });
};
// src/schema/Schema.ts
var BASIC_TYPES = /* @__PURE__ */ new Set(["string", "number", "integer", "object", "array", "boolean", "null"]);
var isBasicType = (type) => BASIC_TYPES.has(type);
// src/schema/parser.ts
var parse = (files2) => {
const schemas = /* @__PURE__ */ new Map();
files2.forEach((content, fileLocation) => {
const rawSchema = JSON.parse(content);
const schema = parseSchema(rawSchema);
schemas.set(fileLocation, schema);
});
return schemas;
};
var parseSchema = (rawSchema) => {
const $id = parseSchemaId(rawSchema.$id);
const type = parseType(rawSchema.type);
const $ref = parseSchemaRef(rawSchema.$ref);
const _enum = parseEnum(rawSchema.enum);
const object = parseObject(rawSchema);
const collection = parseCollection(rawSchema);
const allOf = parseArray(rawSchema.allOf);
const anyOf = parseArray(rawSchema.anyOf);
const oneOf = parseArray(rawSchema.oneOf);
const defs = parseRecord(rawSchema.$defs);
const definitions = parseRecord(rawSchema.definitions);
if (defs && definitions) {
defs?.forEach((schema, key) => {
definitions?.set(key, schema);
});
}
return {
$id,
type,
$ref,
enum: _enum,
object,
collection,
allOf,
anyOf,
oneOf,
definitions: definitions ? definitions : defs
};
};
var parseType = (type) => {
if (!type) {
return;
}
const typeArray = typeof type === "string" ? [type] : type;
return new Set(typeArray.filter(isBasicType));
};
var parseEnum = (_enum) => _enum ? new Set(_enum) : void 0;
var parseObject = (rawSchema) => {
const properties = parseRecord(rawSchema.properties);
const additionalProperties = parseAdditional(rawSchema.additionalProperties);
const required = parseRequired(rawSchema.required);
return properties ? {
properties,
additionalProperties,
required
} : void 0;
};
var parseCollection = (rawSchema) => {
const items = parseItems(rawSchema.items);
const additionalItems = parseAdditional(rawSchema.additionalItems);
const uniqueItems = rawSchema.uniqueItems;
return items ? {
items,
additionalItems,
uniqueItems
} : void 0;
};
var parseItems = (items) => {
if (!items) {
return;
}
if (Array.isArray(items)) {
return items.map(parseSchema);
}
return parseSchema(items);
};
var parseAdditional = (additional) => additional ? parseSchema(additional) : void 0;
var parseArray = (array) => array ? array.map(parseSchema) : void 0;
var parseRecord = (record) => {
if (!record) {
return;
}
const parsed = /* @__PURE__ */ new Map();
for (const key in record) {
const rawSchema = record[key];
const schema = parseSchema(rawSchema);
parsed.set(key, schema);
}
return parsed;
};
var parseRequired = (required) => new Set(required || []);
// src/generateFiles.ts
var generateFiles = async (options) => {
const allOptions = createOptions(options);
await clean(allOptions);
const fileContents = await read(allOptions);
const parsedSchemas = parse(fileContents);
const generatedFileContents = generateFileContents(parsedSchemas, allOptions);
await write(generatedFileContents, allOptions);
};
export {
generateFiles
};