Skip to content

Commit

Permalink
Getting rid of serializer (#360)
Browse files Browse the repository at this point in the history
* Deactivating the serializer, deprecating.

* Replacing serializer with a Map for Integration.

* Apply suggestions from code review

* Changelog : 2.2.0.
  • Loading branch information
RobinTail authored Dec 19, 2024
1 parent bafd227 commit 0a7758c
Show file tree
Hide file tree
Showing 8 changed files with 46 additions and 61 deletions.
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,20 @@

## Version 2

### v2.2.0

- Naming of circular types is now numeric:
- Deprecated `serializer` property on the `Integration` constructor argument (no longer used).

```diff
- type Type2048581c137c5b2130eb860e3ae37da196dfc25b = {
+ type Type1 = {
title: string;
- features: Type2048581c137c5b2130eb860e3ae37da196dfc25b;
+ features: Type1;
}[];
```

### v2.1.1

- Documentation update on compatibility with Express Zod API v21;
Expand Down
6 changes: 3 additions & 3 deletions src/__snapshots__/integration.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ exports[`Integration > print() > should handle circular references 1`] = `
export namespace Root {
/** @desc The actual path of the Root namespace */
export const path = "/";
type Type2048581c137c5b2130eb860e3ae37da196dfc25b = {
type Type1 = {
title: string;
features: Type2048581c137c5b2130eb860e3ae37da196dfc25b;
features: Type1;
}[];
export interface Emission {
time: (currentIsoTime: string) => void;
Expand All @@ -22,7 +22,7 @@ export namespace Root {
export interface Actions {
test: (p1: {
title: string;
features: Type2048581c137c5b2130eb860e3ae37da196dfc25b;
features: Type1;
}) => void;
}
/** @example const socket: Root.Socket = io(Root.path) */
Expand Down
4 changes: 2 additions & 2 deletions src/__snapshots__/zts.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ exports[`zod-to-ts > Example > should produce the expected results 1`] = `
string: string;
}[];
boolean: boolean;
circular: Type118cb3b11b8a1f3b6b1e60a89f96a8be9da32a0f;
circular: SomeType;
union: {
number: number;
} | "hi";
Expand Down Expand Up @@ -60,7 +60,7 @@ exports[`zod-to-ts > Example > should produce the expected results 1`] = `
optDefaultString?: string | undefined;
refinedStringWithSomeBullshit: (string | number) & ((bigint | null) | undefined);
nativeEnum: "A" | "apple" | "banana" | "cantaloupe" | 5;
lazy: Type51497f7e879bae48c3fbad2fa68050d1e08bbf82;
lazy: SomeType;
discUnion: {
kind: "circle";
radius: number;
Expand Down
4 changes: 0 additions & 4 deletions src/integration-helpers.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
import { createHash } from "node:crypto";
import { range } from "ramda";
import ts from "typescript";
import { z } from "zod";

export const f = ts.factory;
export const exportModifier = [f.createModifier(ts.SyntaxKind.ExportKeyword)];

export const defaultSerializer = (schema: z.ZodTypeAny): string =>
createHash("sha1").update(JSON.stringify(schema), "utf8").digest("hex");

export const makeEventFnSchema = (
base: z.AnyZodTuple,
ack?: z.AnyZodTuple,
Expand Down
43 changes: 16 additions & 27 deletions src/integration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,7 @@ import { z } from "zod";
import { AbstractAction } from "./action";
import { makeCleanId } from "./common-helpers";
import { Config } from "./config";
import {
defaultSerializer,
exportModifier,
f,
makeEventFnSchema,
} from "./integration-helpers";
import { exportModifier, f, makeEventFnSchema } from "./integration-helpers";
import { Namespaces, normalizeNS } from "./namespace";
import { zodToTs } from "./zts";
import { addJsDocComment, createTypeAlias, printNode } from "./zts-helpers";
Expand All @@ -24,8 +19,8 @@ interface IntegrationProps {
*/
maxOverloads?: number;
/**
* @desc Used for comparing schemas wrapped into z.lazy() to limit the recursion
* @default JSON.stringify() + SHA1 hash as a hex digest
* @deprecated unused
* @todo remove in next major
* */
serializer?: (schema: z.ZodTypeAny) => string;
/**
Expand Down Expand Up @@ -53,7 +48,7 @@ export class Integration {
protected program: ts.Node[] = [];
protected aliases: Record<
string, // namespace
Record<string, ts.TypeAliasDeclaration>
Map<z.ZodTypeAny, ts.TypeAliasDeclaration>
> = {};
protected ids = {
path: f.createIdentifier("path"),
Expand All @@ -71,28 +66,24 @@ export class Integration {
>
> = {};

protected getAlias(
ns: string,
name: string,
): ts.TypeReferenceNode | undefined {
return name in this.aliases[ns]
? f.createTypeReferenceNode(name)
: undefined;
}

protected makeAlias(
ns: string,
name: string,
type: ts.TypeNode,
schema: z.ZodTypeAny,
produce: () => ts.TypeNode,
): ts.TypeReferenceNode {
this.aliases[ns][name] = createTypeAlias(type, name);
return this.getAlias(ns, name)!;
let name = this.aliases[ns].get(schema)?.name?.text;
if (!name) {
name = `Type${this.aliases[ns].size + 1}`;
const temp = f.createLiteralTypeNode(f.createNull());
this.aliases[ns].set(schema, createTypeAlias(temp, name));
this.aliases[ns].set(schema, createTypeAlias(produce(), name));
}
return f.createTypeReferenceNode(name);
}

constructor({
config: { namespaces },
actions,
serializer = defaultSerializer,
optionalPropStyle = { withQuestionMark: true, withUndefined: true },
maxOverloads = 3,
}: IntegrationProps) {
Expand All @@ -115,12 +106,10 @@ export class Integration {
);

for (const [ns, { emission }] of Object.entries(namespaces)) {
this.aliases[ns] = {};
this.aliases[ns] = new Map<z.ZodTypeAny, ts.TypeAliasDeclaration>();
this.registry[ns] = { emission: [], actions: [] };
const commons = {
getAlias: this.getAlias.bind(this, ns),
makeAlias: this.makeAlias.bind(this, ns),
serializer,
optionalPropStyle,
};
for (const [event, { schema, ack }] of Object.entries(emission)) {
Expand Down Expand Up @@ -197,7 +186,7 @@ export class Integration {
f.createIdentifier(publicName),
f.createModuleBlock([
nsNameNode,
...Object.values(this.aliases[ns]),
...this.aliases[ns].values(),
...interfaces,
socketNode,
]),
Expand Down
15 changes: 7 additions & 8 deletions src/zts-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ export type LiteralType = string | number | boolean;

export interface ZTSContext extends FlatObject {
direction: "in" | "out";
getAlias: (name: string) => ts.TypeReferenceNode | undefined;
makeAlias: (name: string, type: ts.TypeNode) => ts.TypeReferenceNode;
serializer: (schema: z.ZodTypeAny) => string;
makeAlias: (
schema: z.ZodTypeAny,
produce: () => ts.TypeNode,
) => ts.TypeReferenceNode;
optionalPropStyle: { withQuestionMark?: boolean; withUndefined?: boolean };
}

Expand All @@ -28,18 +29,16 @@ export const addJsDocComment = (node: ts.Node, text: string) => {

export const createTypeAlias = (
node: ts.TypeNode,
identifier: string,
name: string,
comment?: string,
) => {
const typeAlias = f.createTypeAliasDeclaration(
undefined,
f.createIdentifier(identifier),
f.createIdentifier(name),
undefined,
node,
);
if (comment) {
addJsDocComment(typeAlias, comment);
}
if (comment) addJsDocComment(typeAlias, comment);
return typeAlias;
};

Expand Down
6 changes: 2 additions & 4 deletions src/zts.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import assert from "node:assert/strict";
import ts from "typescript";
import { z } from "zod";
import { defaultSerializer, f } from "./integration-helpers";
import { f } from "./integration-helpers";
import { zodToTs } from "./zts";
import { ZTSContext, createTypeAlias, printNode } from "./zts-helpers";
import { describe, expect, test, vi } from "vitest";
Expand All @@ -11,9 +11,7 @@ describe("zod-to-ts", () => {
printNode(node, { newLine: ts.NewLineKind.LineFeed });
const defaultCtx: ZTSContext = {
direction: "in",
getAlias: vi.fn((name: string) => f.createTypeReferenceNode(name)),
makeAlias: vi.fn(),
serializer: defaultSerializer,
makeAlias: vi.fn(() => f.createTypeReferenceNode("SomeType")),
optionalPropStyle: { withQuestionMark: true, withUndefined: true },
};

Expand Down
15 changes: 2 additions & 13 deletions src/zts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,19 +196,8 @@ const onNull: Producer = () => f.createLiteralTypeNode(f.createNull());
const onDate: Producer = () =>
f.createTypeReferenceNode(f.createIdentifier("Date"));

const onLazy: Producer = (
lazy: z.ZodLazy<z.ZodTypeAny>,
{ getAlias, makeAlias, next, serializer: serialize },
) => {
const name = `Type${serialize(lazy.schema)}`;
return (
getAlias(name) ||
(() => {
makeAlias(name, f.createLiteralTypeNode(f.createNull())); // make empty type first
return makeAlias(name, next(lazy.schema)); // update
})()
);
};
const onLazy: Producer = (lazy: z.ZodLazy<z.ZodTypeAny>, { makeAlias, next }) =>
makeAlias(lazy, () => next(lazy.schema));

const onFunction: Producer = (
schema: z.ZodFunction<z.AnyZodTuple, z.ZodTypeAny>,
Expand Down

0 comments on commit 0a7758c

Please sign in to comment.