@blzsky/variant 0.2.0 → 0.3.0-canary.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.
Files changed (49) hide show
  1. package/README.md +241 -136
  2. package/dist/{build-2LO7GD2S.js → build-GKWNLU4Q.js} +11 -10
  3. package/dist/{check-ZTKGGF4M.js → check-KO5VY67Z.js} +9 -9
  4. package/dist/{chunk-4LLK2S4R.js → chunk-7NC7AO75.js} +19 -32
  5. package/dist/{chunk-RVXUCJYA.js → chunk-ACTY42RE.js} +27 -0
  6. package/dist/{chunk-SWPVR2DF.js → chunk-CN25SRKD.js} +9 -9
  7. package/dist/{chunk-LXKLCLL6.js → chunk-CWF6QXEW.js} +1 -1
  8. package/dist/{chunk-MZZOE7L4.js → chunk-DMDAJPFJ.js} +1 -1
  9. package/dist/{chunk-O4LZKXNZ.js → chunk-DSQDMFQ6.js} +59 -2
  10. package/dist/{chunk-V22IPJ7A.js → chunk-EISTWXDW.js} +5 -1
  11. package/dist/chunk-H5C4SE73.js +184 -0
  12. package/dist/chunk-IFFZ46YS.js +575 -0
  13. package/dist/chunk-IHXJ72XA.js +31 -0
  14. package/dist/chunk-KCEGA56S.js +670 -0
  15. package/dist/{chunk-LJDDS4RR.js → chunk-MAK3HG46.js} +54 -8
  16. package/dist/chunk-MT4BOAAV.js +167 -0
  17. package/dist/{chunk-VTJ67TT5.js → chunk-NFFXYYM6.js} +122 -27
  18. package/dist/{chunk-ISRF5F3Q.js → chunk-QRPI6TJE.js} +1 -1
  19. package/dist/{chunk-RGD422OM.js → chunk-RJBFCURK.js} +1 -1
  20. package/dist/{chunk-XUBWCTJZ.js → chunk-RSIOYQOQ.js} +85 -8
  21. package/dist/chunk-SGP5JX6I.js +377 -0
  22. package/dist/chunk-X3VVGTEV.js +268 -0
  23. package/dist/{chunk-NSNZOH5P.js → chunk-YII4TAKS.js} +10 -3
  24. package/dist/cli.js +76 -37
  25. package/dist/{diff-GFI6RAEA.js → diff-A3TQWWUJ.js} +9 -5
  26. package/dist/{doctor-GH4RZVEH.js → doctor-6ONS6LKY.js} +5 -5
  27. package/dist/{env-QORDD6FQ.js → env-XF2US3CB.js} +8 -8
  28. package/dist/git-W6N3WMYQ.js +24 -0
  29. package/dist/impact-DPZIAJR4.js +263 -0
  30. package/dist/index.js +2 -2
  31. package/dist/init-VNK5E4H3.js +777 -0
  32. package/dist/{insight-DZ5NQY6D.js → insight-LNYVMCAE.js} +9 -9
  33. package/dist/pr-6CYMM2XU.js +707 -0
  34. package/dist/{run-6PNH4QRA.js → run-44PKMOV5.js} +11 -10
  35. package/dist/{symbol-graph-YYUEMZGR.js → symbol-graph-OB4AJCLW.js} +3 -3
  36. package/dist/{tsconfig-paths-E3UEDGXZ.js → tsconfig-paths-SV3PULCW.js} +3 -1
  37. package/dist/vitest.cjs +629 -0
  38. package/dist/vitest.d.cts +181 -0
  39. package/dist/vitest.d.ts +181 -0
  40. package/dist/vitest.js +356 -0
  41. package/dist/workspace-PIQXG4ZD.js +65 -0
  42. package/package.json +15 -4
  43. package/dist/chunk-DRMASVYN.js +0 -25
  44. package/dist/chunk-KSHWC2XN.js +0 -78
  45. package/dist/chunk-XTYVVUWW.js +0 -279
  46. package/dist/impact-AF453PLK.js +0 -702
  47. package/dist/init-KJIUNGAN.js +0 -158
  48. package/dist/pr-UFZRZX6S.js +0 -152
  49. package/dist/workspace-X5FUBTY3.js +0 -186
