-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgenerate-zod-combinedTypes.js
More file actions
110 lines (102 loc) · 3.86 KB
/
Copy pathgenerate-zod-combinedTypes.js
File metadata and controls
110 lines (102 loc) · 3.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#! /usr/bin/env node
import chalk from "chalk";
import { Command } from "commander";
import fs from "fs";
import path from "path/posix";
import { generateZodScriptInitialiser } from "./utils/generateZodScriptInitialiser.js";
// Define CLI
const program = new Command()
.summary("generate combined zod files for pf2ools-schema")
.description(
"Generates various TypeScript files from the base Zod types. All `.ts` files in a directory are included, even if they aren't zod declarations. The schema export is assumed to have the same name as its file.",
)
.argument("<path>", "Directory")
.option("-r, --recurse", "Find zod files recursively")
.option("-e, --exclude <paths...>", "Paths to files or directories that should be excluded")
.parse(process.argv);
// Collect types to import (if there's a problem it's probably here)
const { relativeFiles, zodDir } = generateZodScriptInitialiser(program);
let contentTypesImportString = "";
const contentTypes = [];
for (const file of relativeFiles) {
const code = fs.readFileSync(path.join(zodDir, file), { encoding: "utf8" });
if (code.match(/\nexport const \w+ = contentTemplate(?:\n\t+)?\.extend\(/)) {
contentTypesImportString += `import { ${path.basename(file, ".ts")} } from "./${file.replace(/\.ts$/, ".js")}";\n`;
contentTypes.push(path.basename(file, ".ts"));
}
}
// Define `.refine()` calls on the combined type
// TODO: Fix the content: any override due to typing errors with Zod4
const REFINE = `.refine(
(content: any) => (content.reference ? (content.reference.type === "reprint" ? true : !!content.data !== !!content.reference.modifications) : content.data),
"Choose one of \`data\` and \`reference.modifications\` to describe the content.",
);\n`;
// Define the header of each file
const getTSTemplate = (...types) =>
`// This file was generated by scripts/generate-zod-combinedTypes.js at ${new Date().toUTCString()}\n\nimport { ${[
"z",
]
.concat(types.map((str) => `type ${str}`))
.join(", ")} } from "zod";\n\n`;
// - Content
fs.writeFileSync(
`${zodDir}/_content.ts`,
getTSTemplate().concat(
contentTypesImportString,
"\n",
'export const content = z.discriminatedUnion("type", [',
"\n\t",
contentTypes.join(",\n\t"),
",\n])",
REFINE,
"export type contentInfer = z.infer<typeof content>;\n",
),
);
console.log(chalk.green(`Generated ${chalk.italic("content")} type file`));
// - Data
const metaTypes = ["license", "source", "sourceGroup"];
const metaTypesImportString = metaTypes
.map((type) => {
const file = relativeFiles.find((file) => file.match(new RegExp(`${type}.ts$`, "i")));
if (!file) return "";
return `import { ${type} } from "./${file.replace(/\.ts$/, ".js")}";`;
})
.join("\n");
fs.writeFileSync(
`${zodDir}/_data.ts`,
getTSTemplate().concat(
metaTypesImportString,
"\n\n",
`const metaType = z.discriminatedUnion("type", [${metaTypes.join(", ")}]);`,
"\n\n",
'import { content } from "./_content.js";\n',
"export const data = content.or(metaType);\n",
"export type dataInfer = z.infer<typeof data>;\n",
),
);
console.log(chalk.green(`Generated ${chalk.italic("data")} type file`));
// - Bundle
fs.writeFileSync(
`${zodDir}/_bundle.ts`,
getTSTemplate("ZodObject").concat(
metaTypesImportString,
"\n",
contentTypesImportString,
"\n",
`const refineContent = (content: ZodObject) =>\n\tcontent${REFINE}`,
"\n",
"export const bundle = z.object({",
"\n\t",
metaTypes
.map((type) => `${type}: z.array(${type}).min(1)${type === "source" ? "" : ".optional()"}`)
.join(",\n\t"),
",\n\t",
contentTypes.map((type) => `${type}: z.array(refineContent(${type})).min(1).optional()`).join(",\n\t"),
",\n});",
"\n\n",
'import { nonEmpty } from "./utils/nonEmpty.js";\n',
"export const anyBundle = bundle.partial().refine(...nonEmpty);\n",
"export type bundleInfer = z.infer<typeof bundle>;\n",
),
);
console.log(chalk.green(`Generated ${chalk.italic("bundle")} types file`));