@dudousxd/nestjs-codegen 0.3.0 → 0.4.0
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/CHANGELOG.md +27 -0
- package/dist/cli/main.cjs +25 -9
- package/dist/cli/main.cjs.map +1 -1
- package/dist/cli/main.js +25 -9
- package/dist/cli/main.js.map +1 -1
- package/dist/extension/index.d.cts +1 -1
- package/dist/extension/index.d.ts +1 -1
- package/dist/{index-oH5t7x4G.d.cts → index-DA4uySjo.d.cts} +29 -1
- package/dist/{index-oH5t7x4G.d.ts → index-DA4uySjo.d.ts} +29 -1
- package/dist/index.cjs +76 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +29 -4
- package/dist/index.d.ts +29 -4
- package/dist/index.js +75 -9
- package/dist/index.js.map +1 -1
- package/dist/nest/index.cjs +24 -8
- package/dist/nest/index.cjs.map +1 -1
- package/dist/nest/index.d.cts +1 -1
- package/dist/nest/index.d.ts +1 -1
- package/dist/nest/index.js +24 -8
- package/dist/nest/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { U as UserConfig, R as ResolvedConfig, a as RouteDescriptor, S as
|
|
2
|
-
export { A as AdapterUsage,
|
|
1
|
+
import { U as UserConfig, R as ResolvedConfig, a as RouteDescriptor, S as SchemaNode, b as SchemaModule, c as ResolvedFormsConfig, V as ValidationAdapter, C as CodegenExtension, E as ExtensionContext } from './index-DA4uySjo.cjs';
|
|
2
|
+
export { A as AdapterUsage, d as ContractDescriptor, e as ContractSource, f as ControllerRef, N as NumberCheck, g as RenderContext, h as RenderedModule, i as ScopeConfig, j as StringCheck, T as TypeRef, k as ValidationOption, r as resolveAdapter } from './index-DA4uySjo.cjs';
|
|
3
3
|
import { ClassDeclaration, SourceFile, Project } from 'ts-morph';
|
|
4
4
|
|
|
5
5
|
declare function defineConfig(c: UserConfig): UserConfig;
|
|
@@ -73,6 +73,31 @@ declare function acquireLock(outDir: string): Promise<{
|
|
|
73
73
|
release: () => Promise<void>;
|
|
74
74
|
} | null>;
|
|
75
75
|
|
|
76
|
+
/**
|
|
77
|
+
* Renders the neutral {@link SchemaNode} IR to a TypeScript *type* expression
|
|
78
|
+
* (not a validation-lib schema). Used to synthesize the hoisted structural type
|
|
79
|
+
* that annotates a recursive zod/valibot const, breaking the implicit-any
|
|
80
|
+
* inference cycle (`type X = {...}` + `const XSchema: z.ZodType<X> = ...`).
|
|
81
|
+
*
|
|
82
|
+
* References to other named schemas resolve two ways:
|
|
83
|
+
* - a `ref`/`lazyRef` to a *recursive* schema → its type-alias name (so the
|
|
84
|
+
* emitted `type` aliases reference each other, terminating the recursion);
|
|
85
|
+
* - a `ref` to a *non-recursive* schema → inlined structurally (keeps the set
|
|
86
|
+
* of emitted `type` aliases limited to exactly the recursive cluster).
|
|
87
|
+
* Inlining always terminates: every reference cycle passes through a recursive
|
|
88
|
+
* name, which is rendered by alias rather than expanded.
|
|
89
|
+
*/
|
|
90
|
+
|
|
91
|
+
interface TsTypeContext {
|
|
92
|
+
/** All hoisted named schemas (for inlining non-recursive refs). */
|
|
93
|
+
named: Map<string, SchemaNode>;
|
|
94
|
+
/** Names that are genuinely recursive (rendered by alias, never inlined). */
|
|
95
|
+
recursive: Set<string>;
|
|
96
|
+
/** schema const name (e.g. `ColumnFilterSchema`) → TS type-alias name. */
|
|
97
|
+
typeNameFor: (schemaName: string) => string;
|
|
98
|
+
}
|
|
99
|
+
declare function renderTsType(node: SchemaNode, ctx: TsTypeContext): string;
|
|
100
|
+
|
|
76
101
|
/**
|
|
77
102
|
* Pure-AST translation of class-validator-decorated DTO classes into the neutral
|
|
78
103
|
* {@link SchemaModule} IR. Reads decorator names + literal args via ts-morph — it
|
|
@@ -139,6 +164,6 @@ interface FastDiscoveryOptions {
|
|
|
139
164
|
}
|
|
140
165
|
declare function discoverContractsFast(opts: FastDiscoveryOptions): Promise<RouteDescriptor[]>;
|
|
141
166
|
|
|
142
|
-
declare const VERSION = "0.
|
|
167
|
+
declare const VERSION = "0.4.0";
|
|
143
168
|
|
|
144
|
-
export { CodegenError, ConfigError, type FastDiscoveryOptions, ResolvedConfig, RouteDescriptor, SchemaModule, UserConfig, VERSION, ValidationAdapter, type Watcher, acquireLock, defineConfig, discoverContractsFast, emitApi, emitForms, emitRoutes, extractSchemaFromDto, generate, loadConfig, resolveConfig, watch };
|
|
169
|
+
export { CodegenError, ConfigError, type FastDiscoveryOptions, ResolvedConfig, RouteDescriptor, SchemaModule, SchemaNode, type TsTypeContext, UserConfig, VERSION, ValidationAdapter, type Watcher, acquireLock, defineConfig, discoverContractsFast, emitApi, emitForms, emitRoutes, extractSchemaFromDto, generate, loadConfig, renderTsType, resolveConfig, watch };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { U as UserConfig, R as ResolvedConfig, a as RouteDescriptor, S as
|
|
2
|
-
export { A as AdapterUsage,
|
|
1
|
+
import { U as UserConfig, R as ResolvedConfig, a as RouteDescriptor, S as SchemaNode, b as SchemaModule, c as ResolvedFormsConfig, V as ValidationAdapter, C as CodegenExtension, E as ExtensionContext } from './index-DA4uySjo.js';
|
|
2
|
+
export { A as AdapterUsage, d as ContractDescriptor, e as ContractSource, f as ControllerRef, N as NumberCheck, g as RenderContext, h as RenderedModule, i as ScopeConfig, j as StringCheck, T as TypeRef, k as ValidationOption, r as resolveAdapter } from './index-DA4uySjo.js';
|
|
3
3
|
import { ClassDeclaration, SourceFile, Project } from 'ts-morph';
|
|
4
4
|
|
|
5
5
|
declare function defineConfig(c: UserConfig): UserConfig;
|
|
@@ -73,6 +73,31 @@ declare function acquireLock(outDir: string): Promise<{
|
|
|
73
73
|
release: () => Promise<void>;
|
|
74
74
|
} | null>;
|
|
75
75
|
|
|
76
|
+
/**
|
|
77
|
+
* Renders the neutral {@link SchemaNode} IR to a TypeScript *type* expression
|
|
78
|
+
* (not a validation-lib schema). Used to synthesize the hoisted structural type
|
|
79
|
+
* that annotates a recursive zod/valibot const, breaking the implicit-any
|
|
80
|
+
* inference cycle (`type X = {...}` + `const XSchema: z.ZodType<X> = ...`).
|
|
81
|
+
*
|
|
82
|
+
* References to other named schemas resolve two ways:
|
|
83
|
+
* - a `ref`/`lazyRef` to a *recursive* schema → its type-alias name (so the
|
|
84
|
+
* emitted `type` aliases reference each other, terminating the recursion);
|
|
85
|
+
* - a `ref` to a *non-recursive* schema → inlined structurally (keeps the set
|
|
86
|
+
* of emitted `type` aliases limited to exactly the recursive cluster).
|
|
87
|
+
* Inlining always terminates: every reference cycle passes through a recursive
|
|
88
|
+
* name, which is rendered by alias rather than expanded.
|
|
89
|
+
*/
|
|
90
|
+
|
|
91
|
+
interface TsTypeContext {
|
|
92
|
+
/** All hoisted named schemas (for inlining non-recursive refs). */
|
|
93
|
+
named: Map<string, SchemaNode>;
|
|
94
|
+
/** Names that are genuinely recursive (rendered by alias, never inlined). */
|
|
95
|
+
recursive: Set<string>;
|
|
96
|
+
/** schema const name (e.g. `ColumnFilterSchema`) → TS type-alias name. */
|
|
97
|
+
typeNameFor: (schemaName: string) => string;
|
|
98
|
+
}
|
|
99
|
+
declare function renderTsType(node: SchemaNode, ctx: TsTypeContext): string;
|
|
100
|
+
|
|
76
101
|
/**
|
|
77
102
|
* Pure-AST translation of class-validator-decorated DTO classes into the neutral
|
|
78
103
|
* {@link SchemaModule} IR. Reads decorator names + literal args via ts-morph — it
|
|
@@ -139,6 +164,6 @@ interface FastDiscoveryOptions {
|
|
|
139
164
|
}
|
|
140
165
|
declare function discoverContractsFast(opts: FastDiscoveryOptions): Promise<RouteDescriptor[]>;
|
|
141
166
|
|
|
142
|
-
declare const VERSION = "0.
|
|
167
|
+
declare const VERSION = "0.4.0";
|
|
143
168
|
|
|
144
|
-
export { CodegenError, ConfigError, type FastDiscoveryOptions, ResolvedConfig, RouteDescriptor, SchemaModule, UserConfig, VERSION, ValidationAdapter, type Watcher, acquireLock, defineConfig, discoverContractsFast, emitApi, emitForms, emitRoutes, extractSchemaFromDto, generate, loadConfig, resolveConfig, watch };
|
|
169
|
+
export { CodegenError, ConfigError, type FastDiscoveryOptions, ResolvedConfig, RouteDescriptor, SchemaModule, SchemaNode, type TsTypeContext, UserConfig, VERSION, ValidationAdapter, type Watcher, acquireLock, defineConfig, discoverContractsFast, emitApi, emitForms, emitRoutes, extractSchemaFromDto, generate, loadConfig, renderTsType, resolveConfig, watch };
|
package/dist/index.js
CHANGED
|
@@ -1256,6 +1256,8 @@ function buildFormsFileWithAdapter(routes, outDir, adapter, config) {
|
|
|
1256
1256
|
}
|
|
1257
1257
|
const { globalSchemas, renamesByEntry } = planNestedSchemas(entries);
|
|
1258
1258
|
const irNamed = /* @__PURE__ */ new Map();
|
|
1259
|
+
const irTypeAliases = /* @__PURE__ */ new Map();
|
|
1260
|
+
const irAnnotations = /* @__PURE__ */ new Map();
|
|
1259
1261
|
const decls = [];
|
|
1260
1262
|
const mapEntries = [];
|
|
1261
1263
|
let used = false;
|
|
@@ -1263,6 +1265,8 @@ function buildFormsFileWithAdapter(routes, outDir, adapter, config) {
|
|
|
1263
1265
|
if (src.schema) {
|
|
1264
1266
|
const r = adapter.renderModule(src.schema);
|
|
1265
1267
|
for (const [n, t] of r.namedNestedSchemas) irNamed.set(n, t);
|
|
1268
|
+
if (r.namedTypeAliases) for (const [n, t] of r.namedTypeAliases) irTypeAliases.set(n, t);
|
|
1269
|
+
if (r.namedAnnotations) for (const [n, a] of r.namedAnnotations) irAnnotations.set(n, a);
|
|
1266
1270
|
return { text: r.schemaText };
|
|
1267
1271
|
}
|
|
1268
1272
|
if (src.zodText) {
|
|
@@ -1336,7 +1340,13 @@ function buildFormsFileWithAdapter(routes, outDir, adapter, config) {
|
|
|
1336
1340
|
for (const [n, t] of irNamed) if (!allNested.has(n)) allNested.set(n, t);
|
|
1337
1341
|
if (allNested.size > 0) {
|
|
1338
1342
|
lines.push("// Hoisted nested schemas (shared across endpoints).");
|
|
1339
|
-
for (const [n,
|
|
1343
|
+
for (const [n, alias] of irTypeAliases) {
|
|
1344
|
+
if (allNested.has(n)) lines.push(`${alias};`);
|
|
1345
|
+
}
|
|
1346
|
+
for (const [n, t] of allNested) {
|
|
1347
|
+
const annotation = irAnnotations.get(n);
|
|
1348
|
+
lines.push(`const ${n}${annotation ? `: ${annotation}` : ""} = ${t};`);
|
|
1349
|
+
}
|
|
1340
1350
|
lines.push("");
|
|
1341
1351
|
}
|
|
1342
1352
|
lines.push(...decls);
|
|
@@ -1888,10 +1898,7 @@ function extractSchemaFromDto(classDecl, sourceFile, project) {
|
|
|
1888
1898
|
depth: 0
|
|
1889
1899
|
};
|
|
1890
1900
|
const root = buildObject(classDecl, sourceFile, ctx);
|
|
1891
|
-
|
|
1892
|
-
ctx.named.set(schemaName, { kind: "unknown", note: "recursive type \u2014 not expanded" });
|
|
1893
|
-
}
|
|
1894
|
-
return { root, named: ctx.named, warnings: ctx.warnings };
|
|
1901
|
+
return { root, named: ctx.named, warnings: ctx.warnings, recursive: ctx.recursiveSchemas };
|
|
1895
1902
|
}
|
|
1896
1903
|
function buildObject(classDecl, classFile, ctx) {
|
|
1897
1904
|
const props = classDecl.getProperties();
|
|
@@ -1911,7 +1918,7 @@ function buildProperty(prop, classFile, ctx) {
|
|
|
1911
1918
|
const dec = (n) => decorators.get(n);
|
|
1912
1919
|
const typeNode = prop.getTypeNode();
|
|
1913
1920
|
const typeText = typeNode?.getText() ?? "unknown";
|
|
1914
|
-
const isArrayType = !!typeNode &&
|
|
1921
|
+
const isArrayType = !!typeNode && Node3.isArrayTypeNode(typeNode);
|
|
1915
1922
|
const typeRefName = resolveTypeFactoryName(dec("Type"));
|
|
1916
1923
|
if (has("ValidateNested") || typeRefName) {
|
|
1917
1924
|
const childName = typeRefName ?? singularClassName(typeText);
|
|
@@ -2042,18 +2049,27 @@ function baseFromType(typeText, isArrayType) {
|
|
|
2042
2049
|
}
|
|
2043
2050
|
}
|
|
2044
2051
|
function buildNestedReference(className, fromFile, ctx) {
|
|
2045
|
-
if (ctx.visiting.has(className)
|
|
2052
|
+
if (ctx.visiting.has(className)) {
|
|
2046
2053
|
const reserved = ctx.emittedClasses.get(className) ?? aliasFor(className, ctx);
|
|
2047
2054
|
ctx.emittedClasses.set(className, reserved);
|
|
2048
2055
|
ctx.recursiveSchemas.add(reserved);
|
|
2049
2056
|
if (!ctx.warnedDecorators.has(`recursive:${reserved}`)) {
|
|
2050
2057
|
ctx.warnedDecorators.add(`recursive:${reserved}`);
|
|
2051
|
-
const msg = `${className} is a recursive type
|
|
2058
|
+
const msg = `${className} is a recursive type; the generated schema validates it via a lazy self-reference.`;
|
|
2052
2059
|
ctx.warnings.push(msg);
|
|
2053
2060
|
console.warn(`[nestjs-codegen] ${msg}`);
|
|
2054
2061
|
}
|
|
2055
2062
|
return { kind: "lazyRef", name: reserved };
|
|
2056
2063
|
}
|
|
2064
|
+
if (ctx.depth >= 8) {
|
|
2065
|
+
if (!ctx.warnedDecorators.has(`deep:${className}`)) {
|
|
2066
|
+
ctx.warnedDecorators.add(`deep:${className}`);
|
|
2067
|
+
const msg = `${className} nesting is too deep to expand; the generated schema uses unknown for it.`;
|
|
2068
|
+
ctx.warnings.push(msg);
|
|
2069
|
+
console.warn(`[nestjs-codegen] ${msg}`);
|
|
2070
|
+
}
|
|
2071
|
+
return { kind: "unknown", note: "nesting too deep \u2014 not expanded" };
|
|
2072
|
+
}
|
|
2057
2073
|
const existing = ctx.emittedClasses.get(className);
|
|
2058
2074
|
if (existing) return { kind: "ref", name: existing };
|
|
2059
2075
|
const schemaName = aliasFor(className, ctx);
|
|
@@ -3463,8 +3479,57 @@ async function watch(config, onChange) {
|
|
|
3463
3479
|
};
|
|
3464
3480
|
}
|
|
3465
3481
|
|
|
3482
|
+
// src/ir/render-ts-type.ts
|
|
3483
|
+
function tsKey(name) {
|
|
3484
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name) ? name : JSON.stringify(name);
|
|
3485
|
+
}
|
|
3486
|
+
function renderTsType(node, ctx) {
|
|
3487
|
+
switch (node.kind) {
|
|
3488
|
+
case "string":
|
|
3489
|
+
return "string";
|
|
3490
|
+
case "number":
|
|
3491
|
+
return "number";
|
|
3492
|
+
case "boolean":
|
|
3493
|
+
return "boolean";
|
|
3494
|
+
case "date":
|
|
3495
|
+
return "Date";
|
|
3496
|
+
case "unknown":
|
|
3497
|
+
return "unknown";
|
|
3498
|
+
case "instanceof":
|
|
3499
|
+
return node.ctor;
|
|
3500
|
+
case "enum":
|
|
3501
|
+
return node.literals.join(" | ");
|
|
3502
|
+
case "literal":
|
|
3503
|
+
return node.raw;
|
|
3504
|
+
case "union":
|
|
3505
|
+
return node.options.map((o) => renderTsType(o, ctx)).join(" | ");
|
|
3506
|
+
case "array":
|
|
3507
|
+
return `Array<${renderTsType(node.element, ctx)}>`;
|
|
3508
|
+
case "optional":
|
|
3509
|
+
return `${renderTsType(node.inner, ctx)} | undefined`;
|
|
3510
|
+
case "annotated":
|
|
3511
|
+
return renderTsType(node.inner, ctx);
|
|
3512
|
+
case "object": {
|
|
3513
|
+
if (node.fields.length === 0) return node.passthrough ? "Record<string, unknown>" : "{}";
|
|
3514
|
+
const inner = node.fields.map((f) => {
|
|
3515
|
+
if (f.value.kind === "optional") {
|
|
3516
|
+
return `${tsKey(f.key)}?: ${renderTsType(f.value.inner, ctx)}`;
|
|
3517
|
+
}
|
|
3518
|
+
return `${tsKey(f.key)}: ${renderTsType(f.value, ctx)}`;
|
|
3519
|
+
}).join("; ");
|
|
3520
|
+
return `{ ${inner} }`;
|
|
3521
|
+
}
|
|
3522
|
+
case "ref":
|
|
3523
|
+
case "lazyRef": {
|
|
3524
|
+
if (ctx.recursive.has(node.name)) return ctx.typeNameFor(node.name);
|
|
3525
|
+
const target = ctx.named.get(node.name);
|
|
3526
|
+
return target ? renderTsType(target, ctx) : "unknown";
|
|
3527
|
+
}
|
|
3528
|
+
}
|
|
3529
|
+
}
|
|
3530
|
+
|
|
3466
3531
|
// src/index.ts
|
|
3467
|
-
var VERSION = "0.
|
|
3532
|
+
var VERSION = "0.4.0";
|
|
3468
3533
|
export {
|
|
3469
3534
|
CodegenError,
|
|
3470
3535
|
ConfigError,
|
|
@@ -3478,6 +3543,7 @@ export {
|
|
|
3478
3543
|
extractSchemaFromDto,
|
|
3479
3544
|
generate,
|
|
3480
3545
|
loadConfig,
|
|
3546
|
+
renderTsType,
|
|
3481
3547
|
resolveAdapter,
|
|
3482
3548
|
resolveConfig,
|
|
3483
3549
|
watch
|