@dep-radius/core 0.1.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 (157) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +48 -0
  3. package/dist/analyze/brief.d.ts +18 -0
  4. package/dist/analyze/brief.d.ts.map +1 -0
  5. package/dist/analyze/brief.js +89 -0
  6. package/dist/analyze/verdict.d.ts +16 -0
  7. package/dist/analyze/verdict.d.ts.map +1 -0
  8. package/dist/analyze/verdict.js +134 -0
  9. package/dist/context.d.ts +4 -0
  10. package/dist/context.d.ts.map +1 -0
  11. package/dist/context.js +36 -0
  12. package/dist/debug.d.ts +2 -0
  13. package/dist/debug.d.ts.map +1 -0
  14. package/dist/debug.js +130 -0
  15. package/dist/index.d.ts +9 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +8 -0
  18. package/dist/infra/cache.d.ts +18 -0
  19. package/dist/infra/cache.d.ts.map +1 -0
  20. package/dist/infra/cache.js +66 -0
  21. package/dist/infra/hash.d.ts +4 -0
  22. package/dist/infra/hash.d.ts.map +1 -0
  23. package/dist/infra/hash.js +18 -0
  24. package/dist/infra/http.d.ts +24 -0
  25. package/dist/infra/http.d.ts.map +1 -0
  26. package/dist/infra/http.js +44 -0
  27. package/dist/infra/limit.d.ts +4 -0
  28. package/dist/infra/limit.d.ts.map +1 -0
  29. package/dist/infra/limit.js +34 -0
  30. package/dist/infra/log.d.ts +8 -0
  31. package/dist/infra/log.d.ts.map +1 -0
  32. package/dist/infra/log.js +13 -0
  33. package/dist/inventory/installed.d.ts +15 -0
  34. package/dist/inventory/installed.d.ts.map +1 -0
  35. package/dist/inventory/installed.js +241 -0
  36. package/dist/inventory/lockfiles/bun.d.ts +3 -0
  37. package/dist/inventory/lockfiles/bun.d.ts.map +1 -0
  38. package/dist/inventory/lockfiles/bun.js +24 -0
  39. package/dist/inventory/lockfiles/npm.d.ts +3 -0
  40. package/dist/inventory/lockfiles/npm.d.ts.map +1 -0
  41. package/dist/inventory/lockfiles/npm.js +29 -0
  42. package/dist/inventory/lockfiles/pnpm.d.ts +3 -0
  43. package/dist/inventory/lockfiles/pnpm.d.ts.map +1 -0
  44. package/dist/inventory/lockfiles/pnpm.js +84 -0
  45. package/dist/inventory/lockfiles/types.d.ts +9 -0
  46. package/dist/inventory/lockfiles/types.d.ts.map +1 -0
  47. package/dist/inventory/lockfiles/types.js +1 -0
  48. package/dist/inventory/lockfiles/yarn.d.ts +3 -0
  49. package/dist/inventory/lockfiles/yarn.d.ts.map +1 -0
  50. package/dist/inventory/lockfiles/yarn.js +46 -0
  51. package/dist/inventory/manifests.d.ts +9 -0
  52. package/dist/inventory/manifests.d.ts.map +1 -0
  53. package/dist/inventory/manifests.js +135 -0
  54. package/dist/inventory/specifiers.d.ts +8 -0
  55. package/dist/inventory/specifiers.d.ts.map +1 -0
  56. package/dist/inventory/specifiers.js +53 -0
  57. package/dist/labels.d.ts +2 -0
  58. package/dist/labels.d.ts.map +1 -0
  59. package/dist/labels.js +18 -0
  60. package/dist/model.d.ts +276 -0
  61. package/dist/model.d.ts.map +1 -0
  62. package/dist/model.js +2 -0
  63. package/dist/notes/changelog.d.ts +11 -0
  64. package/dist/notes/changelog.d.ts.map +1 -0
  65. package/dist/notes/changelog.js +60 -0
  66. package/dist/notes/collect.d.ts +13 -0
  67. package/dist/notes/collect.d.ts.map +1 -0
  68. package/dist/notes/collect.js +225 -0
  69. package/dist/notes/entries.d.ts +3 -0
  70. package/dist/notes/entries.d.ts.map +1 -0
  71. package/dist/notes/entries.js +209 -0
  72. package/dist/notes/github.d.ts +25 -0
  73. package/dist/notes/github.d.ts.map +1 -0
  74. package/dist/notes/github.js +132 -0
  75. package/dist/notes/match.d.ts +13 -0
  76. package/dist/notes/match.d.ts.map +1 -0
  77. package/dist/notes/match.js +75 -0
  78. package/dist/notes/repo-url.d.ts +9 -0
  79. package/dist/notes/repo-url.d.ts.map +1 -0
  80. package/dist/notes/repo-url.js +46 -0
  81. package/dist/options.d.ts +53 -0
  82. package/dist/options.d.ts.map +1 -0
  83. package/dist/options.js +27 -0
  84. package/dist/registry/candidates.d.ts +24 -0
  85. package/dist/registry/candidates.d.ts.map +1 -0
  86. package/dist/registry/candidates.js +139 -0
  87. package/dist/registry/npmrc.d.ts +13 -0
  88. package/dist/registry/npmrc.d.ts.map +1 -0
  89. package/dist/registry/npmrc.js +99 -0
  90. package/dist/registry/packument.d.ts +34 -0
  91. package/dist/registry/packument.d.ts.map +1 -0
  92. package/dist/registry/packument.js +85 -0
  93. package/dist/registry/tar.d.ts +2 -0
  94. package/dist/registry/tar.d.ts.map +1 -0
  95. package/dist/registry/tar.js +75 -0
  96. package/dist/registry/tarball.d.ts +16 -0
  97. package/dist/registry/tarball.d.ts.map +1 -0
  98. package/dist/registry/tarball.js +59 -0
  99. package/dist/render/groups.d.ts +16 -0
  100. package/dist/render/groups.d.ts.map +1 -0
  101. package/dist/render/groups.js +105 -0
  102. package/dist/render/index.d.ts +2 -0
  103. package/dist/render/index.d.ts.map +1 -0
  104. package/dist/render/index.js +3 -0
  105. package/dist/render/json.d.ts +119 -0
  106. package/dist/render/json.d.ts.map +1 -0
  107. package/dist/render/json.js +130 -0
  108. package/dist/render/limits.d.ts +3 -0
  109. package/dist/render/limits.d.ts.map +1 -0
  110. package/dist/render/limits.js +25 -0
  111. package/dist/render/markdown.d.ts +3 -0
  112. package/dist/render/markdown.d.ts.map +1 -0
  113. package/dist/render/markdown.js +62 -0
  114. package/dist/run.d.ts +10 -0
  115. package/dist/run.d.ts.map +1 -0
  116. package/dist/run.js +218 -0
  117. package/dist/surface/analyze.d.ts +13 -0
  118. package/dist/surface/analyze.d.ts.map +1 -0
  119. package/dist/surface/analyze.js +166 -0
  120. package/dist/surface/delta.d.ts +5 -0
  121. package/dist/surface/delta.d.ts.map +1 -0
  122. package/dist/surface/delta.js +173 -0
  123. package/dist/surface/entries.d.ts +16 -0
  124. package/dist/surface/entries.d.ts.map +1 -0
  125. package/dist/surface/entries.js +187 -0
  126. package/dist/surface/extract.d.ts +15 -0
  127. package/dist/surface/extract.d.ts.map +1 -0
  128. package/dist/surface/extract.js +460 -0
  129. package/dist/surface/signature.d.ts +6 -0
  130. package/dist/surface/signature.d.ts.map +1 -0
  131. package/dist/surface/signature.js +229 -0
  132. package/dist/surface/vfs-host.d.ts +5 -0
  133. package/dist/surface/vfs-host.d.ts.map +1 -0
  134. package/dist/surface/vfs-host.js +54 -0
  135. package/dist/symbol-path.d.ts +28 -0
  136. package/dist/symbol-path.d.ts.map +1 -0
  137. package/dist/symbol-path.js +223 -0
  138. package/dist/usage/files.d.ts +18 -0
  139. package/dist/usage/files.d.ts.map +1 -0
  140. package/dist/usage/files.js +83 -0
  141. package/dist/usage/link.d.ts +23 -0
  142. package/dist/usage/link.d.ts.map +1 -0
  143. package/dist/usage/link.js +588 -0
  144. package/dist/usage/opaque.d.ts +7 -0
  145. package/dist/usage/opaque.d.ts.map +1 -0
  146. package/dist/usage/opaque.js +193 -0
  147. package/dist/usage/parse.d.ts +73 -0
  148. package/dist/usage/parse.d.ts.map +1 -0
  149. package/dist/usage/parse.js +608 -0
  150. package/dist/usage/resolve-local.d.ts +35 -0
  151. package/dist/usage/resolve-local.d.ts.map +1 -0
  152. package/dist/usage/resolve-local.js +340 -0
  153. package/dist/usage/scan.d.ts +4 -0
  154. package/dist/usage/scan.d.ts.map +1 -0
  155. package/dist/usage/scan.js +92 -0
  156. package/package.json +79 -0
  157. package/schema/brief-v1.schema.json +319 -0
