zinfer 0.0.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +477 -28
- package/bin/zinfer +2 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +486 -0
- package/dist/cli.js.map +1 -0
- package/dist/core/brand-detector.d.ts +32 -0
- package/dist/core/brand-detector.d.ts.map +1 -0
- package/dist/core/brand-detector.js +121 -0
- package/dist/core/brand-detector.js.map +1 -0
- package/dist/core/config-loader.d.ts +87 -0
- package/dist/core/config-loader.d.ts.map +1 -0
- package/dist/core/config-loader.js +105 -0
- package/dist/core/config-loader.js.map +1 -0
- package/dist/core/description-extractor.d.ts +54 -0
- package/dist/core/description-extractor.d.ts.map +1 -0
- package/dist/core/description-extractor.js +180 -0
- package/dist/core/description-extractor.js.map +1 -0
- package/dist/core/errors.d.ts +35 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +96 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/extractor.d.ts +121 -0
- package/dist/core/extractor.d.ts.map +1 -0
- package/dist/core/extractor.js +504 -0
- package/dist/core/extractor.js.map +1 -0
- package/dist/core/file-resolver.d.ts +41 -0
- package/dist/core/file-resolver.d.ts.map +1 -0
- package/dist/core/file-resolver.js +95 -0
- package/dist/core/file-resolver.js.map +1 -0
- package/dist/core/getter-resolver.d.ts +67 -0
- package/dist/core/getter-resolver.d.ts.map +1 -0
- package/dist/core/getter-resolver.js +234 -0
- package/dist/core/getter-resolver.js.map +1 -0
- package/dist/core/import-resolver.d.ts +51 -0
- package/dist/core/import-resolver.d.ts.map +1 -0
- package/dist/core/import-resolver.js +158 -0
- package/dist/core/import-resolver.js.map +1 -0
- package/dist/core/index.d.ts +14 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +13 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/logger.d.ts +20 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/logger.js +36 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/name-mapper.d.ts +45 -0
- package/dist/core/name-mapper.d.ts.map +1 -0
- package/dist/core/name-mapper.js +82 -0
- package/dist/core/name-mapper.js.map +1 -0
- package/dist/core/normalizer.d.ts +24 -0
- package/dist/core/normalizer.d.ts.map +1 -0
- package/dist/core/normalizer.js +50 -0
- package/dist/core/normalizer.js.map +1 -0
- package/dist/core/schema-detector.d.ts +49 -0
- package/dist/core/schema-detector.d.ts.map +1 -0
- package/dist/core/schema-detector.js +272 -0
- package/dist/core/schema-detector.js.map +1 -0
- package/dist/core/schema-reference-analyzer.d.ts +77 -0
- package/dist/core/schema-reference-analyzer.d.ts.map +1 -0
- package/dist/core/schema-reference-analyzer.js +269 -0
- package/dist/core/schema-reference-analyzer.js.map +1 -0
- package/dist/core/test-generator.d.ts +97 -0
- package/dist/core/test-generator.d.ts.map +1 -0
- package/dist/core/test-generator.js +177 -0
- package/dist/core/test-generator.js.map +1 -0
- package/dist/core/type-printer.d.ts +46 -0
- package/dist/core/type-printer.d.ts.map +1 -0
- package/dist/core/type-printer.js +519 -0
- package/dist/core/type-printer.js.map +1 -0
- package/dist/core/types.d.ts +124 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +5 -0
- package/dist/core/types.js.map +1 -0
- package/dist/index.d.ts +66 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +79 -0
- package/dist/index.js.map +1 -0
- package/package.json +63 -7
package/dist/cli.js
ADDED
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import { resolve, dirname, basename, relative, parse as parsePath } from "pathe";
|
|
4
|
+
import { existsSync, writeFileSync, mkdirSync } from "fs";
|
|
5
|
+
import { ZodTypeExtractor, generateDeclarationFile, NameMapper, FileResolver, DescriptionExtractor, ConfigLoader, formatError, NoFilesMatchedError, NoSchemasFoundError, InvalidOptionError, generateTypeTests, generateImportPrefix, setVerbose, logVerbose, logProgress, } from "./core/index.js";
|
|
6
|
+
const program = new Command();
|
|
7
|
+
program.name("zinfer").description("Extract input/output types from Zod schemas").version("0.1.0");
|
|
8
|
+
program
|
|
9
|
+
.argument("[files...]", "File paths or glob patterns")
|
|
10
|
+
.option("-c, --config <path>", "Path to config file")
|
|
11
|
+
.option("-p, --project <path>", "Path to tsconfig.json")
|
|
12
|
+
.option("--schemas <names>", "Comma-separated schema names to extract")
|
|
13
|
+
.option("--input-only", "Output only input types")
|
|
14
|
+
.option("--output-only", "Output only output types")
|
|
15
|
+
.option("--merge-same", "Single type if input===output")
|
|
16
|
+
.option("--suffix <suffix>", "Remove suffix from schema names (e.g., 'Schema')")
|
|
17
|
+
.option("--input-suffix <suffix>", "Suffix for input type names (default: 'Input')")
|
|
18
|
+
.option("--output-suffix <suffix>", "Suffix for output type names (default: 'Output')")
|
|
19
|
+
.option("--map <mappings>", "Custom name mappings (e.g., 'UserSchema:User')")
|
|
20
|
+
.option("--outDir <dir>", "Output directory for generated files")
|
|
21
|
+
.option("--outFile <file>", "Single output file for all types")
|
|
22
|
+
.option("--outPattern <pattern>", "Output file naming pattern (e.g., '[name].types.ts')")
|
|
23
|
+
.option("-d, --declaration", "Generate .d.ts files")
|
|
24
|
+
.option("--dry-run", "Preview without writing files")
|
|
25
|
+
.option("--with-descriptions", "Include Zod .describe() as TSDoc comments")
|
|
26
|
+
.option("--generate-tests", "Generate vitest type equality tests alongside type files")
|
|
27
|
+
.option("-v, --verbose", "Enable verbose output")
|
|
28
|
+
.action(async (files, options) => {
|
|
29
|
+
try {
|
|
30
|
+
await runCLI(files, options);
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
console.error(formatError(error));
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
program.parse();
|
|
38
|
+
/**
|
|
39
|
+
* Main CLI execution logic.
|
|
40
|
+
*/
|
|
41
|
+
async function runCLI(files, options) {
|
|
42
|
+
const cwd = process.cwd();
|
|
43
|
+
// Enable verbose mode if requested
|
|
44
|
+
if (options.verbose) {
|
|
45
|
+
setVerbose(true);
|
|
46
|
+
logVerbose("Verbose mode enabled");
|
|
47
|
+
}
|
|
48
|
+
// Load config file
|
|
49
|
+
logVerbose("Loading configuration...");
|
|
50
|
+
const configLoader = new ConfigLoader();
|
|
51
|
+
const { config: fileConfig } = await configLoader.load(cwd);
|
|
52
|
+
// Merge CLI options with config file (CLI takes precedence)
|
|
53
|
+
const config = mergeCliWithConfig(options, fileConfig);
|
|
54
|
+
// Validate options
|
|
55
|
+
validateOptions(config);
|
|
56
|
+
logVerbose("Configuration validated");
|
|
57
|
+
// Use files from CLI args, or from config, or fail
|
|
58
|
+
const inputFiles = files.length > 0 ? files : config.include || [];
|
|
59
|
+
if (inputFiles.length === 0) {
|
|
60
|
+
throw new NoFilesMatchedError(["(no files specified)"]);
|
|
61
|
+
}
|
|
62
|
+
// Resolve file paths (support glob patterns)
|
|
63
|
+
logVerbose("Resolving input files...");
|
|
64
|
+
const fileResolver = new FileResolver();
|
|
65
|
+
const resolvedFiles = await fileResolver.resolveInputFiles(inputFiles, cwd);
|
|
66
|
+
if (resolvedFiles.length === 0) {
|
|
67
|
+
throw new NoFilesMatchedError(inputFiles);
|
|
68
|
+
}
|
|
69
|
+
logVerbose(`Found ${resolvedFiles.length} file(s) to process`);
|
|
70
|
+
// Find tsconfig
|
|
71
|
+
const tsconfigPath = config.project ? resolve(cwd, config.project) : findTsConfig(cwd);
|
|
72
|
+
logVerbose(`Using tsconfig: ${tsconfigPath || "(none)"}`);
|
|
73
|
+
// Validate --generate-tests requires file output
|
|
74
|
+
if (config.generateTests && !config.outDir && !config.outFile) {
|
|
75
|
+
throw new Error("--generate-tests requires --outDir or --outFile");
|
|
76
|
+
}
|
|
77
|
+
// Create extractor and name mapper
|
|
78
|
+
const extractor = new ZodTypeExtractor(tsconfigPath);
|
|
79
|
+
const nameMapper = createNameMapper(config);
|
|
80
|
+
const descriptionExtractor = config.withDescriptions ? new DescriptionExtractor() : null;
|
|
81
|
+
// Parse schema names if specified
|
|
82
|
+
const schemaFilter = config.schemas;
|
|
83
|
+
// Output options
|
|
84
|
+
const outputOptions = {
|
|
85
|
+
outDir: config.outDir,
|
|
86
|
+
outFile: config.outFile,
|
|
87
|
+
outPattern: config.outPattern,
|
|
88
|
+
declaration: config.declaration,
|
|
89
|
+
};
|
|
90
|
+
// Declaration options
|
|
91
|
+
const declOptions = {
|
|
92
|
+
inputOnly: config.inputOnly,
|
|
93
|
+
outputOnly: config.outputOnly,
|
|
94
|
+
mergeSame: config.mergeSame,
|
|
95
|
+
};
|
|
96
|
+
// Single output file mode
|
|
97
|
+
if (config.outFile) {
|
|
98
|
+
logVerbose("Processing files for single output...");
|
|
99
|
+
const allResults = [];
|
|
100
|
+
const fileResultsMap = new Map();
|
|
101
|
+
for (let i = 0; i < resolvedFiles.length; i++) {
|
|
102
|
+
const filePath = resolvedFiles[i];
|
|
103
|
+
logProgress(i + 1, resolvedFiles.length, `Processing ${basename(filePath)}`);
|
|
104
|
+
let results = getFilteredResults(extractor, filePath, schemaFilter);
|
|
105
|
+
// Add descriptions if enabled
|
|
106
|
+
if (descriptionExtractor) {
|
|
107
|
+
results = await addDescriptionsToResults(descriptionExtractor, filePath, results);
|
|
108
|
+
}
|
|
109
|
+
if (results.length > 0) {
|
|
110
|
+
allResults.push(...results);
|
|
111
|
+
fileResultsMap.set(filePath, results);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (allResults.length === 0) {
|
|
115
|
+
throw new NoSchemasFoundError(resolvedFiles, schemaFilter);
|
|
116
|
+
}
|
|
117
|
+
const content = generateDeclarationFile(allResults, nameMapper.createMapFunction(), declOptions);
|
|
118
|
+
const outputPath = resolve(cwd, config.outFile);
|
|
119
|
+
if (options.dryRun) {
|
|
120
|
+
console.log(`Would write to: ${outputPath}`);
|
|
121
|
+
console.log("---");
|
|
122
|
+
console.log(content);
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
ensureDir(dirname(outputPath));
|
|
126
|
+
writeFile(outputPath, content);
|
|
127
|
+
console.log(`Generated: ${outputPath} (${allResults.length} types)`);
|
|
128
|
+
}
|
|
129
|
+
// Generate test file if requested
|
|
130
|
+
if (config.generateTests) {
|
|
131
|
+
const testPath = outputPath.replace(/\.ts$/, ".test.ts");
|
|
132
|
+
const testContent = generateTestFileForSingleOutput(fileResultsMap, outputPath, testPath, nameMapper);
|
|
133
|
+
if (options.dryRun) {
|
|
134
|
+
console.log(`Would write to: ${testPath}`);
|
|
135
|
+
console.log("---");
|
|
136
|
+
console.log(testContent);
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
writeFile(testPath, testContent);
|
|
140
|
+
console.log(`Generated: ${testPath} (${allResults.length * 2} test cases)`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
// Per-file output mode or console output
|
|
146
|
+
logVerbose("Processing files...");
|
|
147
|
+
let totalResults = 0;
|
|
148
|
+
for (let i = 0; i < resolvedFiles.length; i++) {
|
|
149
|
+
const filePath = resolvedFiles[i];
|
|
150
|
+
logProgress(i + 1, resolvedFiles.length, `Processing ${basename(filePath)}`);
|
|
151
|
+
let results = getFilteredResults(extractor, filePath, schemaFilter);
|
|
152
|
+
if (results.length === 0) {
|
|
153
|
+
logVerbose(` No schemas found in ${basename(filePath)}`);
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
totalResults += results.length;
|
|
157
|
+
// Add descriptions if enabled
|
|
158
|
+
if (descriptionExtractor) {
|
|
159
|
+
results = await addDescriptionsToResults(descriptionExtractor, filePath, results);
|
|
160
|
+
}
|
|
161
|
+
// File output mode
|
|
162
|
+
if (config.outDir || config.outPattern) {
|
|
163
|
+
const content = generateDeclarationFile(results, nameMapper.createMapFunction(), declOptions);
|
|
164
|
+
const outputPath = fileResolver.resolveOutputPath(filePath, outputOptions, cwd);
|
|
165
|
+
if (options.dryRun) {
|
|
166
|
+
console.log(`Would write to: ${outputPath}`);
|
|
167
|
+
console.log("---");
|
|
168
|
+
console.log(content);
|
|
169
|
+
console.log("");
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
ensureDir(dirname(outputPath));
|
|
173
|
+
writeFile(outputPath, content);
|
|
174
|
+
console.log(`Generated: ${outputPath} (${results.length} types)`);
|
|
175
|
+
}
|
|
176
|
+
// Generate test file if requested
|
|
177
|
+
if (config.generateTests) {
|
|
178
|
+
const testPath = outputPath.replace(/\.ts$/, ".test.ts");
|
|
179
|
+
const testContent = generateTestFileForPerFile(filePath, outputPath, testPath, results, nameMapper);
|
|
180
|
+
if (options.dryRun) {
|
|
181
|
+
console.log(`Would write to: ${testPath}`);
|
|
182
|
+
console.log("---");
|
|
183
|
+
console.log(testContent);
|
|
184
|
+
console.log("");
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
writeFile(testPath, testContent);
|
|
188
|
+
console.log(`Generated: ${testPath} (${results.length * 2} test cases)`);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
// Console output mode
|
|
194
|
+
if (resolvedFiles.length > 1) {
|
|
195
|
+
console.log(`// File: ${filePath}`);
|
|
196
|
+
}
|
|
197
|
+
const content = generateDeclarationFile(results, nameMapper.createMapFunction(), declOptions);
|
|
198
|
+
console.log(content);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
// Error if no schemas were found
|
|
202
|
+
if (totalResults === 0) {
|
|
203
|
+
throw new NoSchemasFoundError(resolvedFiles, schemaFilter);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Adds descriptions from Zod schemas to extraction results.
|
|
208
|
+
*/
|
|
209
|
+
async function addDescriptionsToResults(descriptionExtractor, filePath, results) {
|
|
210
|
+
const schemaNames = results.map((r) => r.schemaName);
|
|
211
|
+
const descriptions = await descriptionExtractor.extractDescriptions(filePath, schemaNames);
|
|
212
|
+
return results.map((result) => {
|
|
213
|
+
const desc = descriptions.get(result.schemaName);
|
|
214
|
+
if (!desc) {
|
|
215
|
+
return result;
|
|
216
|
+
}
|
|
217
|
+
return {
|
|
218
|
+
...result,
|
|
219
|
+
description: desc.description,
|
|
220
|
+
fieldDescriptions: desc.fields,
|
|
221
|
+
};
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Gets extraction results, filtering by schema names if specified.
|
|
226
|
+
* Only extracts schemas that actually exist in the file.
|
|
227
|
+
*/
|
|
228
|
+
function getFilteredResults(extractor, filePath, schemaFilter) {
|
|
229
|
+
if (!schemaFilter) {
|
|
230
|
+
return extractor.extractAll(filePath);
|
|
231
|
+
}
|
|
232
|
+
const existingSchemas = extractor.getSchemaNames(filePath);
|
|
233
|
+
const schemasToExtract = schemaFilter.filter((name) => existingSchemas.includes(name));
|
|
234
|
+
if (schemasToExtract.length === 0) {
|
|
235
|
+
return [];
|
|
236
|
+
}
|
|
237
|
+
return extractor.extractMultiple(filePath, schemasToExtract);
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Merges CLI options with config file options.
|
|
241
|
+
* CLI options take precedence.
|
|
242
|
+
*/
|
|
243
|
+
function mergeCliWithConfig(cliOptions, fileConfig) {
|
|
244
|
+
const merged = { ...fileConfig };
|
|
245
|
+
// Merge CLI options (only non-undefined values)
|
|
246
|
+
if (cliOptions.project !== undefined)
|
|
247
|
+
merged.project = cliOptions.project;
|
|
248
|
+
if (cliOptions.schemas !== undefined) {
|
|
249
|
+
merged.schemas = parseSchemaNames(cliOptions.schemas);
|
|
250
|
+
}
|
|
251
|
+
if (cliOptions.inputOnly !== undefined)
|
|
252
|
+
merged.inputOnly = cliOptions.inputOnly;
|
|
253
|
+
if (cliOptions.outputOnly !== undefined)
|
|
254
|
+
merged.outputOnly = cliOptions.outputOnly;
|
|
255
|
+
if (cliOptions.mergeSame !== undefined)
|
|
256
|
+
merged.mergeSame = cliOptions.mergeSame;
|
|
257
|
+
if (cliOptions.suffix !== undefined)
|
|
258
|
+
merged.suffix = cliOptions.suffix;
|
|
259
|
+
if (cliOptions.inputSuffix !== undefined)
|
|
260
|
+
merged.inputSuffix = cliOptions.inputSuffix;
|
|
261
|
+
if (cliOptions.outputSuffix !== undefined)
|
|
262
|
+
merged.outputSuffix = cliOptions.outputSuffix;
|
|
263
|
+
if (cliOptions.map !== undefined) {
|
|
264
|
+
merged.map = parseCustomMap(cliOptions.map);
|
|
265
|
+
}
|
|
266
|
+
if (cliOptions.outDir !== undefined)
|
|
267
|
+
merged.outDir = cliOptions.outDir;
|
|
268
|
+
if (cliOptions.outFile !== undefined)
|
|
269
|
+
merged.outFile = cliOptions.outFile;
|
|
270
|
+
if (cliOptions.outPattern !== undefined)
|
|
271
|
+
merged.outPattern = cliOptions.outPattern;
|
|
272
|
+
if (cliOptions.declaration !== undefined)
|
|
273
|
+
merged.declaration = cliOptions.declaration;
|
|
274
|
+
if (cliOptions.withDescriptions !== undefined)
|
|
275
|
+
merged.withDescriptions = cliOptions.withDescriptions;
|
|
276
|
+
if (cliOptions.generateTests !== undefined)
|
|
277
|
+
merged.generateTests = cliOptions.generateTests;
|
|
278
|
+
return merged;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Validates CLI options for conflicts and invalid values.
|
|
282
|
+
* @throws InvalidOptionError if any validation fails
|
|
283
|
+
*/
|
|
284
|
+
function validateOptions(config) {
|
|
285
|
+
// Check mutually exclusive options
|
|
286
|
+
if (config.inputOnly && config.outputOnly) {
|
|
287
|
+
throw new InvalidOptionError("--input-only / --output-only", "Cannot use both options together", "Use only one of --input-only or --output-only");
|
|
288
|
+
}
|
|
289
|
+
// Check conflicting output options
|
|
290
|
+
if (config.outFile && (config.outDir || config.outPattern)) {
|
|
291
|
+
throw new InvalidOptionError("--outFile", "Cannot use with --outDir or --outPattern", "Use --outFile for single output, or --outDir/--outPattern for multiple outputs");
|
|
292
|
+
}
|
|
293
|
+
// Check empty suffix
|
|
294
|
+
if (config.suffix === "") {
|
|
295
|
+
throw new InvalidOptionError("--suffix", "Empty suffix is not allowed", "Provide a non-empty suffix value or omit the option");
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Creates a NameMapper from config.
|
|
300
|
+
*/
|
|
301
|
+
function createNameMapper(config) {
|
|
302
|
+
const mappingOptions = {};
|
|
303
|
+
if (config.suffix) {
|
|
304
|
+
mappingOptions.removeSuffix = config.suffix;
|
|
305
|
+
}
|
|
306
|
+
if (config.inputSuffix != null) {
|
|
307
|
+
mappingOptions.inputSuffix = config.inputSuffix;
|
|
308
|
+
}
|
|
309
|
+
if (config.outputSuffix != null) {
|
|
310
|
+
mappingOptions.outputSuffix = config.outputSuffix;
|
|
311
|
+
}
|
|
312
|
+
if (config.map) {
|
|
313
|
+
mappingOptions.customMap = config.map;
|
|
314
|
+
}
|
|
315
|
+
return new NameMapper(mappingOptions);
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Parses schema names from a comma-separated string.
|
|
319
|
+
* Filters out empty strings and validates identifier format.
|
|
320
|
+
*
|
|
321
|
+
* @param schemasStr - Comma-separated schema names
|
|
322
|
+
* @returns Array of valid schema names
|
|
323
|
+
* @throws Error if any schema name is invalid
|
|
324
|
+
*/
|
|
325
|
+
function parseSchemaNames(schemasStr) {
|
|
326
|
+
const schemas = schemasStr
|
|
327
|
+
.split(",")
|
|
328
|
+
.map((s) => s.trim())
|
|
329
|
+
.filter((s) => s.length > 0);
|
|
330
|
+
// Validate that each schema name is a valid identifier
|
|
331
|
+
const identifierPattern = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
|
|
332
|
+
for (const schema of schemas) {
|
|
333
|
+
if (!identifierPattern.test(schema)) {
|
|
334
|
+
throw new Error(`Invalid schema name: "${schema}". Schema names must be valid TypeScript identifiers.`);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
return schemas;
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Parses custom mapping string: "Schema1:Type1,Schema2:Type2"
|
|
341
|
+
* Validates both schema names and type names.
|
|
342
|
+
*
|
|
343
|
+
* @param mapStr - Custom mapping string
|
|
344
|
+
* @returns Map of schema names to type names
|
|
345
|
+
* @throws Error if any mapping is invalid
|
|
346
|
+
*/
|
|
347
|
+
function parseCustomMap(mapStr) {
|
|
348
|
+
const result = {};
|
|
349
|
+
const identifierPattern = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
|
|
350
|
+
for (const pair of mapStr.split(",")) {
|
|
351
|
+
const trimmedPair = pair.trim();
|
|
352
|
+
if (!trimmedPair)
|
|
353
|
+
continue;
|
|
354
|
+
const colonIndex = trimmedPair.indexOf(":");
|
|
355
|
+
if (colonIndex === -1) {
|
|
356
|
+
throw new Error(`Invalid mapping format: "${trimmedPair}". Expected "SchemaName:TypeName".`);
|
|
357
|
+
}
|
|
358
|
+
const from = trimmedPair.substring(0, colonIndex).trim();
|
|
359
|
+
const to = trimmedPair.substring(colonIndex + 1).trim();
|
|
360
|
+
if (!from || !to) {
|
|
361
|
+
throw new Error(`Invalid mapping: "${trimmedPair}". Both schema name and type name are required.`);
|
|
362
|
+
}
|
|
363
|
+
if (!identifierPattern.test(from)) {
|
|
364
|
+
throw new Error(`Invalid schema name in mapping: "${from}". Must be a valid TypeScript identifier.`);
|
|
365
|
+
}
|
|
366
|
+
if (!identifierPattern.test(to)) {
|
|
367
|
+
throw new Error(`Invalid type name in mapping: "${to}". Must be a valid TypeScript identifier.`);
|
|
368
|
+
}
|
|
369
|
+
if (result[from] !== undefined) {
|
|
370
|
+
console.warn(`Warning: Duplicate mapping for "${from}". Using "${to}" (overwriting "${result[from]}").`);
|
|
371
|
+
}
|
|
372
|
+
result[from] = to;
|
|
373
|
+
}
|
|
374
|
+
return result;
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Finds tsconfig.json starting from the given directory.
|
|
378
|
+
*/
|
|
379
|
+
function findTsConfig(startDir) {
|
|
380
|
+
let currentDir = startDir;
|
|
381
|
+
// Get the root directory in a cross-platform way (e.g., "/" on Unix, "C:\" on Windows)
|
|
382
|
+
const root = parsePath(resolve(startDir)).root;
|
|
383
|
+
while (currentDir !== root) {
|
|
384
|
+
const tsconfigPath = resolve(currentDir, "tsconfig.json");
|
|
385
|
+
if (existsSync(tsconfigPath)) {
|
|
386
|
+
return tsconfigPath;
|
|
387
|
+
}
|
|
388
|
+
const parentDir = resolve(currentDir, "..");
|
|
389
|
+
// Prevent infinite loop at root
|
|
390
|
+
if (parentDir === currentDir) {
|
|
391
|
+
break;
|
|
392
|
+
}
|
|
393
|
+
currentDir = parentDir;
|
|
394
|
+
}
|
|
395
|
+
// Check root directory as well
|
|
396
|
+
const rootTsconfig = resolve(root, "tsconfig.json");
|
|
397
|
+
if (existsSync(rootTsconfig)) {
|
|
398
|
+
return rootTsconfig;
|
|
399
|
+
}
|
|
400
|
+
return undefined;
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Ensures a directory exists.
|
|
404
|
+
* @throws Error if the directory cannot be created
|
|
405
|
+
*/
|
|
406
|
+
function ensureDir(dirPath) {
|
|
407
|
+
if (!existsSync(dirPath)) {
|
|
408
|
+
try {
|
|
409
|
+
mkdirSync(dirPath, { recursive: true });
|
|
410
|
+
}
|
|
411
|
+
catch (error) {
|
|
412
|
+
const err = error;
|
|
413
|
+
throw new Error(`Failed to create directory "${dirPath}": ${err.message}`);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Writes content to a file with proper error handling.
|
|
419
|
+
* @throws Error if the file cannot be written
|
|
420
|
+
*/
|
|
421
|
+
function writeFile(filePath, content) {
|
|
422
|
+
try {
|
|
423
|
+
writeFileSync(filePath, content, "utf-8");
|
|
424
|
+
}
|
|
425
|
+
catch (error) {
|
|
426
|
+
const err = error;
|
|
427
|
+
throw new Error(`Failed to write file "${filePath}": ${err.message}`);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Creates test schema info from extraction results.
|
|
432
|
+
*/
|
|
433
|
+
function createTestSchemas(results, nameMapper) {
|
|
434
|
+
return results
|
|
435
|
+
.filter((r) => r.isExported)
|
|
436
|
+
.map((result) => ({
|
|
437
|
+
schemaName: result.schemaName,
|
|
438
|
+
inputTypeName: nameMapper.map(result.schemaName).inputName,
|
|
439
|
+
outputTypeName: nameMapper.map(result.schemaName).outputName,
|
|
440
|
+
}));
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Generates test file content for single output mode (--outFile).
|
|
444
|
+
*/
|
|
445
|
+
function generateTestFileForSingleOutput(fileResultsMap, typesPath, testPath, nameMapper) {
|
|
446
|
+
const testFiles = [];
|
|
447
|
+
const testDir = dirname(testPath);
|
|
448
|
+
for (const [schemaFile, results] of fileResultsMap) {
|
|
449
|
+
const schemas = createTestSchemas(results, nameMapper);
|
|
450
|
+
if (schemas.length === 0)
|
|
451
|
+
continue;
|
|
452
|
+
testFiles.push({
|
|
453
|
+
schemaFilePath: getRelativePath(testDir, schemaFile),
|
|
454
|
+
typesFilePath: getRelativePath(testDir, typesPath),
|
|
455
|
+
importPrefix: generateImportPrefix(basename(schemaFile, ".ts")),
|
|
456
|
+
schemas,
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
return generateTypeTests(testFiles);
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* Generates test file content for per-file output mode (--outDir).
|
|
463
|
+
*/
|
|
464
|
+
function generateTestFileForPerFile(schemaFile, typesPath, testPath, results, nameMapper) {
|
|
465
|
+
const schemas = createTestSchemas(results, nameMapper);
|
|
466
|
+
if (schemas.length === 0) {
|
|
467
|
+
return "";
|
|
468
|
+
}
|
|
469
|
+
const testDir = dirname(testPath);
|
|
470
|
+
return generateTypeTests([
|
|
471
|
+
{
|
|
472
|
+
schemaFilePath: getRelativePath(testDir, schemaFile),
|
|
473
|
+
typesFilePath: getRelativePath(testDir, typesPath),
|
|
474
|
+
importPrefix: generateImportPrefix(basename(schemaFile, ".ts")),
|
|
475
|
+
schemas,
|
|
476
|
+
},
|
|
477
|
+
]);
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Gets relative path from one file to another, ensuring it starts with ./
|
|
481
|
+
*/
|
|
482
|
+
function getRelativePath(from, to) {
|
|
483
|
+
const rel = relative(from, to);
|
|
484
|
+
return rel.startsWith(".") ? rel : "./" + rel;
|
|
485
|
+
}
|
|
486
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { SourceFile } from "ts-morph";
|
|
2
|
+
import type { BrandInfo } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Map of schema name to its brand information.
|
|
5
|
+
*/
|
|
6
|
+
export type SchemaBrandMap = Map<string, BrandInfo[]>;
|
|
7
|
+
/**
|
|
8
|
+
* Detects .brand<...>() calls in Zod schemas.
|
|
9
|
+
*/
|
|
10
|
+
export declare class BrandDetector {
|
|
11
|
+
/**
|
|
12
|
+
* Analyzes a source file to find all branded types in schemas.
|
|
13
|
+
*
|
|
14
|
+
* @param sourceFile - The source file to analyze
|
|
15
|
+
* @param schemaNames - Set of schema names to analyze
|
|
16
|
+
* @returns Map of schema name to brand information
|
|
17
|
+
*/
|
|
18
|
+
detectBrands(sourceFile: SourceFile, schemaNames: Set<string>): SchemaBrandMap;
|
|
19
|
+
/**
|
|
20
|
+
* Recursively finds brand calls in a node.
|
|
21
|
+
*/
|
|
22
|
+
private findBrandsInNode;
|
|
23
|
+
/**
|
|
24
|
+
* Extracts brand information from a .brand<...>() call.
|
|
25
|
+
*/
|
|
26
|
+
private extractBrandFromCall;
|
|
27
|
+
/**
|
|
28
|
+
* Extracts the brand name from a type argument.
|
|
29
|
+
*/
|
|
30
|
+
private extractBrandName;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=brand-detector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"brand-detector.d.ts","sourceRoot":"","sources":["../../src/core/brand-detector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAwB,MAAM,UAAU,CAAC;AAC5D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,GAAG,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;AAEtD;;GAEG;AACH,qBAAa,aAAa;IACxB;;;;;;OAMG;IACH,YAAY,CAAC,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,cAAc;IAsB9E;;OAEG;IACH,OAAO,CAAC,gBAAgB;IA8CxB;;OAEG;IACH,OAAO,CAAC,oBAAoB;IA6B5B;;OAEG;IACH,OAAO,CAAC,gBAAgB;CAkBzB"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { Node } from "ts-morph";
|
|
2
|
+
/**
|
|
3
|
+
* Detects .brand<...>() calls in Zod schemas.
|
|
4
|
+
*/
|
|
5
|
+
export class BrandDetector {
|
|
6
|
+
/**
|
|
7
|
+
* Analyzes a source file to find all branded types in schemas.
|
|
8
|
+
*
|
|
9
|
+
* @param sourceFile - The source file to analyze
|
|
10
|
+
* @param schemaNames - Set of schema names to analyze
|
|
11
|
+
* @returns Map of schema name to brand information
|
|
12
|
+
*/
|
|
13
|
+
detectBrands(sourceFile, schemaNames) {
|
|
14
|
+
const result = new Map();
|
|
15
|
+
const statements = sourceFile.getVariableStatements();
|
|
16
|
+
for (const stmt of statements) {
|
|
17
|
+
for (const decl of stmt.getDeclarations()) {
|
|
18
|
+
const schemaName = decl.getName();
|
|
19
|
+
if (!schemaNames.has(schemaName))
|
|
20
|
+
continue;
|
|
21
|
+
const init = decl.getInitializer();
|
|
22
|
+
if (!init)
|
|
23
|
+
continue;
|
|
24
|
+
const brands = this.findBrandsInNode(init, "");
|
|
25
|
+
if (brands.length > 0) {
|
|
26
|
+
result.set(schemaName, brands);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Recursively finds brand calls in a node.
|
|
34
|
+
*/
|
|
35
|
+
findBrandsInNode(node, currentPath) {
|
|
36
|
+
const brands = [];
|
|
37
|
+
// Check if this node is a .brand<...>() call
|
|
38
|
+
if (Node.isCallExpression(node)) {
|
|
39
|
+
const brandInfo = this.extractBrandFromCall(node, currentPath);
|
|
40
|
+
if (brandInfo) {
|
|
41
|
+
brands.push(brandInfo);
|
|
42
|
+
}
|
|
43
|
+
// Check the expression being called (for method chains)
|
|
44
|
+
const expr = node.getExpression();
|
|
45
|
+
if (Node.isPropertyAccessExpression(expr)) {
|
|
46
|
+
const base = expr.getExpression();
|
|
47
|
+
brands.push(...this.findBrandsInNode(base, currentPath));
|
|
48
|
+
}
|
|
49
|
+
// Check arguments (for z.object({ field: schema }))
|
|
50
|
+
for (const arg of node.getArguments()) {
|
|
51
|
+
brands.push(...this.findBrandsInNode(arg, currentPath));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// Check object literals for field definitions
|
|
55
|
+
if (Node.isObjectLiteralExpression(node)) {
|
|
56
|
+
for (const prop of node.getProperties()) {
|
|
57
|
+
if (Node.isPropertyAssignment(prop)) {
|
|
58
|
+
const fieldName = prop.getName();
|
|
59
|
+
const fieldPath = currentPath ? `${currentPath}.${fieldName}` : fieldName;
|
|
60
|
+
const initializer = prop.getInitializer();
|
|
61
|
+
if (initializer) {
|
|
62
|
+
brands.push(...this.findBrandsInNode(initializer, fieldPath));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// Check property access expressions (method chains)
|
|
68
|
+
if (Node.isPropertyAccessExpression(node)) {
|
|
69
|
+
const base = node.getExpression();
|
|
70
|
+
brands.push(...this.findBrandsInNode(base, currentPath));
|
|
71
|
+
}
|
|
72
|
+
return brands;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Extracts brand information from a .brand<...>() call.
|
|
76
|
+
*/
|
|
77
|
+
extractBrandFromCall(node, fieldPath) {
|
|
78
|
+
const expr = node.getExpression();
|
|
79
|
+
if (!Node.isPropertyAccessExpression(expr)) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
const methodName = expr.getName();
|
|
83
|
+
if (methodName !== "brand") {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
// Get the type argument: .brand<"UserId">()
|
|
87
|
+
const typeArgs = node.getTypeArguments();
|
|
88
|
+
if (typeArgs.length === 0) {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
const typeArg = typeArgs[0];
|
|
92
|
+
const brandName = this.extractBrandName(typeArg);
|
|
93
|
+
if (!brandName) {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
brandName,
|
|
98
|
+
fieldPath,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Extracts the brand name from a type argument.
|
|
103
|
+
*/
|
|
104
|
+
extractBrandName(node) {
|
|
105
|
+
// Handle string literal type: "UserId"
|
|
106
|
+
if (Node.isLiteralTypeNode(node)) {
|
|
107
|
+
const literal = node.getLiteral();
|
|
108
|
+
if (Node.isStringLiteral(literal)) {
|
|
109
|
+
return literal.getLiteralText();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// Handle direct string literal (some TS versions)
|
|
113
|
+
const text = node.getText();
|
|
114
|
+
const match = text.match(/^["'](.+)["']$/);
|
|
115
|
+
if (match) {
|
|
116
|
+
return match[1];
|
|
117
|
+
}
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=brand-detector.js.map
|