Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support standard built-in objects #122

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
75 changes: 75 additions & 0 deletions src/core/const.ts
@@ -0,0 +1,75 @@
// Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects
export const standardBuiltInObjects = [
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects#fundamental_objects
"Object",
"Function",
"Boolean",
"Symbol",
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects#error_objects
"Error",
"AggregateError",
"EvalError",
"RangeError",
"ReferenceError",
"SyntaxError",
"TypeError",
"URIError",
// "InternalError",
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects#numbers_and_dates
"Number",
"BigInt",
"Math",
// "Date",
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects#indexed_collections
// "Array",
"Int8Array",
"Uint8Array",
"Uint8ClampedArray",
"Int16Array",
"Uint16Array",
"Int32Array",
"Uint32Array",
"BigInt64Array",
"BigUint64Array",
"Float32Array",
"Float64Array",
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects#keyed_collections
"Map",
"Set",
"WeakMap",
"WeakSet",
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects#structured_data
"ArrayBuffer",
"SharedArrayBuffer",
"DataView",
"Atomics",
"JSON",
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects#managing_memory
"WeakRef",
"FinalizationRegistry",
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects#control_abstraction_objects
"Promise",
"GeneratorFunction",
"AsyncGeneratorFunction",
"Generator",
"AsyncGenerator",
"AsyncFunction",
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects#reflection
"Reflect",
"Proxy",
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects#internationalization
// "Intl",
// "Intl.Collator",
// "Intl.DateTimeFormat",
// "Intl.DisplayNames",
// "Intl.ListFormat",
// "Intl.Locale",
// "Intl.NumberFormat",
// "Intl.PluralRules",
// "Intl.RelativeTimeFormat",
// "Intl.Segmenter",
];

export const standardBuiltInObjectVarNames = standardBuiltInObjects.map(
(n) => n[0].toLocaleLowerCase() + n.substring(1) + "Schema"
);
3 changes: 2 additions & 1 deletion src/core/generate.test.ts
Expand Up @@ -160,7 +160,7 @@ describe("generate", () => {
}

export interface IHaveUnknownDependency {
dep: UnknwonDependency; // <- Missing dependency
dep: UnknownDependency; // <- Missing dependency
}
`;

Expand Down Expand Up @@ -228,6 +228,7 @@ describe("generate", () => {
expect(errors).toMatchInlineSnapshot(`
Array [
"Some schemas can't be generated due to direct or indirect missing dependencies:
unknownDependencySchema
iHaveUnknownDependencySchema",
]
`);
Expand Down
41 changes: 38 additions & 3 deletions src/core/generate.ts
Expand Up @@ -13,6 +13,7 @@ import { generateIntegrationTests } from "./generateIntegrationTests";
import { generateZodInferredType } from "./generateZodInferredType";
import { generateZodSchemaVariableStatement } from "./generateZodSchema";
import { transformRecursiveSchema } from "./transformRecursiveSchema";
import { standardBuiltInObjectVarNames } from "./const";

export interface GenerateProps {
/**
Expand Down Expand Up @@ -131,6 +132,43 @@ export function generate({
});
const zodSchemaNames = zodSchemas.map(({ varName }) => varName);

// Zod schemas with direct or indirect dependencies that are not in `zodSchemas`, won't be generated
const zodSchemasWithMissingDependencies = new Set<string>();
const standardBuiltInObjects = new Set<string>();
zodSchemas.forEach(
({ varName, dependencies, statement, typeName, requiresImport }) => {
dependencies
.filter((dep) => !zodSchemaNames.includes(dep))
.forEach((dep) => {
if (standardBuiltInObjectVarNames.includes(dep)) {
standardBuiltInObjects.add(dep);
} else {
zodSchemasWithMissingDependencies.add(dep);
zodSchemasWithMissingDependencies.add(varName);
}
});
}
);
zodSchemaNames.push(...standardBuiltInObjects);

zodSchemas.push(
...Array.from(standardBuiltInObjects).map((obj) => {
const typeName = obj[0].toUpperCase() + obj.substring(1, obj.length - 6);
return {
typeName,
varName: obj,
...generateZodSchemaVariableStatement({
typeName,
zodImportValue: "z",
sourceFile,
varName: obj,
getDependencyName: getSchemaName,
skipParseJSDoc,
}),
};
})
);

// Resolves statements order
// A schema can't be declared if all the referenced schemas used inside this one are not previously declared.
const statements = new Map<
Expand All @@ -139,9 +177,6 @@ export function generate({
>();
const typeImports: Set<string> = new Set();

// Zod schemas with direct or indirect dependencies that are not in `zodSchemas`, won't be generated
const zodSchemasWithMissingDependencies = new Set<string>();

let done = false;
// Loop until no more schemas can be generated and no more schemas with direct or indirect missing dependencies are found
while (
Expand Down
28 changes: 19 additions & 9 deletions src/core/generateZodSchema.ts
Expand Up @@ -9,6 +9,7 @@ import {
jsDocTagToZodProperties,
ZodProperty,
} from "./jsDocTags";
import { standardBuiltInObjects } from "./const";

const { factory: f } = ts;

Expand All @@ -18,10 +19,15 @@ export interface GenerateZodSchemaProps {
*/
varName: string;

/**
* Name of the standard built-in object
*/
typeName?: string;

/**
* Interface or type node
*/
node: ts.InterfaceDeclaration | ts.TypeAliasDeclaration | ts.EnumDeclaration;
node?: ts.InterfaceDeclaration | ts.TypeAliasDeclaration | ts.EnumDeclaration;

/**
* Zod import value.
Expand Down Expand Up @@ -59,6 +65,7 @@ export interface GenerateZodSchemaProps {
*/
export function generateZodSchemaVariableStatement({
node,
typeName,
sourceFile,
varName,
zodImportValue = "z",
Expand All @@ -73,7 +80,14 @@ export function generateZodSchemaVariableStatement({
let dependencies: string[] = [];
let requiresImport = false;

if (ts.isInterfaceDeclaration(node)) {
if (!node) {
if (standardBuiltInObjects.includes(typeName!)) {
schema = buildZodSchema(zodImportValue, "instanceof", [
f.createIdentifier(typeName!),
]);
requiresImport = false;
}
} else if (ts.isInterfaceDeclaration(node)) {
let schemaExtensionClauses: string[] | undefined;
if (node.typeParameters) {
throw new Error("Interface with generics are not supported!");
Expand Down Expand Up @@ -108,9 +122,7 @@ export function generateZodSchemaVariableStatement({
schemaExtensionClauses,
skipParseJSDoc,
});
}

if (ts.isTypeAliasDeclaration(node)) {
} else if (ts.isTypeAliasDeclaration(node)) {
if (node.typeParameters) {
throw new Error("Type with generics are not supported!");
}
Expand All @@ -126,17 +138,15 @@ export function generateZodSchemaVariableStatement({
getDependencyName,
skipParseJSDoc,
});
}

if (ts.isEnumDeclaration(node)) {
} else if (ts.isEnumDeclaration(node)) {
schema = buildZodSchema(zodImportValue, "nativeEnum", [node.name]);
requiresImport = true;
}

return {
dependencies: uniq(dependencies),
statement: f.createVariableStatement(
node.modifiers,
node?.modifiers,
f.createVariableDeclarationList(
[
f.createVariableDeclaration(
Expand Down