@@ -0,0 +1,340 @@
1
+ import { join, posix } from "node:path";
2
+ import { builtinModules } from "node:module";
3
+ import { existsSync, readFileSync } from "node:fs";
4
+ import ts from "typescript";
5
+ import { relPath } from "../inventory/manifests.js";
6
+ import { packageNameOf, subpathOf } from "../inventory/specifiers.js";
7
+ import { projectBoundary, resolveInstalled } from "../inventory/installed.js";
8
+ const EXTS = [
9
+ ".ts",
10
+ ".tsx",
11
+ ".mts",
12
+ ".cts",
13
+ ".d.ts",
14
+ ".js",
15
+ ".jsx",
16
+ ".mjs",
17
+ ".cjs",
18
+ ".vue",
19
+ ".svelte",
20
+ ".astro",
21
+ ];
22
+ const BUILTINS = new Set(builtinModules);
23
+ export class LocalResolver {
24
+ fileSet;
25
+ boundary;
26
+ installCache = new Map();
27
+ configCache = new Map();
28
+ byName = new Map();
29
+ root;
30
+ inventory;
31
+ constructor(root, files, inventory) {
32
+ this.root = root;
33
+ this.inventory = inventory;
34
+ this.fileSet = new Set(files);
35
+ this.boundary = projectBoundary(root);
36
+ for (const d of inventory.installed) {
37
+ const list = this.byName.get(d.name) ?? [];
38
+ list.push(d);
39
+ this.byName.set(d.name, list);
40
+ for (const by of d.declaredBy) {
41
+ if (by.key !== d.name) {
42
+ const alias = this.byName.get(by.key) ?? [];
43
+ if (!alias.includes(d))
44
+ alias.push(d);
45
+ this.byName.set(by.key, alias);
46
+ }
47
+ }
48
+ }
49
+ }
50
+ async resolve(fromRel, specifier) {
51
+ if (specifier.startsWith("./") ||
52
+ specifier.startsWith("../") ||
53
+ specifier === "." ||
54
+ specifier === "..") {
55
+ const hit = this.tryFile(posix.join(posix.dirname(fromRel), specifier));
56
+ return hit ? { kind: "file", rel: hit } : { kind: "unresolved-local" };
57
+ }
58
+ if (specifier.startsWith("node:") ||
59
+ BUILTINS.has(specifier) ||
60
+ BUILTINS.has(specifier.split("/")[0])) {
61
+ return { kind: "builtin" };
62
+ }
63
+ const viaPaths = this.fromTsconfigPaths(fromRel, specifier);
64
+ if (viaPaths)
65
+ return { kind: "file", rel: viaPaths };
66
+ const pkg = packageNameOf(specifier);
67
+ if (!pkg) {
68
+ return looksLikeAlias(specifier)
69
+ ? { kind: "unresolved-local" }
70
+ : { kind: "unknown" };
71
+ }
72
+ const fromDir = join(this.root, posix.dirname(fromRel));
73
+ const key = `${fromDir}\0${pkg}`;
74
+ let p = this.installCache.get(key);
75
+ if (!p) {
76
+ p = resolveInstalled(fromDir, pkg, this.boundary);
77
+ this.installCache.set(key, p);
78
+ }
79
+ const found = await p;
80
+ if (found) {
81
+ if (found.local) {
82
+ const rel = this.workspaceEntry(found.realDir, subpathOf(specifier, pkg));
83
+ return rel ? { kind: "file", rel } : { kind: "unresolved-local" };
84
+ }
85
+ const dep = this.inventory.installed.find((d) => d.id === found.realDir);
86
+ return {
87
+ kind: "pkg",
88
+ installedId: found.realDir,
89
+ pkg: dep?.name ?? found.name,
90
+ entry: subpathOf(specifier, pkg),
91
+ };
92
+ }
93
+ // nothing on disk: attribute to the version the inventory read from a lockfile or a range
94
+ const candidates = this.byName.get(pkg);
95
+ if (candidates && candidates.length > 0) {
96
+ const dep = this.nearestDeclared(fromRel, candidates);
97
+ return {
98
+ kind: "pkg",
99
+ installedId: dep.id,
100
+ pkg: dep.name,
101
+ entry: subpathOf(specifier, pkg),
102
+ };
103
+ }
104
+ const ws = this.inventory.manifests.find((m) => m.name === pkg);
105
+ if (ws) {
106
+ const rel = this.workspaceEntry(ws.dir, subpathOf(specifier, pkg));
107
+ if (rel)
108
+ return { kind: "file", rel };
109
+ }
110
+ return looksLikeAlias(specifier)
111
+ ? { kind: "unresolved-local" }
112
+ : { kind: "unknown" };
113
+ }
114
+ nearestDeclared(fromRel, deps) {
115
+ let best = deps[0];
116
+ let bestLen = -1;
117
+ for (const d of deps) {
118
+ for (const by of d.declaredBy) {
119
+ const dir = posix.dirname(by.manifest);
120
+ const prefix = dir === "." ? "" : `${dir}/`;
121
+ if (fromRel.startsWith(prefix) && prefix.length > bestLen) {
122
+ best = d;
123
+ bestLen = prefix.length;
124
+ }
125
+ }
126
+ }
127
+ return best;
128
+ }
129
+ tryFile(rel) {
130
+ const norm = posix.normalize(rel).replace(/^\.\//, "");
131
+ if (this.fileSet.has(norm))
132
+ return norm;
133
+ for (const ext of EXTS)
134
+ if (this.fileSet.has(norm + ext))
135
+ return norm + ext;
136
+ // ESM TypeScript writes the emitted extension: ./a.js means ./a.ts
137
+ const swap = /\.(m|c)?jsx?$/.exec(norm);
138
+ if (swap) {
139
+ const base = norm.slice(0, -swap[0].length);
140
+ const m = swap[1] ?? "";
141
+ for (const ext of [`.${m}ts`, `.${m}tsx`, ".ts", ".tsx"])
142
+ if (this.fileSet.has(base + ext))
143
+ return base + ext;
144
+ }
145
+ for (const ext of EXTS)
146
+ if (this.fileSet.has(`${norm}/index${ext}`))
147
+ return `${norm}/index${ext}`;
148
+ return undefined;
149
+ }
150
+ workspaceEntry(absDir, subpath) {
151
+ const dirRel = relPath(this.root, absDir);
152
+ if (dirRel.startsWith(".."))
153
+ return undefined;
154
+ const pjPath = join(absDir, "package.json");
155
+ let pj = {};
156
+ try {
157
+ pj = JSON.parse(readFileSync(pjPath, "utf8"));
158
+ }
159
+ catch {
160
+ // folder without a manifest: fall back to index files
161
+ }
162
+ const base = dirRel === "." ? "" : `${dirRel}/`;
163
+ const targets = [];
164
+ if (pj.exports !== undefined) {
165
+ const t = exportsTarget(pj.exports, subpath);
166
+ if (t)
167
+ targets.push(t);
168
+ }
169
+ else if (subpath === ".") {
170
+ for (const f of [pj.types, pj.module, pj.main])
171
+ if (f)
172
+ targets.push(f);
173
+ targets.push("index");
174
+ }
175
+ else {
176
+ targets.push(subpath);
177
+ }
178
+ for (const t of targets) {
179
+ const hit = this.tryFile(base + t.replace(/^\.\//, ""));
180
+ if (hit)
181
+ return hit;
182
+ // a manifest pointing at dist/ that was never built: its sources are the next best answer
183
+ const src = t.replace(/^\.?\/?(dist|build|lib|out)\//, "src/");
184
+ const hit2 = src !== t ? this.tryFile(base + src.replace(/\.d\.ts$/, "")) : undefined;
185
+ if (hit2)
186
+ return hit2;
187
+ }
188
+ return undefined;
189
+ }
190
+ fromTsconfigPaths(fromRel, specifier) {
191
+ const cfg = this.pathsConfigFor(posix.dirname(fromRel));
192
+ if (!cfg)
193
+ return undefined;
194
+ for (const [pattern, subs] of cfg.paths) {
195
+ const star = pattern.indexOf("*");
196
+ let captured;
197
+ if (star < 0) {
198
+ if (pattern === specifier)
199
+ captured = "";
200
+ }
201
+ else {
202
+ const pre = pattern.slice(0, star);
203
+ const post = pattern.slice(star + 1);
204
+ if (specifier.startsWith(pre) &&
205
+ specifier.endsWith(post) &&
206
+ specifier.length >= pre.length + post.length) {
207
+ captured = specifier.slice(pre.length, specifier.length - post.length);
208
+ }
209
+ }
210
+ if (captured === undefined)
211
+ continue;
212
+ for (const sub of subs) {
213
+ const rel = posix.join(cfg.baseUrl ?? cfg.dir, sub.replace("*", captured));
214
+ const hit = this.tryFile(rel);
215
+ if (hit)
216
+ return hit;
217
+ }
218
+ }
219
+ if (cfg.baseUrl &&
220
+ !specifier.startsWith("@") &&
221
+ !packageNameOf(specifier)?.includes(".")) {
222
+ const hit = this.tryFile(posix.join(cfg.baseUrl, specifier));
223
+ if (hit && !this.byName.has(packageNameOf(specifier) ?? ""))
224
+ return hit;
225
+ }
226
+ return undefined;
227
+ }
228
+ pathsConfigFor(dirRel) {
229
+ if (this.configCache.has(dirRel))
230
+ return this.configCache.get(dirRel);
231
+ let result;
232
+ for (const name of ["tsconfig.json", "jsconfig.json"]) {
233
+ const rel = dirRel === "." ? name : `${dirRel}/${name}`;
234
+ if (existsSync(join(this.root, rel))) {
235
+ result = this.readPathsConfig(rel, 0);
236
+ break;
237
+ }
238
+ }
239
+ if (!result && dirRel !== "." && dirRel !== "") {
240
+ const parent = posix.dirname(dirRel);
241
+ result = this.pathsConfigFor(parent === dirRel ? "." : parent);
242
+ }
243
+ this.configCache.set(dirRel, result);
244
+ return result;
245
+ }
246
+ readPathsConfig(rel, depth) {
247
+ if (depth > 5)
248
+ return undefined;
249
+ let json;
250
+ try {
251
+ json =
252
+ ts.parseConfigFileTextToJson(rel, readFileSync(join(this.root, rel), "utf8")).config ?? {};
253
+ }
254
+ catch {
255
+ return undefined;
256
+ }
257
+ const dir = posix.dirname(rel);
258
+ let inherited;
259
+ for (const ext of [json.extends].flat()) {
260
+ if (typeof ext !== "string" || !ext.startsWith("."))
261
+ continue;
262
+ const target = posix.join(dir, ext.endsWith(".json") ? ext : `${ext}.json`);
263
+ inherited = this.readPathsConfig(target, depth + 1) ?? inherited;
264
+ }
265
+ const co = json.compilerOptions ?? {};
266
+ const baseUrl = co.baseUrl !== undefined
267
+ ? posix.join(dir, co.baseUrl)
268
+ : inherited?.baseUrl;
269
+ const paths = co.paths
270
+ ? Object.entries(co.paths).map(([k, v]) => [k, v])
271
+ : (inherited?.paths ?? []);
272
+ // paths without baseUrl resolve against the config that declares them
273
+ const pathsDir = co.paths ? dir : (inherited?.dir ?? dir);
274
+ if (!baseUrl && paths.length === 0)
275
+ return undefined;
276
+ return { dir: pathsDir, ...(baseUrl ? { baseUrl } : {}), paths };
277
+ }
278
+ }
279
+ function looksLikeAlias(specifier) {
280
+ return /^(@\/|~\/|#|\$)/.test(specifier) || specifier.startsWith("@/");
281
+ }
282
+ export function exportsTarget(exp, subpath) {
283
+ const conditions = [
284
+ "types",
285
+ "import",
286
+ "module",
287
+ "default",
288
+ "require",
289
+ "node",
290
+ "browser",
291
+ ];
292
+ const pick = (v, star) => {
293
+ if (typeof v === "string")
294
+ return star !== undefined ? v.replace(/\*/g, star) : v;
295
+ if (Array.isArray(v)) {
296
+ for (const x of v) {
297
+ const r = pick(x, star);
298
+ if (r)
299
+ return r;
300
+ }
301
+ return undefined;
302
+ }
303
+ if (v && typeof v === "object") {
304
+ const o = v;
305
+ for (const c of conditions)
306
+ if (c in o) {
307
+ const r = pick(o[c], star);
308
+ if (r)
309
+ return r;
310
+ }
311
+ for (const [k, x] of Object.entries(o))
312
+ if (!k.startsWith(".") && !conditions.includes(k)) {
313
+ const r = pick(x, star);
314
+ if (r)
315
+ return r;
316
+ }
317
+ }
318
+ return undefined;
319
+ };
320
+ if (typeof exp === "string" || Array.isArray(exp))
321
+ return subpath === "." ? pick(exp) : undefined;
322
+ if (!exp || typeof exp !== "object")
323
+ return undefined;
324
+ const map = exp;
325
+ const keys = Object.keys(map);
326
+ if (!keys.some((k) => k.startsWith(".")))
327
+ return subpath === "." ? pick(map) : undefined;
328
+ if (subpath in map)
329
+ return pick(map[subpath]);
330
+ for (const k of keys) {
331
+ const star = k.indexOf("*");
332
+ if (star < 0)
333
+ continue;
334
+ const pre = k.slice(0, star);
335
+ const post = k.slice(star + 1);
336
+ if (subpath.startsWith(pre) && subpath.endsWith(post))
337
+ return pick(map[k], subpath.slice(pre.length, subpath.length - post.length));
338
+ }
339
+ return undefined;
340
+ }
@@ -0,0 +1,4 @@
1
+ import type { Ctx } from "../options.ts";
2
+ import type { Inventory, UsageMap } from "../model.ts";
3
+ export declare function scanUsage(ctx: Ctx | undefined, root: string, files: string[], inventory: Inventory, onFile?: (done: number, total: number) => void): Promise<UsageMap>;
4
+ //# sourceMappingURL=scan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan.d.ts","sourceRoot":"","sources":["../../src/usage/scan.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAA;AAKxC,OAAO,KAAK,EAAE,SAAS,EAAoB,QAAQ,EAAE,MAAM,aAAa,CAAA;AAGxE,wBAAsB,SAAS,CAC7B,GAAG,EAAE,GAAG,GAAG,SAAS,EACpB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EAAE,EACf,SAAS,EAAE,SAAS,EACpB,MAAM,GAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAe,GACvD,OAAO,CAAC,QAAQ,CAAC,CAoFnB"}
@@ -0,0 +1,92 @@
1
+ import { sha1 } from "../infra/hash.js";
2
+ import { findOpaque } from "./opaque.js";
3
+ import { counted, link } from "./link.js";
4
+ import { LocalResolver } from "./resolve-local.js";
5
+ import { isScannable, readSource, skippedByLocation } from "./files.js";
6
+ import { parseSource, SCANNER_VERSION } from "./parse.js";
7
+ export async function scanUsage(ctx, root, files, inventory, onFile = () => { }) {
8
+ const facts = new Map();
9
+ const skipped = [];
10
+ const total = files.filter((f) => isScannable(f) && !skippedByLocation(f)).length;
11
+ let done = 0;
12
+ for (const rel of files) {
13
+ if (skippedByLocation(rel)) {
14
+ skipped.push({
15
+ file: rel,
16
+ line: 1,
17
+ col: 1,
18
+ typeOnly: false,
19
+ text: "generated folder",
20
+ });
21
+ continue;
22
+ }
23
+ if (!isScannable(rel))
24
+ continue;
25
+ if (++done % 25 === 0 || done === total)
26
+ onFile(done, total);
27
+ let src;
28
+ try {
29
+ src = await readSource(root, rel);
30
+ }
31
+ catch {
32
+ continue;
33
+ }
34
+ if ("reason" in src) {
35
+ skipped.push({
36
+ file: rel,
37
+ line: 1,
38
+ col: 1,
39
+ typeOnly: false,
40
+ text: src.reason,
41
+ });
42
+ continue;
43
+ }
44
+ const key = `facts/v${SCANNER_VERSION}/${sha1(rel + "\0" + src.blocks.map((b) => b.text).join("\0"))}.json`;
45
+ const cached = ctx ? await ctx.cache.getJson(key) : undefined;
46
+ if (cached) {
47
+ facts.set(rel, cached.value);
48
+ continue;
49
+ }
50
+ const f = parseSource(src);
51
+ facts.set(rel, f);
52
+ if (ctx)
53
+ await ctx.cache.setJson(key, f, ctx.now);
54
+ }
55
+ const resolver = new LocalResolver(root, files, inventory);
56
+ const linked = await link({
57
+ root,
58
+ facts,
59
+ resolver,
60
+ inventory,
61
+ globalSkipped: skipped,
62
+ });
63
+ const opaque = await findOpaque(root, files, facts, inventory, resolver);
64
+ const packages = linked.packages;
65
+ for (const [id, kinds] of opaque) {
66
+ const dep = inventory.installed.find((d) => d.id === id);
67
+ const existing = packages[id];
68
+ const merged = new Map();
69
+ for (const o of existing?.opaque ?? [])
70
+ merged.set(o.kind, [
71
+ ...(linked.acc.get(id)?.opaque.get(o.kind) ?? o.examples),
72
+ ]);
73
+ for (const [k, sites] of kinds) {
74
+ // a package named in a config string but also imported by name is visible (a lint rule naming
75
+ // a package is not that package configured by string); one known only by the string is not
76
+ if (k === "config-reference" && (existing?.refs.length ?? 0) > 0)
77
+ continue;
78
+ merged.set(k, [...(merged.get(k) ?? []), ...sites]);
79
+ }
80
+ packages[id] = {
81
+ installedId: id,
82
+ pkg: existing?.pkg ?? dep?.name ?? id,
83
+ refs: existing?.refs ?? [],
84
+ strongNames: existing?.strongNames ?? [],
85
+ weakNames: existing?.weakNames ?? [],
86
+ files: existing?.files ?? 0,
87
+ blindSpots: existing?.blindSpots ?? [],
88
+ opaque: counted(merged),
89
+ };
90
+ }
91
+ return { scannedFiles: linked.scannedFiles, packages, global: linked.global };
92
+ }
package/package.json ADDED
@@ -0,0 +1,79 @@
1
+ {
2
+ "name": "@dep-radius/core",
3
+ "version": "0.1.0",
4
+ "description": "The engine behind radius: which changes in a dependency update land on code you actually wrote.",
5
+ "license": "MIT",
6
+ "author": "prakticode",
7
+ "homepage": "https://github.com/prakticode/dep-radius#readme",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/prakticode/dep-radius.git",
11
+ "directory": "packages/core"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/prakticode/dep-radius/issues"
15
+ },
16
+ "keywords": [
17
+ "dependencies",
18
+ "upgrade",
19
+ "semver",
20
+ "changelog",
21
+ "release-notes",
22
+ "renovate",
23
+ "dependabot",
24
+ "breaking-changes",
25
+ "typescript",
26
+ "library",
27
+ "static-analysis"
28
+ ],
29
+ "type": "module",
30
+ "exports": {
31
+ ".": {
32
+ "types": "./dist/index.d.ts",
33
+ "default": "./dist/index.js"
34
+ },
35
+ "./render": {
36
+ "types": "./dist/render/index.d.ts",
37
+ "default": "./dist/render/index.js"
38
+ },
39
+ "./debug": {
40
+ "types": "./dist/debug.d.ts",
41
+ "default": "./dist/debug.js"
42
+ },
43
+ "./schema/brief-v1.schema.json": "./schema/brief-v1.schema.json",
44
+ "./package.json": "./package.json"
45
+ },
46
+ "files": [
47
+ "dist",
48
+ "schema"
49
+ ],
50
+ "sideEffects": false,
51
+ "engines": {
52
+ "node": ">=22.12"
53
+ },
54
+ "publishConfig": {
55
+ "access": "public"
56
+ },
57
+ "dependencies": {
58
+ "semver": "^7.8.0",
59
+ "typescript": "~6.0.3"
60
+ },
61
+ "devDependencies": {
62
+ "@repo/eslint-config": "0.0.0",
63
+ "@repo/typescript-config": "0.0.0",
64
+ "@types/node": "^24.0.0",
65
+ "@types/semver": "^7.7.0",
66
+ "eslint": "^9.39.5",
67
+ "vitest": "^5.0.0"
68
+ },
69
+ "scripts": {
70
+ "build": "tsc -p tsconfig.build.json",
71
+ "typecheck": "tsc --noEmit",
72
+ "lint": "eslint --max-warnings 0",
73
+ "lint:fix": "eslint --fix",
74
+ "test": "vitest run",
75
+ "test:watch": "vitest",
76
+ "backtest": "node scripts/backtest.ts",
77
+ "clean": "rm -rf dist .turbo"
78
+ }
79
+ }