@dudousxd/nestjs-codegen 0.29.0 → 0.30.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 +24 -0
- package/dist/cli/main.cjs +711 -436
- package/dist/cli/main.cjs.map +1 -1
- package/dist/cli/main.js +862 -582
- package/dist/cli/main.js.map +1 -1
- package/dist/extension/index.d.cts +1 -1
- package/dist/extension/index.d.ts +1 -1
- package/dist/{index-Dc4s883u.d.cts → index-D4Blt-RT.d.cts} +1 -13
- package/dist/{index-Dc4s883u.d.ts → index-D4Blt-RT.d.ts} +1 -13
- package/dist/index.cjs +696 -434
- 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 +854 -587
- package/dist/index.js.map +1 -1
- package/dist/nest/index.cjs +559 -284
- 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 +369 -89
- package/dist/nest/index.js.map +1 -1
- package/package.json +9 -9
- package/skills/codegen-setup/SKILL.md +5 -1
package/dist/nest/index.js
CHANGED
|
@@ -87,11 +87,6 @@ function validateUserConfig(userConfig) {
|
|
|
87
87
|
);
|
|
88
88
|
}
|
|
89
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
90
|
}
|
|
96
91
|
function isGlobInput(value) {
|
|
97
92
|
if (typeof value === "string") return true;
|
|
@@ -139,7 +134,6 @@ function applyDefaults(userConfig, cwd) {
|
|
|
139
134
|
glob: userConfig.contracts?.glob ?? "src/**/*.controller.ts",
|
|
140
135
|
debounceMs: userConfig.contracts?.debounceMs ?? 500
|
|
141
136
|
},
|
|
142
|
-
scopes: userConfig.scopes ?? {},
|
|
143
137
|
codegen: {
|
|
144
138
|
outDir,
|
|
145
139
|
cwd: resolvedCwd
|
|
@@ -180,11 +174,51 @@ import { existsSync } from "fs";
|
|
|
180
174
|
import { dirname as dirname2, join as join2, resolve as resolve3 } from "path";
|
|
181
175
|
import fg from "fast-glob";
|
|
182
176
|
import {
|
|
177
|
+
FileSystemRefreshResult as FileSystemRefreshResult3,
|
|
183
178
|
Node as Node10,
|
|
184
179
|
Project as Project3,
|
|
185
|
-
ts as
|
|
180
|
+
ts as ts4
|
|
186
181
|
} from "ts-morph";
|
|
187
182
|
|
|
183
|
+
// src/discovery/disk-fingerprints.ts
|
|
184
|
+
import { readFileSync, statSync } from "fs";
|
|
185
|
+
var DiskFingerprints = class {
|
|
186
|
+
stats = /* @__PURE__ */ new Map();
|
|
187
|
+
/** The paths whose disk content differs from `text`, or that no longer exist. */
|
|
188
|
+
changed(files) {
|
|
189
|
+
const changed = [];
|
|
190
|
+
for (const { path, text } of files) {
|
|
191
|
+
let key;
|
|
192
|
+
try {
|
|
193
|
+
const stat2 = statSync(path);
|
|
194
|
+
key = `${stat2.mtimeMs}:${stat2.size}`;
|
|
195
|
+
} catch {
|
|
196
|
+
this.stats.delete(path);
|
|
197
|
+
changed.push(path);
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
if (this.stats.get(path) === key) continue;
|
|
201
|
+
let disk;
|
|
202
|
+
try {
|
|
203
|
+
disk = readFileSync(path, "utf-8");
|
|
204
|
+
} catch {
|
|
205
|
+
disk = void 0;
|
|
206
|
+
}
|
|
207
|
+
if (disk === text) {
|
|
208
|
+
this.stats.set(path, key);
|
|
209
|
+
} else {
|
|
210
|
+
this.stats.delete(path);
|
|
211
|
+
changed.push(path);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return changed;
|
|
215
|
+
}
|
|
216
|
+
/** Forget `path`, so its next check compares content again. */
|
|
217
|
+
forget(path) {
|
|
218
|
+
this.stats.delete(path);
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
|
|
188
222
|
// src/discovery/dto-type-resolver.ts
|
|
189
223
|
import {
|
|
190
224
|
Node as Node9,
|
|
@@ -1199,7 +1233,54 @@ function toFilterFieldType(name, r) {
|
|
|
1199
1233
|
}
|
|
1200
1234
|
|
|
1201
1235
|
// src/discovery/heritage.ts
|
|
1202
|
-
import {
|
|
1236
|
+
import {
|
|
1237
|
+
FileSystemRefreshResult,
|
|
1238
|
+
Node as Node4,
|
|
1239
|
+
Project
|
|
1240
|
+
} from "ts-morph";
|
|
1241
|
+
|
|
1242
|
+
// src/discovery/unresolved-imports.ts
|
|
1243
|
+
import { ts } from "ts-morph";
|
|
1244
|
+
var UnresolvedImports = class {
|
|
1245
|
+
read;
|
|
1246
|
+
/**
|
|
1247
|
+
* The files with an import that failed to resolve in `program` and now resolves
|
|
1248
|
+
* to a TypeScript file the program does not hold.
|
|
1249
|
+
*/
|
|
1250
|
+
nowResolving(program) {
|
|
1251
|
+
if (this.read?.program !== program) {
|
|
1252
|
+
this.read = { program, imports: failedImports(program) };
|
|
1253
|
+
}
|
|
1254
|
+
const options = program.getCompilerOptions();
|
|
1255
|
+
const files = /* @__PURE__ */ new Set();
|
|
1256
|
+
for (const { specifier, from } of this.read.imports) {
|
|
1257
|
+
const file = ts.resolveModuleName(specifier, from, options, ts.sys).resolvedModule?.resolvedFileName;
|
|
1258
|
+
if (file !== void 0 && /\.(m|c)?tsx?$/.test(file) && !program.getSourceFile(file)) {
|
|
1259
|
+
files.add(from);
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
return [...files];
|
|
1263
|
+
}
|
|
1264
|
+
};
|
|
1265
|
+
function failedImports(program) {
|
|
1266
|
+
const checker = program.getTypeChecker();
|
|
1267
|
+
const imports = [];
|
|
1268
|
+
for (const file of program.getSourceFiles()) {
|
|
1269
|
+
if (program.isSourceFileDefaultLibrary(file) || file.isDeclarationFile) continue;
|
|
1270
|
+
if (program.isSourceFileFromExternalLibrary(file)) continue;
|
|
1271
|
+
for (const statement of file.statements) {
|
|
1272
|
+
if (!ts.isImportDeclaration(statement) && !ts.isExportDeclaration(statement)) continue;
|
|
1273
|
+
const specifier = statement.moduleSpecifier;
|
|
1274
|
+
if (!specifier || !ts.isStringLiteral(specifier)) continue;
|
|
1275
|
+
if (!checker.getSymbolAtLocation(specifier)) {
|
|
1276
|
+
imports.push({ specifier: specifier.text, from: file.fileName });
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
return imports;
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
// src/discovery/heritage.ts
|
|
1203
1284
|
function resolveHeritageCall(node) {
|
|
1204
1285
|
if (!node) return void 0;
|
|
1205
1286
|
const expr = unwrapExpression(node);
|
|
@@ -1399,15 +1480,42 @@ function resolvePropertyClassReference(prop) {
|
|
|
1399
1480
|
return { key, ref };
|
|
1400
1481
|
}
|
|
1401
1482
|
var mixinTypeProject;
|
|
1483
|
+
var mixinFiles = new DiskFingerprints();
|
|
1484
|
+
var mixinStale = false;
|
|
1485
|
+
var mixinUnresolved = new UnresolvedImports();
|
|
1402
1486
|
function getMixinTypeProject() {
|
|
1403
|
-
mixinTypeProject
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1487
|
+
if (mixinTypeProject && mixinStale && !refreshMixinTypeProject(mixinTypeProject)) {
|
|
1488
|
+
mixinTypeProject = void 0;
|
|
1489
|
+
}
|
|
1490
|
+
mixinStale = false;
|
|
1491
|
+
if (!mixinTypeProject) {
|
|
1492
|
+
mixinTypeProject = new Project({
|
|
1493
|
+
skipAddingFilesFromTsConfig: true,
|
|
1494
|
+
compilerOptions: { strict: true }
|
|
1495
|
+
});
|
|
1496
|
+
mixinFiles = new DiskFingerprints();
|
|
1497
|
+
mixinUnresolved = new UnresolvedImports();
|
|
1498
|
+
}
|
|
1407
1499
|
return mixinTypeProject;
|
|
1408
1500
|
}
|
|
1409
1501
|
function clearMixinTypeProject() {
|
|
1410
1502
|
mixinTypeProject = void 0;
|
|
1503
|
+
mixinStale = false;
|
|
1504
|
+
}
|
|
1505
|
+
function invalidateMixinTypeProject() {
|
|
1506
|
+
mixinStale = true;
|
|
1507
|
+
}
|
|
1508
|
+
function refreshMixinTypeProject(project) {
|
|
1509
|
+
const program = project.getProgram().compilerObject;
|
|
1510
|
+
const files = program.getSourceFiles().filter((file) => !program.isSourceFileDefaultLibrary(file)).map((file) => ({ path: file.fileName, text: file.text }));
|
|
1511
|
+
const changed = mixinFiles.changed(files);
|
|
1512
|
+
if (changed.length === 0) return mixinUnresolved.nowResolving(program).length === 0;
|
|
1513
|
+
for (const path of changed) {
|
|
1514
|
+
const sf = project.getSourceFile(path);
|
|
1515
|
+
if (sf?.refreshFromFileSystemSync() !== FileSystemRefreshResult.Updated) return false;
|
|
1516
|
+
}
|
|
1517
|
+
project.resolveSourceFileDependencies();
|
|
1518
|
+
return true;
|
|
1411
1519
|
}
|
|
1412
1520
|
function resolveInstantiatedReturnType(cls, methodName) {
|
|
1413
1521
|
const filePath = cls.getSourceFile().getFilePath();
|
|
@@ -1738,11 +1846,11 @@ import { Node as Node8 } from "ts-morph";
|
|
|
1738
1846
|
|
|
1739
1847
|
// src/discovery/schema-type-inference.ts
|
|
1740
1848
|
import {
|
|
1741
|
-
ts as
|
|
1849
|
+
ts as ts3
|
|
1742
1850
|
} from "ts-morph";
|
|
1743
1851
|
|
|
1744
1852
|
// src/discovery/schema-type-program.ts
|
|
1745
|
-
import { Node as Node6, Project as Project2, ts } from "ts-morph";
|
|
1853
|
+
import { FileSystemRefreshResult as FileSystemRefreshResult2, Node as Node6, Project as Project2, ts as ts2 } from "ts-morph";
|
|
1746
1854
|
var PARAM_DECORATORS = /* @__PURE__ */ new Set(["Body", "Query", "Param"]);
|
|
1747
1855
|
var SchemaTypeProgram = class {
|
|
1748
1856
|
constructor(discovery) {
|
|
@@ -1752,10 +1860,10 @@ var SchemaTypeProgram = class {
|
|
|
1752
1860
|
...base,
|
|
1753
1861
|
// A tsconfig-less consumer gets ES5 by default, whose lib has no `Map`/`Set`/
|
|
1754
1862
|
// `Promise` for the schema libraries' declarations to lean on.
|
|
1755
|
-
target: base.target ??
|
|
1863
|
+
target: base.target ?? ts2.ScriptTarget.ES2022,
|
|
1756
1864
|
// With neither set, resolve the way a bundler does: package `exports`
|
|
1757
1865
|
// (`zod/v4`) and extensionless or `.js` relative imports all resolve.
|
|
1758
|
-
...base.module === void 0 && base.moduleResolution === void 0 ? { module:
|
|
1866
|
+
...base.module === void 0 && base.moduleResolution === void 0 ? { module: ts2.ModuleKind.ESNext, moduleResolution: ts2.ModuleResolutionKind.Bundler } : {},
|
|
1759
1867
|
// Without it `.nullable()` and `.optional()` would erase to the bare type.
|
|
1760
1868
|
strictNullChecks: true,
|
|
1761
1869
|
// No ambient `@types/*` (a tsconfig's `types: ['node']`): a schema does not
|
|
@@ -1767,8 +1875,7 @@ var SchemaTypeProgram = class {
|
|
|
1767
1875
|
skipAddingFilesFromTsConfig: true,
|
|
1768
1876
|
compilerOptions: this.options
|
|
1769
1877
|
});
|
|
1770
|
-
|
|
1771
|
-
this.flush();
|
|
1878
|
+
this.requestDiscoveryFiles();
|
|
1772
1879
|
}
|
|
1773
1880
|
project;
|
|
1774
1881
|
options;
|
|
@@ -1781,6 +1888,53 @@ var SchemaTypeProgram = class {
|
|
|
1781
1888
|
needs = /* @__PURE__ */ new Map();
|
|
1782
1889
|
/** Pruned text per file, as last written into {@link project}. */
|
|
1783
1890
|
written = /* @__PURE__ */ new Map();
|
|
1891
|
+
/** What {@link project} read from disk unpruned (packages, declaration files). */
|
|
1892
|
+
externalFiles = new DiskFingerprints();
|
|
1893
|
+
scratchFiles = new DiskFingerprints();
|
|
1894
|
+
/** The last pruning of each file, with the source text and needs it was pruned for. */
|
|
1895
|
+
pruned = /* @__PURE__ */ new Map();
|
|
1896
|
+
/**
|
|
1897
|
+
* Bring the program up to date with discovery's files and the disk, keeping the
|
|
1898
|
+
* lib and package declarations it has already parsed and checked. Leaves it
|
|
1899
|
+
* holding exactly the files a new program built now would hold, with the same
|
|
1900
|
+
* text. Returns false when a file the program read unpruned has changed on disk
|
|
1901
|
+
* (a package was reinstalled, a `.d.ts` was edited): the caller builds a new
|
|
1902
|
+
* program instead.
|
|
1903
|
+
*/
|
|
1904
|
+
refresh() {
|
|
1905
|
+
const program = this.project.getProgram().compilerObject;
|
|
1906
|
+
const external = program.getSourceFiles().filter(
|
|
1907
|
+
(file) => !program.isSourceFileDefaultLibrary(file) && !this.written.has(file.fileName)
|
|
1908
|
+
).map((file) => ({ path: file.fileName, text: file.text }));
|
|
1909
|
+
if (this.externalFiles.changed(external).length > 0) return false;
|
|
1910
|
+
const scratch = this.scratch.getSourceFiles().map((sf) => ({ path: sf.getFilePath(), text: sf.getFullText() }));
|
|
1911
|
+
for (const path of this.scratchFiles.changed(scratch)) {
|
|
1912
|
+
const sf = this.scratch.getSourceFile(path);
|
|
1913
|
+
if (sf?.refreshFromFileSystemSync() === FileSystemRefreshResult2.Deleted) {
|
|
1914
|
+
this.scratchFiles.forget(path);
|
|
1915
|
+
}
|
|
1916
|
+
}
|
|
1917
|
+
this.needs.clear();
|
|
1918
|
+
this.requestDiscoveryFiles();
|
|
1919
|
+
for (const path of [...this.written.keys()]) {
|
|
1920
|
+
if (this.needs.has(path)) continue;
|
|
1921
|
+
const sf = this.project.getSourceFile(path);
|
|
1922
|
+
if (sf) this.project.removeSourceFile(sf);
|
|
1923
|
+
this.written.delete(path);
|
|
1924
|
+
}
|
|
1925
|
+
return true;
|
|
1926
|
+
}
|
|
1927
|
+
/**
|
|
1928
|
+
* Everything discovery holds now (the controllers) goes in before the first
|
|
1929
|
+
* query: a file added afterwards discards the program and its checker, and
|
|
1930
|
+
* rebuilding them once per controller is exactly the cost this avoids.
|
|
1931
|
+
*/
|
|
1932
|
+
requestDiscoveryFiles() {
|
|
1933
|
+
for (const sf of this.discovery.getSourceFiles()) {
|
|
1934
|
+
this.request(sf.getFilePath(), { schema: true });
|
|
1935
|
+
}
|
|
1936
|
+
this.flush();
|
|
1937
|
+
}
|
|
1784
1938
|
/**
|
|
1785
1939
|
* The typed copy of a discovery file, with its schema positions kept. Adding one
|
|
1786
1940
|
* that was not there yet (a contract file discovery reached lazily) rebuilds the
|
|
@@ -1821,12 +1975,21 @@ var SchemaTypeProgram = class {
|
|
|
1821
1975
|
while (this.queue.length > 0) {
|
|
1822
1976
|
const path = this.queue.pop();
|
|
1823
1977
|
const original = this.original(path);
|
|
1824
|
-
if (!original)
|
|
1825
|
-
|
|
1826
|
-
|
|
1978
|
+
if (!original) {
|
|
1979
|
+
if (this.written.has(path)) changed.set(path, void 0);
|
|
1980
|
+
continue;
|
|
1981
|
+
}
|
|
1982
|
+
const { text, requests } = this.pruneCached(path, original, this.needs.get(path));
|
|
1983
|
+
for (const { path: target, want } of requests) this.request(target, want);
|
|
1827
1984
|
if (this.written.get(path) !== text) changed.set(path, text);
|
|
1828
1985
|
}
|
|
1829
1986
|
for (const [path, text] of changed) {
|
|
1987
|
+
if (text === void 0) {
|
|
1988
|
+
const sf = this.project.getSourceFile(path);
|
|
1989
|
+
if (sf) this.project.removeSourceFile(sf);
|
|
1990
|
+
this.written.delete(path);
|
|
1991
|
+
continue;
|
|
1992
|
+
}
|
|
1830
1993
|
this.project.createSourceFile(path, text, { overwrite: true });
|
|
1831
1994
|
this.written.set(path, text);
|
|
1832
1995
|
}
|
|
@@ -1835,11 +1998,29 @@ var SchemaTypeProgram = class {
|
|
|
1835
1998
|
original(path) {
|
|
1836
1999
|
return this.discovery.getSourceFile(path) ?? this.scratch.getSourceFile(path) ?? this.scratch.addSourceFileAtPathIfExists(path);
|
|
1837
2000
|
}
|
|
2001
|
+
/** {@link prune}, reusing the last result while the file's text and needs are the same. */
|
|
2002
|
+
pruneCached(path, sf, needs) {
|
|
2003
|
+
const source = sf.getFullText();
|
|
2004
|
+
const key = `${needs.schema}:${needs.all}:${[...needs.names].sort().join(",")}`;
|
|
2005
|
+
const last = this.pruned.get(path);
|
|
2006
|
+
if (last && last.source === source && last.needs === key) return last;
|
|
2007
|
+
const result = this.prune(sf, needs);
|
|
2008
|
+
if (result.resolved) this.pruned.set(path, { ...result, source, needs: key });
|
|
2009
|
+
else this.pruned.delete(path);
|
|
2010
|
+
return result;
|
|
2011
|
+
}
|
|
1838
2012
|
/**
|
|
1839
2013
|
* `sf`'s text with every import/re-export nothing in `needs` references blanked,
|
|
1840
|
-
*
|
|
2014
|
+
* and the names each kept one brings in from its target.
|
|
1841
2015
|
*/
|
|
1842
2016
|
prune(sf, needs) {
|
|
2017
|
+
const requests = [];
|
|
2018
|
+
let resolved = true;
|
|
2019
|
+
const resolveFrom = (specifier) => {
|
|
2020
|
+
const target = this.resolve(specifier, sf);
|
|
2021
|
+
if (target === null) resolved = false;
|
|
2022
|
+
return target ?? void 0;
|
|
2023
|
+
};
|
|
1843
2024
|
const locals = topLevelDeclarations(sf);
|
|
1844
2025
|
const roots = [];
|
|
1845
2026
|
if (needs.all) roots.push(...sf.getStatements());
|
|
@@ -1873,7 +2054,7 @@ var SchemaTypeProgram = class {
|
|
|
1873
2054
|
const seen = new Set(queue);
|
|
1874
2055
|
while (queue.length > 0) {
|
|
1875
2056
|
const node = queue.pop();
|
|
1876
|
-
const ids = Node6.isIdentifier(node) ? [node] : node.getDescendantsOfKind(
|
|
2057
|
+
const ids = Node6.isIdentifier(node) ? [node] : node.getDescendantsOfKind(ts2.SyntaxKind.Identifier);
|
|
1877
2058
|
for (const id of ids) {
|
|
1878
2059
|
const name = id.getText();
|
|
1879
2060
|
if (referenced.has(name)) continue;
|
|
@@ -1892,7 +2073,7 @@ var SchemaTypeProgram = class {
|
|
|
1892
2073
|
}
|
|
1893
2074
|
};
|
|
1894
2075
|
for (const decl of sf.getImportDeclarations()) {
|
|
1895
|
-
const target =
|
|
2076
|
+
const target = resolveFrom(decl.getModuleSpecifierValue());
|
|
1896
2077
|
const namespace = decl.getNamespaceImport()?.getText();
|
|
1897
2078
|
const def = decl.getDefaultImport()?.getText();
|
|
1898
2079
|
const named = decl.getNamedImports().map((n) => ({
|
|
@@ -1906,21 +2087,24 @@ var SchemaTypeProgram = class {
|
|
|
1906
2087
|
continue;
|
|
1907
2088
|
}
|
|
1908
2089
|
if (target) {
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
2090
|
+
requests.push({
|
|
2091
|
+
path: target,
|
|
2092
|
+
want: {
|
|
2093
|
+
all: wantsNamespace,
|
|
2094
|
+
names: [...wantsDefault ? ["default"] : [], ...named.map((n) => n.imported)]
|
|
2095
|
+
}
|
|
1912
2096
|
});
|
|
1913
2097
|
}
|
|
1914
2098
|
}
|
|
1915
2099
|
for (const exp of sf.getExportDeclarations()) {
|
|
1916
2100
|
const spec = exp.getModuleSpecifierValue();
|
|
1917
2101
|
if (spec === void 0) continue;
|
|
1918
|
-
const target =
|
|
2102
|
+
const target = resolveFrom(spec);
|
|
1919
2103
|
if (exp.isNamespaceExport() || exp.getNamedExports().length === 0) {
|
|
1920
2104
|
if (!needs.all && unresolvedNames.length === 0) {
|
|
1921
2105
|
blank(exp);
|
|
1922
2106
|
} else if (target) {
|
|
1923
|
-
|
|
2107
|
+
requests.push({ path: target, want: { all: needs.all, names: unresolvedNames } });
|
|
1924
2108
|
}
|
|
1925
2109
|
continue;
|
|
1926
2110
|
}
|
|
@@ -1928,22 +2112,24 @@ var SchemaTypeProgram = class {
|
|
|
1928
2112
|
(n) => needs.all || needs.names.has((n.getAliasNode() ?? n.getNameNode()).getText())
|
|
1929
2113
|
).map((n) => n.getNameNode().getText());
|
|
1930
2114
|
if (wanted.length === 0) blank(exp);
|
|
1931
|
-
else if (target)
|
|
2115
|
+
else if (target) requests.push({ path: target, want: { names: wanted } });
|
|
1932
2116
|
}
|
|
1933
|
-
return chars.join("");
|
|
2117
|
+
return { text: chars.join(""), requests, resolved };
|
|
1934
2118
|
}
|
|
1935
2119
|
/**
|
|
1936
|
-
* The project source file a module specifier resolves to
|
|
1937
|
-
* package (read unpruned by the program itself)
|
|
2120
|
+
* The project source file a module specifier resolves to; undefined for a
|
|
2121
|
+
* package or a declaration file (read unpruned by the program itself), null for
|
|
2122
|
+
* one that does not resolve.
|
|
1938
2123
|
*/
|
|
1939
2124
|
resolve(specifier, from) {
|
|
1940
|
-
const resolved =
|
|
2125
|
+
const resolved = ts2.resolveModuleName(
|
|
1941
2126
|
specifier,
|
|
1942
2127
|
from.getFilePath(),
|
|
1943
2128
|
this.options,
|
|
1944
|
-
|
|
2129
|
+
ts2.sys
|
|
1945
2130
|
).resolvedModule;
|
|
1946
|
-
if (!resolved
|
|
2131
|
+
if (!resolved) return null;
|
|
2132
|
+
if (resolved.isExternalLibraryImport) return void 0;
|
|
1947
2133
|
const file = resolved.resolvedFileName;
|
|
1948
2134
|
if (file.endsWith(".d.ts") || !/\.(m|c)?tsx?$/.test(file)) return void 0;
|
|
1949
2135
|
return file;
|
|
@@ -1998,8 +2184,13 @@ function defaultExport(sf) {
|
|
|
1998
2184
|
|
|
1999
2185
|
// src/discovery/schema-type-inference.ts
|
|
2000
2186
|
var programs = /* @__PURE__ */ new WeakMap();
|
|
2187
|
+
var stale = /* @__PURE__ */ new WeakSet();
|
|
2001
2188
|
function programFor(discovery) {
|
|
2002
2189
|
let program = programs.get(discovery);
|
|
2190
|
+
if (program && stale.has(discovery)) {
|
|
2191
|
+
stale.delete(discovery);
|
|
2192
|
+
if (!program.refresh()) program = void 0;
|
|
2193
|
+
}
|
|
2003
2194
|
if (!program) {
|
|
2004
2195
|
program = new SchemaTypeProgram(discovery);
|
|
2005
2196
|
programs.set(discovery, program);
|
|
@@ -2008,6 +2199,10 @@ function programFor(discovery) {
|
|
|
2008
2199
|
}
|
|
2009
2200
|
function clearSchemaTypeProject(discovery) {
|
|
2010
2201
|
programs.delete(discovery);
|
|
2202
|
+
stale.delete(discovery);
|
|
2203
|
+
}
|
|
2204
|
+
function invalidateSchemaTypeProject(discovery) {
|
|
2205
|
+
if (programs.has(discovery)) stale.add(discovery);
|
|
2011
2206
|
}
|
|
2012
2207
|
function counterpart(typedSf, node) {
|
|
2013
2208
|
const start = node.getStart();
|
|
@@ -2071,7 +2266,7 @@ function objectKey(name) {
|
|
|
2071
2266
|
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name) ? name : JSON.stringify(name);
|
|
2072
2267
|
}
|
|
2073
2268
|
function isOptionalSymbol(sym) {
|
|
2074
|
-
return (sym.getFlags() &
|
|
2269
|
+
return (sym.getFlags() & ts3.SymbolFlags.Optional) !== 0;
|
|
2075
2270
|
}
|
|
2076
2271
|
function isSymbolKeyed(sym) {
|
|
2077
2272
|
return sym.getName().startsWith("__@");
|
|
@@ -2949,25 +3144,25 @@ var NO_TSCONFIG_OPTIONS = {
|
|
|
2949
3144
|
};
|
|
2950
3145
|
function loadDiscoveryTsconfig(tsconfigPath) {
|
|
2951
3146
|
if (!existsSync(tsconfigPath)) return { options: null, files: [] };
|
|
2952
|
-
const read =
|
|
3147
|
+
const read = ts4.readConfigFile(tsconfigPath, (path) => ts4.sys.readFile(path));
|
|
2953
3148
|
if (read.error) {
|
|
2954
3149
|
return {
|
|
2955
3150
|
options: null,
|
|
2956
3151
|
files: [tsconfigPath],
|
|
2957
|
-
error:
|
|
3152
|
+
error: ts4.flattenDiagnosticMessageText(read.error.messageText, " ")
|
|
2958
3153
|
};
|
|
2959
3154
|
}
|
|
2960
3155
|
const host = {
|
|
2961
|
-
useCaseSensitiveFileNames:
|
|
3156
|
+
useCaseSensitiveFileNames: ts4.sys.useCaseSensitiveFileNames,
|
|
2962
3157
|
// The one line that keeps a tsconfig from being a filesystem walk.
|
|
2963
3158
|
readDirectory: () => [],
|
|
2964
|
-
fileExists: (path) =>
|
|
3159
|
+
fileExists: (path) => ts4.sys.fileExists(path),
|
|
2965
3160
|
// `extends` is resolved through the host, so a consumer whose `paths` live
|
|
2966
3161
|
// in a base tsconfig keeps working.
|
|
2967
|
-
readFile: (path) =>
|
|
3162
|
+
readFile: (path) => ts4.sys.readFile(path)
|
|
2968
3163
|
};
|
|
2969
|
-
const sourceFile =
|
|
2970
|
-
const parsed =
|
|
3164
|
+
const sourceFile = ts4.readJsonConfigFile(tsconfigPath, (path) => ts4.sys.readFile(path));
|
|
3165
|
+
const parsed = ts4.parseJsonSourceFileConfigFileContent(
|
|
2971
3166
|
sourceFile,
|
|
2972
3167
|
host,
|
|
2973
3168
|
dirname2(tsconfigPath),
|
|
@@ -3025,6 +3220,9 @@ var PersistentDiscovery = class _PersistentDiscovery {
|
|
|
3025
3220
|
glob;
|
|
3026
3221
|
/** Absolute paths of the controllers currently loaded as extraction roots. */
|
|
3027
3222
|
controllerPaths = /* @__PURE__ */ new Set();
|
|
3223
|
+
/** Every file in {@link project}, as last read from disk. */
|
|
3224
|
+
files = new DiskFingerprints();
|
|
3225
|
+
unresolved = new UnresolvedImports();
|
|
3028
3226
|
constructor(project, cwd, glob) {
|
|
3029
3227
|
this.project = project;
|
|
3030
3228
|
this.cwd = cwd;
|
|
@@ -3050,6 +3248,8 @@ var PersistentDiscovery = class _PersistentDiscovery {
|
|
|
3050
3248
|
}
|
|
3051
3249
|
/** Run the initial extraction (equivalent to a first `discoverContractsFast`). */
|
|
3052
3250
|
discover() {
|
|
3251
|
+
clearMixinTypeProject();
|
|
3252
|
+
clearSchemaTypeProject(this.project);
|
|
3053
3253
|
return this.runExtraction();
|
|
3054
3254
|
}
|
|
3055
3255
|
/**
|
|
@@ -3057,26 +3257,39 @@ var PersistentDiscovery = class _PersistentDiscovery {
|
|
|
3057
3257
|
* from disk (controllers AND any lazily-loaded DTO/imported files), re-globs
|
|
3058
3258
|
* to pick up added/removed controllers, clears the per-Project caches, then
|
|
3059
3259
|
* re-extracts. `changedPaths` is a hint; correctness does not depend on it
|
|
3060
|
-
* being exhaustive because re-globbing +
|
|
3260
|
+
* being exhaustive because re-globbing + comparing every loaded file with the
|
|
3261
|
+
* disk covers the set.
|
|
3061
3262
|
*/
|
|
3062
|
-
async rediscover(changedPaths) {
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3263
|
+
async rediscover(changedPaths, options = {}) {
|
|
3264
|
+
const hinted = new Set([...changedPaths ?? []].map((p) => resolve3(p)));
|
|
3265
|
+
const program = this.project.getProgram().compilerObject;
|
|
3266
|
+
const sources = program.getSourceFiles().filter(
|
|
3267
|
+
(file) => !program.isSourceFileDefaultLibrary(file) && !program.isSourceFileFromExternalLibrary(file)
|
|
3268
|
+
).map((file) => ({ path: file.fileName, text: file.text }));
|
|
3269
|
+
for (const path of /* @__PURE__ */ new Set([...hinted, ...this.files.changed(sources)])) {
|
|
3270
|
+
const sf = this.project.getSourceFile(path);
|
|
3271
|
+
if (sf && await sf.refreshFromFileSystem() === FileSystemRefreshResult3.Deleted) {
|
|
3272
|
+
this.files.forget(path);
|
|
3070
3273
|
}
|
|
3071
3274
|
}
|
|
3275
|
+
const inProject = new Set(this.project.getSourceFiles().map((sf) => sf.getFilePath()));
|
|
3276
|
+
const definitions = this.project.getLanguageService().compilerObject.getProgram();
|
|
3277
|
+
for (const path of definitions ? this.unresolved.nowResolving(definitions) : []) {
|
|
3278
|
+
const sf = this.project.getSourceFile(path);
|
|
3279
|
+
if (!sf) continue;
|
|
3280
|
+
this.project.removeSourceFile(sf);
|
|
3281
|
+
if (inProject.has(path)) this.project.addSourceFileAtPathIfExists(path);
|
|
3282
|
+
}
|
|
3072
3283
|
const globbed = new Set(
|
|
3073
3284
|
await fg(this.glob, { cwd: this.cwd, absolute: true, onlyFiles: true })
|
|
3074
3285
|
);
|
|
3286
|
+
let controllersChanged = false;
|
|
3075
3287
|
for (const f of globbed) {
|
|
3076
3288
|
if (!this.controllerPaths.has(f)) {
|
|
3077
3289
|
try {
|
|
3078
3290
|
this.project.addSourceFileAtPath(f);
|
|
3079
3291
|
this.controllerPaths.add(f);
|
|
3292
|
+
controllersChanged = true;
|
|
3080
3293
|
} catch {
|
|
3081
3294
|
}
|
|
3082
3295
|
}
|
|
@@ -3086,16 +3299,22 @@ var PersistentDiscovery = class _PersistentDiscovery {
|
|
|
3086
3299
|
const sf = this.project.getSourceFile(f);
|
|
3087
3300
|
if (sf) this.project.removeSourceFile(sf);
|
|
3088
3301
|
this.controllerPaths.delete(f);
|
|
3302
|
+
controllersChanged = true;
|
|
3089
3303
|
}
|
|
3090
3304
|
}
|
|
3305
|
+
if (controllersChanged || options.filesAddedOrRemoved) {
|
|
3306
|
+
clearMixinTypeProject();
|
|
3307
|
+
clearSchemaTypeProject(this.project);
|
|
3308
|
+
} else {
|
|
3309
|
+
invalidateMixinTypeProject();
|
|
3310
|
+
invalidateSchemaTypeProject(this.project);
|
|
3311
|
+
}
|
|
3091
3312
|
return this.runExtraction();
|
|
3092
3313
|
}
|
|
3093
3314
|
/** Clear stale per-Project caches, then extract over the controller set. */
|
|
3094
3315
|
runExtraction() {
|
|
3095
3316
|
clearTypeResolutionCaches(this.project);
|
|
3096
3317
|
clearEnumCache(this.project);
|
|
3097
|
-
clearMixinTypeProject();
|
|
3098
|
-
clearSchemaTypeProject(this.project);
|
|
3099
3318
|
return extractRoutesFrom(this.project, this.controllerPaths);
|
|
3100
3319
|
}
|
|
3101
3320
|
};
|
|
@@ -3411,7 +3630,7 @@ async function discoverPages(opts) {
|
|
|
3411
3630
|
const rel = relative2(opts.cwd, file);
|
|
3412
3631
|
const nameRel = relative2(pagesBase, file).split(sep2).join("/");
|
|
3413
3632
|
const name = `${prefix}${computeName(nameRel, opts.componentNameStrategy)}`;
|
|
3414
|
-
const source = await readFile(file, "utf8");
|
|
3633
|
+
const source = await (opts.read ? opts.read(file) : readFile(file, "utf8"));
|
|
3415
3634
|
const propsSource = extractPropsSource(source, opts.propsExport);
|
|
3416
3635
|
out.push({ name, absolutePath: file, relativePath: rel, propsSource, scope });
|
|
3417
3636
|
}
|
|
@@ -3419,10 +3638,14 @@ async function discoverPages(opts) {
|
|
|
3419
3638
|
assertUniqueNames(out);
|
|
3420
3639
|
return out;
|
|
3421
3640
|
}
|
|
3422
|
-
async function discoverPageScopes(config) {
|
|
3641
|
+
async function discoverPageScopes(config, read) {
|
|
3423
3642
|
const pages = config.pages;
|
|
3424
3643
|
if (!pages) return [];
|
|
3425
|
-
const common = {
|
|
3644
|
+
const common = {
|
|
3645
|
+
cwd: config.codegen.cwd,
|
|
3646
|
+
propsExport: pages.propsExport,
|
|
3647
|
+
...read ? { read } : {}
|
|
3648
|
+
};
|
|
3426
3649
|
const out = await discoverPages({
|
|
3427
3650
|
...common,
|
|
3428
3651
|
glob: pages.glob,
|
|
@@ -3497,6 +3720,7 @@ import { mkdir as mkdir9, writeFile as writeFile9 } from "fs/promises";
|
|
|
3497
3720
|
import { dirname as dirname4, join as join13 } from "path";
|
|
3498
3721
|
|
|
3499
3722
|
// src/discovery/shared-props.ts
|
|
3723
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
3500
3724
|
import { join as join4 } from "path";
|
|
3501
3725
|
import { Node as Node11, Project as Project4, SyntaxKind as SyntaxKind4 } from "ts-morph";
|
|
3502
3726
|
function discoverSharedProps(project, moduleEntry) {
|
|
@@ -3524,12 +3748,7 @@ function discoverSharedPropsFromConfig(config) {
|
|
|
3524
3748
|
const tsconfigPath = config.app.tsconfig ?? join4(config.codegen.cwd, "tsconfig.json");
|
|
3525
3749
|
let project;
|
|
3526
3750
|
try {
|
|
3527
|
-
project =
|
|
3528
|
-
tsConfigFilePath: tsconfigPath,
|
|
3529
|
-
skipAddingFilesFromTsConfig: true,
|
|
3530
|
-
skipLoadingLibFiles: true,
|
|
3531
|
-
skipFileDependencyResolution: true
|
|
3532
|
-
});
|
|
3751
|
+
project = sharedPropsProject(tsconfigPath);
|
|
3533
3752
|
} catch {
|
|
3534
3753
|
project = new Project4({
|
|
3535
3754
|
skipAddingFilesFromTsConfig: true,
|
|
@@ -3543,6 +3762,27 @@ function discoverSharedPropsFromConfig(config) {
|
|
|
3543
3762
|
return null;
|
|
3544
3763
|
}
|
|
3545
3764
|
}
|
|
3765
|
+
var parsedTsconfigs = /* @__PURE__ */ new Map();
|
|
3766
|
+
function readText(file) {
|
|
3767
|
+
try {
|
|
3768
|
+
return readFileSync2(file, "utf8");
|
|
3769
|
+
} catch {
|
|
3770
|
+
return "\0missing";
|
|
3771
|
+
}
|
|
3772
|
+
}
|
|
3773
|
+
function sharedPropsProject(tsconfigPath) {
|
|
3774
|
+
const text = loadDiscoveryTsconfig(tsconfigPath).files.map((file) => `${file}\0${readText(file)}`).join("\0");
|
|
3775
|
+
const flags = {
|
|
3776
|
+
skipAddingFilesFromTsConfig: true,
|
|
3777
|
+
skipLoadingLibFiles: true,
|
|
3778
|
+
skipFileDependencyResolution: true
|
|
3779
|
+
};
|
|
3780
|
+
const parsed = parsedTsconfigs.get(tsconfigPath);
|
|
3781
|
+
if (parsed?.text === text) return new Project4({ ...flags, compilerOptions: parsed.options });
|
|
3782
|
+
const project = new Project4({ ...flags, tsConfigFilePath: tsconfigPath });
|
|
3783
|
+
parsedTsconfigs.set(tsconfigPath, { text, options: project.getCompilerOptions() });
|
|
3784
|
+
return project;
|
|
3785
|
+
}
|
|
3546
3786
|
function findForRootCall(sourceFile) {
|
|
3547
3787
|
const callExpressions = sourceFile.getDescendantsOfKind(SyntaxKind4.CallExpression);
|
|
3548
3788
|
for (const call of callExpressions) {
|
|
@@ -5498,14 +5738,15 @@ function driftGuardMessage(outDir, previousEntryPoint, currentEntryPoint, differ
|
|
|
5498
5738
|
async function generate(config, inputRoutes = [], entryPoint = "cli") {
|
|
5499
5739
|
setCodegenDebug(config.debug);
|
|
5500
5740
|
const manifest = await readManifest(config.codegen.outDir);
|
|
5501
|
-
const
|
|
5741
|
+
const inputs = await readInputs(config);
|
|
5742
|
+
const inputsHash = await computeInputsHash(config, manifest?.extraInputs ?? [], inputs);
|
|
5502
5743
|
if (await isManifestFresh(config.codegen.outDir, manifest, inputsHash)) {
|
|
5503
5744
|
console.log(`[nestjs-codegen] ${config.codegen.outDir} up to date, skipped`);
|
|
5504
5745
|
return;
|
|
5505
5746
|
}
|
|
5506
5747
|
const configHash = computeConfigHash(config);
|
|
5507
5748
|
const configKeyHashes = computeConfigKeyHashes(config);
|
|
5508
|
-
if (config.driftGuard && manifest?.entryPoint && manifest.entryPoint !== entryPoint && manifest.configHash && manifest.configHash !== configHash) {
|
|
5749
|
+
if (config.driftGuard && manifest?.version === VERSION && manifest?.entryPoint && manifest.entryPoint !== entryPoint && manifest.configHash && manifest.configHash !== configHash) {
|
|
5509
5750
|
throw new DriftGuardError(
|
|
5510
5751
|
driftGuardMessage(
|
|
5511
5752
|
config.codegen.outDir,
|
|
@@ -5526,7 +5767,7 @@ async function generate(config, inputRoutes = [], entryPoint = "cli") {
|
|
|
5526
5767
|
}
|
|
5527
5768
|
if (config.pages) {
|
|
5528
5769
|
const pagesConfig = config.pages;
|
|
5529
|
-
const pages = await discoverPageScopes(config);
|
|
5770
|
+
const pages = await discoverPageScopes(config, (path) => readInput(inputs, path));
|
|
5530
5771
|
const sharedProps = discoverSharedPropsFromConfig(config);
|
|
5531
5772
|
await emitPages(pages, config.codegen.outDir, {
|
|
5532
5773
|
propsExport: pagesConfig.propsExport,
|
|
@@ -5576,7 +5817,7 @@ async function generate(config, inputRoutes = [], entryPoint = "cli") {
|
|
|
5576
5817
|
}
|
|
5577
5818
|
const outputFiles = await listOutputFiles(config.codegen.outDir);
|
|
5578
5819
|
const extraInputs = [...trackedInputs].sort();
|
|
5579
|
-
const recordedHash = extraInputs.length > 0 ? await computeInputsHash(config, extraInputs) : inputsHash;
|
|
5820
|
+
const recordedHash = extraInputs.length > 0 ? await computeInputsHash(config, extraInputs, inputs) : inputsHash;
|
|
5580
5821
|
await writeManifest(config.codegen.outDir, {
|
|
5581
5822
|
version: VERSION,
|
|
5582
5823
|
hash: recordedHash,
|
|
@@ -5759,13 +6000,15 @@ async function watch(config, onChange, options = {}) {
|
|
|
5759
6000
|
pagesWatcher.on("unlink", schedulePagesRegenerate);
|
|
5760
6001
|
let contractsDebounceTimer;
|
|
5761
6002
|
const pendingChangedPaths = /* @__PURE__ */ new Set();
|
|
6003
|
+
let pendingAddedOrRemoved = false;
|
|
5762
6004
|
const contractsWatcher = chokidar.watch(join15(config.codegen.cwd, config.contracts.glob), {
|
|
5763
6005
|
ignoreInitial: true,
|
|
5764
6006
|
persistent: true,
|
|
5765
6007
|
awaitWriteFinish: { stabilityThreshold: 80, pollInterval: 20 }
|
|
5766
6008
|
});
|
|
5767
|
-
function scheduleContractsRegenerate(changedPath) {
|
|
6009
|
+
function scheduleContractsRegenerate(changedPath, addedOrRemoved = false) {
|
|
5768
6010
|
if (typeof changedPath === "string") pendingChangedPaths.add(changedPath);
|
|
6011
|
+
if (addedOrRemoved) pendingAddedOrRemoved = true;
|
|
5769
6012
|
if (contractsDebounceTimer !== void 0) {
|
|
5770
6013
|
clearTimeout(contractsDebounceTimer);
|
|
5771
6014
|
}
|
|
@@ -5773,8 +6016,12 @@ async function watch(config, onChange, options = {}) {
|
|
|
5773
6016
|
contractsDebounceTimer = void 0;
|
|
5774
6017
|
const changed = [...pendingChangedPaths];
|
|
5775
6018
|
pendingChangedPaths.clear();
|
|
6019
|
+
const filesAddedOrRemoved = pendingAddedOrRemoved;
|
|
6020
|
+
pendingAddedOrRemoved = false;
|
|
5776
6021
|
try {
|
|
5777
|
-
const routes = await (await getDiscovery()).rediscover(changed
|
|
6022
|
+
const routes = await (await getDiscovery()).rediscover(changed, {
|
|
6023
|
+
filesAddedOrRemoved
|
|
6024
|
+
});
|
|
5778
6025
|
lastRoutes = routes;
|
|
5779
6026
|
await generateAndTrack(routes);
|
|
5780
6027
|
} catch (err) {
|
|
@@ -5786,20 +6033,25 @@ async function watch(config, onChange, options = {}) {
|
|
|
5786
6033
|
onChange?.();
|
|
5787
6034
|
}, config.contracts.debounceMs);
|
|
5788
6035
|
}
|
|
5789
|
-
contractsWatcher.on("add", (p) => scheduleContractsRegenerate(p));
|
|
6036
|
+
contractsWatcher.on("add", (p) => scheduleContractsRegenerate(p, true));
|
|
5790
6037
|
contractsWatcher.on("change", (p) => scheduleContractsRegenerate(p));
|
|
5791
|
-
contractsWatcher.on("unlink", (p) => scheduleContractsRegenerate(p));
|
|
6038
|
+
contractsWatcher.on("unlink", (p) => scheduleContractsRegenerate(p, true));
|
|
5792
6039
|
const formsWatcher = chokidar.watch(join15(config.codegen.cwd, config.forms.watch), {
|
|
5793
6040
|
ignoreInitial: true,
|
|
5794
6041
|
persistent: true,
|
|
5795
6042
|
awaitWriteFinish: { stabilityThreshold: 80, pollInterval: 20 }
|
|
5796
6043
|
});
|
|
5797
|
-
formsWatcher.on("add", (p) => scheduleContractsRegenerate(p));
|
|
6044
|
+
formsWatcher.on("add", (p) => scheduleContractsRegenerate(p, true));
|
|
5798
6045
|
formsWatcher.on("change", (p) => scheduleContractsRegenerate(p));
|
|
5799
|
-
formsWatcher.on("unlink", (p) => scheduleContractsRegenerate(p));
|
|
5800
|
-
trackedWatcher.on("add", (p) => scheduleContractsRegenerate(p));
|
|
6046
|
+
formsWatcher.on("unlink", (p) => scheduleContractsRegenerate(p, true));
|
|
6047
|
+
trackedWatcher.on("add", (p) => scheduleContractsRegenerate(p, true));
|
|
5801
6048
|
trackedWatcher.on("change", (p) => scheduleContractsRegenerate(p));
|
|
5802
|
-
trackedWatcher.on("unlink", (p) => scheduleContractsRegenerate(p));
|
|
6049
|
+
trackedWatcher.on("unlink", (p) => scheduleContractsRegenerate(p, true));
|
|
6050
|
+
await Promise.all(
|
|
6051
|
+
[pagesWatcher, contractsWatcher, formsWatcher].map(
|
|
6052
|
+
(w) => new Promise((resolveReady) => w.once("ready", () => resolveReady()))
|
|
6053
|
+
)
|
|
6054
|
+
);
|
|
5803
6055
|
return {
|
|
5804
6056
|
close: async () => {
|
|
5805
6057
|
if (pagesDebounceTimer !== void 0) {
|
|
@@ -5820,7 +6072,7 @@ async function watch(config, onChange, options = {}) {
|
|
|
5820
6072
|
}
|
|
5821
6073
|
|
|
5822
6074
|
// src/index.ts
|
|
5823
|
-
var VERSION = "0.
|
|
6075
|
+
var VERSION = "0.30.0";
|
|
5824
6076
|
|
|
5825
6077
|
// src/generate-manifest.ts
|
|
5826
6078
|
var MANIFEST_FILE = ".codegen-manifest.json";
|
|
@@ -5885,36 +6137,64 @@ async function discoverInputFiles(config) {
|
|
|
5885
6137
|
const matched = await fg3(globs, { cwd, absolute: true, onlyFiles: true });
|
|
5886
6138
|
return [...new Set(matched)].sort();
|
|
5887
6139
|
}
|
|
5888
|
-
|
|
6140
|
+
var READ_CONCURRENCY = 64;
|
|
6141
|
+
async function readAll(paths, read) {
|
|
6142
|
+
const out = /* @__PURE__ */ new Map();
|
|
6143
|
+
for (let i = 0; i < paths.length; i += READ_CONCURRENCY) {
|
|
6144
|
+
const batch = paths.slice(i, i + READ_CONCURRENCY);
|
|
6145
|
+
const texts = await Promise.all(batch.map(read));
|
|
6146
|
+
batch.forEach((path, j) => out.set(path, texts[j]));
|
|
6147
|
+
}
|
|
6148
|
+
return out;
|
|
6149
|
+
}
|
|
6150
|
+
async function readInputs(config) {
|
|
6151
|
+
const cwd = config.codegen.cwd;
|
|
6152
|
+
const tsconfigPath = resolveTsconfigPath(cwd, config.app?.tsconfig ?? void 0);
|
|
6153
|
+
const extended = loadDiscoveryTsconfig(tsconfigPath).files;
|
|
6154
|
+
const tsconfigFiles = extended.length > 0 ? extended : [tsconfigPath];
|
|
6155
|
+
const files = await discoverInputFiles(config);
|
|
6156
|
+
const contents = /* @__PURE__ */ new Map();
|
|
6157
|
+
const tsconfigs = await readAll(
|
|
6158
|
+
tsconfigFiles,
|
|
6159
|
+
(file) => readFile4(file, "utf8").catch(() => null)
|
|
6160
|
+
);
|
|
6161
|
+
for (const [file, text] of tsconfigs) contents.set(file, text);
|
|
6162
|
+
for (const [file, text] of await readAll(files, (file2) => readFile4(file2, "utf8"))) {
|
|
6163
|
+
contents.set(file, text);
|
|
6164
|
+
}
|
|
6165
|
+
return { tsconfigFiles, files, contents, tracked: /* @__PURE__ */ new Map() };
|
|
6166
|
+
}
|
|
6167
|
+
async function readInput(snapshot, path) {
|
|
6168
|
+
return snapshot.contents.get(path) ?? readFile4(path, "utf8");
|
|
6169
|
+
}
|
|
6170
|
+
async function computeInputsHash(config, extraInputs = [], snapshot) {
|
|
6171
|
+
const inputs = snapshot ?? await readInputs(config);
|
|
5889
6172
|
const hash = createHash("sha256");
|
|
5890
6173
|
hash.update(`version:${VERSION}
|
|
5891
6174
|
`);
|
|
5892
6175
|
hash.update(`config:${serializeConfig(config)}
|
|
5893
6176
|
`);
|
|
5894
6177
|
const cwd = config.codegen.cwd;
|
|
5895
|
-
const
|
|
5896
|
-
const tsconfigFiles = loadDiscoveryTsconfig(tsconfigPath).files;
|
|
5897
|
-
for (const file of tsconfigFiles.length > 0 ? tsconfigFiles : [tsconfigPath]) {
|
|
5898
|
-
const contents = await readFile4(file, "utf8").catch(() => null);
|
|
6178
|
+
for (const file of inputs.tsconfigFiles) {
|
|
5899
6179
|
hash.update(`tsconfig:${relative7(cwd, file)}
|
|
5900
6180
|
`);
|
|
5901
|
-
hash.update(contents ?? " missing");
|
|
6181
|
+
hash.update(inputs.contents.get(file) ?? " missing");
|
|
5902
6182
|
hash.update("\n");
|
|
5903
6183
|
}
|
|
5904
|
-
const
|
|
5905
|
-
const globbedRelative = new Set(globbed.map((file) => relative7(cwd, file)));
|
|
6184
|
+
const globbedRelative = new Set(inputs.files.map((file) => relative7(cwd, file)));
|
|
5906
6185
|
const extra = [...new Set(extraInputs)].filter((file) => !globbedRelative.has(file)).sort();
|
|
5907
|
-
for (const file of
|
|
5908
|
-
const contents = await readFile4(file, "utf8");
|
|
6186
|
+
for (const file of inputs.files) {
|
|
5909
6187
|
hash.update(`file:${relative7(cwd, file)}
|
|
5910
6188
|
`);
|
|
5911
|
-
hash.update(contents);
|
|
6189
|
+
hash.update(inputs.contents.get(file));
|
|
5912
6190
|
hash.update("\n");
|
|
5913
6191
|
}
|
|
6192
|
+
const unread = extra.map((file) => join16(cwd, file)).filter((abs) => !inputs.tracked.has(abs));
|
|
6193
|
+
for (const [abs, text] of await readAll(unread, readTrackedInput)) inputs.tracked.set(abs, text);
|
|
5914
6194
|
for (const file of extra) {
|
|
5915
6195
|
hash.update(`extra:${file}
|
|
5916
6196
|
`);
|
|
5917
|
-
hash.update(
|
|
6197
|
+
hash.update(inputs.tracked.get(join16(cwd, file)));
|
|
5918
6198
|
hash.update("\n");
|
|
5919
6199
|
}
|
|
5920
6200
|
return hash.digest("hex");
|