@dudousxd/nestjs-codegen 0.25.2 → 0.27.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 +56 -0
- package/README.md +2 -2
- package/dist/cli/main.cjs +991 -270
- package/dist/cli/main.cjs.map +1 -1
- package/dist/cli/main.js +997 -274
- package/dist/cli/main.js.map +1 -1
- package/dist/extension/index.cjs.map +1 -1
- package/dist/extension/index.d.cts +1 -1
- package/dist/extension/index.d.ts +1 -1
- package/dist/extension/index.js.map +1 -1
- package/dist/{index-Ce3YnfwS.d.cts → index-Dc4s883u.d.cts} +49 -5
- package/dist/{index-Ce3YnfwS.d.ts → index-Dc4s883u.d.ts} +49 -5
- package/dist/index.cjs +994 -273
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +1000 -277
- package/dist/index.js.map +1 -1
- package/dist/nest/index.cjs +1037 -318
- 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 +1040 -319
- package/dist/nest/index.js.map +1 -1
- package/package.json +11 -7
package/dist/nest/index.js
CHANGED
|
@@ -70,11 +70,32 @@ function validateUserConfig(userConfig) {
|
|
|
70
70
|
"validation adapter is required \u2014 install @dudousxd/nestjs-codegen-zod and pass zodAdapter, or use @dudousxd/nestjs-codegen-valibot / -arktype"
|
|
71
71
|
);
|
|
72
72
|
}
|
|
73
|
-
if (userConfig.pages &&
|
|
73
|
+
if (userConfig.pages && !isGlobInput(userConfig.pages.glob)) {
|
|
74
74
|
throw new ConfigError(
|
|
75
|
-
"Config validation failed: `pages.glob` must be a string when `pages` is set"
|
|
75
|
+
"Config validation failed: `pages.glob` must be a string or a non-empty array of strings when `pages` is set"
|
|
76
76
|
);
|
|
77
77
|
}
|
|
78
|
+
for (const [scope, scopeConfig] of Object.entries(userConfig.pages?.scopes ?? {})) {
|
|
79
|
+
if (scope === "default") {
|
|
80
|
+
throw new ConfigError(
|
|
81
|
+
'Config validation failed: `pages.scopes` cannot declare "default" \u2014 the default app is `pages.glob` (InertiaModule.forRoot).'
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
if (!isGlobInput(scopeConfig?.glob)) {
|
|
85
|
+
throw new ConfigError(
|
|
86
|
+
`Config validation failed: \`pages.scopes.${scope}.glob\` must be a string or a non-empty array of strings`
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (userConfig.scopes && Object.keys(userConfig.scopes).length > 0) {
|
|
91
|
+
console.warn(
|
|
92
|
+
"[nestjs-codegen] The top-level `scopes` option has no effect and is deprecated. For several Inertia apps use `pages.scopes` (one entry per InertiaModule.forFeature scope); for several controller sets use one `contracts.glob` (e.g. a brace glob)."
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function isGlobInput(value) {
|
|
97
|
+
if (typeof value === "string") return true;
|
|
98
|
+
return Array.isArray(value) && value.length > 0 && value.every((glob) => typeof glob === "string");
|
|
78
99
|
}
|
|
79
100
|
function applyDefaults(userConfig, cwd) {
|
|
80
101
|
validateUserConfig(userConfig);
|
|
@@ -102,7 +123,17 @@ function applyDefaults(userConfig, cwd) {
|
|
|
102
123
|
pages: userConfig.pages ? {
|
|
103
124
|
glob: userConfig.pages.glob,
|
|
104
125
|
propsExport: userConfig.pages.propsExport ?? "ComponentProps",
|
|
105
|
-
componentNameStrategy: userConfig.pages.componentNameStrategy ?? "relative-no-ext"
|
|
126
|
+
componentNameStrategy: userConfig.pages.componentNameStrategy ?? "relative-no-ext",
|
|
127
|
+
scopes: Object.fromEntries(
|
|
128
|
+
Object.entries(userConfig.pages.scopes ?? {}).map(([scope, scopeConfig]) => [
|
|
129
|
+
scope,
|
|
130
|
+
{
|
|
131
|
+
glob: scopeConfig.glob,
|
|
132
|
+
prefix: scopeConfig.prefix ?? `${scope}/`,
|
|
133
|
+
componentNameStrategy: scopeConfig.componentNameStrategy ?? userConfig.pages?.componentNameStrategy ?? "relative-no-ext"
|
|
134
|
+
}
|
|
135
|
+
])
|
|
136
|
+
)
|
|
106
137
|
} : null,
|
|
107
138
|
contracts: {
|
|
108
139
|
glob: userConfig.contracts?.glob ?? "src/**/*.controller.ts",
|
|
@@ -149,15 +180,15 @@ import { existsSync } from "fs";
|
|
|
149
180
|
import { dirname as dirname2, join as join2, resolve as resolve3 } from "path";
|
|
150
181
|
import fg from "fast-glob";
|
|
151
182
|
import {
|
|
152
|
-
Node as
|
|
153
|
-
Project as
|
|
154
|
-
ts
|
|
183
|
+
Node as Node10,
|
|
184
|
+
Project as Project3,
|
|
185
|
+
ts as ts3
|
|
155
186
|
} from "ts-morph";
|
|
156
187
|
|
|
157
188
|
// src/discovery/dto-type-resolver.ts
|
|
158
189
|
import {
|
|
159
|
-
Node as
|
|
160
|
-
SyntaxKind as
|
|
190
|
+
Node as Node9,
|
|
191
|
+
SyntaxKind as SyntaxKind3
|
|
161
192
|
} from "ts-morph";
|
|
162
193
|
|
|
163
194
|
// src/discovery/dto-to-ir.ts
|
|
@@ -1695,6 +1726,674 @@ function extractFilterableEntityFields(filterClass, project, entityOverride, pre
|
|
|
1695
1726
|
return fields;
|
|
1696
1727
|
}
|
|
1697
1728
|
|
|
1729
|
+
// src/discovery/pipe-schema.ts
|
|
1730
|
+
import { Node as Node8 } from "ts-morph";
|
|
1731
|
+
|
|
1732
|
+
// src/discovery/schema-type-inference.ts
|
|
1733
|
+
import {
|
|
1734
|
+
ts as ts2
|
|
1735
|
+
} from "ts-morph";
|
|
1736
|
+
|
|
1737
|
+
// src/discovery/schema-type-program.ts
|
|
1738
|
+
import { Node as Node6, Project as Project2, ts } from "ts-morph";
|
|
1739
|
+
var PARAM_DECORATORS = /* @__PURE__ */ new Set(["Body", "Query", "Param"]);
|
|
1740
|
+
var SchemaTypeProgram = class {
|
|
1741
|
+
constructor(discovery) {
|
|
1742
|
+
this.discovery = discovery;
|
|
1743
|
+
const base = discovery.getCompilerOptions();
|
|
1744
|
+
this.options = {
|
|
1745
|
+
...base,
|
|
1746
|
+
// A tsconfig-less consumer gets ES5 by default, whose lib has no `Map`/`Set`/
|
|
1747
|
+
// `Promise` for the schema libraries' declarations to lean on.
|
|
1748
|
+
target: base.target ?? ts.ScriptTarget.ES2022,
|
|
1749
|
+
// With neither set, resolve the way a bundler does: package `exports`
|
|
1750
|
+
// (`zod/v4`) and extensionless or `.js` relative imports all resolve.
|
|
1751
|
+
...base.module === void 0 && base.moduleResolution === void 0 ? { module: ts.ModuleKind.ESNext, moduleResolution: ts.ModuleResolutionKind.Bundler } : {},
|
|
1752
|
+
// Without it `.nullable()` and `.optional()` would erase to the bare type.
|
|
1753
|
+
strictNullChecks: true,
|
|
1754
|
+
// No ambient `@types/*` (a tsconfig's `types: ['node']`): a schema does not
|
|
1755
|
+
// need them, and they are a hundred-odd files of program to build.
|
|
1756
|
+
types: [],
|
|
1757
|
+
noEmit: true
|
|
1758
|
+
};
|
|
1759
|
+
this.project = new Project2({
|
|
1760
|
+
skipAddingFilesFromTsConfig: true,
|
|
1761
|
+
compilerOptions: this.options
|
|
1762
|
+
});
|
|
1763
|
+
for (const sf of discovery.getSourceFiles()) this.request(sf.getFilePath(), { schema: true });
|
|
1764
|
+
this.flush();
|
|
1765
|
+
}
|
|
1766
|
+
project;
|
|
1767
|
+
options;
|
|
1768
|
+
/** Parses a file that is not in discovery's Project, without adding it there. */
|
|
1769
|
+
scratch = new Project2({
|
|
1770
|
+
skipAddingFilesFromTsConfig: true,
|
|
1771
|
+
skipLoadingLibFiles: true,
|
|
1772
|
+
skipFileDependencyResolution: true
|
|
1773
|
+
});
|
|
1774
|
+
needs = /* @__PURE__ */ new Map();
|
|
1775
|
+
/** Pruned text per file, as last written into {@link project}. */
|
|
1776
|
+
written = /* @__PURE__ */ new Map();
|
|
1777
|
+
/**
|
|
1778
|
+
* The typed copy of a discovery file, with its schema positions kept. Adding one
|
|
1779
|
+
* that was not there yet (a contract file discovery reached lazily) rebuilds the
|
|
1780
|
+
* program once.
|
|
1781
|
+
*/
|
|
1782
|
+
sourceFileFor(sf) {
|
|
1783
|
+
this.request(sf.getFilePath(), { schema: true });
|
|
1784
|
+
this.flush();
|
|
1785
|
+
return this.project.getSourceFile(sf.getFilePath());
|
|
1786
|
+
}
|
|
1787
|
+
// -------------------------------------------------------------------------
|
|
1788
|
+
queue = [];
|
|
1789
|
+
request(path, want) {
|
|
1790
|
+
let needs = this.needs.get(path);
|
|
1791
|
+
const isNew = !needs;
|
|
1792
|
+
needs ??= { schema: false, all: false, names: /* @__PURE__ */ new Set() };
|
|
1793
|
+
let grew = isNew;
|
|
1794
|
+
if (want.schema && !needs.schema) {
|
|
1795
|
+
needs.schema = true;
|
|
1796
|
+
grew = true;
|
|
1797
|
+
}
|
|
1798
|
+
if (want.all && !needs.all) {
|
|
1799
|
+
needs.all = true;
|
|
1800
|
+
grew = true;
|
|
1801
|
+
}
|
|
1802
|
+
for (const name of want.names ?? []) {
|
|
1803
|
+
if (!needs.names.has(name)) {
|
|
1804
|
+
needs.names.add(name);
|
|
1805
|
+
grew = true;
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
1808
|
+
this.needs.set(path, needs);
|
|
1809
|
+
if (grew) this.queue.push(path);
|
|
1810
|
+
}
|
|
1811
|
+
/** Run the worklist to a fixpoint, then write every changed file into the program. */
|
|
1812
|
+
flush() {
|
|
1813
|
+
const changed = /* @__PURE__ */ new Map();
|
|
1814
|
+
while (this.queue.length > 0) {
|
|
1815
|
+
const path = this.queue.pop();
|
|
1816
|
+
const original = this.original(path);
|
|
1817
|
+
if (!original) continue;
|
|
1818
|
+
const needs = this.needs.get(path);
|
|
1819
|
+
const text = this.prune(original, needs);
|
|
1820
|
+
if (this.written.get(path) !== text) changed.set(path, text);
|
|
1821
|
+
}
|
|
1822
|
+
for (const [path, text] of changed) {
|
|
1823
|
+
this.project.createSourceFile(path, text, { overwrite: true });
|
|
1824
|
+
this.written.set(path, text);
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
/** The file as discovery sees it (its in-memory text), else as it is on disk. */
|
|
1828
|
+
original(path) {
|
|
1829
|
+
return this.discovery.getSourceFile(path) ?? this.scratch.getSourceFile(path) ?? this.scratch.addSourceFileAtPathIfExists(path);
|
|
1830
|
+
}
|
|
1831
|
+
/**
|
|
1832
|
+
* `sf`'s text with every import/re-export nothing in `needs` references blanked,
|
|
1833
|
+
* requesting from each kept one's target the names it brings in.
|
|
1834
|
+
*/
|
|
1835
|
+
prune(sf, needs) {
|
|
1836
|
+
const locals = topLevelDeclarations(sf);
|
|
1837
|
+
const roots = [];
|
|
1838
|
+
if (needs.all) roots.push(...sf.getStatements());
|
|
1839
|
+
if (needs.schema) roots.push(...schemaPositions(sf));
|
|
1840
|
+
const reexported = /* @__PURE__ */ new Map();
|
|
1841
|
+
for (const exp of sf.getExportDeclarations()) {
|
|
1842
|
+
const spec = exp.getModuleSpecifierValue();
|
|
1843
|
+
if (spec === void 0) {
|
|
1844
|
+
for (const named of exp.getNamedExports()) {
|
|
1845
|
+
const exposed = (named.getAliasNode() ?? named.getNameNode()).getText();
|
|
1846
|
+
if (needs.all || needs.names.has(exposed)) roots.push(named.getNameNode());
|
|
1847
|
+
}
|
|
1848
|
+
continue;
|
|
1849
|
+
}
|
|
1850
|
+
if (exp.isNamespaceExport() || exp.getNamedExports().length === 0) continue;
|
|
1851
|
+
for (const named of exp.getNamedExports()) {
|
|
1852
|
+
const exposed = (named.getAliasNode() ?? named.getNameNode()).getText();
|
|
1853
|
+
const list = reexported.get(exposed) ?? [];
|
|
1854
|
+
list.push({ from: spec, name: named.getNameNode().getText() });
|
|
1855
|
+
reexported.set(exposed, list);
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
const unresolvedNames = [];
|
|
1859
|
+
for (const name of needs.names) {
|
|
1860
|
+
const decls = name === "default" ? defaultExport(sf) : locals.get(name) ?? [];
|
|
1861
|
+
if (decls.length > 0) roots.push(...decls);
|
|
1862
|
+
else if (!reexported.has(name)) unresolvedNames.push(name);
|
|
1863
|
+
}
|
|
1864
|
+
const referenced = /* @__PURE__ */ new Set();
|
|
1865
|
+
const queue = [...roots];
|
|
1866
|
+
const seen = new Set(queue);
|
|
1867
|
+
while (queue.length > 0) {
|
|
1868
|
+
const node = queue.pop();
|
|
1869
|
+
const ids = Node6.isIdentifier(node) ? [node] : node.getDescendantsOfKind(ts.SyntaxKind.Identifier);
|
|
1870
|
+
for (const id of ids) {
|
|
1871
|
+
const name = id.getText();
|
|
1872
|
+
if (referenced.has(name)) continue;
|
|
1873
|
+
referenced.add(name);
|
|
1874
|
+
for (const local of locals.get(name) ?? []) {
|
|
1875
|
+
if (seen.has(local)) continue;
|
|
1876
|
+
seen.add(local);
|
|
1877
|
+
queue.push(local);
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
}
|
|
1881
|
+
const chars = sf.getFullText().split("");
|
|
1882
|
+
const blank = (node) => {
|
|
1883
|
+
for (let i = node.getStart(); i < node.getEnd(); i++) {
|
|
1884
|
+
if (chars[i] !== "\n" && chars[i] !== "\r") chars[i] = " ";
|
|
1885
|
+
}
|
|
1886
|
+
};
|
|
1887
|
+
for (const decl of sf.getImportDeclarations()) {
|
|
1888
|
+
const target = this.resolve(decl.getModuleSpecifierValue(), sf);
|
|
1889
|
+
const namespace = decl.getNamespaceImport()?.getText();
|
|
1890
|
+
const def = decl.getDefaultImport()?.getText();
|
|
1891
|
+
const named = decl.getNamedImports().map((n) => ({
|
|
1892
|
+
local: (n.getAliasNode() ?? n.getNameNode()).getText(),
|
|
1893
|
+
imported: n.getName()
|
|
1894
|
+
})).filter((n) => referenced.has(n.local));
|
|
1895
|
+
const wantsNamespace = namespace !== void 0 && referenced.has(namespace);
|
|
1896
|
+
const wantsDefault = def !== void 0 && referenced.has(def);
|
|
1897
|
+
if (!wantsNamespace && !wantsDefault && named.length === 0) {
|
|
1898
|
+
blank(decl);
|
|
1899
|
+
continue;
|
|
1900
|
+
}
|
|
1901
|
+
if (target) {
|
|
1902
|
+
this.request(target, {
|
|
1903
|
+
all: wantsNamespace,
|
|
1904
|
+
names: [...wantsDefault ? ["default"] : [], ...named.map((n) => n.imported)]
|
|
1905
|
+
});
|
|
1906
|
+
}
|
|
1907
|
+
}
|
|
1908
|
+
for (const exp of sf.getExportDeclarations()) {
|
|
1909
|
+
const spec = exp.getModuleSpecifierValue();
|
|
1910
|
+
if (spec === void 0) continue;
|
|
1911
|
+
const target = this.resolve(spec, sf);
|
|
1912
|
+
if (exp.isNamespaceExport() || exp.getNamedExports().length === 0) {
|
|
1913
|
+
if (!needs.all && unresolvedNames.length === 0) {
|
|
1914
|
+
blank(exp);
|
|
1915
|
+
} else if (target) {
|
|
1916
|
+
this.request(target, { all: needs.all, names: unresolvedNames });
|
|
1917
|
+
}
|
|
1918
|
+
continue;
|
|
1919
|
+
}
|
|
1920
|
+
const wanted = exp.getNamedExports().filter(
|
|
1921
|
+
(n) => needs.all || needs.names.has((n.getAliasNode() ?? n.getNameNode()).getText())
|
|
1922
|
+
).map((n) => n.getNameNode().getText());
|
|
1923
|
+
if (wanted.length === 0) blank(exp);
|
|
1924
|
+
else if (target) this.request(target, { names: wanted });
|
|
1925
|
+
}
|
|
1926
|
+
return chars.join("");
|
|
1927
|
+
}
|
|
1928
|
+
/**
|
|
1929
|
+
* The project source file a module specifier resolves to, or undefined for a
|
|
1930
|
+
* package (read unpruned by the program itself) or an unresolvable one.
|
|
1931
|
+
*/
|
|
1932
|
+
resolve(specifier, from) {
|
|
1933
|
+
const resolved = ts.resolveModuleName(
|
|
1934
|
+
specifier,
|
|
1935
|
+
from.getFilePath(),
|
|
1936
|
+
this.options,
|
|
1937
|
+
ts.sys
|
|
1938
|
+
).resolvedModule;
|
|
1939
|
+
if (!resolved || resolved.isExternalLibraryImport) return void 0;
|
|
1940
|
+
const file = resolved.resolvedFileName;
|
|
1941
|
+
if (file.endsWith(".d.ts") || !/\.(m|c)?tsx?$/.test(file)) return void 0;
|
|
1942
|
+
return file;
|
|
1943
|
+
}
|
|
1944
|
+
};
|
|
1945
|
+
function schemaPositions(sf) {
|
|
1946
|
+
const roots = [];
|
|
1947
|
+
sf.forEachDescendant((node) => {
|
|
1948
|
+
if (Node6.isDecorator(node)) {
|
|
1949
|
+
const name = node.getName();
|
|
1950
|
+
if (name === "ApplyContract") {
|
|
1951
|
+
roots.push(...node.getArguments());
|
|
1952
|
+
} else if (PARAM_DECORATORS.has(name)) {
|
|
1953
|
+
for (const arg of node.getArguments()) {
|
|
1954
|
+
if (Node6.isNewExpression(arg) || Node6.isCallExpression(arg)) {
|
|
1955
|
+
roots.push(...arg.getArguments());
|
|
1956
|
+
}
|
|
1957
|
+
}
|
|
1958
|
+
}
|
|
1959
|
+
} else if (Node6.isCallExpression(node)) {
|
|
1960
|
+
const callee = node.getExpression();
|
|
1961
|
+
const name = Node6.isIdentifier(callee) ? callee.getText() : Node6.isPropertyAccessExpression(callee) ? callee.getName() : void 0;
|
|
1962
|
+
if (name === "defineContract") roots.push(...node.getArguments());
|
|
1963
|
+
}
|
|
1964
|
+
});
|
|
1965
|
+
return roots;
|
|
1966
|
+
}
|
|
1967
|
+
function topLevelDeclarations(sf) {
|
|
1968
|
+
const byName = /* @__PURE__ */ new Map();
|
|
1969
|
+
const add = (name, node) => {
|
|
1970
|
+
if (!name) return;
|
|
1971
|
+
const list = byName.get(name);
|
|
1972
|
+
if (list) list.push(node);
|
|
1973
|
+
else byName.set(name, [node]);
|
|
1974
|
+
};
|
|
1975
|
+
for (const decl of sf.getVariableDeclarations()) add(decl.getName(), decl);
|
|
1976
|
+
for (const decl of sf.getFunctions()) add(decl.getName(), decl);
|
|
1977
|
+
for (const decl of sf.getClasses()) add(decl.getName(), decl);
|
|
1978
|
+
for (const decl of sf.getEnums()) add(decl.getName(), decl);
|
|
1979
|
+
for (const decl of sf.getInterfaces()) add(decl.getName(), decl);
|
|
1980
|
+
for (const decl of sf.getTypeAliases()) add(decl.getName(), decl);
|
|
1981
|
+
for (const decl of sf.getModules()) add(decl.getName(), decl);
|
|
1982
|
+
return byName;
|
|
1983
|
+
}
|
|
1984
|
+
function defaultExport(sf) {
|
|
1985
|
+
const nodes = [];
|
|
1986
|
+
for (const assignment of sf.getExportAssignments()) nodes.push(assignment);
|
|
1987
|
+
for (const fn of sf.getFunctions()) if (fn.isDefaultExport()) nodes.push(fn);
|
|
1988
|
+
for (const cls of sf.getClasses()) if (cls.isDefaultExport()) nodes.push(cls);
|
|
1989
|
+
return nodes;
|
|
1990
|
+
}
|
|
1991
|
+
|
|
1992
|
+
// src/discovery/schema-type-inference.ts
|
|
1993
|
+
var programs = /* @__PURE__ */ new WeakMap();
|
|
1994
|
+
function programFor(discovery) {
|
|
1995
|
+
let program = programs.get(discovery);
|
|
1996
|
+
if (!program) {
|
|
1997
|
+
program = new SchemaTypeProgram(discovery);
|
|
1998
|
+
programs.set(discovery, program);
|
|
1999
|
+
}
|
|
2000
|
+
return program;
|
|
2001
|
+
}
|
|
2002
|
+
function clearSchemaTypeProject(discovery) {
|
|
2003
|
+
programs.delete(discovery);
|
|
2004
|
+
}
|
|
2005
|
+
function counterpart(typedSf, node) {
|
|
2006
|
+
const start = node.getStart();
|
|
2007
|
+
const end = node.getEnd();
|
|
2008
|
+
const kind = node.getKind();
|
|
2009
|
+
let candidate = typedSf.getDescendantAtPos(start);
|
|
2010
|
+
while (candidate) {
|
|
2011
|
+
if (candidate.getStart() === start && candidate.getEnd() === end && candidate.getKind() === kind) {
|
|
2012
|
+
return candidate;
|
|
2013
|
+
}
|
|
2014
|
+
if (candidate.getStart() < start || candidate.getEnd() > end) return void 0;
|
|
2015
|
+
candidate = candidate.getParent();
|
|
2016
|
+
}
|
|
2017
|
+
return void 0;
|
|
2018
|
+
}
|
|
2019
|
+
function propType(type, name, at) {
|
|
2020
|
+
return type.getProperty(name)?.getTypeAtLocation(at);
|
|
2021
|
+
}
|
|
2022
|
+
function isAnyOrUnknown(type) {
|
|
2023
|
+
return type.isAny() || type.isUnknown();
|
|
2024
|
+
}
|
|
2025
|
+
function inferSchema(expr, discovery) {
|
|
2026
|
+
try {
|
|
2027
|
+
const typedSf = programFor(discovery).sourceFileFor(expr.getSourceFile());
|
|
2028
|
+
const at = typedSf && counterpart(typedSf, expr);
|
|
2029
|
+
if (!at) return { kind: "unresolved" };
|
|
2030
|
+
const schemaType = at.getType();
|
|
2031
|
+
if (isAnyOrUnknown(schemaType)) return { kind: "unresolved" };
|
|
2032
|
+
const standard = propType(schemaType, "~standard", at);
|
|
2033
|
+
const stdTypes = standard ? propType(standard, "types", at)?.getNonNullableType() : void 0;
|
|
2034
|
+
const stdInput = stdTypes ? propType(stdTypes, "input", at) : void 0;
|
|
2035
|
+
const stdOutput = stdTypes ? propType(stdTypes, "output", at) : void 0;
|
|
2036
|
+
if (stdInput && stdOutput) {
|
|
2037
|
+
return { kind: "schema", types: { input: stdInput, output: stdOutput, at } };
|
|
2038
|
+
}
|
|
2039
|
+
const zodInput = propType(schemaType, "_input", at);
|
|
2040
|
+
const zodOutput = propType(schemaType, "_output", at);
|
|
2041
|
+
if (zodInput && zodOutput) {
|
|
2042
|
+
return { kind: "schema", types: { input: zodInput, output: zodOutput, at } };
|
|
2043
|
+
}
|
|
2044
|
+
return { kind: "not-schema" };
|
|
2045
|
+
} catch (err) {
|
|
2046
|
+
dbg("schema inference failed:", err);
|
|
2047
|
+
return { kind: "unresolved" };
|
|
2048
|
+
}
|
|
2049
|
+
}
|
|
2050
|
+
var GLOBAL_NAMED_TYPES = /* @__PURE__ */ new Set([
|
|
2051
|
+
"Date",
|
|
2052
|
+
"File",
|
|
2053
|
+
"Blob",
|
|
2054
|
+
"FormData",
|
|
2055
|
+
"URL",
|
|
2056
|
+
"RegExp",
|
|
2057
|
+
"Map",
|
|
2058
|
+
"Set",
|
|
2059
|
+
"ArrayBuffer",
|
|
2060
|
+
"Uint8Array"
|
|
2061
|
+
]);
|
|
2062
|
+
var MAX_DEPTH = 12;
|
|
2063
|
+
function objectKey(name) {
|
|
2064
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name) ? name : JSON.stringify(name);
|
|
2065
|
+
}
|
|
2066
|
+
function isOptionalSymbol(sym) {
|
|
2067
|
+
return (sym.getFlags() & ts2.SymbolFlags.Optional) !== 0;
|
|
2068
|
+
}
|
|
2069
|
+
function isSymbolKeyed(sym) {
|
|
2070
|
+
return sym.getName().startsWith("__@");
|
|
2071
|
+
}
|
|
2072
|
+
function orderedProperties(type) {
|
|
2073
|
+
const position = (sym) => {
|
|
2074
|
+
const decl = sym.getDeclarations()[0];
|
|
2075
|
+
return decl ? [decl.getSourceFile().getFilePath(), decl.getStart()] : void 0;
|
|
2076
|
+
};
|
|
2077
|
+
return type.getProperties().map((sym, index) => ({ sym, index, at: position(sym) })).sort((a, b) => {
|
|
2078
|
+
if (a.at && b.at) {
|
|
2079
|
+
if (a.at[0] !== b.at[0]) return a.at[0] < b.at[0] ? -1 : 1;
|
|
2080
|
+
if (a.at[1] !== b.at[1]) return a.at[1] - b.at[1];
|
|
2081
|
+
} else if (a.at || b.at) {
|
|
2082
|
+
return a.at ? -1 : 1;
|
|
2083
|
+
}
|
|
2084
|
+
return a.index - b.index;
|
|
2085
|
+
}).map((entry) => entry.sym);
|
|
2086
|
+
}
|
|
2087
|
+
function isFromDeclarationFile(sym) {
|
|
2088
|
+
const decl = sym?.getDeclarations()[0];
|
|
2089
|
+
return !!decl && decl.getSourceFile().isDeclarationFile();
|
|
2090
|
+
}
|
|
2091
|
+
function render(type, state, paired) {
|
|
2092
|
+
if (state.depth > MAX_DEPTH) return "unknown";
|
|
2093
|
+
if (isAnyOrUnknown(type)) {
|
|
2094
|
+
return paired && !isAnyOrUnknown(paired) ? render(paired, state) : "unknown";
|
|
2095
|
+
}
|
|
2096
|
+
if (type.isNever()) return "never";
|
|
2097
|
+
if (type.isString() || type.isTemplateLiteral()) return "string";
|
|
2098
|
+
if (type.isNumber()) return "number";
|
|
2099
|
+
if (type.isBoolean()) return "boolean";
|
|
2100
|
+
if (type.isBigInt()) return "bigint";
|
|
2101
|
+
if (type.isNull()) return "null";
|
|
2102
|
+
if (type.isUndefined() || type.isVoid()) return "undefined";
|
|
2103
|
+
if (type.isBooleanLiteral()) return type.getText();
|
|
2104
|
+
if (type.isStringLiteral() || type.isNumberLiteral() || type.isEnumLiteral()) {
|
|
2105
|
+
const value = type.getLiteralValue();
|
|
2106
|
+
return typeof value === "string" ? JSON.stringify(value) : String(value);
|
|
2107
|
+
}
|
|
2108
|
+
if (type.isBigIntLiteral()) return type.getText();
|
|
2109
|
+
if (type.isTypeParameter()) return "unknown";
|
|
2110
|
+
if (type.isUnion()) return renderUnion(type, state, paired);
|
|
2111
|
+
if (type.isIntersection()) return renderIntersection(type, state);
|
|
2112
|
+
const next = { ...state, depth: state.depth + 1 };
|
|
2113
|
+
if (type.isArray() || type.isReadonlyArray()) {
|
|
2114
|
+
const element = type.getArrayElementType() ?? type.getTypeArguments()[0];
|
|
2115
|
+
const pairedElement = paired?.getArrayElementType() ?? paired?.getTypeArguments()[0];
|
|
2116
|
+
return element ? `Array<${render(element, next, pairedElement)}>` : "Array<unknown>";
|
|
2117
|
+
}
|
|
2118
|
+
if (type.isTuple()) {
|
|
2119
|
+
const elements = type.getTupleElements();
|
|
2120
|
+
const pairedElements = paired?.isTuple() ? paired.getTupleElements() : [];
|
|
2121
|
+
return `[${elements.map((e, i) => render(e, next, pairedElements[i])).join(", ")}]`;
|
|
2122
|
+
}
|
|
2123
|
+
if (type.isObject()) return renderObject(type, next, paired);
|
|
2124
|
+
return "unknown";
|
|
2125
|
+
}
|
|
2126
|
+
function renderUnion(type, state, paired) {
|
|
2127
|
+
const members = type.getUnionTypes();
|
|
2128
|
+
const hasTrue = members.some((m) => m.isBooleanLiteral() && m.getText() === "true");
|
|
2129
|
+
const hasFalse = members.some((m) => m.isBooleanLiteral() && m.getText() === "false");
|
|
2130
|
+
const pairedNonNull = paired?.getNonNullableType();
|
|
2131
|
+
const arms = [];
|
|
2132
|
+
let sawBoolean = false;
|
|
2133
|
+
let hasNull = false;
|
|
2134
|
+
let hasUndefined = false;
|
|
2135
|
+
for (const member of members) {
|
|
2136
|
+
if (member.isNull()) {
|
|
2137
|
+
hasNull = true;
|
|
2138
|
+
continue;
|
|
2139
|
+
}
|
|
2140
|
+
if (member.isUndefined()) {
|
|
2141
|
+
hasUndefined = true;
|
|
2142
|
+
continue;
|
|
2143
|
+
}
|
|
2144
|
+
if (member.isBooleanLiteral() && hasTrue && hasFalse) {
|
|
2145
|
+
if (!sawBoolean) arms.push("boolean");
|
|
2146
|
+
sawBoolean = true;
|
|
2147
|
+
continue;
|
|
2148
|
+
}
|
|
2149
|
+
arms.push(render(member, state, members.length === 1 ? paired : pairedNonNull));
|
|
2150
|
+
}
|
|
2151
|
+
if (hasNull) arms.push("null");
|
|
2152
|
+
if (hasUndefined) arms.push("undefined");
|
|
2153
|
+
const unique = [...new Set(arms)];
|
|
2154
|
+
if (unique.includes("unknown")) return "unknown";
|
|
2155
|
+
return unique.join(" | ");
|
|
2156
|
+
}
|
|
2157
|
+
function renderIntersection(type, state) {
|
|
2158
|
+
const members = type.getIntersectionTypes();
|
|
2159
|
+
const primitive = members.find((m) => !m.isObject());
|
|
2160
|
+
if (primitive) return render(primitive, state);
|
|
2161
|
+
return renderObject(type, { ...state, depth: state.depth + 1 }, void 0);
|
|
2162
|
+
}
|
|
2163
|
+
function renderObject(type, state, paired) {
|
|
2164
|
+
const symbol = type.getSymbol() ?? type.getAliasSymbol();
|
|
2165
|
+
const name = symbol?.getName();
|
|
2166
|
+
if (name && GLOBAL_NAMED_TYPES.has(name) && isFromDeclarationFile(symbol)) {
|
|
2167
|
+
const args = type.getTypeArguments();
|
|
2168
|
+
return args.length ? `${name}<${args.map((a) => render(a, state)).join(", ")}>` : name;
|
|
2169
|
+
}
|
|
2170
|
+
if (type.getCallSignatures().length > 0 || type.getConstructSignatures().length > 0) {
|
|
2171
|
+
return "unknown";
|
|
2172
|
+
}
|
|
2173
|
+
if (type.isClassOrInterface() && isFromDeclarationFile(symbol)) return "unknown";
|
|
2174
|
+
if (state.stack.has(type.compilerType)) return "unknown";
|
|
2175
|
+
const stack = new Set(state.stack).add(type.compilerType);
|
|
2176
|
+
const inner = { ...state, stack };
|
|
2177
|
+
const fields = [];
|
|
2178
|
+
for (const prop of orderedProperties(type)) {
|
|
2179
|
+
if (isSymbolKeyed(prop)) continue;
|
|
2180
|
+
const propName = prop.getName();
|
|
2181
|
+
const propT = prop.getTypeAtLocation(state.at);
|
|
2182
|
+
if (propT.getCallSignatures().length > 0 && !propT.isUnion()) continue;
|
|
2183
|
+
const pairedProp = paired?.getProperty(propName)?.getTypeAtLocation(state.at);
|
|
2184
|
+
const optional = isOptionalSymbol(prop) ? "?" : "";
|
|
2185
|
+
fields.push(`${objectKey(propName)}${optional}: ${render(propT, inner, pairedProp)}`);
|
|
2186
|
+
}
|
|
2187
|
+
const stringIndex = type.getStringIndexType();
|
|
2188
|
+
if (stringIndex) {
|
|
2189
|
+
const pairedIndex = paired?.getStringIndexType();
|
|
2190
|
+
const value = render(stringIndex, inner, pairedIndex);
|
|
2191
|
+
if (fields.length === 0) return `Record<string, ${value}>`;
|
|
2192
|
+
fields.push(`[key: string]: ${value}`);
|
|
2193
|
+
}
|
|
2194
|
+
return fields.length ? `{ ${fields.join("; ")} }` : "{}";
|
|
2195
|
+
}
|
|
2196
|
+
function stateAt(at) {
|
|
2197
|
+
return { at, stack: /* @__PURE__ */ new Set(), depth: 0 };
|
|
2198
|
+
}
|
|
2199
|
+
function requestTypeOf(types) {
|
|
2200
|
+
return render(types.input, stateAt(types.at), types.output);
|
|
2201
|
+
}
|
|
2202
|
+
function responseTypeOf(types) {
|
|
2203
|
+
return render(types.output, stateAt(types.at));
|
|
2204
|
+
}
|
|
2205
|
+
function requestFieldsOf(types) {
|
|
2206
|
+
const { input, output, at } = types;
|
|
2207
|
+
const objectInput = input.getNonNullableType();
|
|
2208
|
+
if (!objectInput.isObject() || objectInput.isArray() || objectInput.isTuple()) return null;
|
|
2209
|
+
const state = stateAt(at);
|
|
2210
|
+
return orderedProperties(objectInput).filter((prop) => !isSymbolKeyed(prop)).map((prop) => ({
|
|
2211
|
+
name: prop.getName(),
|
|
2212
|
+
type: render(
|
|
2213
|
+
prop.getTypeAtLocation(at),
|
|
2214
|
+
state,
|
|
2215
|
+
output.getProperty(prop.getName())?.getTypeAtLocation(at)
|
|
2216
|
+
),
|
|
2217
|
+
optional: isOptionalSymbol(prop)
|
|
2218
|
+
}));
|
|
2219
|
+
}
|
|
2220
|
+
function acceptsUndefined(types) {
|
|
2221
|
+
const { input, output } = types;
|
|
2222
|
+
const accepted = isAnyOrUnknown(input) ? output : input;
|
|
2223
|
+
if (isAnyOrUnknown(accepted) || accepted.isUndefined()) return true;
|
|
2224
|
+
return accepted.isUnion() && accepted.getUnionTypes().some((t) => t.isUndefined());
|
|
2225
|
+
}
|
|
2226
|
+
|
|
2227
|
+
// src/discovery/zod-ast-to-ts.ts
|
|
2228
|
+
import { Node as Node7, SyntaxKind as SyntaxKind2 } from "ts-morph";
|
|
2229
|
+
function zodAstToTs(node) {
|
|
2230
|
+
if (!Node7.isCallExpression(node)) return "unknown";
|
|
2231
|
+
const expr = node.getExpression();
|
|
2232
|
+
if (Node7.isPropertyAccessExpression(expr)) {
|
|
2233
|
+
const methodName = expr.getName();
|
|
2234
|
+
const receiver = expr.getExpression();
|
|
2235
|
+
if (methodName === "optional") {
|
|
2236
|
+
return `${zodAstToTs(receiver)} | undefined`;
|
|
2237
|
+
}
|
|
2238
|
+
if (methodName === "nullable") {
|
|
2239
|
+
return `${zodAstToTs(receiver)} | null`;
|
|
2240
|
+
}
|
|
2241
|
+
const args = node.getArguments();
|
|
2242
|
+
switch (methodName) {
|
|
2243
|
+
case "string":
|
|
2244
|
+
return "string";
|
|
2245
|
+
case "number":
|
|
2246
|
+
return "number";
|
|
2247
|
+
case "boolean":
|
|
2248
|
+
return "boolean";
|
|
2249
|
+
case "unknown":
|
|
2250
|
+
return "unknown";
|
|
2251
|
+
case "any":
|
|
2252
|
+
return "unknown";
|
|
2253
|
+
case "literal": {
|
|
2254
|
+
const lit = args[0];
|
|
2255
|
+
if (!lit) return "unknown";
|
|
2256
|
+
if (Node7.isStringLiteral(lit)) return JSON.stringify(lit.getLiteralValue());
|
|
2257
|
+
if (Node7.isNumericLiteral(lit)) return lit.getLiteralValue().toString();
|
|
2258
|
+
if (lit.getKind() === SyntaxKind2.TrueKeyword) return "true";
|
|
2259
|
+
if (lit.getKind() === SyntaxKind2.FalseKeyword) return "false";
|
|
2260
|
+
return "unknown";
|
|
2261
|
+
}
|
|
2262
|
+
case "enum": {
|
|
2263
|
+
const arrArg = args[0];
|
|
2264
|
+
if (!arrArg || !Node7.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
2265
|
+
const members = arrArg.getElements().map(
|
|
2266
|
+
(el) => Node7.isStringLiteral(el) ? JSON.stringify(el.getLiteralValue()) : "unknown"
|
|
2267
|
+
);
|
|
2268
|
+
return members.join(" | ");
|
|
2269
|
+
}
|
|
2270
|
+
case "array": {
|
|
2271
|
+
const inner = args[0];
|
|
2272
|
+
if (!inner) return "unknown";
|
|
2273
|
+
return `Array<${zodAstToTs(inner)}>`;
|
|
2274
|
+
}
|
|
2275
|
+
case "object": {
|
|
2276
|
+
const objArg = args[0];
|
|
2277
|
+
if (!objArg || !Node7.isObjectLiteralExpression(objArg)) return "unknown";
|
|
2278
|
+
const lines = [];
|
|
2279
|
+
for (const prop of objArg.getProperties()) {
|
|
2280
|
+
if (!Node7.isPropertyAssignment(prop)) continue;
|
|
2281
|
+
const key = prop.getName();
|
|
2282
|
+
const valNode = prop.getInitializer();
|
|
2283
|
+
if (!valNode) continue;
|
|
2284
|
+
const tsType = zodAstToTs(valNode);
|
|
2285
|
+
const isOpt = isOptionalChain(valNode);
|
|
2286
|
+
lines.push(`${key}${isOpt ? "?" : ""}: ${tsType}`);
|
|
2287
|
+
}
|
|
2288
|
+
return `{ ${lines.join("; ")} }`;
|
|
2289
|
+
}
|
|
2290
|
+
case "union": {
|
|
2291
|
+
const arrArg = args[0];
|
|
2292
|
+
if (!arrArg || !Node7.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
2293
|
+
return arrArg.getElements().map(zodAstToTs).join(" | ");
|
|
2294
|
+
}
|
|
2295
|
+
case "record": {
|
|
2296
|
+
const valArg = args.length === 1 ? args[0] : args[1];
|
|
2297
|
+
if (!valArg) return "unknown";
|
|
2298
|
+
return `Record<string, ${zodAstToTs(valArg)}>`;
|
|
2299
|
+
}
|
|
2300
|
+
case "tuple": {
|
|
2301
|
+
const arrArg = args[0];
|
|
2302
|
+
if (!arrArg || !Node7.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
2303
|
+
return `[${arrArg.getElements().map(zodAstToTs).join(", ")}]`;
|
|
2304
|
+
}
|
|
2305
|
+
default:
|
|
2306
|
+
return "unknown";
|
|
2307
|
+
}
|
|
2308
|
+
}
|
|
2309
|
+
return "unknown";
|
|
2310
|
+
}
|
|
2311
|
+
function isOptionalChain(node) {
|
|
2312
|
+
if (!Node7.isCallExpression(node)) return false;
|
|
2313
|
+
const expr = node.getExpression();
|
|
2314
|
+
return Node7.isPropertyAccessExpression(expr) && expr.getName() === "optional";
|
|
2315
|
+
}
|
|
2316
|
+
function memberType(val, position, project) {
|
|
2317
|
+
if (project) {
|
|
2318
|
+
const inferred = inferSchema(val, project);
|
|
2319
|
+
if (inferred.kind === "schema") {
|
|
2320
|
+
return position === "request" ? requestTypeOf(inferred.types) : responseTypeOf(inferred.types);
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2323
|
+
return zodAstToTs(val);
|
|
2324
|
+
}
|
|
2325
|
+
function parseDefineContractCall(callExpr, project) {
|
|
2326
|
+
if (!Node7.isCallExpression(callExpr)) return null;
|
|
2327
|
+
const callee = callExpr.getExpression();
|
|
2328
|
+
const calleeName = Node7.isIdentifier(callee) ? callee.getText() : Node7.isPropertyAccessExpression(callee) ? callee.getName() : "";
|
|
2329
|
+
if (calleeName !== "defineContract") return null;
|
|
2330
|
+
const args = callExpr.getArguments();
|
|
2331
|
+
const optsArg = args[0];
|
|
2332
|
+
if (!optsArg || !Node7.isObjectLiteralExpression(optsArg)) return null;
|
|
2333
|
+
let query = null;
|
|
2334
|
+
let body = null;
|
|
2335
|
+
let response = "unknown";
|
|
2336
|
+
let error = null;
|
|
2337
|
+
let bodyZodText = null;
|
|
2338
|
+
let queryZodText = null;
|
|
2339
|
+
for (const prop of optsArg.getProperties()) {
|
|
2340
|
+
if (!Node7.isPropertyAssignment(prop)) continue;
|
|
2341
|
+
const propName = prop.getName();
|
|
2342
|
+
const val = prop.getInitializer();
|
|
2343
|
+
if (!val) continue;
|
|
2344
|
+
if (propName === "query") {
|
|
2345
|
+
query = memberType(val, "request", project);
|
|
2346
|
+
queryZodText = val.getText();
|
|
2347
|
+
} else if (propName === "body") {
|
|
2348
|
+
body = memberType(val, "request", project);
|
|
2349
|
+
bodyZodText = val.getText();
|
|
2350
|
+
} else if (propName === "response") {
|
|
2351
|
+
response = memberType(val, "response", project);
|
|
2352
|
+
} else if (propName === "error") {
|
|
2353
|
+
error = memberType(val, "response", project);
|
|
2354
|
+
}
|
|
2355
|
+
}
|
|
2356
|
+
return { query, body, response, error, bodyZodText, queryZodText };
|
|
2357
|
+
}
|
|
2358
|
+
|
|
2359
|
+
// src/discovery/pipe-schema.ts
|
|
2360
|
+
function pipeSchemaOf(decorator, project) {
|
|
2361
|
+
for (const pipe of decorator.getArguments()) {
|
|
2362
|
+
if (!Node8.isNewExpression(pipe) && !Node8.isCallExpression(pipe)) continue;
|
|
2363
|
+
for (const arg of pipe.getArguments()) {
|
|
2364
|
+
if (!couldBeSchema(arg)) continue;
|
|
2365
|
+
const inferred = inferSchema(arg, project);
|
|
2366
|
+
if (inferred.kind === "schema") {
|
|
2367
|
+
return {
|
|
2368
|
+
type: requestTypeOf(inferred.types),
|
|
2369
|
+
optional: acceptsUndefined(inferred.types),
|
|
2370
|
+
fields: requestFieldsOf(inferred.types)
|
|
2371
|
+
};
|
|
2372
|
+
}
|
|
2373
|
+
if (inferred.kind === "unresolved") {
|
|
2374
|
+
const fallback = syntacticZodType(arg, project);
|
|
2375
|
+
if (fallback) {
|
|
2376
|
+
return { type: fallback, optional: fallback.endsWith("| undefined"), fields: null };
|
|
2377
|
+
}
|
|
2378
|
+
}
|
|
2379
|
+
}
|
|
2380
|
+
}
|
|
2381
|
+
return null;
|
|
2382
|
+
}
|
|
2383
|
+
function couldBeSchema(arg) {
|
|
2384
|
+
return Node8.isIdentifier(arg) || Node8.isPropertyAccessExpression(arg) || Node8.isCallExpression(arg) || Node8.isAsExpression(arg) || Node8.isSatisfiesExpression(arg) || Node8.isParenthesizedExpression(arg);
|
|
2385
|
+
}
|
|
2386
|
+
function syntacticZodType(arg, project) {
|
|
2387
|
+
let expr = arg;
|
|
2388
|
+
if (Node8.isIdentifier(arg)) {
|
|
2389
|
+
const resolved = resolveImportedVariable(arg.getText(), arg.getSourceFile(), project);
|
|
2390
|
+
expr = resolved?.decl.getInitializer();
|
|
2391
|
+
}
|
|
2392
|
+
if (!expr || !Node8.isCallExpression(expr)) return null;
|
|
2393
|
+
const type = zodAstToTs(expr);
|
|
2394
|
+
return type === "unknown" ? null : type;
|
|
2395
|
+
}
|
|
2396
|
+
|
|
1698
2397
|
// src/discovery/dto-type-resolver.ts
|
|
1699
2398
|
var WRAPPER_TYPES = {
|
|
1700
2399
|
// MikroORM Ref/Reference/LoadedReference/IdentifiedReference are server-side
|
|
@@ -1726,22 +2425,22 @@ var PASSTHROUGH_UTILITY = /* @__PURE__ */ new Set([
|
|
|
1726
2425
|
]);
|
|
1727
2426
|
function resolveTypeNodeToString(typeNode, sourceFile, project, depth, subst = /* @__PURE__ */ new Map()) {
|
|
1728
2427
|
if (depth <= 0) return "unknown";
|
|
1729
|
-
if (
|
|
2428
|
+
if (Node9.isArrayTypeNode(typeNode)) {
|
|
1730
2429
|
const elementType = typeNode.getElementTypeNode();
|
|
1731
2430
|
return `Array<${resolveTypeNodeToString(elementType, sourceFile, project, depth, subst)}>`;
|
|
1732
2431
|
}
|
|
1733
|
-
if (
|
|
2432
|
+
if (Node9.isUnionTypeNode(typeNode)) {
|
|
1734
2433
|
return typeNode.getTypeNodes().map((t) => resolveTypeNodeToString(t, sourceFile, project, depth, subst)).join(" | ");
|
|
1735
2434
|
}
|
|
1736
|
-
if (
|
|
2435
|
+
if (Node9.isIntersectionTypeNode(typeNode)) {
|
|
1737
2436
|
return typeNode.getTypeNodes().map((t) => resolveTypeNodeToString(t, sourceFile, project, depth, subst)).join(" & ");
|
|
1738
2437
|
}
|
|
1739
|
-
if (
|
|
2438
|
+
if (Node9.isParenthesizedTypeNode(typeNode)) {
|
|
1740
2439
|
return `(${resolveTypeNodeToString(typeNode.getTypeNode(), sourceFile, project, depth, subst)})`;
|
|
1741
2440
|
}
|
|
1742
|
-
if (
|
|
2441
|
+
if (Node9.isTypeReference(typeNode)) {
|
|
1743
2442
|
const typeName = typeNode.getTypeName();
|
|
1744
|
-
const name =
|
|
2443
|
+
const name = Node9.isIdentifier(typeName) ? typeName.getText() : typeNode.getText();
|
|
1745
2444
|
const bound = subst.get(name);
|
|
1746
2445
|
if (bound !== void 0) return bound;
|
|
1747
2446
|
if (name === "string" || name === "number" || name === "boolean") return name;
|
|
@@ -1764,13 +2463,13 @@ function resolveTypeNodeToString(typeNode, sourceFile, project, depth, subst = /
|
|
|
1764
2463
|
dbg("unresolvable type:", name, "in", sourceFile.getFilePath());
|
|
1765
2464
|
return "unknown";
|
|
1766
2465
|
}
|
|
1767
|
-
if (
|
|
2466
|
+
if (Node9.isTypeLiteral(typeNode)) {
|
|
1768
2467
|
const members = [];
|
|
1769
2468
|
for (const member of typeNode.getMembers()) {
|
|
1770
|
-
if (
|
|
2469
|
+
if (Node9.isPropertySignature(member)) {
|
|
1771
2470
|
const memberTypeNode = member.getTypeNode();
|
|
1772
|
-
const
|
|
1773
|
-
members.push(`${member.getName()}${member.hasQuestionToken() ? "?" : ""}: ${
|
|
2471
|
+
const memberType2 = memberTypeNode ? resolveTypeNodeToString(memberTypeNode, sourceFile, project, depth, subst) : "unknown";
|
|
2472
|
+
members.push(`${member.getName()}${member.hasQuestionToken() ? "?" : ""}: ${memberType2}`);
|
|
1774
2473
|
} else {
|
|
1775
2474
|
members.push(member.getText());
|
|
1776
2475
|
}
|
|
@@ -1778,11 +2477,11 @@ function resolveTypeNodeToString(typeNode, sourceFile, project, depth, subst = /
|
|
|
1778
2477
|
return members.length > 0 ? `{ ${members.join("; ")} }` : "{}";
|
|
1779
2478
|
}
|
|
1780
2479
|
const kind = typeNode.getKind();
|
|
1781
|
-
if (kind ===
|
|
1782
|
-
if (kind ===
|
|
1783
|
-
if (kind ===
|
|
1784
|
-
if (kind ===
|
|
1785
|
-
if (kind ===
|
|
2480
|
+
if (kind === SyntaxKind3.StringKeyword) return "string";
|
|
2481
|
+
if (kind === SyntaxKind3.NumberKeyword) return "number";
|
|
2482
|
+
if (kind === SyntaxKind3.BooleanKeyword) return "boolean";
|
|
2483
|
+
if (kind === SyntaxKind3.UnknownKeyword) return "unknown";
|
|
2484
|
+
if (kind === SyntaxKind3.AnyKeyword) return "unknown";
|
|
1786
2485
|
return typeNode.getText();
|
|
1787
2486
|
}
|
|
1788
2487
|
function unwrapFirstTypeArg(typeNode, sourceFile, project, depth, mode, subst = /* @__PURE__ */ new Map()) {
|
|
@@ -1830,20 +2529,24 @@ function resolvePropertied(decl, sourceFile, project, depth, subst = /* @__PURE_
|
|
|
1830
2529
|
const propName = prop.getName();
|
|
1831
2530
|
const isOptional = prop.hasQuestionToken();
|
|
1832
2531
|
const propTypeNode = prop.getTypeNode();
|
|
1833
|
-
let
|
|
2532
|
+
let propType2 = "unknown";
|
|
1834
2533
|
if (propTypeNode) {
|
|
1835
|
-
|
|
2534
|
+
propType2 = resolveTypeNodeToString(propTypeNode, sourceFile, project, depth, subst);
|
|
1836
2535
|
}
|
|
1837
|
-
lines.push(`${propName}${isOptional ? "?" : ""}: ${
|
|
2536
|
+
lines.push(`${propName}${isOptional ? "?" : ""}: ${propType2}`);
|
|
1838
2537
|
}
|
|
1839
2538
|
return `{ ${lines.join("; ")} }`;
|
|
1840
2539
|
}
|
|
2540
|
+
function namedField(decorator) {
|
|
2541
|
+
const first = decorator.getArguments()[0];
|
|
2542
|
+
return first && Node9.isStringLiteral(first) ? first.getLiteralValue() : null;
|
|
2543
|
+
}
|
|
1841
2544
|
function extractBodyType(method, sourceFile, project) {
|
|
1842
2545
|
for (const param of method.getParameters()) {
|
|
1843
2546
|
const bodyDecorator = param.getDecorators().find((d) => d.getName() === "Body");
|
|
1844
|
-
if (!bodyDecorator) continue;
|
|
1845
|
-
const
|
|
1846
|
-
if (
|
|
2547
|
+
if (!bodyDecorator || namedField(bodyDecorator) !== null) continue;
|
|
2548
|
+
const piped = pipeSchemaOf(bodyDecorator, project);
|
|
2549
|
+
if (piped) return piped.type;
|
|
1847
2550
|
const typeNode = param.getTypeNode();
|
|
1848
2551
|
if (typeNode) {
|
|
1849
2552
|
return resolveTypeNodeToString(typeNode, sourceFile, project, 3);
|
|
@@ -1851,11 +2554,16 @@ function extractBodyType(method, sourceFile, project) {
|
|
|
1851
2554
|
}
|
|
1852
2555
|
return null;
|
|
1853
2556
|
}
|
|
2557
|
+
function hasPipeSchema(param, decoratorName, project) {
|
|
2558
|
+
const decorator = param.getDecorators().find((d) => d.getName() === decoratorName);
|
|
2559
|
+
return !!decorator && pipeSchemaOf(decorator, project) !== null;
|
|
2560
|
+
}
|
|
1854
2561
|
function extractQueryType(method, sourceFile, project) {
|
|
1855
2562
|
for (const param of method.getParameters()) {
|
|
1856
2563
|
const queryDecorator = param.getDecorators().find((d) => d.getName() === "Query");
|
|
1857
|
-
if (!queryDecorator) continue;
|
|
1858
|
-
|
|
2564
|
+
if (!queryDecorator || namedField(queryDecorator) !== null) continue;
|
|
2565
|
+
const piped = pipeSchemaOf(queryDecorator, project);
|
|
2566
|
+
if (piped) return piped.type;
|
|
1859
2567
|
const typeNode = param.getTypeNode();
|
|
1860
2568
|
if (typeNode) {
|
|
1861
2569
|
return resolveTypeNodeToString(typeNode, sourceFile, project, 3);
|
|
@@ -1865,10 +2573,13 @@ function extractQueryType(method, sourceFile, project) {
|
|
|
1865
2573
|
for (const param of method.getParameters()) {
|
|
1866
2574
|
const queryDecorator = param.getDecorators().find((d) => d.getName() === "Query");
|
|
1867
2575
|
if (!queryDecorator) continue;
|
|
1868
|
-
const
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
2576
|
+
const queryName = namedField(queryDecorator);
|
|
2577
|
+
if (queryName === null) continue;
|
|
2578
|
+
const piped = pipeSchemaOf(queryDecorator, project);
|
|
2579
|
+
if (piped) {
|
|
2580
|
+
entries.push(`${queryName}${piped.optional ? "?" : ""}: ${piped.type}`);
|
|
2581
|
+
continue;
|
|
2582
|
+
}
|
|
1872
2583
|
const typeNode = param.getTypeNode();
|
|
1873
2584
|
const queryType = typeNode ? resolveTypeNodeToString(typeNode, sourceFile, project, 3) : "string";
|
|
1874
2585
|
entries.push(`${queryName}${isParamOptional(param) ? "?" : ""}: ${queryType}`);
|
|
@@ -1878,8 +2589,8 @@ function extractQueryType(method, sourceFile, project) {
|
|
|
1878
2589
|
function isParamOptional(param) {
|
|
1879
2590
|
if (param.hasQuestionToken() || param.hasInitializer()) return true;
|
|
1880
2591
|
const typeNode = param.getTypeNode();
|
|
1881
|
-
if (typeNode &&
|
|
1882
|
-
return typeNode.getTypeNodes().some((t) => t.getKind() ===
|
|
2592
|
+
if (typeNode && Node9.isUnionTypeNode(typeNode)) {
|
|
2593
|
+
return typeNode.getTypeNodes().some((t) => t.getKind() === SyntaxKind3.UndefinedKeyword);
|
|
1883
2594
|
}
|
|
1884
2595
|
return false;
|
|
1885
2596
|
}
|
|
@@ -1891,7 +2602,7 @@ function extractParamsType(method, sourceFile, project) {
|
|
|
1891
2602
|
const paramArgs = paramDecorator.getArguments();
|
|
1892
2603
|
if (paramArgs.length === 0) continue;
|
|
1893
2604
|
const nameArg = paramArgs[0];
|
|
1894
|
-
if (!
|
|
2605
|
+
if (!Node9.isStringLiteral(nameArg)) continue;
|
|
1895
2606
|
const paramName = nameArg.getLiteralValue();
|
|
1896
2607
|
const typeNode = param.getTypeNode();
|
|
1897
2608
|
const paramType = typeNode ? resolveTypeNodeToString(typeNode, sourceFile, project, 3) : "string";
|
|
@@ -1899,6 +2610,22 @@ function extractParamsType(method, sourceFile, project) {
|
|
|
1899
2610
|
}
|
|
1900
2611
|
return entries.length > 0 ? `{ ${entries.join("; ")} }` : null;
|
|
1901
2612
|
}
|
|
2613
|
+
function extractPipedParamTypes(method, project) {
|
|
2614
|
+
const types = {};
|
|
2615
|
+
for (const param of method.getParameters()) {
|
|
2616
|
+
const paramDecorator = param.getDecorators().find((d) => d.getName() === "Param");
|
|
2617
|
+
if (!paramDecorator) continue;
|
|
2618
|
+
const piped = pipeSchemaOf(paramDecorator, project);
|
|
2619
|
+
if (!piped) continue;
|
|
2620
|
+
const name = namedField(paramDecorator);
|
|
2621
|
+
if (name !== null) {
|
|
2622
|
+
types[name] = piped.type;
|
|
2623
|
+
} else {
|
|
2624
|
+
for (const field of piped.fields ?? []) types[field.name] = field.type;
|
|
2625
|
+
}
|
|
2626
|
+
}
|
|
2627
|
+
return Object.keys(types).length > 0 ? types : null;
|
|
2628
|
+
}
|
|
1902
2629
|
function extractUploadedFiles(method) {
|
|
1903
2630
|
const FILE = "File | Blob";
|
|
1904
2631
|
const entries = [];
|
|
@@ -1912,28 +2639,28 @@ function extractUploadedFiles(method) {
|
|
|
1912
2639
|
for (const decorator of method.getDecorators()) {
|
|
1913
2640
|
if (decorator.getName() !== "UseInterceptors") continue;
|
|
1914
2641
|
for (const arg of decorator.getArguments()) {
|
|
1915
|
-
if (!
|
|
2642
|
+
if (!Node9.isCallExpression(arg)) continue;
|
|
1916
2643
|
const interceptor = arg.getExpression().getText();
|
|
1917
2644
|
const callArgs = arg.getArguments();
|
|
1918
2645
|
const firstArg2 = callArgs[0];
|
|
1919
2646
|
if (interceptor === "FileInterceptor") {
|
|
1920
|
-
if (firstArg2 &&
|
|
2647
|
+
if (firstArg2 && Node9.isStringLiteral(firstArg2)) {
|
|
1921
2648
|
entries.push(`${firstArg2.getLiteralValue()}: ${FILE}`);
|
|
1922
2649
|
multipart = true;
|
|
1923
2650
|
}
|
|
1924
2651
|
} else if (interceptor === "FilesInterceptor") {
|
|
1925
|
-
if (firstArg2 &&
|
|
2652
|
+
if (firstArg2 && Node9.isStringLiteral(firstArg2)) {
|
|
1926
2653
|
entries.push(`${firstArg2.getLiteralValue()}: Array<${FILE}>`);
|
|
1927
2654
|
multipart = true;
|
|
1928
2655
|
}
|
|
1929
2656
|
} else if (interceptor === "FileFieldsInterceptor") {
|
|
1930
|
-
if (firstArg2 &&
|
|
2657
|
+
if (firstArg2 && Node9.isArrayLiteralExpression(firstArg2)) {
|
|
1931
2658
|
for (const el of firstArg2.getElements()) {
|
|
1932
|
-
if (!
|
|
2659
|
+
if (!Node9.isObjectLiteralExpression(el)) continue;
|
|
1933
2660
|
const nameProp = el.getProperty("name");
|
|
1934
|
-
if (nameProp &&
|
|
2661
|
+
if (nameProp && Node9.isPropertyAssignment(nameProp)) {
|
|
1935
2662
|
const init = nameProp.getInitializer();
|
|
1936
|
-
if (init &&
|
|
2663
|
+
if (init && Node9.isStringLiteral(init)) {
|
|
1937
2664
|
entries.push(`${init.getLiteralValue()}: Array<${FILE}>`);
|
|
1938
2665
|
}
|
|
1939
2666
|
}
|
|
@@ -1953,13 +2680,13 @@ function extractResponseType(method, sourceFile, project) {
|
|
|
1953
2680
|
if (apiResponseDecorator) {
|
|
1954
2681
|
const args = apiResponseDecorator.getArguments();
|
|
1955
2682
|
const optsArg = args[0];
|
|
1956
|
-
if (optsArg &&
|
|
2683
|
+
if (optsArg && Node9.isObjectLiteralExpression(optsArg)) {
|
|
1957
2684
|
for (const prop of optsArg.getProperties()) {
|
|
1958
|
-
if (!
|
|
2685
|
+
if (!Node9.isPropertyAssignment(prop)) continue;
|
|
1959
2686
|
if (prop.getName() !== "type") continue;
|
|
1960
2687
|
const val = prop.getInitializer();
|
|
1961
2688
|
if (!val) continue;
|
|
1962
|
-
if (
|
|
2689
|
+
if (Node9.isArrayLiteralExpression(val)) {
|
|
1963
2690
|
const elements = val.getElements();
|
|
1964
2691
|
const firstEl = elements[0];
|
|
1965
2692
|
if (elements.length > 0 && firstEl !== void 0) {
|
|
@@ -1980,24 +2707,24 @@ function extractResponseType(method, sourceFile, project) {
|
|
|
1980
2707
|
}
|
|
1981
2708
|
function apiResponseStatus(decorator) {
|
|
1982
2709
|
const optsArg = decorator.getArguments()[0];
|
|
1983
|
-
if (!optsArg || !
|
|
2710
|
+
if (!optsArg || !Node9.isObjectLiteralExpression(optsArg)) return null;
|
|
1984
2711
|
for (const prop of optsArg.getProperties()) {
|
|
1985
|
-
if (!
|
|
2712
|
+
if (!Node9.isPropertyAssignment(prop)) continue;
|
|
1986
2713
|
if (prop.getName() !== "status") continue;
|
|
1987
2714
|
const val = prop.getInitializer();
|
|
1988
|
-
if (val &&
|
|
2715
|
+
if (val && Node9.isNumericLiteral(val)) return Number(val.getLiteralValue());
|
|
1989
2716
|
}
|
|
1990
2717
|
return null;
|
|
1991
2718
|
}
|
|
1992
2719
|
function apiResponseTypeNode(decorator) {
|
|
1993
2720
|
const optsArg = decorator.getArguments()[0];
|
|
1994
|
-
if (!optsArg || !
|
|
2721
|
+
if (!optsArg || !Node9.isObjectLiteralExpression(optsArg)) return null;
|
|
1995
2722
|
for (const prop of optsArg.getProperties()) {
|
|
1996
|
-
if (!
|
|
2723
|
+
if (!Node9.isPropertyAssignment(prop)) continue;
|
|
1997
2724
|
if (prop.getName() !== "type") continue;
|
|
1998
2725
|
const val = prop.getInitializer();
|
|
1999
2726
|
if (!val) return null;
|
|
2000
|
-
if (
|
|
2727
|
+
if (Node9.isArrayLiteralExpression(val)) {
|
|
2001
2728
|
const first = val.getElements()[0];
|
|
2002
2729
|
return first ? { node: first, isArray: true } : null;
|
|
2003
2730
|
}
|
|
@@ -2015,7 +2742,7 @@ function extractErrorType(method, sourceFile, project) {
|
|
|
2015
2742
|
const inner = resolveIdentifierToClassType(typeInfo.node, sourceFile, project, 3);
|
|
2016
2743
|
const type = typeInfo.isArray ? `Array<${inner}>` : inner;
|
|
2017
2744
|
let ref = null;
|
|
2018
|
-
if (
|
|
2745
|
+
if (Node9.isIdentifier(typeInfo.node)) {
|
|
2019
2746
|
const name = typeInfo.node.getText();
|
|
2020
2747
|
const localDecl = sourceFile.getInterface(name) || sourceFile.getClass(name) || sourceFile.getTypeAlias(name);
|
|
2021
2748
|
if (localDecl?.isExported()) {
|
|
@@ -2032,7 +2759,7 @@ function extractErrorType(method, sourceFile, project) {
|
|
|
2032
2759
|
return null;
|
|
2033
2760
|
}
|
|
2034
2761
|
function resolveIdentifierToClassType(node, sourceFile, project, depth) {
|
|
2035
|
-
if (!
|
|
2762
|
+
if (!Node9.isIdentifier(node)) return "unknown";
|
|
2036
2763
|
const name = node.getText();
|
|
2037
2764
|
const resolved = findType(name, sourceFile, project);
|
|
2038
2765
|
if (resolved) {
|
|
@@ -2052,9 +2779,9 @@ var STREAM_ENVELOPES = /* @__PURE__ */ new Set(["MessageEvent", "MessageEventLik
|
|
|
2052
2779
|
var BINARY_RESPONSE_TYPES = /* @__PURE__ */ new Set(["StreamableFile", "Buffer"]);
|
|
2053
2780
|
function detectBinaryResponse(method) {
|
|
2054
2781
|
const node = unwrapNamedContainer(method.getReturnTypeNode(), /* @__PURE__ */ new Set(["Promise"]));
|
|
2055
|
-
if (!node || !
|
|
2782
|
+
if (!node || !Node9.isTypeReference(node)) return false;
|
|
2056
2783
|
const typeName = node.getTypeName();
|
|
2057
|
-
const name =
|
|
2784
|
+
const name = Node9.isIdentifier(typeName) ? typeName.getText() : "";
|
|
2058
2785
|
return BINARY_RESPONSE_TYPES.has(name);
|
|
2059
2786
|
}
|
|
2060
2787
|
function detectStreamElement(method) {
|
|
@@ -2069,9 +2796,9 @@ function detectStreamElement(method) {
|
|
|
2069
2796
|
return null;
|
|
2070
2797
|
}
|
|
2071
2798
|
function streamContainerElement(node) {
|
|
2072
|
-
if (!node || !
|
|
2799
|
+
if (!node || !Node9.isTypeReference(node)) return null;
|
|
2073
2800
|
const typeName = node.getTypeName();
|
|
2074
|
-
const name =
|
|
2801
|
+
const name = Node9.isIdentifier(typeName) ? typeName.getText() : "";
|
|
2075
2802
|
if (STREAM_CONTAINERS.has(name) || STREAM_CONTAINERS_GENERATOR.has(name)) {
|
|
2076
2803
|
return node.getTypeArguments()[0] ?? null;
|
|
2077
2804
|
}
|
|
@@ -2081,9 +2808,9 @@ function hasAsQueryDecorator(method) {
|
|
|
2081
2808
|
return method.getDecorators().some((d) => d.getName() === "AsQuery");
|
|
2082
2809
|
}
|
|
2083
2810
|
function unwrapNamedContainer(node, names) {
|
|
2084
|
-
if (!node || !
|
|
2811
|
+
if (!node || !Node9.isTypeReference(node)) return node;
|
|
2085
2812
|
const typeName = node.getTypeName();
|
|
2086
|
-
const name =
|
|
2813
|
+
const name = Node9.isIdentifier(typeName) ? typeName.getText() : "";
|
|
2087
2814
|
if (names.has(name)) {
|
|
2088
2815
|
return node.getTypeArguments()[0] ?? node;
|
|
2089
2816
|
}
|
|
@@ -2104,19 +2831,20 @@ function extractDtoContract(method, sourceFile, project, mixin, controllerClass)
|
|
|
2104
2831
|
body = body ?? bodyType;
|
|
2105
2832
|
}
|
|
2106
2833
|
const paramsType = extractParamsType(method, sourceFile, project);
|
|
2834
|
+
const paramTypes = extractPipedParamTypes(method, project);
|
|
2107
2835
|
const response = isStream ? resolveTypeNodeToString(streamElement, sourceFile, project, 3) : extractResponseType(method, sourceFile, project);
|
|
2108
2836
|
const errorInfo = extractErrorType(method, sourceFile, project);
|
|
2109
|
-
if (body === null && query === null && paramsType === null && response === "unknown" && errorInfo === null && filterInfo === null && !isStream && !uploads.multipart && !binaryResponse && !asQuery) {
|
|
2837
|
+
if (body === null && query === null && paramsType === null && paramTypes === null && response === "unknown" && errorInfo === null && filterInfo === null && !isStream && !uploads.multipart && !binaryResponse && !asQuery) {
|
|
2110
2838
|
return null;
|
|
2111
2839
|
}
|
|
2112
2840
|
let bodyRef = null;
|
|
2113
2841
|
let queryRef = null;
|
|
2114
2842
|
let responseRef = null;
|
|
2115
2843
|
for (const param of method.getParameters()) {
|
|
2116
|
-
if (param.getDecorators().some((d) => d.getName() === "Body") && param.getTypeNode()) {
|
|
2844
|
+
if (param.getDecorators().some((d) => d.getName() === "Body") && param.getTypeNode() && !hasPipeSchema(param, "Body", project)) {
|
|
2117
2845
|
bodyRef = resolveBodyQueryResponseRef(param.getTypeNode(), sourceFile, project);
|
|
2118
2846
|
}
|
|
2119
|
-
if (param.getDecorators().some((d) => d.getName() === "Query") && param.getTypeNode()) {
|
|
2847
|
+
if (param.getDecorators().some((d) => d.getName() === "Query") && param.getTypeNode() && !hasPipeSchema(param, "Query", project)) {
|
|
2120
2848
|
queryRef = resolveBodyQueryResponseRef(param.getTypeNode(), sourceFile, project);
|
|
2121
2849
|
}
|
|
2122
2850
|
}
|
|
@@ -2129,11 +2857,11 @@ function extractDtoContract(method, sourceFile, project, mixin, controllerClass)
|
|
|
2129
2857
|
if (apiResp) {
|
|
2130
2858
|
const args = apiResp.getArguments();
|
|
2131
2859
|
const optsArg = args[0];
|
|
2132
|
-
if (optsArg &&
|
|
2860
|
+
if (optsArg && Node9.isObjectLiteralExpression(optsArg)) {
|
|
2133
2861
|
for (const prop of optsArg.getProperties()) {
|
|
2134
|
-
if (
|
|
2862
|
+
if (Node9.isPropertyAssignment(prop) && prop.getName() === "type") {
|
|
2135
2863
|
const val = prop.getInitializer();
|
|
2136
|
-
if (val &&
|
|
2864
|
+
if (val && Node9.isIdentifier(val)) {
|
|
2137
2865
|
const name = val.getText();
|
|
2138
2866
|
const localDecl = sourceFile.getInterface(name) || sourceFile.getClass(name) || sourceFile.getTypeAlias(name);
|
|
2139
2867
|
if (localDecl?.isExported()) {
|
|
@@ -2169,6 +2897,7 @@ function extractDtoContract(method, sourceFile, project, mixin, controllerClass)
|
|
|
2169
2897
|
response,
|
|
2170
2898
|
error: errorInfo?.type ?? null,
|
|
2171
2899
|
params: paramsType,
|
|
2900
|
+
paramTypes,
|
|
2172
2901
|
queryRef,
|
|
2173
2902
|
bodyRef,
|
|
2174
2903
|
responseRef,
|
|
@@ -2189,6 +2918,7 @@ function extractDtoContract(method, sourceFile, project, mixin, controllerClass)
|
|
|
2189
2918
|
function resolveParamClass(method, decoratorName, sourceFile, project) {
|
|
2190
2919
|
for (const param of method.getParameters()) {
|
|
2191
2920
|
if (!param.getDecorators().some((d) => d.getName() === decoratorName)) continue;
|
|
2921
|
+
if (hasPipeSchema(param, decoratorName, project)) continue;
|
|
2192
2922
|
const typeNode = param.getTypeNode();
|
|
2193
2923
|
if (!typeNode) continue;
|
|
2194
2924
|
const text = typeNode.getText().replace(/\[\]$/, "");
|
|
@@ -2201,129 +2931,6 @@ function resolveParamClass(method, decoratorName, sourceFile, project) {
|
|
|
2201
2931
|
return null;
|
|
2202
2932
|
}
|
|
2203
2933
|
|
|
2204
|
-
// src/discovery/zod-ast-to-ts.ts
|
|
2205
|
-
import { Node as Node7, SyntaxKind as SyntaxKind3 } from "ts-morph";
|
|
2206
|
-
function zodAstToTs(node) {
|
|
2207
|
-
if (!Node7.isCallExpression(node)) return "unknown";
|
|
2208
|
-
const expr = node.getExpression();
|
|
2209
|
-
if (Node7.isPropertyAccessExpression(expr)) {
|
|
2210
|
-
const methodName = expr.getName();
|
|
2211
|
-
const receiver = expr.getExpression();
|
|
2212
|
-
if (methodName === "optional") {
|
|
2213
|
-
return `${zodAstToTs(receiver)} | undefined`;
|
|
2214
|
-
}
|
|
2215
|
-
if (methodName === "nullable") {
|
|
2216
|
-
return `${zodAstToTs(receiver)} | null`;
|
|
2217
|
-
}
|
|
2218
|
-
const args = node.getArguments();
|
|
2219
|
-
switch (methodName) {
|
|
2220
|
-
case "string":
|
|
2221
|
-
return "string";
|
|
2222
|
-
case "number":
|
|
2223
|
-
return "number";
|
|
2224
|
-
case "boolean":
|
|
2225
|
-
return "boolean";
|
|
2226
|
-
case "unknown":
|
|
2227
|
-
return "unknown";
|
|
2228
|
-
case "any":
|
|
2229
|
-
return "unknown";
|
|
2230
|
-
case "literal": {
|
|
2231
|
-
const lit = args[0];
|
|
2232
|
-
if (!lit) return "unknown";
|
|
2233
|
-
if (Node7.isStringLiteral(lit)) return JSON.stringify(lit.getLiteralValue());
|
|
2234
|
-
if (Node7.isNumericLiteral(lit)) return lit.getLiteralValue().toString();
|
|
2235
|
-
if (lit.getKind() === SyntaxKind3.TrueKeyword) return "true";
|
|
2236
|
-
if (lit.getKind() === SyntaxKind3.FalseKeyword) return "false";
|
|
2237
|
-
return "unknown";
|
|
2238
|
-
}
|
|
2239
|
-
case "enum": {
|
|
2240
|
-
const arrArg = args[0];
|
|
2241
|
-
if (!arrArg || !Node7.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
2242
|
-
const members = arrArg.getElements().map(
|
|
2243
|
-
(el) => Node7.isStringLiteral(el) ? JSON.stringify(el.getLiteralValue()) : "unknown"
|
|
2244
|
-
);
|
|
2245
|
-
return members.join(" | ");
|
|
2246
|
-
}
|
|
2247
|
-
case "array": {
|
|
2248
|
-
const inner = args[0];
|
|
2249
|
-
if (!inner) return "unknown";
|
|
2250
|
-
return `Array<${zodAstToTs(inner)}>`;
|
|
2251
|
-
}
|
|
2252
|
-
case "object": {
|
|
2253
|
-
const objArg = args[0];
|
|
2254
|
-
if (!objArg || !Node7.isObjectLiteralExpression(objArg)) return "unknown";
|
|
2255
|
-
const lines = [];
|
|
2256
|
-
for (const prop of objArg.getProperties()) {
|
|
2257
|
-
if (!Node7.isPropertyAssignment(prop)) continue;
|
|
2258
|
-
const key = prop.getName();
|
|
2259
|
-
const valNode = prop.getInitializer();
|
|
2260
|
-
if (!valNode) continue;
|
|
2261
|
-
const tsType = zodAstToTs(valNode);
|
|
2262
|
-
const isOpt = isOptionalChain(valNode);
|
|
2263
|
-
lines.push(`${key}${isOpt ? "?" : ""}: ${tsType}`);
|
|
2264
|
-
}
|
|
2265
|
-
return `{ ${lines.join("; ")} }`;
|
|
2266
|
-
}
|
|
2267
|
-
case "union": {
|
|
2268
|
-
const arrArg = args[0];
|
|
2269
|
-
if (!arrArg || !Node7.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
2270
|
-
return arrArg.getElements().map(zodAstToTs).join(" | ");
|
|
2271
|
-
}
|
|
2272
|
-
case "record": {
|
|
2273
|
-
const valArg = args.length === 1 ? args[0] : args[1];
|
|
2274
|
-
if (!valArg) return "unknown";
|
|
2275
|
-
return `Record<string, ${zodAstToTs(valArg)}>`;
|
|
2276
|
-
}
|
|
2277
|
-
case "tuple": {
|
|
2278
|
-
const arrArg = args[0];
|
|
2279
|
-
if (!arrArg || !Node7.isArrayLiteralExpression(arrArg)) return "unknown";
|
|
2280
|
-
return `[${arrArg.getElements().map(zodAstToTs).join(", ")}]`;
|
|
2281
|
-
}
|
|
2282
|
-
default:
|
|
2283
|
-
return "unknown";
|
|
2284
|
-
}
|
|
2285
|
-
}
|
|
2286
|
-
return "unknown";
|
|
2287
|
-
}
|
|
2288
|
-
function isOptionalChain(node) {
|
|
2289
|
-
if (!Node7.isCallExpression(node)) return false;
|
|
2290
|
-
const expr = node.getExpression();
|
|
2291
|
-
return Node7.isPropertyAccessExpression(expr) && expr.getName() === "optional";
|
|
2292
|
-
}
|
|
2293
|
-
function parseDefineContractCall(callExpr) {
|
|
2294
|
-
if (!Node7.isCallExpression(callExpr)) return null;
|
|
2295
|
-
const callee = callExpr.getExpression();
|
|
2296
|
-
const calleeName = Node7.isIdentifier(callee) ? callee.getText() : Node7.isPropertyAccessExpression(callee) ? callee.getName() : "";
|
|
2297
|
-
if (calleeName !== "defineContract") return null;
|
|
2298
|
-
const args = callExpr.getArguments();
|
|
2299
|
-
const optsArg = args[0];
|
|
2300
|
-
if (!optsArg || !Node7.isObjectLiteralExpression(optsArg)) return null;
|
|
2301
|
-
let query = null;
|
|
2302
|
-
let body = null;
|
|
2303
|
-
let response = "unknown";
|
|
2304
|
-
let error = null;
|
|
2305
|
-
let bodyZodText = null;
|
|
2306
|
-
let queryZodText = null;
|
|
2307
|
-
for (const prop of optsArg.getProperties()) {
|
|
2308
|
-
if (!Node7.isPropertyAssignment(prop)) continue;
|
|
2309
|
-
const propName = prop.getName();
|
|
2310
|
-
const val = prop.getInitializer();
|
|
2311
|
-
if (!val) continue;
|
|
2312
|
-
if (propName === "query") {
|
|
2313
|
-
query = zodAstToTs(val);
|
|
2314
|
-
queryZodText = val.getText();
|
|
2315
|
-
} else if (propName === "body") {
|
|
2316
|
-
body = zodAstToTs(val);
|
|
2317
|
-
bodyZodText = val.getText();
|
|
2318
|
-
} else if (propName === "response") {
|
|
2319
|
-
response = zodAstToTs(val);
|
|
2320
|
-
} else if (propName === "error") {
|
|
2321
|
-
error = zodAstToTs(val);
|
|
2322
|
-
}
|
|
2323
|
-
}
|
|
2324
|
-
return { query, body, response, error, bodyZodText, queryZodText };
|
|
2325
|
-
}
|
|
2326
|
-
|
|
2327
2934
|
// src/discovery/contracts-fast.ts
|
|
2328
2935
|
function resolveTsconfigPath(cwd, tsconfig) {
|
|
2329
2936
|
return tsconfig ? resolve3(tsconfig) : join2(cwd, "tsconfig.json");
|
|
@@ -2335,25 +2942,25 @@ var NO_TSCONFIG_OPTIONS = {
|
|
|
2335
2942
|
};
|
|
2336
2943
|
function loadDiscoveryTsconfig(tsconfigPath) {
|
|
2337
2944
|
if (!existsSync(tsconfigPath)) return { options: null, files: [] };
|
|
2338
|
-
const read =
|
|
2945
|
+
const read = ts3.readConfigFile(tsconfigPath, (path) => ts3.sys.readFile(path));
|
|
2339
2946
|
if (read.error) {
|
|
2340
2947
|
return {
|
|
2341
2948
|
options: null,
|
|
2342
2949
|
files: [tsconfigPath],
|
|
2343
|
-
error:
|
|
2950
|
+
error: ts3.flattenDiagnosticMessageText(read.error.messageText, " ")
|
|
2344
2951
|
};
|
|
2345
2952
|
}
|
|
2346
2953
|
const host = {
|
|
2347
|
-
useCaseSensitiveFileNames:
|
|
2954
|
+
useCaseSensitiveFileNames: ts3.sys.useCaseSensitiveFileNames,
|
|
2348
2955
|
// The one line that keeps a tsconfig from being a filesystem walk.
|
|
2349
2956
|
readDirectory: () => [],
|
|
2350
|
-
fileExists: (path) =>
|
|
2957
|
+
fileExists: (path) => ts3.sys.fileExists(path),
|
|
2351
2958
|
// `extends` is resolved through the host, so a consumer whose `paths` live
|
|
2352
2959
|
// in a base tsconfig keeps working.
|
|
2353
|
-
readFile: (path) =>
|
|
2960
|
+
readFile: (path) => ts3.sys.readFile(path)
|
|
2354
2961
|
};
|
|
2355
|
-
const sourceFile =
|
|
2356
|
-
const parsed =
|
|
2962
|
+
const sourceFile = ts3.readJsonConfigFile(tsconfigPath, (path) => ts3.sys.readFile(path));
|
|
2963
|
+
const parsed = ts3.parseJsonSourceFileConfigFileContent(
|
|
2357
2964
|
sourceFile,
|
|
2358
2965
|
host,
|
|
2359
2966
|
dirname2(tsconfigPath),
|
|
@@ -2376,7 +2983,7 @@ function createDiscoveryProject(tsconfigPath, tsconfig = loadDiscoveryTsconfig(t
|
|
|
2376
2983
|
`[nestjs-codegen/fast] Could not load ${tsconfigPath}: ${tsconfig.error} \u2014 continuing WITHOUT its compiler options, so path alias imports (e.g. '@/...') will not resolve and controllers extending an alias-imported factory will contribute NO routes to the generated client.`
|
|
2377
2984
|
);
|
|
2378
2985
|
}
|
|
2379
|
-
return new
|
|
2986
|
+
return new Project3({
|
|
2380
2987
|
compilerOptions: tsconfig.options ?? { ...NO_TSCONFIG_OPTIONS },
|
|
2381
2988
|
skipAddingFilesFromTsConfig: true,
|
|
2382
2989
|
skipLoadingLibFiles: true,
|
|
@@ -2481,15 +3088,16 @@ var PersistentDiscovery = class _PersistentDiscovery {
|
|
|
2481
3088
|
clearTypeResolutionCaches(this.project);
|
|
2482
3089
|
clearEnumCache(this.project);
|
|
2483
3090
|
clearMixinTypeProject();
|
|
3091
|
+
clearSchemaTypeProject(this.project);
|
|
2484
3092
|
return extractRoutesFrom(this.project, this.controllerPaths);
|
|
2485
3093
|
}
|
|
2486
3094
|
};
|
|
2487
3095
|
function decoratorStringArg(decoratorExpr) {
|
|
2488
3096
|
if (!decoratorExpr) return void 0;
|
|
2489
|
-
if (
|
|
2490
|
-
if (
|
|
3097
|
+
if (Node10.isStringLiteral(decoratorExpr)) return decoratorExpr.getLiteralValue();
|
|
3098
|
+
if (Node10.isArrayLiteralExpression(decoratorExpr)) {
|
|
2491
3099
|
const first = decoratorExpr.getElements()[0];
|
|
2492
|
-
if (first &&
|
|
3100
|
+
if (first && Node10.isStringLiteral(first)) return first.getLiteralValue();
|
|
2493
3101
|
}
|
|
2494
3102
|
return void 0;
|
|
2495
3103
|
}
|
|
@@ -2610,9 +3218,9 @@ function extractContractRoute(args) {
|
|
|
2610
3218
|
let contractDef = null;
|
|
2611
3219
|
let bodyZodRef = null;
|
|
2612
3220
|
let queryZodRef = null;
|
|
2613
|
-
if (
|
|
2614
|
-
contractDef = parseDefineContractCall(firstDecoratorArg);
|
|
2615
|
-
} else if (
|
|
3221
|
+
if (Node10.isCallExpression(firstDecoratorArg)) {
|
|
3222
|
+
contractDef = parseDefineContractCall(firstDecoratorArg, project);
|
|
3223
|
+
} else if (Node10.isIdentifier(firstDecoratorArg)) {
|
|
2616
3224
|
const identName = firstDecoratorArg.getText();
|
|
2617
3225
|
const resolvedVar = resolveImportedVariable(identName, sourceFile, project);
|
|
2618
3226
|
if (!resolvedVar) {
|
|
@@ -2624,7 +3232,7 @@ function extractContractRoute(args) {
|
|
|
2624
3232
|
const { decl: varDecl, file: declFile } = resolvedVar;
|
|
2625
3233
|
const initializer = varDecl.getInitializer();
|
|
2626
3234
|
if (!initializer) return null;
|
|
2627
|
-
contractDef = parseDefineContractCall(initializer);
|
|
3235
|
+
contractDef = parseDefineContractCall(initializer, project);
|
|
2628
3236
|
if (contractDef && varDecl.isExported()) {
|
|
2629
3237
|
const filePath = declFile.getFilePath();
|
|
2630
3238
|
if (contractDef.body !== null) {
|
|
@@ -2695,6 +3303,7 @@ function extractDtoRoute(args) {
|
|
|
2695
3303
|
body: dtoContract?.body ?? null,
|
|
2696
3304
|
response: mixinResponse ?? dtoContract?.response ?? "unknown",
|
|
2697
3305
|
error: dtoContract?.error ?? null,
|
|
3306
|
+
paramTypes: dtoContract?.paramTypes ?? null,
|
|
2698
3307
|
queryRef: dtoContract?.queryRef ?? null,
|
|
2699
3308
|
bodyRef: dtoContract?.bodyRef ?? null,
|
|
2700
3309
|
responseRef: dtoContract?.responseRef ?? null,
|
|
@@ -2766,32 +3375,86 @@ function extractFromSourceFile(sourceFile, project) {
|
|
|
2766
3375
|
return routes;
|
|
2767
3376
|
}
|
|
2768
3377
|
|
|
2769
|
-
// src/generate.ts
|
|
2770
|
-
import { mkdir as mkdir9, writeFile as writeFile9 } from "fs/promises";
|
|
2771
|
-
import { dirname as dirname4, join as join13 } from "path";
|
|
2772
|
-
|
|
2773
3378
|
// src/discovery/pages.ts
|
|
2774
3379
|
import { readFile } from "fs/promises";
|
|
2775
|
-
import { join as join3, relative as relative2 } from "path";
|
|
3380
|
+
import { join as join3, relative as relative2, sep as sep2 } from "path";
|
|
2776
3381
|
import fg2 from "fast-glob";
|
|
2777
3382
|
var NON_PAGE_FILE_RE = /\.(?:test|spec|stories|story)\.(?:tsx?|jsx?|vue|svelte)$/i;
|
|
3383
|
+
var MAGIC_SEGMENT_RE = /[*?{}[\]()!]/;
|
|
3384
|
+
function globStaticBase(glob) {
|
|
3385
|
+
const segments = glob.split("/");
|
|
3386
|
+
const staticSegments = [];
|
|
3387
|
+
for (const segment of segments.slice(0, -1)) {
|
|
3388
|
+
if (MAGIC_SEGMENT_RE.test(segment)) break;
|
|
3389
|
+
staticSegments.push(segment);
|
|
3390
|
+
}
|
|
3391
|
+
return staticSegments.join("/");
|
|
3392
|
+
}
|
|
2778
3393
|
async function discoverPages(opts) {
|
|
2779
|
-
const
|
|
2780
|
-
const
|
|
2781
|
-
|
|
2782
|
-
const
|
|
2783
|
-
const pagesBase = join3(opts.cwd, globStatic);
|
|
3394
|
+
const globs = typeof opts.glob === "string" ? [opts.glob] : opts.glob;
|
|
3395
|
+
const prefix = opts.prefix ?? "";
|
|
3396
|
+
const scope = opts.scope ?? "default";
|
|
3397
|
+
const seenFiles = /* @__PURE__ */ new Set();
|
|
2784
3398
|
const out = [];
|
|
2785
|
-
for (const
|
|
2786
|
-
const
|
|
2787
|
-
const
|
|
2788
|
-
const
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
3399
|
+
for (const glob of globs) {
|
|
3400
|
+
const files = (await fg2(glob, { cwd: opts.cwd, absolute: true })).filter((f) => !NON_PAGE_FILE_RE.test(f) && !seenFiles.has(f)).sort();
|
|
3401
|
+
const pagesBase = join3(opts.cwd, globStaticBase(glob));
|
|
3402
|
+
for (const file of files) {
|
|
3403
|
+
seenFiles.add(file);
|
|
3404
|
+
const rel = relative2(opts.cwd, file);
|
|
3405
|
+
const nameRel = relative2(pagesBase, file).split(sep2).join("/");
|
|
3406
|
+
const name = `${prefix}${computeName(nameRel, opts.componentNameStrategy)}`;
|
|
3407
|
+
const source = await readFile(file, "utf8");
|
|
3408
|
+
const propsSource = extractPropsSource(source, opts.propsExport);
|
|
3409
|
+
out.push({ name, absolutePath: file, relativePath: rel, propsSource, scope });
|
|
3410
|
+
}
|
|
3411
|
+
}
|
|
3412
|
+
assertUniqueNames(out);
|
|
3413
|
+
return out;
|
|
3414
|
+
}
|
|
3415
|
+
async function discoverPageScopes(config) {
|
|
3416
|
+
const pages = config.pages;
|
|
3417
|
+
if (!pages) return [];
|
|
3418
|
+
const common = { cwd: config.codegen.cwd, propsExport: pages.propsExport };
|
|
3419
|
+
const out = await discoverPages({
|
|
3420
|
+
...common,
|
|
3421
|
+
glob: pages.glob,
|
|
3422
|
+
componentNameStrategy: pages.componentNameStrategy
|
|
3423
|
+
});
|
|
3424
|
+
for (const [scope, scopeConfig] of Object.entries(pages.scopes ?? {})) {
|
|
3425
|
+
out.push(
|
|
3426
|
+
...await discoverPages({
|
|
3427
|
+
...common,
|
|
3428
|
+
glob: scopeConfig.glob,
|
|
3429
|
+
componentNameStrategy: scopeConfig.componentNameStrategy,
|
|
3430
|
+
prefix: scopeConfig.prefix,
|
|
3431
|
+
scope
|
|
3432
|
+
})
|
|
3433
|
+
);
|
|
2792
3434
|
}
|
|
3435
|
+
assertUniqueNames(out);
|
|
2793
3436
|
return out;
|
|
2794
3437
|
}
|
|
3438
|
+
function allPageGlobs(config) {
|
|
3439
|
+
if (!config.pages) return [];
|
|
3440
|
+
const asList = (glob) => typeof glob === "string" ? [glob] : glob;
|
|
3441
|
+
return [
|
|
3442
|
+
...asList(config.pages.glob),
|
|
3443
|
+
...Object.values(config.pages.scopes ?? {}).flatMap((scope) => asList(scope.glob))
|
|
3444
|
+
];
|
|
3445
|
+
}
|
|
3446
|
+
function assertUniqueNames(pages) {
|
|
3447
|
+
const byName = /* @__PURE__ */ new Map();
|
|
3448
|
+
for (const page of pages) {
|
|
3449
|
+
const previous = byName.get(page.name);
|
|
3450
|
+
if (previous) {
|
|
3451
|
+
throw new Error(
|
|
3452
|
+
`Two Inertia pages resolve to the component name "${page.name}": ${previous.relativePath} (scope "${previous.scope}") and ${page.relativePath} (scope "${page.scope}"). Give one of them another name, directory or scope prefix.`
|
|
3453
|
+
);
|
|
3454
|
+
}
|
|
3455
|
+
byName.set(page.name, page);
|
|
3456
|
+
}
|
|
3457
|
+
}
|
|
2795
3458
|
function computeName(rel, strat) {
|
|
2796
3459
|
if (typeof strat === "function") return strat(rel);
|
|
2797
3460
|
const noExt = rel.replace(/\.(tsx?|vue|svelte)$/, "");
|
|
@@ -2822,9 +3485,13 @@ function extractPropsSource(source, exportName) {
|
|
|
2822
3485
|
return source.slice(start);
|
|
2823
3486
|
}
|
|
2824
3487
|
|
|
3488
|
+
// src/generate.ts
|
|
3489
|
+
import { mkdir as mkdir9, writeFile as writeFile9 } from "fs/promises";
|
|
3490
|
+
import { dirname as dirname4, join as join13 } from "path";
|
|
3491
|
+
|
|
2825
3492
|
// src/discovery/shared-props.ts
|
|
2826
3493
|
import { join as join4 } from "path";
|
|
2827
|
-
import { Node as
|
|
3494
|
+
import { Node as Node11, Project as Project4, SyntaxKind as SyntaxKind4 } from "ts-morph";
|
|
2828
3495
|
function discoverSharedProps(project, moduleEntry) {
|
|
2829
3496
|
try {
|
|
2830
3497
|
let sourceFile = project.getSourceFile(moduleEntry);
|
|
@@ -2850,14 +3517,14 @@ function discoverSharedPropsFromConfig(config) {
|
|
|
2850
3517
|
const tsconfigPath = config.app.tsconfig ?? join4(config.codegen.cwd, "tsconfig.json");
|
|
2851
3518
|
let project;
|
|
2852
3519
|
try {
|
|
2853
|
-
project = new
|
|
3520
|
+
project = new Project4({
|
|
2854
3521
|
tsConfigFilePath: tsconfigPath,
|
|
2855
3522
|
skipAddingFilesFromTsConfig: true,
|
|
2856
3523
|
skipLoadingLibFiles: true,
|
|
2857
3524
|
skipFileDependencyResolution: true
|
|
2858
3525
|
});
|
|
2859
3526
|
} catch {
|
|
2860
|
-
project = new
|
|
3527
|
+
project = new Project4({
|
|
2861
3528
|
skipAddingFilesFromTsConfig: true,
|
|
2862
3529
|
skipLoadingLibFiles: true,
|
|
2863
3530
|
skipFileDependencyResolution: true,
|
|
@@ -2873,10 +3540,10 @@ function findForRootCall(sourceFile) {
|
|
|
2873
3540
|
const callExpressions = sourceFile.getDescendantsOfKind(SyntaxKind4.CallExpression);
|
|
2874
3541
|
for (const call of callExpressions) {
|
|
2875
3542
|
const expr = call.getExpression();
|
|
2876
|
-
if (!
|
|
3543
|
+
if (!Node11.isPropertyAccessExpression(expr)) continue;
|
|
2877
3544
|
const methodName = expr.getName();
|
|
2878
3545
|
const objectExpr = expr.getExpression();
|
|
2879
|
-
if (methodName === "forRoot" &&
|
|
3546
|
+
if (methodName === "forRoot" && Node11.isIdentifier(objectExpr)) {
|
|
2880
3547
|
const name = objectExpr.getText();
|
|
2881
3548
|
if (name === "InertiaModule") {
|
|
2882
3549
|
return call;
|
|
@@ -2886,19 +3553,19 @@ function findForRootCall(sourceFile) {
|
|
|
2886
3553
|
return null;
|
|
2887
3554
|
}
|
|
2888
3555
|
function findShareInitializer(forRootCall) {
|
|
2889
|
-
if (!
|
|
3556
|
+
if (!Node11.isCallExpression(forRootCall)) return null;
|
|
2890
3557
|
const args = forRootCall.getArguments();
|
|
2891
3558
|
const firstArg2 = args[0];
|
|
2892
|
-
if (!firstArg2 || !
|
|
3559
|
+
if (!firstArg2 || !Node11.isObjectLiteralExpression(firstArg2)) return null;
|
|
2893
3560
|
for (const prop of firstArg2.getProperties()) {
|
|
2894
|
-
if (
|
|
3561
|
+
if (Node11.isPropertyAssignment(prop) && prop.getName() === "share") {
|
|
2895
3562
|
return prop.getInitializer() ?? null;
|
|
2896
3563
|
}
|
|
2897
3564
|
}
|
|
2898
3565
|
return null;
|
|
2899
3566
|
}
|
|
2900
3567
|
function extractShareType(node, sourceFile, project) {
|
|
2901
|
-
if (
|
|
3568
|
+
if (Node11.isIdentifier(node)) {
|
|
2902
3569
|
const ref = resolveIdentifierToImportRef(node, sourceFile, project);
|
|
2903
3570
|
if (ref) {
|
|
2904
3571
|
return {
|
|
@@ -2908,22 +3575,22 @@ function extractShareType(node, sourceFile, project) {
|
|
|
2908
3575
|
};
|
|
2909
3576
|
}
|
|
2910
3577
|
}
|
|
2911
|
-
if (
|
|
3578
|
+
if (Node11.isArrowFunction(node)) {
|
|
2912
3579
|
const result = extractFromFunctionLike(node, sourceFile);
|
|
2913
3580
|
return result ? { ...result, isImportRef: false } : null;
|
|
2914
3581
|
}
|
|
2915
|
-
if (
|
|
3582
|
+
if (Node11.isFunctionExpression(node)) {
|
|
2916
3583
|
const result = extractFromFunctionLike(node, sourceFile);
|
|
2917
3584
|
return result ? { ...result, isImportRef: false } : null;
|
|
2918
3585
|
}
|
|
2919
|
-
if (
|
|
3586
|
+
if (Node11.isObjectLiteralExpression(node)) {
|
|
2920
3587
|
const result = extractFromObjectLiteral(node);
|
|
2921
3588
|
return result ? { ...result, isImportRef: false } : null;
|
|
2922
3589
|
}
|
|
2923
3590
|
return null;
|
|
2924
3591
|
}
|
|
2925
3592
|
function resolveIdentifierToImportRef(id, sourceFile, project) {
|
|
2926
|
-
if (!
|
|
3593
|
+
if (!Node11.isIdentifier(id)) return null;
|
|
2927
3594
|
const name = id.getText();
|
|
2928
3595
|
const localFunc = sourceFile.getFunction(name);
|
|
2929
3596
|
if (localFunc?.isExported()) {
|
|
@@ -2957,37 +3624,37 @@ function resolveIdentifierToImportRef(id, sourceFile, project) {
|
|
|
2957
3624
|
return null;
|
|
2958
3625
|
}
|
|
2959
3626
|
function extractFromFunctionLike(node, _sourceFile) {
|
|
2960
|
-
const returnTypeNode =
|
|
3627
|
+
const returnTypeNode = Node11.isArrowFunction(node) || Node11.isFunctionExpression(node) ? node.getReturnTypeNode() : null;
|
|
2961
3628
|
if (returnTypeNode) {
|
|
2962
3629
|
return extractFromReturnTypeAnnotation(returnTypeNode);
|
|
2963
3630
|
}
|
|
2964
|
-
if (
|
|
3631
|
+
if (Node11.isArrowFunction(node)) {
|
|
2965
3632
|
const body = node.getBody();
|
|
2966
|
-
if (
|
|
3633
|
+
if (Node11.isParenthesizedExpression(body)) {
|
|
2967
3634
|
const inner = body.getExpression();
|
|
2968
|
-
if (
|
|
3635
|
+
if (Node11.isObjectLiteralExpression(inner)) {
|
|
2969
3636
|
return extractFromObjectLiteral(inner);
|
|
2970
3637
|
}
|
|
2971
3638
|
}
|
|
2972
|
-
if (
|
|
3639
|
+
if (Node11.isObjectLiteralExpression(body)) {
|
|
2973
3640
|
return extractFromObjectLiteral(body);
|
|
2974
3641
|
}
|
|
2975
|
-
if (
|
|
3642
|
+
if (Node11.isBlock(body)) {
|
|
2976
3643
|
return extractFromBlockReturn(body);
|
|
2977
3644
|
}
|
|
2978
3645
|
}
|
|
2979
|
-
if (
|
|
3646
|
+
if (Node11.isFunctionExpression(node)) {
|
|
2980
3647
|
const body = node.getBody();
|
|
2981
|
-
if (
|
|
3648
|
+
if (Node11.isBlock(body)) {
|
|
2982
3649
|
return extractFromBlockReturn(body);
|
|
2983
3650
|
}
|
|
2984
3651
|
}
|
|
2985
3652
|
return null;
|
|
2986
3653
|
}
|
|
2987
3654
|
function extractFromReturnTypeAnnotation(typeNode) {
|
|
2988
|
-
if (
|
|
3655
|
+
if (Node11.isTypeReference(typeNode)) {
|
|
2989
3656
|
const typeName = typeNode.getTypeName();
|
|
2990
|
-
if (
|
|
3657
|
+
if (Node11.isIdentifier(typeName) && typeName.getText() === "Promise") {
|
|
2991
3658
|
const typeArgs = typeNode.getTypeArguments();
|
|
2992
3659
|
const firstArg2 = typeArgs[0];
|
|
2993
3660
|
if (firstArg2) {
|
|
@@ -2996,10 +3663,10 @@ function extractFromReturnTypeAnnotation(typeNode) {
|
|
|
2996
3663
|
return null;
|
|
2997
3664
|
}
|
|
2998
3665
|
}
|
|
2999
|
-
if (
|
|
3666
|
+
if (Node11.isTypeLiteral(typeNode)) {
|
|
3000
3667
|
const properties = [];
|
|
3001
3668
|
for (const member of typeNode.getMembers()) {
|
|
3002
|
-
if (
|
|
3669
|
+
if (Node11.isPropertySignature(member)) {
|
|
3003
3670
|
const name = member.getName();
|
|
3004
3671
|
const memberTypeNode = member.getTypeNode();
|
|
3005
3672
|
const type = memberTypeNode ? memberTypeNode.getText() : "unknown";
|
|
@@ -3013,19 +3680,19 @@ function extractFromReturnTypeAnnotation(typeNode) {
|
|
|
3013
3680
|
return null;
|
|
3014
3681
|
}
|
|
3015
3682
|
function extractFromBlockReturn(block) {
|
|
3016
|
-
if (!
|
|
3683
|
+
if (!Node11.isBlock(block)) return null;
|
|
3017
3684
|
const statements = block.getStatements();
|
|
3018
3685
|
for (let i = statements.length - 1; i >= 0; i--) {
|
|
3019
3686
|
const stmt = statements[i];
|
|
3020
|
-
if (!
|
|
3687
|
+
if (!Node11.isReturnStatement(stmt)) continue;
|
|
3021
3688
|
const expr = stmt.getExpression();
|
|
3022
3689
|
if (!expr) continue;
|
|
3023
|
-
if (
|
|
3690
|
+
if (Node11.isObjectLiteralExpression(expr)) {
|
|
3024
3691
|
return extractFromObjectLiteral(expr);
|
|
3025
3692
|
}
|
|
3026
|
-
if (
|
|
3693
|
+
if (Node11.isParenthesizedExpression(expr)) {
|
|
3027
3694
|
const inner = expr.getExpression();
|
|
3028
|
-
if (
|
|
3695
|
+
if (Node11.isObjectLiteralExpression(inner)) {
|
|
3029
3696
|
return extractFromObjectLiteral(inner);
|
|
3030
3697
|
}
|
|
3031
3698
|
}
|
|
@@ -3034,10 +3701,10 @@ function extractFromBlockReturn(block) {
|
|
|
3034
3701
|
return null;
|
|
3035
3702
|
}
|
|
3036
3703
|
function extractFromObjectLiteral(objLiteral) {
|
|
3037
|
-
if (!
|
|
3704
|
+
if (!Node11.isObjectLiteralExpression(objLiteral)) return null;
|
|
3038
3705
|
const properties = [];
|
|
3039
3706
|
for (const prop of objLiteral.getProperties()) {
|
|
3040
|
-
if (!
|
|
3707
|
+
if (!Node11.isPropertyAssignment(prop)) continue;
|
|
3041
3708
|
const name = prop.getName();
|
|
3042
3709
|
const initializer = prop.getInitializer();
|
|
3043
3710
|
if (!initializer) continue;
|
|
@@ -3049,21 +3716,21 @@ function extractFromObjectLiteral(objLiteral) {
|
|
|
3049
3716
|
return { typeString, properties, isImportRef: false };
|
|
3050
3717
|
}
|
|
3051
3718
|
function inferExpressionType(node) {
|
|
3052
|
-
if (
|
|
3053
|
-
if (
|
|
3719
|
+
if (Node11.isStringLiteral(node)) return "string";
|
|
3720
|
+
if (Node11.isTemplateExpression(node) || Node11.isNoSubstitutionTemplateLiteral(node))
|
|
3054
3721
|
return "string";
|
|
3055
|
-
if (
|
|
3722
|
+
if (Node11.isNumericLiteral(node)) return "number";
|
|
3056
3723
|
if (node.getKind() === SyntaxKind4.TrueKeyword || node.getKind() === SyntaxKind4.FalseKeyword) {
|
|
3057
3724
|
return "boolean";
|
|
3058
3725
|
}
|
|
3059
3726
|
if (node.getKind() === SyntaxKind4.NullKeyword) return "null";
|
|
3060
|
-
if (
|
|
3061
|
-
if (
|
|
3727
|
+
if (Node11.isIdentifier(node) && node.getText() === "undefined") return "undefined";
|
|
3728
|
+
if (Node11.isObjectLiteralExpression(node)) {
|
|
3062
3729
|
const props = node.getProperties();
|
|
3063
3730
|
if (props.length === 0) return "Record<string, unknown>";
|
|
3064
3731
|
const entries = [];
|
|
3065
3732
|
for (const prop of props) {
|
|
3066
|
-
if (!
|
|
3733
|
+
if (!Node11.isPropertyAssignment(prop)) continue;
|
|
3067
3734
|
const key = prop.getName();
|
|
3068
3735
|
const init = prop.getInitializer();
|
|
3069
3736
|
if (!init) continue;
|
|
@@ -3072,23 +3739,23 @@ function inferExpressionType(node) {
|
|
|
3072
3739
|
if (entries.length === 0) return "Record<string, unknown>";
|
|
3073
3740
|
return `{ ${entries.join("; ")} }`;
|
|
3074
3741
|
}
|
|
3075
|
-
if (
|
|
3742
|
+
if (Node11.isArrayLiteralExpression(node)) {
|
|
3076
3743
|
const elements = node.getElements();
|
|
3077
3744
|
if (elements.length === 0) return "Array<unknown>";
|
|
3078
3745
|
const first = elements[0];
|
|
3079
3746
|
if (first) return `Array<${inferExpressionType(first)}>`;
|
|
3080
3747
|
return "Array<unknown>";
|
|
3081
3748
|
}
|
|
3082
|
-
if (
|
|
3749
|
+
if (Node11.isConditionalExpression(node)) {
|
|
3083
3750
|
const whenTrue = inferExpressionType(node.getWhenTrue());
|
|
3084
3751
|
const whenFalse = inferExpressionType(node.getWhenFalse());
|
|
3085
3752
|
if (whenTrue === whenFalse) return whenTrue;
|
|
3086
3753
|
return `${whenTrue} | ${whenFalse}`;
|
|
3087
3754
|
}
|
|
3088
|
-
if (
|
|
3755
|
+
if (Node11.isParenthesizedExpression(node)) {
|
|
3089
3756
|
return inferExpressionType(node.getExpression());
|
|
3090
3757
|
}
|
|
3091
|
-
if (
|
|
3758
|
+
if (Node11.isAsExpression(node)) {
|
|
3092
3759
|
const typeNode = node.getTypeNode();
|
|
3093
3760
|
if (typeNode) return typeNode.getText();
|
|
3094
3761
|
}
|
|
@@ -3101,7 +3768,7 @@ import { isAbsolute as isAbsolute2, join as join5, relative as relative4 } from
|
|
|
3101
3768
|
|
|
3102
3769
|
// src/extension/registry.ts
|
|
3103
3770
|
import { relative as relative3, resolve as resolve4 } from "path";
|
|
3104
|
-
import { Project as
|
|
3771
|
+
import { Project as Project5 } from "ts-morph";
|
|
3105
3772
|
function resolveApiSlots(extensions) {
|
|
3106
3773
|
let layer;
|
|
3107
3774
|
let layerOwner;
|
|
@@ -3152,7 +3819,7 @@ function createExtensionContext(config, getRoutes, trackedInputs) {
|
|
|
3152
3819
|
},
|
|
3153
3820
|
project() {
|
|
3154
3821
|
if (!project) {
|
|
3155
|
-
project = new
|
|
3822
|
+
project = new Project5({
|
|
3156
3823
|
skipAddingFilesFromTsConfig: true,
|
|
3157
3824
|
skipLoadingLibFiles: true,
|
|
3158
3825
|
skipFileDependencyResolution: true,
|
|
@@ -3270,10 +3937,11 @@ function insertIntoTree(tree, segments, leaf, fullName) {
|
|
|
3270
3937
|
insertIntoTree(branch.children, rest, leaf, fullName);
|
|
3271
3938
|
}
|
|
3272
3939
|
}
|
|
3273
|
-
function buildParamsType(params) {
|
|
3940
|
+
function buildParamsType(params, paramTypes) {
|
|
3274
3941
|
const pathParams = params.filter((p) => p.source === "path");
|
|
3275
3942
|
if (pathParams.length === 0) return "never";
|
|
3276
|
-
|
|
3943
|
+
const typeOf = (name) => paramTypes && Object.hasOwn(paramTypes, name) && paramTypes[name] || "string";
|
|
3944
|
+
return `{ ${pathParams.map((p) => `${p.name}: ${typeOf(p.name)}`).join("; ")} }`;
|
|
3277
3945
|
}
|
|
3278
3946
|
function hasPathParams(params) {
|
|
3279
3947
|
return params.some((p) => p.source === "path");
|
|
@@ -3367,7 +4035,7 @@ function emitRouterTypeBlock(tree, indent, outDir, serialization) {
|
|
|
3367
4035
|
}
|
|
3368
4036
|
const response = buildResponseType(c, outDir, serialization);
|
|
3369
4037
|
const error = buildErrorType(c);
|
|
3370
|
-
const params = buildParamsType(c.params);
|
|
4038
|
+
const params = buildParamsType(c.params, c.contractSource.paramTypes);
|
|
3371
4039
|
const safeMethod = JSON.stringify(method);
|
|
3372
4040
|
const safeUrl = JSON.stringify(c.path);
|
|
3373
4041
|
const filterLiterals = filterFieldLiterals(c.contractSource.filterFields);
|
|
@@ -3455,28 +4123,28 @@ function renderFetcherRequest(req, binaryResponse) {
|
|
|
3455
4123
|
}
|
|
3456
4124
|
function emitReqHelper() {
|
|
3457
4125
|
return [
|
|
3458
|
-
"/**
|
|
3459
|
-
"type
|
|
3460
|
-
"
|
|
4126
|
+
"/** An awaitable request handle: a lazy, memoized `Promise` that also carries `fetch()` and the members extensions add. */",
|
|
4127
|
+
"export type RequestHandle<R, M = {}> = Promise<R> & { fetch(): Promise<R> } & M;",
|
|
4128
|
+
"class __Request<R> extends Promise<R> {",
|
|
4129
|
+
" static override get [Symbol.species](): PromiseConstructor {",
|
|
4130
|
+
" return Promise;",
|
|
4131
|
+
" }",
|
|
4132
|
+
" private __run: () => Promise<R>;",
|
|
4133
|
+
" private __p: Promise<R> | undefined;",
|
|
4134
|
+
" constructor(run: () => Promise<R>) {",
|
|
4135
|
+
" super(() => {});",
|
|
4136
|
+
" this.__run = run;",
|
|
4137
|
+
" }",
|
|
4138
|
+
" override then<T1 = R, T2 = never>(",
|
|
3461
4139
|
" onfulfilled?: ((value: R) => T1 | PromiseLike<T1>) | null,",
|
|
3462
4140
|
" onrejected?: ((reason: unknown) => T2 | PromiseLike<T2>) | null,",
|
|
3463
|
-
" ): Promise<T1 | T2
|
|
3464
|
-
"
|
|
3465
|
-
"
|
|
3466
|
-
"
|
|
3467
|
-
"}
|
|
3468
|
-
"function __req<R>(run: () => Promise<R
|
|
3469
|
-
"
|
|
3470
|
-
" const __promise = () => {",
|
|
3471
|
-
" __p ??= run();",
|
|
3472
|
-
" return __p;",
|
|
3473
|
-
" };",
|
|
3474
|
-
" return {",
|
|
3475
|
-
" then: (onfulfilled, onrejected) => __promise().then(onfulfilled, onrejected),",
|
|
3476
|
-
" catch: (onrejected) => __promise().catch(onrejected),",
|
|
3477
|
-
" finally: (onfinally) => __promise().finally(onfinally),",
|
|
3478
|
-
" fetch: run,",
|
|
3479
|
-
" };",
|
|
4141
|
+
" ): Promise<T1 | T2> {",
|
|
4142
|
+
" this.__p ??= this.__run();",
|
|
4143
|
+
" return this.__p.then(onfulfilled, onrejected);",
|
|
4144
|
+
" }",
|
|
4145
|
+
"}",
|
|
4146
|
+
"function __req<R, M extends object = {}>(run: () => Promise<R>, members?: M): RequestHandle<R, M> {",
|
|
4147
|
+
" return Object.assign(new __Request<R>(run), { fetch: run }, members) as RequestHandle<R, M>;",
|
|
3480
4148
|
"}",
|
|
3481
4149
|
""
|
|
3482
4150
|
];
|
|
@@ -3494,18 +4162,26 @@ function emitFilterFieldGuard() {
|
|
|
3494
4162
|
];
|
|
3495
4163
|
}
|
|
3496
4164
|
function renderLeaf(pad, objKey, req, requestExpr, members, streamExpr) {
|
|
3497
|
-
const
|
|
3498
|
-
lines.push(`${pad} ...__req<${req.responseType}>(() => ${requestExpr}),`);
|
|
4165
|
+
const memberLines = [];
|
|
3499
4166
|
if (req.filterFieldsExpr) {
|
|
3500
|
-
|
|
4167
|
+
memberLines.push(`${pad} filterFields: ${req.filterFieldsExpr},`);
|
|
3501
4168
|
}
|
|
3502
4169
|
if (streamExpr) {
|
|
3503
|
-
|
|
4170
|
+
memberLines.push(`${pad} stream: () => ${streamExpr},`);
|
|
3504
4171
|
}
|
|
3505
4172
|
for (const [name, value] of Object.entries(members)) {
|
|
3506
|
-
|
|
3507
|
-
}
|
|
3508
|
-
|
|
4173
|
+
memberLines.push(`${pad} ${name}: ${value},`);
|
|
4174
|
+
}
|
|
4175
|
+
const body = [requestExpr, ...memberLines].join("\n");
|
|
4176
|
+
const param = /\binput\b/.test(body) ? `input?: ${req.inputType}` : "";
|
|
4177
|
+
const lines = [`${pad}${objKey}: (${param}) =>`];
|
|
4178
|
+
if (memberLines.length === 0) {
|
|
4179
|
+
lines.push(`${pad} __req<${req.responseType}>(() => ${requestExpr}),`);
|
|
4180
|
+
return lines;
|
|
4181
|
+
}
|
|
4182
|
+
lines.push(`${pad} __req(() => ${requestExpr}, {`);
|
|
4183
|
+
lines.push(...memberLines);
|
|
4184
|
+
lines.push(`${pad} }),`);
|
|
3509
4185
|
return lines;
|
|
3510
4186
|
}
|
|
3511
4187
|
function renderStreamExpr(req) {
|
|
@@ -3636,6 +4312,26 @@ var EMPTY_PATH_NAMESPACE = [
|
|
|
3636
4312
|
"}",
|
|
3637
4313
|
""
|
|
3638
4314
|
];
|
|
4315
|
+
function isClientCallable(route) {
|
|
4316
|
+
return route.method.toUpperCase() !== "ALL";
|
|
4317
|
+
}
|
|
4318
|
+
var ROUTES_IMPORT_PLACEHOLDER = "/* @@routes-import@@ */";
|
|
4319
|
+
var ROUTES_EXPORTS = [
|
|
4320
|
+
// Called, never mentioned in prose: match the call so a comment saying "route" doesn't count.
|
|
4321
|
+
{ name: "route", typeOnly: false },
|
|
4322
|
+
{ name: "ROUTES", typeOnly: false },
|
|
4323
|
+
{ name: "RouteName", typeOnly: true },
|
|
4324
|
+
{ name: "ExtractParams", typeOnly: true },
|
|
4325
|
+
{ name: "RouteParams", typeOnly: true }
|
|
4326
|
+
];
|
|
4327
|
+
function finishRoutesImport(lines) {
|
|
4328
|
+
const body = lines.filter((l) => l !== ROUTES_IMPORT_PLACEHOLDER).join("\n");
|
|
4329
|
+
const used = ROUTES_EXPORTS.filter(
|
|
4330
|
+
({ name }) => new RegExp(name === "route" ? "\\broute\\(" : `\\b${name}\\b`).test(body)
|
|
4331
|
+
).map(({ name, typeOnly }) => typeOnly ? `type ${name}` : name);
|
|
4332
|
+
const importLine = used.length ? `import { ${used.join(", ")} } from './routes.js';` : null;
|
|
4333
|
+
return lines.flatMap((l) => l === ROUTES_IMPORT_PLACEHOLDER ? importLine ? [importLine] : [] : [l]).join("\n");
|
|
4334
|
+
}
|
|
3639
4335
|
function buildApiFile(routes, outDir, opts = {}) {
|
|
3640
4336
|
const fetcherImportPath = opts.fetcherImportPath;
|
|
3641
4337
|
const serialization = opts.serialization ?? "json";
|
|
@@ -3643,7 +4339,7 @@ function buildApiFile(routes, outDir, opts = {}) {
|
|
|
3643
4339
|
const { layer } = resolveApiSlots(extensions);
|
|
3644
4340
|
const memberExts = extensions.filter((e) => e.apiMembers);
|
|
3645
4341
|
const headerExts = extensions.filter((e) => e.apiHeader);
|
|
3646
|
-
const contracted = routes.filter((r) => r.contract);
|
|
4342
|
+
const contracted = routes.filter((r) => r.contract && isClientCallable(r));
|
|
3647
4343
|
const ctx = opts.ctx ?? {
|
|
3648
4344
|
cwd: outDir ?? "",
|
|
3649
4345
|
outDir: outDir ?? "",
|
|
@@ -3695,9 +4391,7 @@ function buildApiFile(routes, outDir, opts = {}) {
|
|
|
3695
4391
|
for (const imp of ext.apiHeader?.(ctx)?.imports ?? []) pushImport(imp);
|
|
3696
4392
|
}
|
|
3697
4393
|
lines.push(...extImports);
|
|
3698
|
-
lines.push(
|
|
3699
|
-
"import { route, ROUTES, type RouteName, type ExtractParams, type RouteParams } from './routes.js';"
|
|
3700
|
-
);
|
|
4394
|
+
lines.push(ROUTES_IMPORT_PLACEHOLDER);
|
|
3701
4395
|
const runtimeImport = fetcherImportPath ?? "@dudousxd/nestjs-client";
|
|
3702
4396
|
lines.push(`import type { Fetcher } from '${runtimeImport}';`);
|
|
3703
4397
|
if (serialization === "json" && contracted.length > 0) {
|
|
@@ -3748,7 +4442,7 @@ function buildApiFile(routes, outDir, opts = {}) {
|
|
|
3748
4442
|
lines.push(...statements, "");
|
|
3749
4443
|
}
|
|
3750
4444
|
}
|
|
3751
|
-
return lines
|
|
4445
|
+
return finishRoutesImport(lines);
|
|
3752
4446
|
}
|
|
3753
4447
|
const tree = /* @__PURE__ */ new Map();
|
|
3754
4448
|
for (const r of contracted) {
|
|
@@ -3801,7 +4495,7 @@ function buildApiFile(routes, outDir, opts = {}) {
|
|
|
3801
4495
|
lines.push(...statements, "");
|
|
3802
4496
|
}
|
|
3803
4497
|
}
|
|
3804
|
-
return lines
|
|
4498
|
+
return finishRoutesImport(lines);
|
|
3805
4499
|
}
|
|
3806
4500
|
|
|
3807
4501
|
// src/emit/emit-cache.ts
|
|
@@ -3814,6 +4508,7 @@ async function emitCache(pages, outDir) {
|
|
|
3814
4508
|
const s = await stat(p.absolutePath);
|
|
3815
4509
|
return {
|
|
3816
4510
|
name: p.name,
|
|
4511
|
+
scope: p.scope ?? "default",
|
|
3817
4512
|
relativePath: p.relativePath,
|
|
3818
4513
|
mtime: s.mtime.toISOString()
|
|
3819
4514
|
};
|
|
@@ -4561,6 +5256,7 @@ function buildOpenApiSpec(routes, opts = {}) {
|
|
|
4561
5256
|
const paths = {};
|
|
4562
5257
|
for (const route of routes) {
|
|
4563
5258
|
if (!route.contract) continue;
|
|
5259
|
+
if (route.method.toUpperCase() === "ALL") continue;
|
|
4564
5260
|
const oaPath = toOpenApiPath(route.path);
|
|
4565
5261
|
const method = route.method.toLowerCase();
|
|
4566
5262
|
let pathItem = paths[oaPath];
|
|
@@ -4604,10 +5300,11 @@ async function emitPages(pages, outDir, _options = {}) {
|
|
|
4604
5300
|
}).join("\n");
|
|
4605
5301
|
const propsHelper = "\nexport type InertiaProps<K extends InertiaPageName> = import('@dudousxd/nestjs-inertia').InertiaPages[K];\n";
|
|
4606
5302
|
const sharedPropsBlock = buildSharedPropsBlock(_options.sharedProps ?? null);
|
|
5303
|
+
const scopesBlock = buildScopesBlock(pages);
|
|
4607
5304
|
const content = `// Generated by @dudousxd/nestjs-codegen. Do not edit.
|
|
4608
5305
|
|
|
4609
5306
|
export type InertiaPageName = ${pageNameUnion};
|
|
4610
|
-
${propsHelper}
|
|
5307
|
+
${propsHelper}${scopesBlock}
|
|
4611
5308
|
declare module '@dudousxd/nestjs-inertia' {
|
|
4612
5309
|
interface InertiaPages {
|
|
4613
5310
|
${augBody}
|
|
@@ -4616,6 +5313,31 @@ ${sharedPropsBlock}}
|
|
|
4616
5313
|
`;
|
|
4617
5314
|
await writeFile7(join11(outDir, "pages.d.ts"), content, "utf8");
|
|
4618
5315
|
}
|
|
5316
|
+
function buildScopesBlock(pages) {
|
|
5317
|
+
const byScope = /* @__PURE__ */ new Map();
|
|
5318
|
+
for (const page of pages) {
|
|
5319
|
+
const scope = page.scope ?? "default";
|
|
5320
|
+
const names = byScope.get(scope) ?? [];
|
|
5321
|
+
names.push(JSON.stringify(page.name));
|
|
5322
|
+
byScope.set(scope, names);
|
|
5323
|
+
}
|
|
5324
|
+
if (![...byScope.keys()].some((scope) => scope !== "default")) return "";
|
|
5325
|
+
if (!byScope.has("default")) byScope.set("default", []);
|
|
5326
|
+
const scopes = ["default", ...[...byScope.keys()].filter((s) => s !== "default")];
|
|
5327
|
+
const body = scopes.map((scope) => {
|
|
5328
|
+
const key = needsQuotes(scope) ? JSON.stringify(scope) : scope;
|
|
5329
|
+
const names = byScope.get(scope) ?? [];
|
|
5330
|
+
return ` ${key}: ${names.length ? names.join(" | ") : "never"};`;
|
|
5331
|
+
}).join("\n");
|
|
5332
|
+
return `
|
|
5333
|
+
/** The page names of each Inertia app: \`default\` (forRoot) and every \`pages.scopes\` entry. */
|
|
5334
|
+
export interface InertiaScopePages {
|
|
5335
|
+
${body}
|
|
5336
|
+
}
|
|
5337
|
+
|
|
5338
|
+
export type InertiaScopePageName<S extends keyof InertiaScopePages> = InertiaScopePages[S];
|
|
5339
|
+
`;
|
|
5340
|
+
}
|
|
4619
5341
|
function buildSharedPropsBlock(sharedProps) {
|
|
4620
5342
|
if (!sharedProps) return "";
|
|
4621
5343
|
if (sharedProps.isImportRef) {
|
|
@@ -4797,12 +5519,7 @@ async function generate(config, inputRoutes = [], entryPoint = "cli") {
|
|
|
4797
5519
|
}
|
|
4798
5520
|
if (config.pages) {
|
|
4799
5521
|
const pagesConfig = config.pages;
|
|
4800
|
-
const pages = await
|
|
4801
|
-
glob: pagesConfig.glob,
|
|
4802
|
-
cwd: config.codegen.cwd,
|
|
4803
|
-
propsExport: pagesConfig.propsExport,
|
|
4804
|
-
componentNameStrategy: pagesConfig.componentNameStrategy
|
|
4805
|
-
});
|
|
5522
|
+
const pages = await discoverPageScopes(config);
|
|
4806
5523
|
const sharedProps = discoverSharedPropsFromConfig(config);
|
|
4807
5524
|
await emitPages(pages, config.codegen.outDir, {
|
|
4808
5525
|
propsExport: pagesConfig.propsExport,
|
|
@@ -4977,12 +5694,17 @@ async function watch(config, onChange, options = {}) {
|
|
|
4977
5694
|
await runInitialPass();
|
|
4978
5695
|
}
|
|
4979
5696
|
let pagesDebounceTimer;
|
|
4980
|
-
const
|
|
4981
|
-
const pagesWatcher = chokidar.watch(
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
|
|
4985
|
-
|
|
5697
|
+
const pageGlobs = allPageGlobs(config);
|
|
5698
|
+
const pagesWatcher = chokidar.watch(
|
|
5699
|
+
(pageGlobs.length ? pageGlobs : [".nestjs-codegen-no-pages"]).map(
|
|
5700
|
+
(glob) => join15(config.codegen.cwd, glob)
|
|
5701
|
+
),
|
|
5702
|
+
{
|
|
5703
|
+
ignoreInitial: true,
|
|
5704
|
+
persistent: true,
|
|
5705
|
+
awaitWriteFinish: { stabilityThreshold: 80, pollInterval: 20 }
|
|
5706
|
+
}
|
|
5707
|
+
);
|
|
4986
5708
|
function schedulePagesRegenerate() {
|
|
4987
5709
|
if (pagesDebounceTimer !== void 0) {
|
|
4988
5710
|
clearTimeout(pagesDebounceTimer);
|
|
@@ -5062,7 +5784,7 @@ async function watch(config, onChange, options = {}) {
|
|
|
5062
5784
|
}
|
|
5063
5785
|
|
|
5064
5786
|
// src/index.ts
|
|
5065
|
-
var VERSION = "0.
|
|
5787
|
+
var VERSION = "0.27.0";
|
|
5066
5788
|
|
|
5067
5789
|
// src/generate-manifest.ts
|
|
5068
5790
|
var MANIFEST_FILE = ".codegen-manifest.json";
|
|
@@ -5122,8 +5844,7 @@ function diffConfigKeyHashes(previous, current) {
|
|
|
5122
5844
|
return [...keys].filter((key) => previous[key] !== current[key]).sort();
|
|
5123
5845
|
}
|
|
5124
5846
|
async function discoverInputFiles(config) {
|
|
5125
|
-
const globs = [config.contracts.glob, config.forms.watch];
|
|
5126
|
-
if (config.pages) globs.push(config.pages.glob);
|
|
5847
|
+
const globs = [config.contracts.glob, config.forms.watch, ...allPageGlobs(config)];
|
|
5127
5848
|
const cwd = config.codegen.cwd;
|
|
5128
5849
|
const matched = await fg3(globs, { cwd, absolute: true, onlyFiles: true });
|
|
5129
5850
|
return [...new Set(matched)].sort();
|