import { Project, SourceFile } from "ts-morph"; //#region ../core/dist/types.d.ts /** * Shared type definitions for the schema-library-agnostic core. */ /** * Options for extracting a single schema's types. */ interface ExtractOptions { /** Absolute or relative path to the TypeScript file containing the schema */ filePath: string; /** Name of the exported schema (e.g., "UserSchema") */ schemaName: string; /** Optional path to tsconfig.json for project configuration */ tsconfigPath?: string; } /** * How far an `import("...").Name` type reference gets expanded in place: * `"project"` follows a reference into another file of the project being * processed; `"all"` also follows a reference into a plain type declared in * a dependency package (resolved through TypeScript's own module resolution, * not filesystem probing). */ type TypeReferenceScope = "project" | "all"; /** * Extra context that lets extraction reach beyond the file being processed. */ interface ExtractContext { /** * Absolute paths of the files that get generated types of their own. * * A recursive schema imported from one of them is referenced by name rather * than inlined - an inline copy of a recursive type can only ever be an * approximation - leaving the caller to `import type` it. Schemas from files * outside this set are inlined as before. * * Paths are compared canonicalized, so a caller's separators do not have to * match the spelling TypeScript reports for the same file. */ importableFiles?: ReadonlySet; /** * Schema names actually generated for this run (e.g. from `--schemas`). * A schema outside this set is never declared by its own file either, so * referencing it by name would point at a declaration that doesn't exist - * it is inlined instead. Undefined means every schema in an importable file * is generated. * * Only zinfer's extractor currently honors this field for precision; * vinfer's extractor ignores it and instead omits `importableFiles` * entirely whenever a schema filter is active. */ generatedSchemaNames?: ReadonlySet; /** * When an explicit type annotation's referenced type reaches a plain * (non-schema) type declared in another file, TypeScript's printer * synthesizes an `import("...").Name` reference to it rather than * expanding it in place - there is nothing else to point at from this * print location. Setting this replaces that reference with the * referenced type's own structure instead, recursively, so the generated * output carries no dependency on the original file layout. A reference * that would recurse into itself (directly or through another file) is * left as `import(...)` at the point it would cycle. `"all"` additionally * expands a reference into a plain type declared in a dependency package; * `undefined` leaves every reference as printed. */ inlineTypeReferences?: TypeReferenceScope; } /** * Information about a detected schema in a source file. */ interface DetectedSchema { /** Variable name of the schema */ name: string; /** * Identifier the schema is actually declared under in the source file. * * Differs from `name` for aliased re-exports (`export { XSchema as YSchema }`), * where the type must still be read from the original declaration. */ localName?: string; /** Whether the schema is exported */ isExported: boolean; /** Line number where the schema is defined */ line: number; /** Explicit type annotation if present */ explicitType?: string; /** JSDoc comment if present */ jsDoc?: string; } /** * A field-level description extracted from a schema. */ interface FieldDescription { /** Field path (e.g., "user.name" for nested fields) */ path: string; /** Description text */ description: string; } /** * Result of extracting types from a single schema. */ interface ExtractResult { /** Name of the schema */ schemaName: string; /** Extracted input type as string */ input: string; /** Extracted output type as string */ output: string; /** Whether the original schema was exported */ isExported: boolean; /** * Absolute path of the file declaring the schema, when it lives in another * file and the types generated for it are referenced by name instead of being * inlined. The generated file has to `import type` them from there. */ importedFrom?: string; /** * Set when the schema is not exported but still gets its own (non-exported) * type declaration in the output file - because it is self-recursive and * reached only inline through another schema, so its recursion point (and * every other reference to it) needs a declared name to point at instead of * being widened to `any`. */ declaredLocally?: boolean; /** Schema-level description */ description?: string; /** Field descriptions */ fieldDescriptions?: FieldDescription[]; } /** * Result of extracting types from a single file with multiple schemas. */ interface FileExtractResult { /** Path to the source file */ filePath: string; /** Extracted schemas */ schemas: ExtractResult[]; } /** * Mapped type names for a schema. */ interface MappedTypeName { /** Original schema name */ originalName: string; /** Generated input type name */ inputName: string; /** Generated output type name */ outputName: string; /** Unified name (when input === output) */ unifiedName: string; } /** * Options for name mapping. */ interface NameMappingOptions { /** Suffix to remove from schema names (e.g., "Schema") */ removeSuffix?: string; /** Suffix to add for input types (default: "Input") */ inputSuffix?: string; /** Suffix to add for output types (default: "Output") */ outputSuffix?: string; /** Custom name mappings */ customMap?: Record; } /** * Options for output generation. */ interface OutputOptions { /** Output directory */ outDir?: string; /** Single output file path */ outFile?: string; /** Output file naming pattern (e.g., "[name].types.ts") */ outPattern?: string; /** Generate .d.ts declaration files */ declaration?: boolean; } /** * Generated file information. */ interface GeneratedFile { /** Output file path */ path: string; /** File content */ content: string; } /** * Options for type declaration formatting. */ interface DeclarationOptions$1 { /** Output only input types */ inputOnly?: boolean; /** Output only output types */ outputOnly?: boolean; /** Merge input/output if they are identical */ mergeSame?: boolean; /** * Module specifier to `import type` a schema's generated types from, keyed by * schema name. Only schemas whose `ExtractResult` carries `importedFrom` are * looked up here; an entry is what turns a cross-file reference into an * import instead of leaving the name undeclared. */ importSources?: ReadonlyMap; } //#endregion //#region ../core/dist/config-loader.d.ts /** * Configuration options that can be specified in config file. */ interface InferConfig { /** File paths or glob patterns to process */ include?: string[]; /** Glob patterns to exclude */ exclude?: string[]; /** Path to tsconfig.json */ project?: string; /** Schema names to extract (if not specified, all are extracted) */ schemas?: string[]; /** Output only input types */ inputOnly?: boolean; /** Output only output types */ outputOnly?: boolean; /** Merge input/output if they are identical */ mergeSame?: boolean; /** Suffix to remove from schema names */ suffix?: string; /** Suffix to add for input types */ inputSuffix?: string; /** Suffix to add for output types */ outputSuffix?: string; /** Custom name mappings */ map?: Record; /** Output directory */ outDir?: string; /** Single output file path */ outFile?: string; /** Output file naming pattern */ outPattern?: string; /** Generate .d.ts files */ declaration?: boolean; /** Include schema-level descriptions as TSDoc comments */ withDescriptions?: boolean; /** Generate vitest type equality tests alongside type files */ generateTests?: boolean; /** * Replace the `import("...")` reference an explicit annotation's `T` * synthesizes for a plain type declared in another file with that type's * own structure, instead of leaving the generated output pointing back at * it. `"project"` follows a reference within this project; `"all"` also * follows one into a dependency package. */ inlineTypeReferences?: TypeReferenceScope; } //#endregion //#region ../core/dist/file-resolver.d.ts /** * Resolves file paths from glob patterns and generates output paths. */ export declare class FileResolver { /** * Resolves input file paths from a glob pattern or array of patterns. * * @param pattern - Glob pattern(s) to match * @param cwd - Current working directory (default: process.cwd()) * @param exclude - Glob pattern(s) to exclude * @returns Array of absolute file paths */ resolveInputFiles(pattern: string | string[], cwd?: string, exclude?: string[]): Promise; /** * Resolves the output path for a given input file. * * @param inputPath - Absolute path to the input file * @param options - Output options * @param cwd - Current working directory * @returns Absolute path to the output file */ resolveOutputPath(inputPath: string, options: OutputOptions, cwd?: string): string; /** * Applies a pattern template to generate an output filename. * * Supported placeholders: * - [name]: Input filename without extension * - [dir]: Parent directory name of the input file * - [ext]: Output extension (including dot) * * @param pattern - Pattern template (e.g., "[name].types[ext]") * @param vars - Variables to substitute * @returns Generated filename */ applyPattern(pattern: string, vars: { name: string; dir?: string; ext: string; }): string; } //#endregion //#region ../core/dist/name-mapper.d.ts /** * Maps schema names to TypeScript type names. */ export declare class NameMapper { private options; /** * Creates a new NameMapper instance. * * @param options - Name mapping options */ constructor(options?: NameMappingOptions); /** * Maps a schema name to input, output, and unified type names. * * @param schemaName - The original schema name (e.g., "UserSchema") * @returns The mapped type names * * @example * ```typescript * const mapper = new NameMapper({ removeSuffix: "Schema" }); * mapper.map("UserSchema"); * // Returns: { originalName: "UserSchema", inputName: "UserInput", outputName: "UserOutput", unifiedName: "User" } * ``` */ map(schemaName: string): MappedTypeName; /** * Creates the full mapped names object from a base name. */ private createMappedNames; /** * Creates a mapping function for use with formatMultipleAsDeclarations. * * @returns A function that maps schema names to type names */ createMapFunction(): (schemaName: string) => MappedTypeName; } /** * Creates a simple name mapper with the given options. * * @param options - Name mapping options * @returns A function that maps schema names to type names */ export declare function createNameMapper(options?: NameMappingOptions): (schemaName: string) => MappedTypeName; //#endregion //#region src/core/types.d.ts /** * Options for type declaration formatting. * * A type alias (not `interface X extends Y`) on purpose: tsdown's dts bundler (rolldown-plugin-dts) * flattens the published .d.ts into one file, and an `extends` of a * same-named core interface makes it try to export both under the identical * name "DeclarationOptions", colliding and silently renaming one of them. */ type DeclarationOptions = DeclarationOptions$1 & { /** * How a `.brand()` marker is represented in the generated output. * `"zod-import"` (default) prints `BRAND<"Tag">` and imports `BRAND` from * zod. `"local-symbol"` prints a self-contained `unique symbol`-keyed * property instead, so the generated file never imports zod. */ brandStrategy?: "zod-import" | "local-symbol"; }; //#endregion //#region src/core/extractor.d.ts /** * Extracts input and output types from Zod schemas using TypeScript Compiler API. */ export declare class ZodTypeExtractor { private project; private schemaDetector; private getterResolver; private referenceAnalyzer; private importResolver; private importedSchemaCache; /** * Creates a new ZodTypeExtractor instance. * * @param tsconfigPath - Optional path to tsconfig.json. If not provided, * default compiler options will be used. */ constructor(tsconfigPath?: string); /** * Extracts input and output types from a Zod schema. * * @param options - Extraction options including file path and schema name * @returns The extracted input and output types as strings */ extract(options: ExtractOptions): ExtractResult; /** * Extracts types from all exported Zod schemas in a file. * * @param filePath - Path to the TypeScript file * @returns Array of extraction results for each schema */ extractAll(filePath: string, context?: ExtractContext): ExtractResult[]; /** * Extracts types from specific schemas in a file. * * @param filePath - Path to the TypeScript file * @param schemaNames - Names of schemas to extract * @returns Array of extraction results */ extractMultiple(filePath: string, schemaNames: string[], context?: ExtractContext): ExtractResult[]; /** * Extracts types from all exported schemas and returns file-level result. * * @param filePath - Path to the TypeScript file * @returns File extraction result with all schemas */ extractFile(filePath: string, context?: ExtractContext): FileExtractResult; /** * Gets the list of detected schema names in a file. * * @param filePath - Path to the TypeScript file * @returns Array of schema names */ getSchemaNames(filePath: string): string[]; /** * Gets or adds a source file to the project. */ private getOrAddSourceFile; /** * Internal method to extract multiple schemas from a source file. */ private extractMultipleFromSourceFile; /** * Picks the form a same-file schema whose types nothing declares is inlined * as. * * A self-recursive schema is never inlined this way - it is promoted to * its own (non-exported) declaration instead (`declaredLocally`) and * referenced by name, like an exported schema, before this is ever * reached. The defensive check below only guards against inlining a * dangling self-reference should that invariant ever not hold. * * @returns The type to inline, or undefined to leave the reference as * TypeScript printed it */ private inlinableForm; /** * Replaces an inline schema reference with a type name. * * A reference is recorded under the bare name of the field holding it, which * a nested field elsewhere in the printed type may share. Every occurrence of * the name is therefore scored by how much its printed value looks like the * referenced schema, and only the best one is rewritten - the schema's own * printed shape beats an unrelated field that merely happens to be inlined or * to have been given up on. */ private replaceSchemaReference; /** * Finds the occurrence of `fieldPath` whose printed value best matches the * referenced schema, or undefined when no occurrence looks like the reference. */ private findReferenceOccurrence; /** * Parenthesizes an inline type so that wrapping it in `[]` keeps its meaning. * A named reference needs nothing; an approximation that prints as a union * would otherwise bind `[]` to its last member alone. */ private asArrayElement; /** * Injects temporary type for explicit type (without normalization for circular refs). */ private injectExplicitType; /** * Cleans up explicit type temporaries. */ private cleanupExplicitType; /** * Creates a ts-morph Project with appropriate compiler options. */ private createProject; /** * Injects the __Normalize type definition into a source file if not already present. */ private ensureNormalizeType; /** * Removes the __Normalize type definition from a source file. */ private cleanupNormalizeType; /** * Injects temporary type aliases into the source file. * The __Normalize type must already be present (via ensureNormalizeType). * These are added in-memory only and never saved to disk. */ private injectTemporaryTypes; /** * Resolves a type alias and returns its fully expanded string representation. */ private resolveType; /** * The shared TypeFormatFlags for every printed type in this file: fully * expanded (no truncation), without widening named aliases (which is what * lets a same-file enum print as a bare identifier for the expansion right * below to catch, instead of being expanded away already). */ private typeTextFormatFlags; /** * Removes trailing spaces ts-morph 27+ may add to printed type text. * Skips split/map/join for single-line types (most common case). */ private trimPrintedType; /** * Rewrites relative `import("...")` specifiers in printed type text to * absolute paths, using `sourceDir` as the base. The printer emits these * relative to the file the type was read from - not the eventual output * file - so this is the form `relativizeImportPaths` (which only rewrites * absolute paths) can correctly re-anchor later. * * `sourceDir` is realpath'd first: it always exists (a file was just read * from it), and on a symlinked working directory (e.g. macOS's * `/var` -> `/private/var` tmpdir) leaving it un-resolved here would * produce an absolute path on a different symlink base than the output * directory `relativizeImportPaths` later resolves against, corrupting the * relative path between the two. */ private absolutizeImportPaths; /** * Prints an enum declaration's members as a literal union, e.g. * `"a" | "b"`. Returns undefined when the enum has no members, or when * any member's value can't be statically resolved (e.g. initialized from * a function call) - printing a union missing that member would be * narrower than the enum itself and reject a value the enum actually * allows, which is worse than not expanding it at all. */ private printEnumAsLiteralUnion; /** * Replaces `import("path").TypeName` references to a plain (non-schema) * type declared in another file with that type's own structure, so the * generated output no longer depends on the original file layout. * * `visiting` tracks the `file#TypeName` pairs currently being expanded in * this call chain. A reference that would revisit one of them - a type * that (directly or through another file) refers back to itself - is left * as `import(...)` at that point instead of recursing forever; everything * that isn't part of the cycle is still fully expanded. * * Scoped to plain types only: a reference this can't resolve to a * `type`/`interface`/`enum` declaration (a class, a renamed/default * export, or anything else `resolveExternalTypeReference` gives up on) is * left as `import(...)` unchanged - the same safe fallback the * `degenerate-explicit-type` fixtures already rely on for local types. * * Only `import("path").Name` on its own is expanded - `import("path") * .Name.Member` (a qualified name, e.g. an enum member) or * `import("path").Name` (a generic instantiation) is left as-is: * substituting only `Name` would strand `.Member`/`` against * whatever replaces it. The identifier after the dot is found by a plain * character scan, not a regex lookahead - a backtracking engine can * satisfy `(?!\s*<)` by giving back characters (matching `Bo` instead of * `Box` when `Box` follows), which a scan never does. */ private inlineExternalTypeReferences; /** * The `import("path").Name` matched-text branch of `inlineExternalTypeReferences`: * expand `typeName` in `targetFile`, or fall back to `originalText` * unchanged (on a cycle, when it isn't a plain type declaration, or when * the expansion carries an unresolvable computed property key - see * `hasUnresolvableComputedKey`). */ private resolveOrKeepImportText; /** * Looks up `typeName` as a type alias, interface, or enum declared in * `targetFile` and returns its expanded structure - recursing into any * further external references it carries. Returns undefined when * `typeName` isn't one of those (a class, or an export this couldn't * match by its declared name), leaving the caller's `import(...)` as-is. */ private resolveExternalTypeReference; /** * Prints a type alias's or interface's own structure and recurses into * whatever further references it carries - both the `import("...")` * TypeScript itself synthesizes for names invisible from `targetFile`, * and the bare names of anything that *is* visible there (its own * same-file declarations, or types it imports for its own use). The * latter print exactly like any other in-scope identifier - correct * only inside `targetFile` itself - so `promoteBareTypeReferences` has * to turn them into the same explicit, resolvable form before this text * is embedded anywhere else. */ private expandExternalDeclaration; /** * Replaces bare identifiers in `text` - printed type text read from * `targetFile`, valid only within its own scope - with an explicit, * resolvable reference: either the fully expanded structure of the type * they name, or (only on a cycle, and only when that type is exported * from wherever it's declared) an `import("...")` pointing at it. * * A same-file declaration that isn't exported has no importable name to * fall back to - a cycle through one is left as the bare identifier, * same documented limitation as `nonexported-explicit-type-schema.ts`. */ private promoteBareTypeReferences; /** * Resolves one entry from `collectFileLocalTypeReferences`: expands it * (recursing, with the same cycle handling as `inlineExternalTypeReferences`), * or - on a cycle, when no `scope` was requested (expansion across files * is opt-in via `--inline-type-references`), or when the expansion carries * an unresolvable computed property key (see `hasUnresolvableComputedKey`) * - falls back to an `import(...)` reference if one is valid, else the * original bare identifier. */ private resolveReferenceOrFallback; /** * True when `text` - the result of expanding an external declaration - * still contains a bare identifier used as a computed property key (e.g. * `{ [brand]: true; ... }`) that isn't tracked by * `collectFileLocalTypeReferences`. A `unique symbol`-typed `const` used * this way is the one shape a printed type can carry that can never be * promoted to a resolvable form: unlike a type reference, the value * binding it names has no expressible spelling from outside its declaring * file at all (not even `import("...").brand` when it isn't exported - and * importing a value just to key off it would be an odd thing for a * generated declaration to depend on even when it is exported). This is * exactly why the checker itself refuses to expand such a type inline when * printing it from anywhere but its own declaring file, falling back to * the bare name instead (see the `promoteBareTypeReferences` comment in * `resolveType`) - `expandExternalDeclaration` has no such restraint, * since it always prints a declaration's structure relative to its own * file. The caller should discard an expansion this flags and keep the * safe, self-contained reference to the type itself instead. * * Scans with quote awareness, the same as `promoteBareTypeReferences`, so * a string literal that happens to contain bracketed text (e.g. * `"[brand]"`) is left alone. * * Only a bracket immediately followed by `:`/`?:` is treated as a * computed property key - the same distinguishing signal * `promoteBareTypeReferences`'s own `isPropertyKey` check uses (the * checker always prints a property key as `name:`/`name?:` with no space * before the colon). A bracketed identifier with nothing after it, like * `Foo[K]` (an indexed-access type) or `[string]` (a single-element * tuple), is ordinary, fully-resolvable type syntax with no bearing on * this check - flagging it would discard a perfectly safe expansion. */ private hasUnresolvableComputedKey; /** * The safe, non-expanding text for a reference: an `import(...)` pointing * at it if one is valid, else the original bare identifier unchanged. */ private referenceFallbackText; /** * Maps every name usable bare within `targetFile`'s own scope - its own * exported or non-exported type/interface/enum declarations, and named * imports of the same (default and namespace imports aren't tracked; * a bare reference through either is left untouched, the same fallback * as an unresolvable one) - to where it actually lives. */ private collectFileLocalTypeReferences; /** * Resolves a printed `import("...")` module specifier to the `SourceFile` * it points at. Loads the file into the shared project on demand so a * type declared there can be read the same way as any file passed to * `extractAll`. * * An absolute specifier is probed as a filesystem path, trying each * extension TypeScript itself would resolve. `absolutizeImportPaths` * already makes every relative specifier (`./...`) TypeScript prints * absolute, so a non-absolute one here is a bare package specifier * (`import("zod").Foo`) - treating `zod` as a relative filename could * accidentally resolve to an unrelated same-named file the caller never * intended to reach. A bare specifier is only resolved under `scope === * "all"`, and then only through TypeScript's own module resolution * (`ts.resolveModuleName`, from `containingFilePath` - the file whose * printed text is currently being expanded, not necessarily the one that * originally wrote the `import`, which the printed text alone can't * recover) rather than filesystem probing, for the same reason: an * ambient module with no backing file (e.g. a `declare module "..."` * block) resolves to nothing and is correctly left as a reference. */ private resolveModuleSourceFile; /** * Whether `typeText` needs wrapping in parens before the caller appends a * suffix like `[]` or an indexed access directly after it, because doing * so unparenthesized would bind to only part of the type or change its * meaning outright: * * - A top-level `|` or `&` (union/intersection) - `A | B[]` reads as * `A | (B[])`, not `(A | B)[]`. * - A top-level `?` (a conditional type, `T extends U ? A : B`) - the same * binding problem, and a bare `?` never otherwise appears at depth 0 in * printed type text (an optional property/tuple element's `?` is always * inside the `{...}`/`[...]` that owns it). * - A top-level `=>` (a function type) - `(x: X) => Y[]` means a function * returning `Y[]`, not an array of such functions; the parameter list's * own `(...)` is already balanced by the time this is reached. */ private needsParensBeforeSuffix; /** * Simplifies Zod internal function types to Function. * Replaces patterns like z.core.$InferInnerFunctionType<...> with Function. * Handles nested type parameters properly. */ private simplifyZodFunctionTypes; /** * Resolves an imported schema's printed types, including its own recursion. * * The getters of an imported schema live in the file that declares it, so its * recursion has to be resolved against that file. What the recursion points at * depends on whether the declaring file gets generated types of its own: if it * does, the self-reference is the type name the importing file will `import * type`; if it does not, there is no name to point at, and the recursion is * left as an `any` - widened to the index signature / array the getter * describes, so property access stays type-checked - with the inline copy * around it kept for whatever detail it still carries. */ private resolveImportedSchemaType; /** * Names among this file's importable candidates that would collide if * referenced by name: the same exported name imported from two different * files would print (and `import type`) the same generated identifier for * two unrelated schemas. */ private findAmbiguousImportedNames; /** * Removes the temporary input/output types that were injected during extraction. * Does not remove __Normalize (managed separately via ensureNormalizeType/cleanupNormalizeType). */ private cleanupTemporaryTypes; /** * Checks if a string is a valid TypeScript identifier. * Used to determine if a type name can be safely used in regex replacement. */ private isValidIdentifier; /** * Returns the type alias, interface, or class declaration for `typeName` * in `sourceFile`, or undefined if it isn't declared there (e.g. a global * type like `Function`, or a type merely imported into the file). */ private getLocalTypeDeclaration; /** * Checks if a type name is declared in the given source file (as opposed * to a global type, or a type merely imported into the file - the caller * checks for that case separately). Gates the same-file self-reference * rewrite below: a global name like `Function` must be left as-is, * since rewriting it to `Input`/`Output` would produce a * self-referential alias instead of the explicit annotation it names. */ private isLocallyDeclaredType; /** * Rewrites every bare occurrence of `typeName` in an explicit annotation's * resolved `input`/`output` text to the schema's own generated * `Input`/`Output` name - the self-reference a recursive * schema's own recursion point needs, whether `typeName` is declared in * this file or merely imported into it (the two callers only differ in * which case applies and how to qualify the degenerate one below). * * `Input`/`Output` is only a name something actually * declares when the schema itself is exported, or is a non-exported * schema promoted to its own local declaration for being self-recursive * (see `declaredLocally` on `RawSchemaType`) - the type-printer emits no * declaration for any other schema that is neither exported nor imported * from elsewhere (see `formatMultipleAsDeclarations`). A non-exported, * non-promoted schema reached only inline through another schema (#518), * e.g. one whose annotation reaches another file (#527 left this case * out of promotion), would otherwise trade one undeclared bare identifier * for another; widen the recursion point to `any` instead, the same "no * name to point at" fallback a getter-based self-reference with no * declared name already falls back to. * * When the resolved text is exactly `typeName` itself (not embedded in a * larger composite type, e.g. a recursive union member), rewriting it * this way would produce a circular alias like `type FooInput = * FooInput` instead. `qualifyExact` supplies the non-circular form for * that case - an inline `import("...")` reference to the declaration, * which points at the annotation's own type rather than the schema's * generated name, so it stays valid whether or not the schema has one. */ private rewriteExplicitTypeSelfReference; /** * When an explicit annotation resolves to exactly a locally declared * class/interface/type (not a composite type it merely appears inside), * rewriting it to `Input`/`Output` would produce a * circular alias like `type FooInput = FooInput`. Printing the bare * identifier instead is also wrong, since the generated declaration file * never imports it. Reference it via an inline `import("...")` type * instead - this also sidesteps name collisions in `--outFile` mode, * where multiple source files are combined into one output and a name * like `LocalClass` could collide across files. * * The member accessed on the `import(...)` must be the name the module * actually exports the declaration under, which isn't always `typeName`: * a default export (`export default class LocalClass {}`) is reachable * only as `.default`, and a renamed export (`export { LocalClass as Foo * }`) only as `.Foo`. `getExportedDeclarations()` is keyed by that * external name, so find the key whose declarations include this one. * Returns null (falling back to the bare, still-broken identifier) when * the declaration isn't exported under any name. */ private qualifyLocalTypeReference; /** * The module specifier form of a file's own path: absolute, without a * source extension, matching what TypeScript itself prints inside a * synthesized `import("...")` type and what `resolveModuleSourceFile` * resolves back from - so a type reached either way lands on the same * cycle-detection key. * * realpath'd for the same reason `absolutizeImportPaths` realpath's its * source directory: on a symlinked working directory (e.g. macOS's `/var` * -> `/private/var` tmpdir), the file was added to the project at * whatever path was handed in - not necessarily realpath'd - and leaving * it as-is here would produce an absolute path on a different symlink * base than the rest of a printed type, corrupting `resolveModuleSourceFile`'s * filesystem lookup and the cycle-detection keys built from it. * * `realpathSync` returns OS-native separators, which are backslashes on * Windows - embedding that directly into an `import("...")` string would * produce an invalid module specifier (and a mis-escaped string literal). * Routed through pathe's `resolve()`, the same normalization * `absolutizeImportPaths` already relies on for its own realpath'd * `sourceDir`, to always land on the forward-slash form. */ private modulePathFor; } //#endregion //#region src/core/type-printer.d.ts /** * Options for formatting type output. */ interface PrintOptions { /** Indentation string (default: " ") */ indent?: string; /** Whether to include the schema name in the output */ includeSchemaName?: boolean; } /** * Formats the extraction result for console output. * * @param result - The extraction result containing input and output types * @param options - Formatting options * @returns Formatted string ready for console output */ export declare function formatResult(result: ExtractResult, options?: PrintOptions): string; /** * Formats the extraction result as input type only. */ export declare function formatInputOnly(result: ExtractResult, options?: PrintOptions): string; /** * Formats the extraction result as output type only. */ export declare function formatOutputOnly(result: ExtractResult, options?: PrintOptions): string; /** * Formats a single extraction result as TypeScript type declaration(s). * * @param result - The extraction result * @param typeName - The mapped type names * @param options - Declaration options * @returns TypeScript type declaration string */ export declare function formatAsDeclaration(result: ExtractResult, typeName: MappedTypeName, options?: DeclarationOptions): string; /** * Formats multiple extraction results as TypeScript type declarations. * * @param results - Array of extraction results * @param mapName - Function to map schema name to type names * @param options - Declaration options * @returns TypeScript type declarations string */ export declare function formatMultipleAsDeclarations(results: ExtractResult[], mapName: (schemaName: string) => MappedTypeName, options?: DeclarationOptions): string; /** * Checks whether a printed type contains an actual `BRAND<` marker, as * opposed to a plain string literal that merely contains that text (e.g. * `z.literal("BRAND")`, printed as the string literal type * `"BRAND"`). Shares `localizeBrandMarkers`'s string-literal-aware * scan rather than a plain regex, for the same reason. */ export declare function containsBrandMarker(typeStr: string): boolean; /** * Generates a complete TypeScript declaration file content. * * @param results - Array of extraction results * @param mapName - Function to map schema name to type names * @param options - Declaration options * @returns Complete .d.ts or .ts file content */ export declare function generateDeclarationFile(results: ExtractResult[], mapName: (schemaName: string) => MappedTypeName, options?: DeclarationOptions): string; //#endregion //#region src/core/schema-detector.d.ts /** * Detects Zod schemas in TypeScript source files. */ export declare class SchemaDetector { private cache; /** * Detects all Zod schemas in a source file. * * @param sourceFile - The ts-morph SourceFile to analyze * @returns Array of detected schema information (including non-exported schemas) */ detectExportedSchemas(sourceFile: SourceFile): DetectedSchema[]; /** * Known Zod schema builder functions that follow the z. prefix. */ private static readonly ZOD_SCHEMA_BUILDERS; private static readonly ZOD_VALUE_METHODS; /** * Checks if a variable declaration is a Zod schema. * * @param declaration - The variable declaration to check * @returns true if the declaration is a Zod schema */ private isZodSchema; private hasSchemaType; /** * Gets all schema names from a source file. * * @param sourceFile - The ts-morph SourceFile to analyze * @returns Array of schema names */ getSchemaNames(sourceFile: SourceFile): string[]; /** * Extracts explicit type annotation from z.ZodType or z.ZodSchema. * * @param declaration - The variable declaration to check * @returns The explicit type string if found, undefined otherwise */ private extractExplicitType; /** * Extracts the first type parameter from a generic type string. * Handles nested brackets properly. * * @param typeText - The full type text (e.g., "ZodType<{ a: string }, ZodTypeDef>") * @param startIdx - The index after the opening "<" * @returns The first type parameter, or undefined if parsing fails */ private extractFirstTypeParameter; } //#endregion //#region src/core/config-loader.d.ts /** * Configuration options that can be specified in config file. */ interface ZinferConfig extends InferConfig { /** * How a `.brand()` marker is represented in the generated output. * `"zod-import"` (default) prints `BRAND<"Tag">` and imports `BRAND` from * zod. `"local-symbol"` prints a self-contained `unique symbol`-keyed * property instead, so the generated file never imports zod. */ brandStrategy?: "zod-import" | "local-symbol"; } /** * Defines a zinfer configuration with type checking. * Use this in zinfer.config.ts for type safety. * * @example * ```typescript * // zinfer.config.ts * import { defineConfig } from 'zinfer'; * * export default defineConfig({ * include: ['src/** /*.schema.ts'], * outDir: 'src/types', * suffix: 'Schema', * }); * ``` */ export declare function defineConfig(config: ZinferConfig): ZinferConfig; //#endregion //#region src/core/test-generator.d.ts /** * Information about a schema for test generation. */ interface TestSchemaInfo { /** Schema variable name (e.g., "UserSchema") */ schemaName: string; /** Generated input type name (e.g., "UserInput") */ inputTypeName: string; /** Generated output type name (e.g., "UserOutput") */ outputTypeName: string; /** * Whether this schema's *output* carries a `.brand()` marker. Brands never * touch the input side, so this only changes how the output assertion is * generated. */ hasBrand?: boolean; } /** * Information about a file for test generation. */ interface TestFileInfo { /** Path to the schema source file (for import) */ schemaFilePath: string; /** Path to the generated types file (for import) */ typesFilePath: string; /** Unique prefix for this file's imports (e.g., "Basic" for basic-schema.ts) */ importPrefix: string; /** Schemas in this file */ schemas: TestSchemaInfo[]; } /** * Options for test generation. */ interface TestGeneratorOptions { /** Whether to include a file header comment */ includeHeader?: boolean; /** * The `brandStrategy` the types were generated with. Only affects a * schema whose `hasBrand` is set: under `"local-symbol"`, its output * assertion is generated against zod's own brand marker via a * canonicalizing comparison instead of plain `toEqualTypeOf`, since a * local-symbol marker is intentionally a different shape from zod's own. */ brandStrategy?: "zod-import" | "local-symbol"; } /** * Converts a kebab-case or snake_case string to PascalCase. */ export declare function toPascalCase(str: string): string; /** * Generates a unique prefix from a file path. */ export declare function generateImportPrefix(filePath: string): string; /** * Creates TestSchemaInfo from schema name and mapped type names. * * @param hasBrand - Whether this schema's output carries a `.brand()` * marker. Required for a correct output assertion under * `brandStrategy: "local-symbol"` (see `TestSchemaInfo.hasBrand`) - pass * `containsBrandMarker(result.output)` from the corresponding * `ExtractResult`. */ export declare function createTestSchemaInfo(schemaName: string, mappedNames: MappedTypeName, hasBrand?: boolean): TestSchemaInfo; /** * Generates vitest type equality test file content. */ export declare class TestGenerator { private options; constructor(options?: TestGeneratorOptions); /** * Generates the complete test file content. * * @param files - Array of file information for test generation * @returns Generated test file content */ generate(files: TestFileInfo[]): string; /** * Generates the header comment. */ private generateHeader; /** * Generates core import statements. * * @param usesLocalSymbolBrand - Whether any schema being tested needs the * brand-canonicalizing comparison, which additionally requires the * `__ZinferCanonBrand` utility type this defines. */ private generateCoreImports; /** * Generates import statements for a single file. * * @param usesLocalSymbolBrand - Whether the local `__brand` symbol needs * importing for this file (only when it has a branded schema under * `brandStrategy: "local-symbol"`). */ private generateFileImports; /** * Generates the test suite with all describe blocks. */ private generateTestSuite; /** * Generates a describe block for a single file. */ private generateFileDescribe; /** * Generates test cases for a single schema. */ private generateSchemaTests; } /** * Convenience function to generate test file content. * * @param files - Array of file information for test generation * @param options - Generation options * @returns Generated test file content */ export declare function generateTypeTests(files: TestFileInfo[], options?: TestGeneratorOptions): string; //#endregion //#region src/index.d.ts /** * Simple API to extract input and output types from a Zod schema. * * @param filePath - Path to the TypeScript file containing the Zod schema * @param schemaName - Name of the exported Zod schema * @param tsconfigPath - Optional path to tsconfig.json * @returns Object containing input and output type strings * * @example * ```typescript * import { extractZodTypes } from 'zinfer'; * * const { input, output } = extractZodTypes('./schemas.ts', 'UserSchema'); * console.log('Input:', input); * console.log('Output:', output); * ``` */ export declare function extractZodTypes(filePath: string, schemaName: string, tsconfigPath?: string): { input: string; output: string; }; /** * Extracts types and returns a formatted string ready for console output. * * @param filePath - Path to the TypeScript file containing the Zod schema * @param schemaName - Name of the exported Zod schema * @param tsconfigPath - Optional path to tsconfig.json * @returns Formatted string with input and output types * * @example * ```typescript * import { extractAndFormat } from 'zinfer'; * * console.log(extractAndFormat('./schemas.ts', 'UserSchema')); * // Output: * // // input * // { id: string; name: string; } * // * // // output * // { id: string; name: string; } * ``` */ export declare function extractAndFormat(filePath: string, schemaName: string, tsconfigPath?: string): string; /** * Extracts all schemas from a file. * * @param filePath - Path to the TypeScript file * @param tsconfigPath - Optional path to tsconfig.json * @returns Array of extraction results */ export declare function extractAllSchemas(filePath: string, tsconfigPath?: string): ExtractResult[]; /** * Generates TypeScript type declarations from extraction results. * * @param results - Array of extraction results * @param options - Generation options * @returns TypeScript declaration file content */ export declare function generateTypeDeclarations(results: ExtractResult[], options?: { nameMapping?: NameMappingOptions; declaration?: DeclarationOptions; }): string; //#endregion export type { DeclarationOptions, DetectedSchema, ExtractContext, ExtractOptions, ExtractResult, FieldDescription, FileExtractResult, GeneratedFile, MappedTypeName, NameMappingOptions, OutputOptions, PrintOptions, TestFileInfo, TestGeneratorOptions, TestSchemaInfo, ZinferConfig }; //# sourceMappingURL=index.d.ts.map