@@ -0,0 +1,575 @@
1
+ import {
2
+ loadPackageGraph
3
+ } from "./chunk-3E5LBU3O.js";
4
+ import {
5
+ aliasesInScope,
6
+ exportsCandidates,
7
+ matchPathAlias,
8
+ readPathAliases
9
+ } from "./chunk-X3VVGTEV.js";
10
+ import {
11
+ updateSymbolGraph
12
+ } from "./chunk-NFFXYYM6.js";
13
+
14
+ // src/core/graph/workspace-audit.ts
15
+ import { readFile } from "fs/promises";
16
+ import path3 from "path";
17
+
18
+ // src/core/graph/workspace-check.ts
19
+ import { builtinModules } from "module";
20
+ import path2 from "path";
21
+
22
+ // src/core/graph/import-graph.ts
23
+ import path from "path";
24
+ var TS_EXTENSIONS = [".ts", ".tsx", ".mts", ".cts"];
25
+ var JS_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".mjs", ".cjs", ".jsx"]);
26
+ var JS_TO_TS = {
27
+ ".js": [".ts", ".tsx"],
28
+ ".mjs": [".mts"],
29
+ ".cjs": [".cts"],
30
+ ".jsx": [".tsx"]
31
+ };
32
+ function buildImportGraph(symbolGraph, options = {}) {
33
+ const files = new Set(Object.keys(symbolGraph.files));
34
+ const packageDirs = options.packageDirs ?? {};
35
+ const allAliases = options.pathAliases ?? [];
36
+ const packageExports = options.packageExports ?? {};
37
+ const imports = /* @__PURE__ */ new Map();
38
+ const dependents = /* @__PURE__ */ new Map();
39
+ const externals = /* @__PURE__ */ new Map();
40
+ const unresolved = /* @__PURE__ */ new Map();
41
+ const edges = /* @__PURE__ */ new Map();
42
+ const computed = /* @__PURE__ */ new Set();
43
+ const unresolvedTargets = /* @__PURE__ */ new Map();
44
+ const patternTargets = /* @__PURE__ */ new Map();
45
+ const sortedFiles = [...files].sort();
46
+ for (const file of sortedFiles) {
47
+ imports.set(file, /* @__PURE__ */ new Set());
48
+ dependents.set(file, /* @__PURE__ */ new Set());
49
+ externals.set(file, /* @__PURE__ */ new Set());
50
+ unresolved.set(file, /* @__PURE__ */ new Set());
51
+ edges.set(file, /* @__PURE__ */ new Map());
52
+ }
53
+ const addEdge = (from, to, imp) => {
54
+ imports.get(from)?.add(to);
55
+ const perFile = edges.get(from);
56
+ if (perFile === void 0) return;
57
+ let edge = perFile.get(to);
58
+ if (edge === void 0) {
59
+ edge = { names: /* @__PURE__ */ new Set(), typeOnly: true, dynamic: false };
60
+ perFile.set(to, edge);
61
+ }
62
+ for (const name of imp.names) edge.names.add(name);
63
+ if (imp.kind === "dynamic" || imp.kind === "pattern") edge.dynamic = true;
64
+ edge.typeOnly = edge.typeOnly && imp.typeOnly;
65
+ };
66
+ const addUnresolved = (from, spec, bases) => {
67
+ unresolved.get(from)?.add(spec);
68
+ for (const base of bases) {
69
+ for (const key of unresolvedKeys(base))
70
+ addTo(unresolvedTargets, key, from);
71
+ }
72
+ };
73
+ for (const file of sortedFiles) {
74
+ const index = symbolGraph.files[file];
75
+ if (index === void 0) continue;
76
+ const pathAliases = aliasesInScope(file, allAliases);
77
+ for (const imp of index.imports) {
78
+ const spec = imp.module;
79
+ if (imp.kind === "pattern") {
80
+ const bases = patternBases(file, spec, packageDirs, pathAliases);
81
+ if (bases === void 0) {
82
+ computed.add(file);
83
+ continue;
84
+ }
85
+ for (const base of bases) addTo(patternTargets, base, file);
86
+ for (const target of sortedFiles) {
87
+ if (target !== file && bases.some((b) => target.startsWith(b))) {
88
+ addEdge(file, target, imp);
89
+ }
90
+ }
91
+ continue;
92
+ }
93
+ if (isRelativeSpecifier(spec)) {
94
+ const resolved = resolveRelativeImport(file, spec, files);
95
+ if (resolved !== void 0) {
96
+ addEdge(file, resolved, imp);
97
+ continue;
98
+ }
99
+ addUnresolved(file, spec, [
100
+ path.posix.join(path.posix.dirname(file), spec)
101
+ ]);
102
+ continue;
103
+ }
104
+ if (spec.startsWith("node:")) {
105
+ externals.get(file)?.add(spec);
106
+ continue;
107
+ }
108
+ const pkg = matchWorkspacePackage(spec, packageDirs);
109
+ if (pkg !== void 0) {
110
+ const resolved = resolvePackageImport(
111
+ pkg.dir,
112
+ pkg.subpath,
113
+ files,
114
+ packageExports[pkg.name]
115
+ );
116
+ if (resolved !== void 0) {
117
+ addEdge(file, resolved, imp);
118
+ continue;
119
+ }
120
+ addUnresolved(
121
+ file,
122
+ spec,
123
+ pkg.subpath === "" ? [pkg.dir] : [
124
+ path.posix.join(pkg.dir, pkg.subpath),
125
+ path.posix.join(pkg.dir, "src", pkg.subpath)
126
+ ]
127
+ );
128
+ continue;
129
+ }
130
+ const aliased = resolveAliasImport(spec, pathAliases, files);
131
+ if (aliased !== void 0) {
132
+ addEdge(file, aliased, imp);
133
+ continue;
134
+ }
135
+ const aliasBases = matchPathAlias(spec, pathAliases);
136
+ if (aliasBases.length > 0) {
137
+ addUnresolved(file, spec, aliasBases);
138
+ continue;
139
+ }
140
+ if (spec.startsWith("#")) {
141
+ addUnresolved(file, spec, []);
142
+ continue;
143
+ }
144
+ externals.get(file)?.add(spec);
145
+ }
146
+ }
147
+ for (const [file, targets] of imports) {
148
+ for (const target of targets) {
149
+ dependents.get(target)?.add(file);
150
+ }
151
+ }
152
+ return {
153
+ imports,
154
+ dependents,
155
+ externals,
156
+ unresolved,
157
+ edges,
158
+ computed,
159
+ unresolvedTargets,
160
+ patternTargets
161
+ };
162
+ }
163
+ function importersOfUnindexed(graph, file) {
164
+ const target = toPosix(file);
165
+ const stem = stripSourceExtension(target);
166
+ const keys = [target, stem];
167
+ if (path.posix.basename(stem) === "index") {
168
+ keys.push(path.posix.dirname(stem));
169
+ }
170
+ const importers = /* @__PURE__ */ new Set();
171
+ for (const key of keys) {
172
+ for (const importer of graph.unresolvedTargets.get(key) ?? []) {
173
+ importers.add(importer);
174
+ }
175
+ }
176
+ for (const [prefix, loaders] of graph.patternTargets) {
177
+ if (!target.startsWith(prefix)) continue;
178
+ for (const loader of loaders) importers.add(loader);
179
+ }
180
+ return importers;
181
+ }
182
+ function unresolvedKeys(base) {
183
+ const ext = path.posix.extname(base);
184
+ return JS_EXTENSIONS.has(ext) ? [base, base.slice(0, -ext.length)] : [base];
185
+ }
186
+ function stripSourceExtension(file) {
187
+ const declaration = /\.d\.[cm]?ts$/.exec(file);
188
+ if (declaration !== null) return file.slice(0, declaration.index);
189
+ const ext = path.posix.extname(file);
190
+ return ext === "" ? file : file.slice(0, -ext.length);
191
+ }
192
+ function addTo(map, key, value) {
193
+ let set = map.get(key);
194
+ if (set === void 0) {
195
+ set = /* @__PURE__ */ new Set();
196
+ map.set(key, set);
197
+ }
198
+ set.add(value);
199
+ }
200
+ function computeAffectedFiles(changed, graph) {
201
+ const affected = /* @__PURE__ */ new Set();
202
+ for (const file of changed) affected.add(toPosix(file));
203
+ let frontier = new Set(affected);
204
+ while (frontier.size > 0) {
205
+ const next = /* @__PURE__ */ new Set();
206
+ for (const file of frontier) {
207
+ for (const dependent of graph.dependents.get(file) ?? []) {
208
+ if (!affected.has(dependent)) {
209
+ affected.add(dependent);
210
+ next.add(dependent);
211
+ }
212
+ }
213
+ }
214
+ frontier = next;
215
+ }
216
+ return affected;
217
+ }
218
+ function resolveRelativeImport(fromFile, spec, files) {
219
+ const base = path.posix.normalize(
220
+ path.posix.join(path.posix.dirname(fromFile), spec)
221
+ );
222
+ if (base === ".." || base.startsWith("../")) return void 0;
223
+ return firstExisting(candidatePaths(base === "." ? "index" : base), files);
224
+ }
225
+ function isRelativeSpecifier(spec) {
226
+ return spec === "." || spec === ".." || spec.startsWith("./") || spec.startsWith("../");
227
+ }
228
+ function patternBases(fromFile, prefix, packageDirs, aliases) {
229
+ if (prefix === "") return void 0;
230
+ if (prefix.startsWith(".")) {
231
+ const base = path.posix.join(path.posix.dirname(fromFile), prefix);
232
+ return [base === "." || base === "./" ? "" : base];
233
+ }
234
+ if (prefix.startsWith("node:")) return [];
235
+ const bases = [];
236
+ for (const [name, dir] of Object.entries(packageDirs)) {
237
+ if (prefix.startsWith(`${name}/`) || name.startsWith(prefix)) {
238
+ bases.push(`${toPosix(dir)}/`);
239
+ }
240
+ }
241
+ for (const alias of aliases) {
242
+ for (const target of alias.targets) {
243
+ const targetStem = alias.wildcard ? target.slice(0, target.indexOf("*")) : target;
244
+ if (alias.wildcard && prefix.startsWith(alias.prefix)) {
245
+ bases.push(targetStem + prefix.slice(alias.prefix.length));
246
+ } else if (alias.prefix.startsWith(prefix)) {
247
+ bases.push(targetStem);
248
+ }
249
+ }
250
+ }
251
+ if (bases.length > 0) return bases;
252
+ const segments = prefix.split("/");
253
+ const nameLength = prefix.startsWith("@") ? 2 : 1;
254
+ return segments.length > nameLength ? [] : void 0;
255
+ }
256
+ function resolvePackageImport(pkgDir, subpath, files, exportsField) {
257
+ const fromExports = exportsCandidates(exportsField, subpath);
258
+ const candidates = fromExports.flatMap(
259
+ (base) => candidatePaths(path.posix.join(pkgDir, base))
260
+ );
261
+ if (subpath === "") {
262
+ candidates.push(
263
+ ...candidatePaths(`${pkgDir}/src/index`),
264
+ ...candidatePaths(`${pkgDir}/index`)
265
+ );
266
+ } else {
267
+ candidates.push(
268
+ ...candidatePaths(path.posix.join(pkgDir, subpath)),
269
+ ...candidatePaths(path.posix.join(pkgDir, "src", subpath))
270
+ );
271
+ }
272
+ return firstExisting(candidates, files);
273
+ }
274
+ function resolveAliasImport(spec, aliases, files) {
275
+ for (const base of matchPathAlias(spec, aliases)) {
276
+ const resolved = firstExisting(candidatePaths(base), files);
277
+ if (resolved !== void 0) return resolved;
278
+ }
279
+ return void 0;
280
+ }
281
+ function matchWorkspacePackage(spec, packageDirs) {
282
+ const segments = spec.split("/");
283
+ const nameLength = spec.startsWith("@") ? 2 : 1;
284
+ if (segments.length < nameLength) return void 0;
285
+ const name = segments.slice(0, nameLength).join("/");
286
+ const dir = packageDirs[name];
287
+ if (dir === void 0) return void 0;
288
+ return {
289
+ name,
290
+ dir: toPosix(dir),
291
+ subpath: segments.slice(nameLength).join("/")
292
+ };
293
+ }
294
+ function candidatePaths(base) {
295
+ const ext = path.posix.extname(base);
296
+ if (TS_EXTENSIONS.includes(ext)) {
297
+ return [base];
298
+ }
299
+ const tsExts = JS_TO_TS[ext];
300
+ if (tsExts !== void 0) {
301
+ const stem = base.slice(0, -ext.length);
302
+ return [...tsExts.map((e) => stem + e), base];
303
+ }
304
+ const extensions = [...TS_EXTENSIONS, ...JS_EXTENSIONS];
305
+ return [
306
+ ...extensions.map((e) => base + e),
307
+ ...extensions.map((e) => `${base}/index${e}`)
308
+ ];
309
+ }
310
+ function firstExisting(candidates, files) {
311
+ for (const c of candidates) {
312
+ if (files.has(c)) return c;
313
+ }
314
+ return void 0;
315
+ }
316
+ function toPosix(p) {
317
+ return p.replace(/\\/g, "/");
318
+ }
319
+
320
+ // src/core/graph/workspace-check.ts
321
+ var BUILTINS = new Set(builtinModules);
322
+ function checkWorkspace(input) {
323
+ const { symbolGraph, packages } = input;
324
+ const rootDeclared = input.rootDeclared ?? /* @__PURE__ */ new Set();
325
+ const byName = new Map(packages.map((p) => [p.name, p]));
326
+ const files = new Set(Object.keys(symbolGraph.files));
327
+ const grouped = /* @__PURE__ */ new Map();
328
+ const addViolation = (kind, pkg, target, file) => {
329
+ const key = `${kind}|${pkg}|${target}`;
330
+ let violation = grouped.get(key);
331
+ if (violation === void 0) {
332
+ violation = { kind, package: pkg, target, files: [] };
333
+ grouped.set(key, violation);
334
+ }
335
+ if (!violation.files.includes(file)) violation.files.push(file);
336
+ };
337
+ for (const file of [...files].sort()) {
338
+ const owner = fileToPackage(file, packages);
339
+ if (owner === void 0) continue;
340
+ const aliases = aliasesInScope(file, input.pathAliases ?? []);
341
+ const nested = nestedManifests(file, owner.dir, input.manifests);
342
+ const declares = (name) => owner.declared.has(name) || nested.some((manifest) => name in allDependencies(manifest));
343
+ const isSelf = (name) => name === owner.name || nested.some((manifest) => manifest.name === name);
344
+ for (const imp of symbolGraph.files[file]?.imports ?? []) {
345
+ if (imp.kind === "pattern") continue;
346
+ const spec = imp.module;
347
+ if (isRelativeSpecifier(spec)) {
348
+ const target = resolveRelativeImport(file, spec, files);
349
+ if (target === void 0) continue;
350
+ const targetOwner = fileToPackage(target, packages);
351
+ if (targetOwner !== void 0 && targetOwner.name !== owner.name) {
352
+ addViolation(
353
+ "cross-package-relative-import",
354
+ owner.name,
355
+ targetOwner.name,
356
+ file
357
+ );
358
+ }
359
+ continue;
360
+ }
361
+ if (matchPathAlias(spec, aliases).length > 0) {
362
+ const target = resolveAliasImport(spec, aliases, files);
363
+ const targetOwner = target === void 0 ? void 0 : fileToPackage(target, packages);
364
+ if (targetOwner !== void 0 && targetOwner.name !== owner.name && !owner.declared.has(targetOwner.name)) {
365
+ addViolation(
366
+ "undeclared-workspace-dep",
367
+ owner.name,
368
+ targetOwner.name,
369
+ file
370
+ );
371
+ }
372
+ continue;
373
+ }
374
+ if (spec.startsWith("#")) continue;
375
+ const name = packageNameOf(stripNodePrefix(spec));
376
+ if (name === void 0 || BUILTINS.has(name)) continue;
377
+ if (spec.startsWith("node:") || isSelf(name)) continue;
378
+ if (byName.has(name)) {
379
+ if (!declares(name)) {
380
+ addViolation("undeclared-workspace-dep", owner.name, name, file);
381
+ }
382
+ continue;
383
+ }
384
+ if (declares(name) || rootDeclared.has(name)) continue;
385
+ const types = typesPackageOf(name);
386
+ if (imp.typeOnly && (declares(types) || rootDeclared.has(types))) {
387
+ continue;
388
+ }
389
+ addViolation("undeclared-external-dep", owner.name, name, file);
390
+ }
391
+ }
392
+ const violations = [...grouped.values()].map((v) => ({ ...v, files: [...v.files].sort() })).sort(
393
+ (a, b) => a.package.localeCompare(b.package) || a.target.localeCompare(b.target) || a.kind.localeCompare(b.kind)
394
+ );
395
+ return { packagesChecked: packages.length, violations };
396
+ }
397
+ function fileToPackage(file, packages) {
398
+ let best;
399
+ let bestLength = -1;
400
+ for (const pkg of packages) {
401
+ const prefix = pkg.dir === "" ? "" : `${pkg.dir}/`;
402
+ if (file.startsWith(prefix) && prefix.length > bestLength) {
403
+ best = pkg;
404
+ bestLength = prefix.length;
405
+ }
406
+ }
407
+ return best;
408
+ }
409
+ function nestedManifests(file, ownerDir, manifests) {
410
+ if (manifests === void 0) return [];
411
+ const found = [];
412
+ let dir = path2.posix.dirname(file);
413
+ while (dir !== ownerDir && dir !== "." && dir !== "/" && dir !== "") {
414
+ const manifest = manifests.get(dir);
415
+ if (manifest !== void 0) found.push(manifest);
416
+ dir = path2.posix.dirname(dir);
417
+ }
418
+ return found;
419
+ }
420
+ function typesPackageOf(name) {
421
+ return `@types/${name.startsWith("@") ? name.slice(1).replace("/", "__") : name}`;
422
+ }
423
+ function packageNameOf(spec) {
424
+ const segments = spec.split("/");
425
+ const nameLength = spec.startsWith("@") ? 2 : 1;
426
+ if (segments.length < nameLength) return void 0;
427
+ const name = segments.slice(0, nameLength).join("/");
428
+ return name === "" ? void 0 : name;
429
+ }
430
+ function stripNodePrefix(spec) {
431
+ return spec.startsWith("node:") ? spec.slice(5) : spec;
432
+ }
433
+ var LOCAL_PROTOCOL = /^(?:workspace|link|file|portal):/;
434
+ var MAX_NOTE_NAMES = 3;
435
+ function workspaceBlindSpots(input) {
436
+ const { packageNames, manifests } = input;
437
+ const root = manifests.get("") ?? {};
438
+ const rootDeclared = new Set(Object.keys(allDependencies(root)));
439
+ const notes = [];
440
+ const missingLocal = /* @__PURE__ */ new Set();
441
+ for (const [dir, manifest] of manifests) {
442
+ const member = dir === "" || manifest.name !== void 0 && packageNames.has(manifest.name);
443
+ if (!member) continue;
444
+ for (const [name, range] of Object.entries(allDependencies(manifest))) {
445
+ if (LOCAL_PROTOCOL.test(range) && !packageNames.has(name)) {
446
+ missingLocal.add(name);
447
+ }
448
+ }
449
+ }
450
+ if (missingLocal.size > 0) {
451
+ const one = missingLocal.size === 1;
452
+ notes.push(
453
+ `${missingLocal.size} ${one ? "dependency" : "dependencies"} declared with a local protocol ${one ? "is not a workspace package" : "are not workspace packages"} variant found (${listNames(missingLocal)}); imports of ${one ? "it" : "them"} count as external, so a change to ${one ? "it" : "them"} reaches no importer`
454
+ );
455
+ }
456
+ const undeclared = /* @__PURE__ */ new Set();
457
+ for (const [file, specs] of input.externals) {
458
+ const nearest = nearestManifest(file, manifests) ?? root;
459
+ const declared = allDependencies(nearest);
460
+ for (const spec of specs) {
461
+ if (spec.includes(":")) continue;
462
+ const name = packageNameOf(spec);
463
+ if (name === void 0 || BUILTINS.has(name)) continue;
464
+ if (name === nearest.name || packageNames.has(name)) continue;
465
+ if (name in declared || rootDeclared.has(name)) continue;
466
+ if (!missingLocal.has(name)) undeclared.add(name);
467
+ }
468
+ }
469
+ if (undeclared.size > 0) {
470
+ const one = undeclared.size === 1;
471
+ notes.push(
472
+ `${undeclared.size} bare import ${one ? "name is" : "names are"} neither workspace packages nor declared dependencies (${listNames(undeclared)}); if one is a workspace package variant did not find, or a tsconfig alias it did not read, a change behind it reaches no test`
473
+ );
474
+ }
475
+ const undiscovered = /* @__PURE__ */ new Map();
476
+ for (const [dir, manifest] of manifests) {
477
+ if (manifest.name !== void 0 && missingLocal.has(manifest.name)) {
478
+ undiscovered.set(manifest.name, dir);
479
+ }
480
+ }
481
+ return { notes, undiscovered };
482
+ }
483
+ function nearestManifest(file, manifests) {
484
+ let dir = path2.posix.dirname(file);
485
+ while (true) {
486
+ const manifest = manifests.get(dir === "." ? "" : dir);
487
+ if (manifest !== void 0) return manifest;
488
+ if (dir === "." || dir === "/" || dir === "") return void 0;
489
+ dir = path2.posix.dirname(dir);
490
+ }
491
+ }
492
+ function allDependencies(manifest) {
493
+ return {
494
+ ...manifest.dependencies ?? {},
495
+ ...manifest.devDependencies ?? {},
496
+ ...manifest.peerDependencies ?? {},
497
+ ...manifest.optionalDependencies ?? {}
498
+ };
499
+ }
500
+ function listNames(names) {
501
+ const sorted = [...names].sort();
502
+ return sorted.length > MAX_NOTE_NAMES ? `${sorted.slice(0, MAX_NOTE_NAMES).join(", ")}, \u2026 ${sorted.length - MAX_NOTE_NAMES} more` : sorted.join(", ");
503
+ }
504
+
505
+ // src/core/graph/workspace-audit.ts
506
+ function declaredNames(manifest) {
507
+ return /* @__PURE__ */ new Set([
508
+ ...Object.keys(manifest.dependencies ?? {}),
509
+ ...Object.keys(manifest.devDependencies ?? {}),
510
+ ...Object.keys(manifest.peerDependencies ?? {})
511
+ ]);
512
+ }
513
+ async function readRootDeclared(cwd) {
514
+ try {
515
+ const manifest = JSON.parse(
516
+ await readFile(path3.join(cwd, "package.json"), "utf8")
517
+ );
518
+ return declaredNames(manifest);
519
+ } catch {
520
+ return /* @__PURE__ */ new Set();
521
+ }
522
+ }
523
+ async function readManifests(cwd) {
524
+ const fg = (await import("fast-glob")).default;
525
+ const found = await fg("**/package.json", {
526
+ cwd,
527
+ onlyFiles: true,
528
+ ignore: ["**/node_modules/**", ".git/**", ".variant/**"]
529
+ });
530
+ const entries = await Promise.all(
531
+ found.map(async (rel) => {
532
+ try {
533
+ const manifest = JSON.parse(
534
+ await readFile(path3.join(cwd, rel), "utf8")
535
+ );
536
+ const dir = path3.posix.dirname(rel.replace(/\\/g, "/"));
537
+ return [dir === "." ? "" : dir, manifest];
538
+ } catch {
539
+ return void 0;
540
+ }
541
+ })
542
+ );
543
+ return new Map(entries.filter((entry) => entry !== void 0));
544
+ }
545
+ async function auditWorkspace(cwd) {
546
+ const packageGraph = await loadPackageGraph(cwd).catch(() => null);
547
+ if (packageGraph === null || packageGraph.packages.length === 0) return null;
548
+ const packages = packageGraph.packages.map((pkg) => ({
549
+ name: pkg.manifest.name,
550
+ dir: path3.relative(cwd, pkg.dir).replace(/\\/g, "/"),
551
+ declared: declaredNames(pkg.manifest)
552
+ }));
553
+ const [{ graph: symbolGraph }, rootDeclared, pathAliases, manifests] = await Promise.all([
554
+ updateSymbolGraph(cwd),
555
+ readRootDeclared(cwd),
556
+ readPathAliases(cwd),
557
+ readManifests(cwd)
558
+ ]);
559
+ return checkWorkspace({
560
+ symbolGraph,
561
+ packages,
562
+ rootDeclared,
563
+ pathAliases,
564
+ manifests
565
+ });
566
+ }
567
+
568
+ export {
569
+ buildImportGraph,
570
+ importersOfUnindexed,
571
+ computeAffectedFiles,
572
+ workspaceBlindSpots,
573
+ readManifests,
574
+ auditWorkspace
575
+ };
@@ -0,0 +1,31 @@
1
+ // src/cli/visuals/ansi.ts
2
+ var ESC = "\x1B";
3
+ var HIDE_CURSOR = `${ESC}[?25l`;
4
+ var SHOW_CURSOR = `${ESC}[?25h`;
5
+ var CLEAR_LINE = `${ESC}[2K`;
6
+ var SYNC_START = `${ESC}[?2026h`;
7
+ var SYNC_END = `${ESC}[?2026l`;
8
+ function cursorUp(lines) {
9
+ return lines > 0 ? `${ESC}[${lines}A` : "";
10
+ }
11
+ var STYLE_PATTERN = new RegExp(`${ESC}\\[[0-9;]*m`, "g");
12
+ function stripStyles(text) {
13
+ return text.replace(STYLE_PATTERN, "");
14
+ }
15
+ function visibleLength(line) {
16
+ return stripStyles(line).length;
17
+ }
18
+ function fitWidth(line, columns) {
19
+ if (visibleLength(line) <= columns) return line;
20
+ return stripStyles(line).slice(0, Math.max(0, columns - 1));
21
+ }
22
+
23
+ export {
24
+ HIDE_CURSOR,
25
+ SHOW_CURSOR,
26
+ CLEAR_LINE,
27
+ SYNC_START,
28
+ SYNC_END,
29
+ cursorUp,
30
+ fitWidth
31
+ };