Skip to content

Commit

Permalink
feat: Update to zod 3.0.2
Browse files Browse the repository at this point in the history
  • Loading branch information
fabien0102 committed May 23, 2021
1 parent 8e74dce commit f638921
Show file tree
Hide file tree
Showing 6 changed files with 225 additions and 6 deletions.
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -56,7 +56,7 @@
"tslib": "^2.1.0",
"tsutils": "^3.21.0",
"typescript": "^4.2.3",
"zod": "^3.0.0-alpha.33"
"zod": "^3.0.2"
},
"devDependencies": {
"@types/async": "^3.2.6",
Expand Down
52 changes: 52 additions & 0 deletions src/core/validateGeneratedTypes.test.ts
Expand Up @@ -97,4 +97,56 @@ describe("validateGeneratedTypes", () => {
]
`);
});

it("should deal with optional value with default", () => {
const sourceTypes = {
sourceText: `
export interface Citizen {
/**
* @default true
*/
isVillain?: boolean;
};
`,
relativePath: "source.ts",
};

const zodSchemas = {
sourceText: `// Generated by ts-to-zod
import { z } from "zod";
export const citizenSchema = z.object({
isVillain: z.boolean().optional().default(true)
});
`,
relativePath: "source.zod.ts",
};

const integrationTests = {
sourceText: `// Generated by ts-to-zod
import { z } from "zod";
import * as spec from "./${sourceTypes.relativePath.slice(0, -3)}";
import * as generated from "./${zodSchemas.relativePath.slice(0, -3)}";
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function expectType<T>(_: T) {
/* noop */
}
export type CitizenInferredType = z.infer<typeof generated.citizenSchema>;
expectType<CitizenInferredType>({} as spec.Citizen);
expectType<spec.Citizen>({} as CitizenInferredType);
`,
relativePath: "source.integration.ts",
};

const errors = validateGeneratedTypes({
sourceTypes,
zodSchemas,
integrationTests,
});

expect(errors).toEqual([]);
});
});
6 changes: 5 additions & 1 deletion src/core/validateGeneratedTypes.ts
Expand Up @@ -5,6 +5,7 @@ import {
} from "@typescript/vfs";
import ts from "typescript";
import { join } from "path";
import { resolveDefaultProperties } from "../utils/resolveDefaultProperties";
interface File {
sourceText: string;
relativePath: string;
Expand Down Expand Up @@ -34,7 +35,10 @@ export function validateGeneratedTypes({
target: compilerOptions.target,
});
const projectRoot = process.cwd();
fsMap.set(getPath(sourceTypes), sourceTypes.sourceText);
fsMap.set(
getPath(sourceTypes),
resolveDefaultProperties(sourceTypes.sourceText)
);
fsMap.set(getPath(zodSchemas), zodSchemas.sourceText);
fsMap.set(getPath(integrationTests), integrationTests.sourceText);

Expand Down
109 changes: 109 additions & 0 deletions src/utils/resolveDefaultProperties.test.ts
@@ -0,0 +1,109 @@
import { resolveDefaultProperties } from "./resolveDefaultProperties";

describe("resolveDefaultProperties", () => {
it("should remove the question mark if @default is defined (interface)", () => {
const sourceText = `
/**
* A citizen
*/
export interface Citizen {
name: string;
/**
* @default true
*/
isVillain?: boolean;
}
`;
expect(resolveDefaultProperties(sourceText)).toMatchInlineSnapshot(`
"/**
* A citizen
*/
export interface Citizen {
name: string;
/**
* @default true
*/
isVillain: boolean;
}
"
`);
});

it("should remove the question mark if @default is defined (type)", () => {
const sourceText = `
/**
* A citizen
*/
export type Citizen = {
name: string;
/**
* @default true
*/
isVillain?: boolean;
};
`;
expect(resolveDefaultProperties(sourceText)).toMatchInlineSnapshot(`
"/**
* A citizen
*/
export type Citizen = {
name: string;
/**
* @default true
*/
isVillain: boolean;
};
"
`);
});

it("should remove `undefined` if @default is defined", () => {
const sourceText = `
/**
* A citizen
*/
export interface Citizen {
name: string;
/**
* @default true
*/
isVillain: boolean | undefined;
}
`;
expect(resolveDefaultProperties(sourceText)).toMatchInlineSnapshot(`
"/**
* A citizen
*/
export interface Citizen {
name: string;
/**
* @default true
*/
isVillain: boolean;
}
"
`);
});

it("should do nothing if no @default", () => {
const sourceText = `
/**
* A citizen
*/
export interface Citizen {
name: string;
isVillain?: boolean;
}
`;
expect(resolveDefaultProperties(sourceText)).toMatchInlineSnapshot(`
"/**
* A citizen
*/
export interface Citizen {
name: string;
isVillain?: boolean;
}
"
`);
});
});
54 changes: 54 additions & 0 deletions src/utils/resolveDefaultProperties.ts
@@ -0,0 +1,54 @@
import ts from "typescript";
import { getJSDocTags } from "../core/jsDocTags";

/**
* Remove optional properties when `@default` jsdoc tag is defined.
*
* Indeed, `z.{type}().optional().default({value})` will be
* compile as a non-optional type.
*/
export function resolveDefaultProperties(sourceText: string) {
const sourceFile = ts.createSourceFile(
"index.ts",
sourceText,
ts.ScriptTarget.Latest
);
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });

const removeOptionalTransformer: ts.TransformerFactory<ts.SourceFile> = (
context
) => {
const visit: ts.Visitor = (node) => {
node = ts.visitEachChild(node, visit, context);

if (ts.isPropertySignature(node)) {
const jsDocTags = getJSDocTags(node, sourceFile);
if (jsDocTags.default !== undefined) {
const type = node.type
? ts.visitEachChild(node.type, omitUndefinedKeyword, context)
: undefined;
return ts.factory.createPropertySignature(
node.modifiers,
node.name,
undefined, // Remove `questionToken`
type
);
}
}
return node;
};

return (node) => ts.visitNode(node, visit);
};

const outputFile = ts.transform(sourceFile, [removeOptionalTransformer]);

return printer.printFile(outputFile.transformed[0]);
}

function omitUndefinedKeyword(node: ts.Node) {
if (node.kind === ts.SyntaxKind.UndefinedKeyword) {
return undefined;
}
return node;
}
8 changes: 4 additions & 4 deletions yarn.lock
Expand Up @@ -5028,7 +5028,7 @@ yn@3.1.1:
resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"
integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==

zod@^3.0.0-alpha.33:
version "3.0.0-alpha.33"
resolved "https://registry.yarnpkg.com/zod/-/zod-3.0.0-alpha.33.tgz#8562cc92e57b7df7e0f99f3376181782d7233cfb"
integrity sha512-zxh7bbKBd1gPiHhjFL1f4QNJm7fnw8IJir84pFhqVsHalSBFsxIhDTduV9MdpCTpyfOvRnFgOF0rqrdLeQTEyA==
zod@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/zod/-/zod-3.0.2.tgz#0d8f0adbc7569e1a3c67b2cc788f81a55dc8a403"
integrity sha512-a+9VrxBi5CWBFq2LO5aNgbAaIRzPpBLbH4qGjSFeKd/ClLAXZq1dNFLTe9N1VDUBKxqXgHVkMlyp5MtSJylJww==

0 comments on commit f638921

Please sign in to comment.