zinfer 0.0.1 → 0.1.1
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 +484 -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 +85 -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 +494 -0
- package/dist/core/extractor.js.map +1 -0
- package/dist/core/file-resolver.d.ts +39 -0
- package/dist/core/file-resolver.d.ts.map +1 -0
- package/dist/core/file-resolver.js +88 -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 +424 -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
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { OutputOptions } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Resolves file paths from glob patterns and generates output paths.
|
|
4
|
+
*/
|
|
5
|
+
export declare class FileResolver {
|
|
6
|
+
/**
|
|
7
|
+
* Resolves input file paths from a glob pattern or array of patterns.
|
|
8
|
+
*
|
|
9
|
+
* @param pattern - Glob pattern(s) to match
|
|
10
|
+
* @param cwd - Current working directory (default: process.cwd())
|
|
11
|
+
* @returns Array of absolute file paths
|
|
12
|
+
*/
|
|
13
|
+
resolveInputFiles(pattern: string | string[], cwd?: string): Promise<string[]>;
|
|
14
|
+
/**
|
|
15
|
+
* Resolves the output path for a given input file.
|
|
16
|
+
*
|
|
17
|
+
* @param inputPath - Absolute path to the input file
|
|
18
|
+
* @param options - Output options
|
|
19
|
+
* @param cwd - Current working directory
|
|
20
|
+
* @returns Absolute path to the output file
|
|
21
|
+
*/
|
|
22
|
+
resolveOutputPath(inputPath: string, options: OutputOptions, cwd?: string): string;
|
|
23
|
+
/**
|
|
24
|
+
* Applies a pattern template to generate an output filename.
|
|
25
|
+
*
|
|
26
|
+
* Supported placeholders:
|
|
27
|
+
* - [name]: Input filename without extension
|
|
28
|
+
* - [ext]: Output extension (including dot)
|
|
29
|
+
*
|
|
30
|
+
* @param pattern - Pattern template (e.g., "[name].types[ext]")
|
|
31
|
+
* @param vars - Variables to substitute
|
|
32
|
+
* @returns Generated filename
|
|
33
|
+
*/
|
|
34
|
+
applyPattern(pattern: string, vars: {
|
|
35
|
+
name: string;
|
|
36
|
+
ext: string;
|
|
37
|
+
}): string;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=file-resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-resolver.d.ts","sourceRoot":"","sources":["../../src/core/file-resolver.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD;;GAEG;AACH,qBAAa,YAAY;IACvB;;;;;;OAMG;IACG,iBAAiB,CACrB,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,EAC1B,GAAG,GAAE,MAAsB,GAC1B,OAAO,CAAC,MAAM,EAAE,CAAC;IAiBpB;;;;;;;OAOG;IACH,iBAAiB,CACf,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,aAAa,EACtB,GAAG,GAAE,MAAsB,GAC1B,MAAM;IAoCT;;;;;;;;;;OAUG;IACH,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM;CAO3E"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { glob } from "glob";
|
|
2
|
+
import { resolve, dirname, basename, extname, join } from "pathe";
|
|
3
|
+
/**
|
|
4
|
+
* Resolves file paths from glob patterns and generates output paths.
|
|
5
|
+
*/
|
|
6
|
+
export class FileResolver {
|
|
7
|
+
/**
|
|
8
|
+
* Resolves input file paths from a glob pattern or array of patterns.
|
|
9
|
+
*
|
|
10
|
+
* @param pattern - Glob pattern(s) to match
|
|
11
|
+
* @param cwd - Current working directory (default: process.cwd())
|
|
12
|
+
* @returns Array of absolute file paths
|
|
13
|
+
*/
|
|
14
|
+
async resolveInputFiles(pattern, cwd = process.cwd()) {
|
|
15
|
+
const patterns = Array.isArray(pattern) ? pattern : [pattern];
|
|
16
|
+
const allFiles = [];
|
|
17
|
+
for (const p of patterns) {
|
|
18
|
+
const files = await glob(p, {
|
|
19
|
+
cwd,
|
|
20
|
+
absolute: true,
|
|
21
|
+
nodir: true,
|
|
22
|
+
});
|
|
23
|
+
allFiles.push(...files);
|
|
24
|
+
}
|
|
25
|
+
// Remove duplicates and sort
|
|
26
|
+
return [...new Set(allFiles)].sort();
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Resolves the output path for a given input file.
|
|
30
|
+
*
|
|
31
|
+
* @param inputPath - Absolute path to the input file
|
|
32
|
+
* @param options - Output options
|
|
33
|
+
* @param cwd - Current working directory
|
|
34
|
+
* @returns Absolute path to the output file
|
|
35
|
+
*/
|
|
36
|
+
resolveOutputPath(inputPath, options, cwd = process.cwd()) {
|
|
37
|
+
// If outFile is specified, use it directly
|
|
38
|
+
if (options.outFile) {
|
|
39
|
+
return resolve(cwd, options.outFile);
|
|
40
|
+
}
|
|
41
|
+
// Determine base name and extension
|
|
42
|
+
const inputBasename = basename(inputPath);
|
|
43
|
+
const inputExt = extname(inputPath);
|
|
44
|
+
const inputName = inputBasename.slice(0, -inputExt.length);
|
|
45
|
+
// Apply output pattern
|
|
46
|
+
let outputName;
|
|
47
|
+
if (options.outPattern) {
|
|
48
|
+
outputName = this.applyPattern(options.outPattern, {
|
|
49
|
+
name: inputName,
|
|
50
|
+
ext: options.declaration ? ".d.ts" : ".ts",
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
// Default pattern: [name].types.ts or [name].types.d.ts
|
|
55
|
+
const ext = options.declaration ? ".d.ts" : ".ts";
|
|
56
|
+
outputName = `${inputName}.types${ext}`;
|
|
57
|
+
}
|
|
58
|
+
// Determine output directory
|
|
59
|
+
let outputDir;
|
|
60
|
+
if (options.outDir) {
|
|
61
|
+
outputDir = resolve(cwd, options.outDir);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
// Same directory as input
|
|
65
|
+
outputDir = dirname(inputPath);
|
|
66
|
+
}
|
|
67
|
+
return join(outputDir, outputName);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Applies a pattern template to generate an output filename.
|
|
71
|
+
*
|
|
72
|
+
* Supported placeholders:
|
|
73
|
+
* - [name]: Input filename without extension
|
|
74
|
+
* - [ext]: Output extension (including dot)
|
|
75
|
+
*
|
|
76
|
+
* @param pattern - Pattern template (e.g., "[name].types[ext]")
|
|
77
|
+
* @param vars - Variables to substitute
|
|
78
|
+
* @returns Generated filename
|
|
79
|
+
*/
|
|
80
|
+
applyPattern(pattern, vars) {
|
|
81
|
+
// Escape $ in replacement strings to prevent regex special character interpretation
|
|
82
|
+
// In String.replace(), $& means the matched substring, $1 means first capture group, etc.
|
|
83
|
+
const escapedName = vars.name.replace(/\$/g, "$$$$");
|
|
84
|
+
const escapedExt = vars.ext.replace(/\$/g, "$$$$");
|
|
85
|
+
return pattern.replace(/\[name\]/g, escapedName).replace(/\[ext\]/g, escapedExt);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=file-resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-resolver.js","sourceRoot":"","sources":["../../src/core/file-resolver.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { SourceFile } from "ts-morph";
|
|
2
|
+
/**
|
|
3
|
+
* Information about a getter field in a z.object schema.
|
|
4
|
+
*/
|
|
5
|
+
export interface GetterFieldInfo {
|
|
6
|
+
/** Referenced schema name */
|
|
7
|
+
refSchema: string;
|
|
8
|
+
/** Whether the reference is wrapped in z.array() */
|
|
9
|
+
isArray: boolean;
|
|
10
|
+
/** Whether the reference is wrapped in z.record() */
|
|
11
|
+
isRecord: boolean;
|
|
12
|
+
/** Whether the field is optional (.optional() or .nullable()) */
|
|
13
|
+
isOptional: boolean;
|
|
14
|
+
/** Whether this is a self-reference */
|
|
15
|
+
isSelfRef: boolean;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Mapping of schema name to its getter field information.
|
|
19
|
+
*/
|
|
20
|
+
export type GetterFieldMap = Map<string, Map<string, GetterFieldInfo>>;
|
|
21
|
+
/**
|
|
22
|
+
* Detects and resolves getter-based recursive patterns in Zod schemas.
|
|
23
|
+
*/
|
|
24
|
+
export declare class GetterResolver {
|
|
25
|
+
/**
|
|
26
|
+
* Analyzes a source file to find getter field mappings.
|
|
27
|
+
*
|
|
28
|
+
* @param sourceFile - The ts-morph SourceFile to analyze
|
|
29
|
+
* @returns Map of schema name to field info
|
|
30
|
+
*/
|
|
31
|
+
analyzeGetterFields(sourceFile: SourceFile): GetterFieldMap;
|
|
32
|
+
/**
|
|
33
|
+
* Extracts getter field info from AST nodes.
|
|
34
|
+
*/
|
|
35
|
+
private extractGetterFieldsFromAST;
|
|
36
|
+
/**
|
|
37
|
+
* Parses the return expression AST to extract schema reference info.
|
|
38
|
+
*/
|
|
39
|
+
private parseReturnExpressionAST;
|
|
40
|
+
/**
|
|
41
|
+
* Extracts schema reference from an argument node.
|
|
42
|
+
*/
|
|
43
|
+
private extractSchemaRef;
|
|
44
|
+
/**
|
|
45
|
+
* Resolves `any` types in extracted type string by replacing with self-references.
|
|
46
|
+
* Uses structured parsing instead of regex.
|
|
47
|
+
*
|
|
48
|
+
* @param typeStr - The extracted type string with `any` placeholders
|
|
49
|
+
* @param getterFields - Map of field name to getter field info
|
|
50
|
+
* @param typeName - The generated type name to use for self-references
|
|
51
|
+
* @returns The resolved type string with proper self-references
|
|
52
|
+
*/
|
|
53
|
+
resolveAnyTypes(typeStr: string, getterFields: Map<string, GetterFieldInfo>, typeName: string): string;
|
|
54
|
+
/**
|
|
55
|
+
* Replaces any type in a record field.
|
|
56
|
+
*/
|
|
57
|
+
private replaceRecordAny;
|
|
58
|
+
/**
|
|
59
|
+
* Replaces any type in a regular field.
|
|
60
|
+
*/
|
|
61
|
+
private replaceFieldAny;
|
|
62
|
+
/**
|
|
63
|
+
* Checks if a schema has getter-based self-references.
|
|
64
|
+
*/
|
|
65
|
+
hasSelfReferences(getterFields: Map<string, GetterFieldInfo>): boolean;
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=getter-resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getter-resolver.d.ts","sourceRoot":"","sources":["../../src/core/getter-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAA8D,MAAM,UAAU,CAAC;AAElG;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,6BAA6B;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,OAAO,EAAE,OAAO,CAAC;IACjB,qDAAqD;IACrD,QAAQ,EAAE,OAAO,CAAC;IAClB,iEAAiE;IACjE,UAAU,EAAE,OAAO,CAAC;IACpB,uCAAuC;IACvC,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC,CAAC;AAEvE;;GAEG;AACH,qBAAa,cAAc;IACzB;;;;;OAKG;IACH,mBAAmB,CAAC,UAAU,EAAE,UAAU,GAAG,cAAc;IAqB3D;;OAEG;IACH,OAAO,CAAC,0BAA0B;IA2BlC;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAyEhC;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAOxB;;;;;;;;OAQG;IACH,eAAe,CACb,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,EAC1C,QAAQ,EAAE,MAAM,GACf,MAAM;IAoBT;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAkCxB;;OAEG;IACH,OAAO,CAAC,eAAe;IAuDvB;;OAEG;IACH,iBAAiB,CAAC,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,GAAG,OAAO;CAGvE"}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { SyntaxKind, Node } from "ts-morph";
|
|
2
|
+
/**
|
|
3
|
+
* Detects and resolves getter-based recursive patterns in Zod schemas.
|
|
4
|
+
*/
|
|
5
|
+
export class GetterResolver {
|
|
6
|
+
/**
|
|
7
|
+
* Analyzes a source file to find getter field mappings.
|
|
8
|
+
*
|
|
9
|
+
* @param sourceFile - The ts-morph SourceFile to analyze
|
|
10
|
+
* @returns Map of schema name to field info
|
|
11
|
+
*/
|
|
12
|
+
analyzeGetterFields(sourceFile) {
|
|
13
|
+
const result = new Map();
|
|
14
|
+
const statements = sourceFile.getVariableStatements();
|
|
15
|
+
for (const stmt of statements) {
|
|
16
|
+
for (const decl of stmt.getDeclarations()) {
|
|
17
|
+
const schemaName = decl.getName();
|
|
18
|
+
const init = decl.getInitializer();
|
|
19
|
+
if (!init)
|
|
20
|
+
continue;
|
|
21
|
+
const fieldMap = this.extractGetterFieldsFromAST(init, schemaName);
|
|
22
|
+
if (fieldMap.size > 0) {
|
|
23
|
+
result.set(schemaName, fieldMap);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return result;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Extracts getter field info from AST nodes.
|
|
31
|
+
*/
|
|
32
|
+
extractGetterFieldsFromAST(node, schemaName) {
|
|
33
|
+
const fieldMap = new Map();
|
|
34
|
+
// Find all getter declarations within the node
|
|
35
|
+
const getters = node.getDescendantsOfKind(SyntaxKind.GetAccessor);
|
|
36
|
+
for (const getter of getters) {
|
|
37
|
+
const fieldName = getter.getName();
|
|
38
|
+
const body = getter.getBody();
|
|
39
|
+
if (!body)
|
|
40
|
+
continue;
|
|
41
|
+
// Find return statement
|
|
42
|
+
const returnStmt = body.getFirstDescendantByKind(SyntaxKind.ReturnStatement);
|
|
43
|
+
if (!returnStmt)
|
|
44
|
+
continue;
|
|
45
|
+
const returnExpr = returnStmt.getExpression();
|
|
46
|
+
if (!returnExpr)
|
|
47
|
+
continue;
|
|
48
|
+
const info = this.parseReturnExpressionAST(returnExpr, schemaName);
|
|
49
|
+
if (info) {
|
|
50
|
+
fieldMap.set(fieldName, info);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return fieldMap;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Parses the return expression AST to extract schema reference info.
|
|
57
|
+
*/
|
|
58
|
+
parseReturnExpressionAST(expr, schemaName) {
|
|
59
|
+
let isArray = false;
|
|
60
|
+
let isRecord = false;
|
|
61
|
+
let isOptional = false;
|
|
62
|
+
let refSchema = null;
|
|
63
|
+
// Unwrap method chains like .optional(), .nullable(), .array()
|
|
64
|
+
let currentExpr = expr;
|
|
65
|
+
while (Node.isCallExpression(currentExpr)) {
|
|
66
|
+
const callExpr = currentExpr;
|
|
67
|
+
const exprNode = callExpr.getExpression();
|
|
68
|
+
if (Node.isPropertyAccessExpression(exprNode)) {
|
|
69
|
+
const propAccess = exprNode;
|
|
70
|
+
const methodName = propAccess.getName();
|
|
71
|
+
const baseExpr = propAccess.getExpression();
|
|
72
|
+
// Check for .optional() or .nullable() on a schema
|
|
73
|
+
if (methodName === "optional" || methodName === "nullable") {
|
|
74
|
+
isOptional = true;
|
|
75
|
+
currentExpr = baseExpr;
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
// Check for SchemaName.array() pattern
|
|
79
|
+
if (methodName === "array" && Node.isIdentifier(baseExpr)) {
|
|
80
|
+
const baseName = baseExpr.getText();
|
|
81
|
+
if (baseName !== "z") {
|
|
82
|
+
isArray = true;
|
|
83
|
+
refSchema = baseName;
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// Check for z.array(SchemaName) or z.record(key, SchemaName)
|
|
88
|
+
if (Node.isIdentifier(baseExpr) && baseExpr.getText() === "z") {
|
|
89
|
+
const args = callExpr.getArguments();
|
|
90
|
+
if (methodName === "array" && args.length > 0) {
|
|
91
|
+
isArray = true;
|
|
92
|
+
refSchema = this.extractSchemaRef(args[0]);
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
if (methodName === "record" && args.length >= 2) {
|
|
96
|
+
isRecord = true;
|
|
97
|
+
refSchema = this.extractSchemaRef(args[1]);
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
// If we haven't found a ref yet, check if currentExpr is an identifier
|
|
105
|
+
if (!refSchema && Node.isIdentifier(currentExpr)) {
|
|
106
|
+
refSchema = currentExpr.getText();
|
|
107
|
+
}
|
|
108
|
+
if (!refSchema) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
refSchema,
|
|
113
|
+
isArray,
|
|
114
|
+
isRecord,
|
|
115
|
+
isOptional,
|
|
116
|
+
isSelfRef: refSchema === schemaName,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Extracts schema reference from an argument node.
|
|
121
|
+
*/
|
|
122
|
+
extractSchemaRef(node) {
|
|
123
|
+
if (Node.isIdentifier(node)) {
|
|
124
|
+
return node.getText();
|
|
125
|
+
}
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Resolves `any` types in extracted type string by replacing with self-references.
|
|
130
|
+
* Uses structured parsing instead of regex.
|
|
131
|
+
*
|
|
132
|
+
* @param typeStr - The extracted type string with `any` placeholders
|
|
133
|
+
* @param getterFields - Map of field name to getter field info
|
|
134
|
+
* @param typeName - The generated type name to use for self-references
|
|
135
|
+
* @returns The resolved type string with proper self-references
|
|
136
|
+
*/
|
|
137
|
+
resolveAnyTypes(typeStr, getterFields, typeName) {
|
|
138
|
+
let result = typeStr;
|
|
139
|
+
for (const [fieldName, info] of getterFields) {
|
|
140
|
+
if (!info.isSelfRef) {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
// For z.record pattern: { [x: string]: any } -> { [x: string]: TypeName }
|
|
144
|
+
if (info.isRecord) {
|
|
145
|
+
result = this.replaceRecordAny(result, fieldName, typeName);
|
|
146
|
+
}
|
|
147
|
+
// Handle any field (array or single reference)
|
|
148
|
+
result = this.replaceFieldAny(result, fieldName, typeName, info.isArray);
|
|
149
|
+
}
|
|
150
|
+
return result;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Replaces any type in a record field.
|
|
154
|
+
*/
|
|
155
|
+
replaceRecordAny(typeStr, fieldName, typeName) {
|
|
156
|
+
// Find "fieldName: { [x: string]: any" or "fieldName?: { [x: string]: any"
|
|
157
|
+
const fieldPatterns = [`${fieldName}: {`, `${fieldName}?: {`];
|
|
158
|
+
const indexSigPattern = "[x: string]:";
|
|
159
|
+
for (const pattern of fieldPatterns) {
|
|
160
|
+
let idx = typeStr.indexOf(pattern);
|
|
161
|
+
while (idx !== -1) {
|
|
162
|
+
// Find the index signature pattern
|
|
163
|
+
const afterBrace = idx + pattern.length;
|
|
164
|
+
const indexSigStart = typeStr.indexOf(indexSigPattern, afterBrace);
|
|
165
|
+
if (indexSigStart !== -1 && indexSigStart < afterBrace + 20) {
|
|
166
|
+
const colonPos = indexSigStart + indexSigPattern.length;
|
|
167
|
+
// Find "any" after the colon - count whitespace explicitly
|
|
168
|
+
let scanPos = colonPos;
|
|
169
|
+
while (scanPos < typeStr.length && /\s/.test(typeStr[scanPos])) {
|
|
170
|
+
scanPos++;
|
|
171
|
+
}
|
|
172
|
+
if (typeStr.substring(scanPos, scanPos + 3) === "any") {
|
|
173
|
+
const anyStart = scanPos;
|
|
174
|
+
const anyEnd = anyStart + 3;
|
|
175
|
+
typeStr = typeStr.substring(0, anyStart) + typeName + typeStr.substring(anyEnd);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
idx = typeStr.indexOf(pattern, idx + 1);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return typeStr;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Replaces any type in a regular field.
|
|
185
|
+
*/
|
|
186
|
+
replaceFieldAny(typeStr, fieldName, typeName, isArray) {
|
|
187
|
+
// Find "fieldName: any" or "fieldName?: any" patterns
|
|
188
|
+
const fieldPatterns = [`${fieldName}: `, `${fieldName}?: `];
|
|
189
|
+
for (const pattern of fieldPatterns) {
|
|
190
|
+
let idx = typeStr.indexOf(pattern);
|
|
191
|
+
while (idx !== -1) {
|
|
192
|
+
const valueStart = idx + pattern.length;
|
|
193
|
+
const restOfType = typeStr.substring(valueStart);
|
|
194
|
+
// Check for "readonly any" or just "any"
|
|
195
|
+
let anyIdx = -1;
|
|
196
|
+
let prefixLen = 0;
|
|
197
|
+
if (restOfType.startsWith("readonly ")) {
|
|
198
|
+
prefixLen = "readonly ".length;
|
|
199
|
+
if (restOfType.substring(prefixLen).startsWith("any")) {
|
|
200
|
+
anyIdx = valueStart + prefixLen;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
else if (restOfType.startsWith("any")) {
|
|
204
|
+
anyIdx = valueStart;
|
|
205
|
+
}
|
|
206
|
+
if (anyIdx !== -1) {
|
|
207
|
+
// Check what comes after "any"
|
|
208
|
+
const afterAny = typeStr.substring(anyIdx + 3);
|
|
209
|
+
const hasArrayBrackets = afterAny.startsWith("[]");
|
|
210
|
+
// Build replacement
|
|
211
|
+
let replacement = typeName;
|
|
212
|
+
if (isArray || hasArrayBrackets) {
|
|
213
|
+
replacement = `${typeName}[]`;
|
|
214
|
+
}
|
|
215
|
+
// Calculate end position (include [] if present)
|
|
216
|
+
let endPos = anyIdx + 3;
|
|
217
|
+
if (hasArrayBrackets) {
|
|
218
|
+
endPos += 2;
|
|
219
|
+
}
|
|
220
|
+
typeStr = typeStr.substring(0, anyIdx) + replacement + typeStr.substring(endPos);
|
|
221
|
+
}
|
|
222
|
+
idx = typeStr.indexOf(pattern, idx + 1);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return typeStr;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Checks if a schema has getter-based self-references.
|
|
229
|
+
*/
|
|
230
|
+
hasSelfReferences(getterFields) {
|
|
231
|
+
return Array.from(getterFields.values()).some((info) => info.isSelfRef);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
//# sourceMappingURL=getter-resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getter-resolver.js","sourceRoot":"","sources":["../../src/core/getter-resolver.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { SourceFile, Project } from "ts-morph";
|
|
2
|
+
/**
|
|
3
|
+
* Information about an imported schema.
|
|
4
|
+
*/
|
|
5
|
+
export interface ImportedSchemaInfo {
|
|
6
|
+
/** Local name used in the importing file */
|
|
7
|
+
localName: string;
|
|
8
|
+
/** Original name in the source file */
|
|
9
|
+
originalName: string;
|
|
10
|
+
/** Resolved source file path */
|
|
11
|
+
sourceFilePath: string;
|
|
12
|
+
/** Whether the schema was successfully resolved */
|
|
13
|
+
resolved: boolean;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Map of local schema name to imported schema info.
|
|
17
|
+
*/
|
|
18
|
+
export type ImportedSchemaMap = Map<string, ImportedSchemaInfo>;
|
|
19
|
+
/**
|
|
20
|
+
* Resolves imported schemas from other files.
|
|
21
|
+
*/
|
|
22
|
+
export declare class ImportResolver {
|
|
23
|
+
private schemaDetector;
|
|
24
|
+
constructor();
|
|
25
|
+
/**
|
|
26
|
+
* Finds all imported schemas in a source file.
|
|
27
|
+
*
|
|
28
|
+
* @param sourceFile - The source file to analyze
|
|
29
|
+
* @param project - The ts-morph project for module resolution
|
|
30
|
+
* @returns Map of local names to imported schema info
|
|
31
|
+
*/
|
|
32
|
+
findImportedSchemas(sourceFile: SourceFile, project: Project): ImportedSchemaMap;
|
|
33
|
+
/**
|
|
34
|
+
* Finds the actual source file containing a schema definition.
|
|
35
|
+
* Follows re-exports (export * from "./other") to find the original.
|
|
36
|
+
*/
|
|
37
|
+
private findSchemaSource;
|
|
38
|
+
/**
|
|
39
|
+
* Resolves a module specifier to a source file.
|
|
40
|
+
*/
|
|
41
|
+
private resolveModuleSpecifier;
|
|
42
|
+
/**
|
|
43
|
+
* Generates possible file paths for a module specifier and resolves to a source file.
|
|
44
|
+
*/
|
|
45
|
+
private resolveFromPossiblePaths;
|
|
46
|
+
/**
|
|
47
|
+
* Resolves an import declaration to its source file.
|
|
48
|
+
*/
|
|
49
|
+
private resolveImportedFile;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=import-resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"import-resolver.d.ts","sourceRoot":"","sources":["../../src/core/import-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAqB,OAAO,EAAE,MAAM,UAAU,CAAC;AAKlE;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,4CAA4C;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB,uCAAuC;IACvC,YAAY,EAAE,MAAM,CAAC;IACrB,gCAAgC;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,mDAAmD;IACnD,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG,GAAG,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;AAEhE;;GAEG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,cAAc,CAAiB;;IAMvC;;;;;;OAMG;IACH,mBAAmB,CAAC,UAAU,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,GAAG,iBAAiB;IA2ChF;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IA4DxB;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAS9B;;OAEG;IACH,OAAO,CAAC,wBAAwB;IA8BhC;;OAEG;IACH,OAAO,CAAC,mBAAmB;CAwB5B"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { join } from "pathe";
|
|
2
|
+
import { SchemaDetector } from "./schema-detector.js";
|
|
3
|
+
import { logDebugError } from "./logger.js";
|
|
4
|
+
/**
|
|
5
|
+
* Resolves imported schemas from other files.
|
|
6
|
+
*/
|
|
7
|
+
export class ImportResolver {
|
|
8
|
+
schemaDetector;
|
|
9
|
+
constructor() {
|
|
10
|
+
this.schemaDetector = new SchemaDetector();
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Finds all imported schemas in a source file.
|
|
14
|
+
*
|
|
15
|
+
* @param sourceFile - The source file to analyze
|
|
16
|
+
* @param project - The ts-morph project for module resolution
|
|
17
|
+
* @returns Map of local names to imported schema info
|
|
18
|
+
*/
|
|
19
|
+
findImportedSchemas(sourceFile, project) {
|
|
20
|
+
const result = new Map();
|
|
21
|
+
const imports = sourceFile.getImportDeclarations();
|
|
22
|
+
for (const importDecl of imports) {
|
|
23
|
+
const moduleSpecifier = importDecl.getModuleSpecifierValue();
|
|
24
|
+
// Skip node_modules imports
|
|
25
|
+
if (!moduleSpecifier.startsWith(".") && !moduleSpecifier.startsWith("/")) {
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
// Resolve the module to a source file
|
|
29
|
+
const resolvedSourceFile = this.resolveImportedFile(importDecl, sourceFile, project);
|
|
30
|
+
if (!resolvedSourceFile) {
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
// Check named imports
|
|
34
|
+
const namedImports = importDecl.getNamedImports();
|
|
35
|
+
for (const namedImport of namedImports) {
|
|
36
|
+
const importedName = namedImport.getName();
|
|
37
|
+
const localName = namedImport.getAliasNode()?.getText() || importedName;
|
|
38
|
+
// Find the actual source file containing the schema definition
|
|
39
|
+
// This handles re-exports from index.ts files
|
|
40
|
+
const actualSource = this.findSchemaSource(resolvedSourceFile, importedName, project);
|
|
41
|
+
if (actualSource) {
|
|
42
|
+
result.set(localName, {
|
|
43
|
+
localName,
|
|
44
|
+
originalName: actualSource.schemaName,
|
|
45
|
+
sourceFilePath: actualSource.sourceFile.getFilePath(),
|
|
46
|
+
resolved: true,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Finds the actual source file containing a schema definition.
|
|
55
|
+
* Follows re-exports (export * from "./other") to find the original.
|
|
56
|
+
*/
|
|
57
|
+
findSchemaSource(sourceFile, schemaName, project, visited = new Set()) {
|
|
58
|
+
const filePath = sourceFile.getFilePath();
|
|
59
|
+
// Prevent infinite loops
|
|
60
|
+
if (visited.has(filePath)) {
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
visited.add(filePath);
|
|
64
|
+
// Check if the schema is defined directly in this file
|
|
65
|
+
const schemas = this.schemaDetector.detectExportedSchemas(sourceFile);
|
|
66
|
+
if (schemas.some((s) => s.name === schemaName)) {
|
|
67
|
+
return { sourceFile, schemaName };
|
|
68
|
+
}
|
|
69
|
+
// Check export declarations for re-exports
|
|
70
|
+
const exportDecls = sourceFile.getExportDeclarations();
|
|
71
|
+
for (const exportDecl of exportDecls) {
|
|
72
|
+
const moduleSpecifier = exportDecl.getModuleSpecifierValue();
|
|
73
|
+
if (!moduleSpecifier)
|
|
74
|
+
continue;
|
|
75
|
+
// Check if this is "export * from" or "export { name } from"
|
|
76
|
+
const namedExports = exportDecl.getNamedExports();
|
|
77
|
+
if (namedExports.length === 0) {
|
|
78
|
+
// This is "export * from './module'" - follow it
|
|
79
|
+
const reExportedFile = this.resolveModuleSpecifier(sourceFile, moduleSpecifier, project);
|
|
80
|
+
if (reExportedFile) {
|
|
81
|
+
const found = this.findSchemaSource(reExportedFile, schemaName, project, visited);
|
|
82
|
+
if (found) {
|
|
83
|
+
return found;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
// Check named exports
|
|
89
|
+
for (const namedExport of namedExports) {
|
|
90
|
+
const exportedName = namedExport.getAliasNode()?.getText() || namedExport.getName();
|
|
91
|
+
if (exportedName === schemaName) {
|
|
92
|
+
const originalName = namedExport.getName();
|
|
93
|
+
const reExportedFile = this.resolveModuleSpecifier(sourceFile, moduleSpecifier, project);
|
|
94
|
+
if (reExportedFile) {
|
|
95
|
+
return this.findSchemaSource(reExportedFile, originalName, project, visited);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Resolves a module specifier to a source file.
|
|
105
|
+
*/
|
|
106
|
+
resolveModuleSpecifier(fromFile, moduleSpecifier, project) {
|
|
107
|
+
const sourceDir = fromFile.getDirectoryPath();
|
|
108
|
+
return this.resolveFromPossiblePaths(sourceDir, moduleSpecifier, project);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Generates possible file paths for a module specifier and resolves to a source file.
|
|
112
|
+
*/
|
|
113
|
+
resolveFromPossiblePaths(sourceDir, moduleSpecifier, project) {
|
|
114
|
+
const possiblePaths = [
|
|
115
|
+
join(sourceDir, `${moduleSpecifier}.ts`),
|
|
116
|
+
join(sourceDir, moduleSpecifier, "index.ts"),
|
|
117
|
+
join(sourceDir, `${moduleSpecifier}.js`),
|
|
118
|
+
join(sourceDir, moduleSpecifier, "index.js"),
|
|
119
|
+
];
|
|
120
|
+
for (const possiblePath of possiblePaths) {
|
|
121
|
+
let resolved = project.getSourceFile(possiblePath);
|
|
122
|
+
if (!resolved) {
|
|
123
|
+
try {
|
|
124
|
+
resolved = project.addSourceFileAtPathIfExists(possiblePath);
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
logDebugError(`Failed to add source file at ${possiblePath}`, error);
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (resolved) {
|
|
132
|
+
return resolved;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return undefined;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Resolves an import declaration to its source file.
|
|
139
|
+
*/
|
|
140
|
+
resolveImportedFile(importDecl, sourceFile, project) {
|
|
141
|
+
const moduleSpecifier = importDecl.getModuleSpecifierValue();
|
|
142
|
+
try {
|
|
143
|
+
// Get the module specifier source file
|
|
144
|
+
const moduleSourceFile = importDecl.getModuleSpecifierSourceFile();
|
|
145
|
+
if (moduleSourceFile) {
|
|
146
|
+
return moduleSourceFile;
|
|
147
|
+
}
|
|
148
|
+
// Try to resolve manually for index.ts patterns
|
|
149
|
+
const sourceDir = sourceFile.getDirectoryPath();
|
|
150
|
+
return this.resolveFromPossiblePaths(sourceDir, moduleSpecifier, project);
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
logDebugError(`Module resolution failed for ${moduleSpecifier}`, error);
|
|
154
|
+
}
|
|
155
|
+
return undefined;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=import-resolver.js.map
|