@component-compass/cli 0.1.23 → 0.1.24

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 (223) hide show
  1. package/dist/cli.js +103503 -104
  2. package/package.json +21 -25
  3. package/dist/auth/browser.d.ts +0 -13
  4. package/dist/auth/browser.js +0 -21
  5. package/dist/auth/browser.js.map +0 -1
  6. package/dist/auth/client.d.ts +0 -48
  7. package/dist/auth/client.js +0 -89
  8. package/dist/auth/client.js.map +0 -1
  9. package/dist/auth/paths.d.ts +0 -2
  10. package/dist/auth/paths.js +0 -14
  11. package/dist/auth/paths.js.map +0 -1
  12. package/dist/auth/session.d.ts +0 -38
  13. package/dist/auth/session.js +0 -88
  14. package/dist/auth/session.js.map +0 -1
  15. package/dist/auth/store.d.ts +0 -16
  16. package/dist/auth/store.js +0 -37
  17. package/dist/auth/store.js.map +0 -1
  18. package/dist/auth/upload-auth.d.ts +0 -22
  19. package/dist/auth/upload-auth.js +0 -51
  20. package/dist/auth/upload-auth.js.map +0 -1
  21. package/dist/cli/help.d.ts +0 -4
  22. package/dist/cli/help.js +0 -52
  23. package/dist/cli/help.js.map +0 -1
  24. package/dist/cli/parse.d.ts +0 -14
  25. package/dist/cli/parse.js +0 -75
  26. package/dist/cli/parse.js.map +0 -1
  27. package/dist/cli/suggest.d.ts +0 -4
  28. package/dist/cli/suggest.js +0 -38
  29. package/dist/cli/suggest.js.map +0 -1
  30. package/dist/cli.d.ts +0 -2
  31. package/dist/cli.js.map +0 -1
  32. package/dist/commands/auth.d.ts +0 -33
  33. package/dist/commands/auth.js +0 -248
  34. package/dist/commands/auth.js.map +0 -1
  35. package/dist/commands/init.d.ts +0 -11
  36. package/dist/commands/init.js +0 -112
  37. package/dist/commands/init.js.map +0 -1
  38. package/dist/commands/scan.d.ts +0 -33
  39. package/dist/commands/scan.js +0 -691
  40. package/dist/commands/scan.js.map +0 -1
  41. package/dist/composition-rollup.d.ts +0 -23
  42. package/dist/composition-rollup.js +0 -64
  43. package/dist/composition-rollup.js.map +0 -1
  44. package/dist/config/loader.d.ts +0 -6
  45. package/dist/config/loader.js +0 -71
  46. package/dist/config/loader.js.map +0 -1
  47. package/dist/config/schema.d.ts +0 -18
  48. package/dist/config/schema.js +0 -15
  49. package/dist/config/schema.js.map +0 -1
  50. package/dist/dispatcher.d.ts +0 -8
  51. package/dist/dispatcher.js +0 -22
  52. package/dist/dispatcher.js.map +0 -1
  53. package/dist/envelope/git.d.ts +0 -13
  54. package/dist/envelope/git.js +0 -69
  55. package/dist/envelope/git.js.map +0 -1
  56. package/dist/envelope/index.d.ts +0 -1
  57. package/dist/envelope/index.js +0 -12
  58. package/dist/envelope/index.js.map +0 -1
  59. package/dist/envelope/repo-id.d.ts +0 -16
  60. package/dist/envelope/repo-id.js +0 -40
  61. package/dist/envelope/repo-id.js.map +0 -1
  62. package/dist/identity.d.ts +0 -56
  63. package/dist/identity.js +0 -115
  64. package/dist/identity.js.map +0 -1
  65. package/dist/index.d.ts +0 -3
  66. package/dist/index.js +0 -3
  67. package/dist/index.js.map +0 -1
  68. package/dist/local-index/declared-props.d.ts +0 -15
  69. package/dist/local-index/declared-props.js +0 -101
  70. package/dist/local-index/declared-props.js.map +0 -1
  71. package/dist/local-index/detect-react.d.ts +0 -3
  72. package/dist/local-index/detect-react.js +0 -378
  73. package/dist/local-index/detect-react.js.map +0 -1
  74. package/dist/local-index/detect-vue.d.ts +0 -16
  75. package/dist/local-index/detect-vue.js +0 -284
  76. package/dist/local-index/detect-vue.js.map +0 -1
  77. package/dist/local-index/detect-wc.d.ts +0 -8
  78. package/dist/local-index/detect-wc.js +0 -70
  79. package/dist/local-index/detect-wc.js.map +0 -1
  80. package/dist/local-index/index.d.ts +0 -14
  81. package/dist/local-index/index.js +0 -46
  82. package/dist/local-index/index.js.map +0 -1
  83. package/dist/local-index/types.d.ts +0 -2
  84. package/dist/local-index/types.js +0 -2
  85. package/dist/local-index/types.js.map +0 -1
  86. package/dist/manifest/barrel-parser.d.ts +0 -35
  87. package/dist/manifest/barrel-parser.js +0 -76
  88. package/dist/manifest/barrel-parser.js.map +0 -1
  89. package/dist/manifest/lazy-resolver.d.ts +0 -123
  90. package/dist/manifest/lazy-resolver.js +0 -712
  91. package/dist/manifest/lazy-resolver.js.map +0 -1
  92. package/dist/occurrences.d.ts +0 -15
  93. package/dist/occurrences.js +0 -101
  94. package/dist/occurrences.js.map +0 -1
  95. package/dist/parse-by-ext.d.ts +0 -21
  96. package/dist/parse-by-ext.js +0 -53
  97. package/dist/parse-by-ext.js.map +0 -1
  98. package/dist/prompts/adapter.d.ts +0 -43
  99. package/dist/prompts/adapter.js +0 -32
  100. package/dist/prompts/adapter.js.map +0 -1
  101. package/dist/reporter/diagnostic-lines.d.ts +0 -7
  102. package/dist/reporter/diagnostic-lines.js +0 -40
  103. package/dist/reporter/diagnostic-lines.js.map +0 -1
  104. package/dist/reporter/index.d.ts +0 -39
  105. package/dist/reporter/index.js +0 -144
  106. package/dist/reporter/index.js.map +0 -1
  107. package/dist/reporter/json.d.ts +0 -6
  108. package/dist/reporter/json.js +0 -11
  109. package/dist/reporter/json.js.map +0 -1
  110. package/dist/reporter/stdout.d.ts +0 -3
  111. package/dist/reporter/stdout.js +0 -41
  112. package/dist/reporter/stdout.js.map +0 -1
  113. package/dist/rollup.d.ts +0 -26
  114. package/dist/rollup.js +0 -135
  115. package/dist/rollup.js.map +0 -1
  116. package/dist/scan/cem-index.d.ts +0 -38
  117. package/dist/scan/cem-index.js +0 -139
  118. package/dist/scan/cem-index.js.map +0 -1
  119. package/dist/scan/entry-packages.d.ts +0 -42
  120. package/dist/scan/entry-packages.js +0 -87
  121. package/dist/scan/entry-packages.js.map +0 -1
  122. package/dist/scan/framework-root.d.ts +0 -1
  123. package/dist/scan/framework-root.js +0 -22
  124. package/dist/scan/framework-root.js.map +0 -1
  125. package/dist/scan/git-mtime.d.ts +0 -2
  126. package/dist/scan/git-mtime.js +0 -22
  127. package/dist/scan/git-mtime.js.map +0 -1
  128. package/dist/scan/global-components.d.ts +0 -37
  129. package/dist/scan/global-components.js +0 -228
  130. package/dist/scan/global-components.js.map +0 -1
  131. package/dist/scan/meta.d.ts +0 -5
  132. package/dist/scan/meta.js +0 -58
  133. package/dist/scan/meta.js.map +0 -1
  134. package/dist/scan/stamp-version.d.ts +0 -18
  135. package/dist/scan/stamp-version.js +0 -94
  136. package/dist/scan/stamp-version.js.map +0 -1
  137. package/dist/scan/stamp-wraps.d.ts +0 -11
  138. package/dist/scan/stamp-wraps.js +0 -24
  139. package/dist/scan/stamp-wraps.js.map +0 -1
  140. package/dist/scan/wrap-relationships.d.ts +0 -12
  141. package/dist/scan/wrap-relationships.js +0 -38
  142. package/dist/scan/wrap-relationships.js.map +0 -1
  143. package/dist/seeds.d.ts +0 -81
  144. package/dist/seeds.js +0 -258
  145. package/dist/seeds.js.map +0 -1
  146. package/dist/types/brand.d.ts +0 -11
  147. package/dist/types/brand.js +0 -5
  148. package/dist/types/brand.js.map +0 -1
  149. package/dist/types/component-id.d.ts +0 -2
  150. package/dist/types/component-id.js +0 -2
  151. package/dist/types/component-id.js.map +0 -1
  152. package/dist/types.d.ts +0 -13
  153. package/dist/types.js +0 -2
  154. package/dist/types.js.map +0 -1
  155. package/dist/upload.d.ts +0 -18
  156. package/dist/upload.js +0 -34
  157. package/dist/upload.js.map +0 -1
  158. package/dist/util/color.d.ts +0 -24
  159. package/dist/util/color.js +0 -45
  160. package/dist/util/color.js.map +0 -1
  161. package/dist/util/git.d.ts +0 -10
  162. package/dist/util/git.js +0 -95
  163. package/dist/util/git.js.map +0 -1
  164. package/dist/util/interactive.d.ts +0 -17
  165. package/dist/util/interactive.js +0 -19
  166. package/dist/util/interactive.js.map +0 -1
  167. package/dist/util/log.d.ts +0 -9
  168. package/dist/util/log.js +0 -20
  169. package/dist/util/log.js.map +0 -1
  170. package/dist/util/pkg.d.ts +0 -1
  171. package/dist/util/pkg.js +0 -13
  172. package/dist/util/pkg.js.map +0 -1
  173. package/dist/util/pnp-check.d.ts +0 -1
  174. package/dist/util/pnp-check.js +0 -6
  175. package/dist/util/pnp-check.js.map +0 -1
  176. package/dist/util/posix.d.ts +0 -2
  177. package/dist/util/posix.js +0 -15
  178. package/dist/util/posix.js.map +0 -1
  179. package/dist/util/progress.d.ts +0 -21
  180. package/dist/util/progress.js +0 -54
  181. package/dist/util/progress.js.map +0 -1
  182. package/dist/util/version.d.ts +0 -7
  183. package/dist/util/version.js +0 -13
  184. package/dist/util/version.js.map +0 -1
  185. package/dist/walker/bounded-definition-resolver.d.ts +0 -9
  186. package/dist/walker/bounded-definition-resolver.js +0 -125
  187. package/dist/walker/bounded-definition-resolver.js.map +0 -1
  188. package/dist/walker/files.d.ts +0 -7
  189. package/dist/walker/files.js +0 -24
  190. package/dist/walker/files.js.map +0 -1
  191. package/dist/walker/package-alias-layers.d.ts +0 -7
  192. package/dist/walker/package-alias-layers.js +0 -40
  193. package/dist/walker/package-alias-layers.js.map +0 -1
  194. package/dist/walker/resolve-import.d.ts +0 -25
  195. package/dist/walker/resolve-import.js +0 -243
  196. package/dist/walker/resolve-import.js.map +0 -1
  197. package/dist/walker/tsconfig-discovery.d.ts +0 -36
  198. package/dist/walker/tsconfig-discovery.js +0 -52
  199. package/dist/walker/tsconfig-discovery.js.map +0 -1
  200. package/dist/walker/tsconfig-loader.d.ts +0 -35
  201. package/dist/walker/tsconfig-loader.js +0 -138
  202. package/dist/walker/tsconfig-loader.js.map +0 -1
  203. package/dist/walker/workspace-exports.d.ts +0 -3
  204. package/dist/walker/workspace-exports.js +0 -120
  205. package/dist/walker/workspace-exports.js.map +0 -1
  206. package/dist/workspace/build-graph.d.ts +0 -15
  207. package/dist/workspace/build-graph.js +0 -120
  208. package/dist/workspace/build-graph.js.map +0 -1
  209. package/dist/workspace/declared-deps.d.ts +0 -5
  210. package/dist/workspace/declared-deps.js +0 -42
  211. package/dist/workspace/declared-deps.js.map +0 -1
  212. package/dist/workspace/find-owning-package.d.ts +0 -8
  213. package/dist/workspace/find-owning-package.js +0 -69
  214. package/dist/workspace/find-owning-package.js.map +0 -1
  215. package/dist/workspace/find-workspace-root.d.ts +0 -1
  216. package/dist/workspace/find-workspace-root.js +0 -60
  217. package/dist/workspace/find-workspace-root.js.map +0 -1
  218. package/dist/workspace/index.d.ts +0 -4
  219. package/dist/workspace/index.js +0 -4
  220. package/dist/workspace/index.js.map +0 -1
  221. package/dist/workspace/types.d.ts +0 -49
  222. package/dist/workspace/types.js +0 -13
  223. package/dist/workspace/types.js.map +0 -1
@@ -1,691 +0,0 @@
1
- import { performance } from "node:perf_hooks";
2
- import { isAbsolute, relative, resolve } from "node:path";
3
- import { readFile } from "node:fs/promises";
4
- import { loadConfig, ConfigError } from "../config/loader.js";
5
- import { createLazyResolver } from "../manifest/lazy-resolver.js";
6
- import { buildLocalIndex } from "../local-index/index.js";
7
- import { detectReactComponents } from "../local-index/detect-react.js";
8
- import { detectVueComponents } from "../local-index/detect-vue.js";
9
- import { detectWebComponents } from "../local-index/detect-wc.js";
10
- import { parseByExt } from "../parse-by-ext.js";
11
- import { createProgress } from "../util/progress.js";
12
- import { walkFiles } from "../walker/files.js";
13
- import { createImportResolver } from "../walker/resolve-import.js";
14
- import { resolveTsconfigPath } from "../walker/tsconfig-discovery.js";
15
- import { buildPackageAliasLayers } from "../walker/package-alias-layers.js";
16
- import { Dispatcher } from "../dispatcher.js";
17
- import { reactPlugin, emitReact } from "@component-compass/parser-react";
18
- import { htmlPlugin } from "@component-compass/parser-html";
19
- import { litTemplatePlugin } from "@component-compass/parser-lit";
20
- import { emitVueTemplate } from "@component-compass/parser-vue";
21
- import { writeJson } from "../reporter/json.js";
22
- import { printSummary } from "../reporter/stdout.js";
23
- import { stampMeta } from "../scan/meta.js";
24
- import { stampGitMTime } from "../scan/git-mtime.js";
25
- import { stampVersion } from "../scan/stamp-version.js";
26
- import { applyWrapRelationships } from "../scan/wrap-relationships.js";
27
- import { stampWraps } from "../scan/stamp-wraps.js";
28
- import { applyEntryPackages, createEntryPackageRecorder } from "../scan/entry-packages.js";
29
- import { buildCemIndex } from "../scan/cem-index.js";
30
- import { isPnpProject } from "../util/pnp-check.js";
31
- import { readGitToplevel } from "../util/git.js";
32
- import { findWorkspaceRoot } from "../workspace/find-workspace-root.js";
33
- import { Logger } from "../util/log.js";
34
- import { errorMessage } from "@component-compass/ast-utils";
35
- import { createGraphBuilder, resolve as resolveGraph, } from "@component-compass/reference-graph";
36
- import { attachManifestEnrichment, buildComponentSeeds } from "../seeds.js";
37
- import { parseResultsToOccurrences } from "../occurrences.js";
38
- import { computeStableIdFromComponentId, computeOutputIdentity } from "../identity.js";
39
- import { buildScanOutput } from "../reporter/index.js";
40
- import { diagnosticLogLines, formatWarning } from "../reporter/diagnostic-lines.js";
41
- import { buildWorkspaceGraph, declaredDeps, findOwningPackage, resetFindOwningPackageCache, } from "../workspace/index.js";
42
- import { createBoundedDefinitionResolver } from "../walker/bounded-definition-resolver.js";
43
- import { loadGlobalComponents, detectAutoImportFramework } from "../scan/global-components.js";
44
- import { createDiagnosticCollector, computeOccurrenceId, externalPackageOf, posixPath } from "@component-compass/plugin-core";
45
- /** Map a scan result to a process exit code: 2 = scan failed, 1 = requested upload failed, 0 = ok. */
46
- export function scanExitCode(result) {
47
- if (result.output === null)
48
- return 2;
49
- if (result.upload === "failed")
50
- return 1;
51
- return 0;
52
- }
53
- export async function runScan(opts) {
54
- const log = new Logger(opts.quiet);
55
- const t0 = performance.now();
56
- const configPath = opts.configPath ??
57
- resolve(opts.cwd ?? process.cwd(), "component-compass.config.json");
58
- let cfg;
59
- try {
60
- cfg = await loadConfig(configPath);
61
- }
62
- catch (err) {
63
- if (err instanceof ConfigError) {
64
- log.error(err.message);
65
- return { output: null, upload: "skipped" };
66
- }
67
- throw err;
68
- }
69
- if (isPnpProject(cfg.configDir)) {
70
- log.error("yarn PnP detected; component-compass requires nodeLinker: node-modules.");
71
- return { output: null, upload: "skipped" };
72
- }
73
- // Stamp meta early — throws if not a git work tree, which is a hard
74
- // prerequisite for a stable repo identity.
75
- const metaOpts = { cwd: cfg.configDir };
76
- const repoIdOverride = opts.repoId ?? cfg.repoId;
77
- if (repoIdOverride !== undefined)
78
- metaOpts.repoIdOverride = repoIdOverride;
79
- const meta = await stampMeta(metaOpts);
80
- // Output path base. An explicit `repoRoot` override wins;
81
- // otherwise the git toplevel when the scan dir sits inside a work tree; else
82
- // configDir (non-git or pathological cases).
83
- // Guard: outputRoot must contain configDir, else re-basing would produce
84
- // `..`-prefixed paths for every file.
85
- const gitToplevel = await readGitToplevel(cfg.configDir);
86
- const outputRoot = opts.repoRoot
87
- ? (isAbsolute(opts.repoRoot)
88
- ? opts.repoRoot
89
- : resolve(opts.cwd ?? process.cwd(), opts.repoRoot))
90
- : (gitToplevel && !relative(gitToplevel, cfg.configDir).startsWith("..")
91
- ? gitToplevel
92
- : cfg.configDir);
93
- // Re-base a scanRoot-relative POSIX path into outputRoot-relative POSIX.
94
- // Identity when the roots coincide.
95
- const rebase = (scanRel) => outputRoot === cfg.configDir
96
- ? scanRel
97
- : posixPath(relative(outputRoot, resolve(cfg.configDir, scanRel)));
98
- if (opts.outputOverride)
99
- cfg.output = opts.outputOverride;
100
- const writer = opts.quiet ? () => { } : (s) => process.stderr.write(s);
101
- // Ownership/resolution anchor. Walks up from configDir to the nearest
102
- // enclosing workspace root (bounded by outputRoot), so scanning a monorepo
103
- // subfolder still resolves against the real hoisted node_modules / tsconfig
104
- // instead of a partial view rooted at the scan dir. Falls back to configDir
105
- // when no enclosing workspace claims it (the common case — existing
106
- // fixtures all scan from their own root).
107
- const workspaceRoot = findWorkspaceRoot(cfg.configDir, outputRoot) ?? cfg.configDir;
108
- if (workspaceRoot !== cfg.configDir) {
109
- writer(`[scan] workspace root: ${workspaceRoot}\n`);
110
- }
111
- // Reset per-scan caches so in-process invocations (tests, watch mode) start
112
- // clean. Memoized realpath results from a prior scan would still be valid,
113
- // but explicit reset avoids state leakage if the filesystem changed.
114
- resetFindOwningPackageCache();
115
- const workspaceGraph = buildWorkspaceGraph(workspaceRoot);
116
- const resolveImportOpts = {
117
- repoRoot: workspaceRoot,
118
- workspaceGraph,
119
- onWarning: (msg) => writer(`[scan] tsconfig: ${msg}\n`),
120
- };
121
- if (cfg.aliases)
122
- resolveImportOpts.aliases = cfg.aliases;
123
- resolveImportOpts.packageAliasLayers = buildPackageAliasLayers(workspaceGraph, (msg) => writer(`[scan] tsconfig(workspace): ${msg}\n`));
124
- const tsconfigPath = resolveTsconfigPath({
125
- configDir: cfg.configDir,
126
- repoRoot: workspaceRoot,
127
- ...(cfg.tsconfigPath !== undefined ? { tsconfigPath: cfg.tsconfigPath } : {}),
128
- });
129
- if (tsconfigPath)
130
- resolveImportOpts.tsconfigPath = tsconfigPath;
131
- const resolveImport = createImportResolver(resolveImportOpts);
132
- const collector = createDiagnosticCollector();
133
- const graphKeyCollector = {
134
- emit: (d) => collector.emit({ ...d, filePath: rebase(d.filePath) }),
135
- drain: collector.drain,
136
- };
137
- // Build global CEM index once per scan. Enumerates every package under
138
- // <root>/node_modules, finds CEM/CCM files, and builds byTag + byPackageName
139
- // maps so parsers can resolve hyphenated tags without consumer-side config.
140
- // Parsers consume cemIndex.
141
- const cemIndex = await buildCemIndex(workspaceRoot);
142
- // Identity resolution (leafPackage + modulePath) runs unconditionally for
143
- // external imports. `manifestPatterns` is the per-package gate for manifest
144
- // IO only. `["**"]` opts every package in — picomatch's `*` won't cross `/`
145
- // so it misses scoped names, but `**` does. The workspace gate inside
146
- // extractDerived (realpathSync check for node_modules in path) decides which
147
- // packages actually get extractor enrichment; published packages bail
148
- // immediately, so there is no meaningful performance cost for the wider gate.
149
- const manifestPatterns = ["**"];
150
- // Entry-package provenance capture. Fed by the resolver's walk for
151
- // both lanes (JS relabel via lookupExternalLeaf, side-effect priming via
152
- // resolveLazyManifest); drained into applyEntryPackages post-resolve.
153
- const entryPackageRecorder = createEntryPackageRecorder();
154
- const lazyResolver = createLazyResolver({
155
- resolveImport,
156
- repoRoot: workspaceRoot,
157
- collector,
158
- manifestPatterns,
159
- onEntryPackage: entryPackageRecorder.record,
160
- });
161
- // Warm-up: load each declared dep's root CEM/CCM (if any) so tag-only
162
- // consumers (HTML files, Vue templates with no JS-side import for the tag)
163
- // can resolve external custom elements via lookupByTag. Cheap (one
164
- // package.json + one manifest read per dep) and matches the lazy
165
- // resolver's terminal-load step in depth — no barrel walking, no
166
- // re-export chasing. `.warm()` returns a WarmedResolver whose lookupByTag
167
- // is the only typed access to the byTag index.
168
- const declared = declaredDeps(workspaceGraph);
169
- const warmedResolver = lazyResolver.warm(declared);
170
- const files = await walkFiles({
171
- root: cfg.configDir,
172
- include: cfg.include,
173
- exclude: cfg.exclude,
174
- gitignore: cfg.gitignore,
175
- });
176
- if (files.length === 0) {
177
- log.warn(`No files matched include globs: ${cfg.include.join(", ")}`);
178
- }
179
- // Sort files so JS/TS/Vue process before HTML; this primes the by-tag cache
180
- // in the lazy resolver before plain HTML files (which only have tag names,
181
- // no import specifiers) need to look up custom elements.
182
- const orderedFiles = [...files].sort((a, b) => {
183
- const aHtml = /\.html?$/.test(a) ? 1 : 0;
184
- const bHtml = /\.html?$/.test(b) ? 1 : 0;
185
- return aHtml - bHtml;
186
- });
187
- const dispatcher = new Dispatcher([reactPlugin, htmlPlugin, litTemplatePlugin]);
188
- const results = [];
189
- // Single-pass scan: one IO + one parse per file, shared across the local-
190
- // index build and plugin dispatch. ASTs are cached for the gap between the
191
- // two phases, then evicted post-dispatch to bound peak memory on large
192
- // monorepos. Two progress reporters cover each phase so the user sees both
193
- // legs — phase 2's plugin dispatch can dominate on JSX-heavy repos, and a
194
- // single phase-1-only reporter looked frozen at 99% during phase 2.
195
- if (tsconfigPath) {
196
- const rel = relative(cfg.configDir, tsconfigPath);
197
- writer(`[scan] using tsconfig: ${rel || tsconfigPath}\n`);
198
- }
199
- else {
200
- writer("[scan] tsconfig: not found (path aliases will not resolve)\n");
201
- }
202
- const isTTY = !!process.stderr.isTTY;
203
- const parseProgress = createProgress({
204
- total: orderedFiles.length,
205
- writer,
206
- isTTY,
207
- label: "Parsing",
208
- });
209
- const astCache = new Map();
210
- const sourceCache = new Map();
211
- const localDefs = [];
212
- // Graph builder for the reference-graph engine. Populated during Phase 1
213
- // (alongside the local-definition scan) so both the local index and the
214
- // graph see every file exactly once. The engine's Resolve phase runs after
215
- // Phase 2's plugin dispatch and its output is merged with the
216
- // Vue/Lit/HTML occurrences before Assemble.
217
- const graphBuilder = createGraphBuilder({ moduleResolver: resolveImport, repoRoot: cfg.configDir });
218
- // Generated GlobalComponents declarations: the Vue-ecosystem
219
- // registry of globally-available components (Nuxt writes one). Loaded once
220
- // per scan; misses degrade to diagnostics, never gates.
221
- const autoImports = loadGlobalComponents(cfg.configDir);
222
- if (autoImports !== null) {
223
- for (const s of autoImports.stale) {
224
- collector.emit({
225
- code: "auto-import-stale-entry",
226
- severity: "warning",
227
- filePath: autoImports.declarationPath,
228
- componentName: s.componentName,
229
- // s.target is absolute (global-components.ts resolves in-repo
230
- // entries to an absolute path before checking existence) — emit
231
- // repo-relative POSIX so the artefact stays portable across machines,
232
- // matching the convention used for occurrence/component filePaths.
233
- target: posixPath(relative(cfg.configDir, s.target)),
234
- });
235
- }
236
- }
237
- else {
238
- const framework = detectAutoImportFramework(cfg.configDir);
239
- if (framework !== null) {
240
- const missing = {
241
- code: "auto-import-manifest-missing",
242
- severity: "warning",
243
- filePath: framework.expectedPath,
244
- detail: "auto-import framework detected but no components manifest was found — auto-imported components can't be resolved. Run `nuxt prepare` (or any dev/build) and re-scan.",
245
- };
246
- collector.emit(missing);
247
- // Surface at scan time, not only in the artifact.
248
- log.warn(formatWarning(missing));
249
- }
250
- }
251
- // ── Phase 1: read + parse + emit local definitions ──────────────────────
252
- for (const file of orderedFiles) {
253
- const source = await readFile(file, "utf8").catch(() => "");
254
- if (!source) {
255
- parseProgress.tick();
256
- continue;
257
- }
258
- let parsed;
259
- try {
260
- parsed = parseByExt(file, source);
261
- }
262
- catch (err) {
263
- // parseByExt throws on unrecoverable syntax (EOF mid-expression, malformed
264
- // SFC). Mirror the plugin-loop's warn-and-continue pattern so one broken
265
- // file can't tank the scan.
266
- log.warn(`[component-compass] parse failure in ${file}: ${errorMessage(err)}`);
267
- parseProgress.tick();
268
- continue;
269
- }
270
- // `unsupported` extensions never reach phase 2; skip caching to bound
271
- // peak memory on scans whose include glob covers unsupported file types.
272
- if (parsed.kind === "unsupported") {
273
- parseProgress.tick();
274
- continue;
275
- }
276
- astCache.set(file, parsed);
277
- sourceCache.set(file, source);
278
- // Detectors record `componentId.source.filePath` as POSIX repo-relative so
279
- // it joins with seeds + parser occurrences without a normalisation step.
280
- const relPath = posixPath(relative(cfg.configDir, file));
281
- // Detectors feed the local-definition index, which is output-space
282
- // Rebase to outputRoot before handing paths to them.
283
- const outRel = rebase(relPath);
284
- if (parsed.kind === "babel") {
285
- localDefs.push(...detectReactComponents(parsed.ast, outRel, parsed.source));
286
- localDefs.push(...detectWebComponents(parsed.ast, outRel));
287
- // Vue script-mode catches `defineComponent` in plain .ts/.js, mirroring
288
- // local-index/index.ts routing for non-SFC files.
289
- localDefs.push(...detectVueComponents({ kind: "script", program: parsed.ast, source: parsed.source }, outRel));
290
- // Emit JSX references into the graph for later resolution by the engine.
291
- // relPath is repo-relative POSIX — the same form used for FileGraph keys.
292
- try {
293
- const fileBuilder = graphBuilder.beginFile(relPath);
294
- emitReact({
295
- file: relPath,
296
- source,
297
- ast: parsed.ast,
298
- fileBuilder,
299
- // Side-effect imports prime the lazy resolver: mirrors the Vue
300
- // lane's priming (emit-template.ts), warming byTag for tag
301
- // resolution and feeding entry-package provenance.
302
- // exportName "" = side-effect signal. Anchor is absolute here,
303
- // which resolveLeaf handles identically — the Vue lane
304
- // (emit-template.ts, below) anchors absolute too, for the same
305
- // reason.
306
- onSideEffectImport: (spec) => {
307
- warmedResolver.resolveLazyManifest(file, spec, "");
308
- },
309
- });
310
- }
311
- catch (err) {
312
- log.warn(`[component-compass] emit failure in ${file}: ${errorMessage(err)}`);
313
- }
314
- }
315
- else if (parsed.kind === "vue") {
316
- const wrapper = parsed.scriptAst !== undefined
317
- ? { descriptor: parsed.descriptor, scriptProgram: parsed.scriptAst }
318
- : { descriptor: parsed.descriptor };
319
- const sfcDefs = detectVueComponents({ kind: "sfc", wrapper }, outRel);
320
- localDefs.push(...sfcDefs);
321
- try {
322
- const fileBuilder = graphBuilder.beginFile(relPath, "vue");
323
- emitVueTemplate({
324
- file: relPath,
325
- source,
326
- wrapper,
327
- fileBuilder,
328
- // Anchor on the absolute `file`, discarding the emitter's own
329
- // repo-relative `_from` (relPath, relative to cfg.configDir).
330
- // resolveLazyManifest ultimately calls resolveImport(from, spec),
331
- // which rebases a non-absolute `from` onto the import resolver's
332
- // repoRoot — workspaceRoot, not cfg.configDir (walker/resolve-
333
- // import.ts). Those coincide for every scan rooted at its own
334
- // workspace root, but whenever cfg.configDir sits in a workspace
335
- // subdirectory (consumer-ci's per-app targets), rebasing the
336
- // relative anchor onto workspaceRoot produces a path that doesn't
337
- // exist, and the node_modules walk starts from the wrong
338
- // directory — nested per-app deps become unreachable. The
339
- // React/babel lane (emitReact's onSideEffectImport, above) already
340
- // anchors on the absolute `file` for the same reason; mirror it
341
- // here instead of "simplifying" this back to the emitter's arg.
342
- resolveLazyManifest: (_from, spec, exportName) => warmedResolver.resolveLazyManifest(file, spec, exportName),
343
- // Reuse detectVueComponents' resolved symbol so the SFC's local-index
344
- // seed and the engine's owner ComponentId hash to the same id —
345
- // otherwise the SFC seed is pruned as an unreachable local.
346
- ...(sfcDefs[0]?.exportName !== undefined ? { sfcSymbol: sfcDefs[0].exportName } : {}),
347
- ...(autoImports !== null ? { resolveAutoImport: autoImports.lookup } : {}),
348
- collector: graphKeyCollector,
349
- });
350
- }
351
- catch (err) {
352
- log.warn(`[component-compass] emit failure in ${file}: ${errorMessage(err)}`);
353
- }
354
- }
355
- parseProgress.tick();
356
- }
357
- parseProgress.done();
358
- const localIndex = buildLocalIndex(localDefs, collector);
359
- // ── Phase 2: dispatch plugins with shared oxc Program through ctx.ast ────
360
- // walkProgress total is the cached-file count: files dropped by phase 1
361
- // (empty source / parse failure / unsupported extension) aren't in the
362
- // cache and won't be walked.
363
- const walkProgress = createProgress({
364
- total: astCache.size,
365
- writer,
366
- isTTY,
367
- label: "Walking",
368
- });
369
- for (const file of orderedFiles) {
370
- const parsed = astCache.get(file);
371
- const source = sourceCache.get(file);
372
- if (!parsed || parsed.kind === "unsupported" || source === undefined)
373
- continue;
374
- // Carrier shape per file-kind; plugins narrow internally via their own
375
- // ctx.ast cast. `babel` → oxc Program, `vue` → VueParseWrapper,
376
- // `html` → parse5 Document. Unsupported never reaches here.
377
- const astForCtx = parsed.kind === "babel" ? parsed.ast :
378
- parsed.kind === "vue" ? (parsed.scriptAst !== undefined
379
- ? { descriptor: parsed.descriptor, scriptProgram: parsed.scriptAst }
380
- : { descriptor: parsed.descriptor }) :
381
- parsed.kind === "html" ? parsed.document :
382
- undefined;
383
- for (const plugin of dispatcher.allPluginsFor(file)) {
384
- try {
385
- results.push(plugin.parse(file, source, {
386
- localIndex,
387
- config: cfg,
388
- resolveImport,
389
- resolveLazyManifest: warmedResolver.resolveLazyManifest,
390
- resolveLocalExport: warmedResolver.resolveLocalExport,
391
- lookupByTag: warmedResolver.lookupByTag,
392
- repoRoot: outputRoot,
393
- collector,
394
- ast: astForCtx,
395
- cemIndex,
396
- }));
397
- }
398
- catch (err) {
399
- log.warn(`[component-compass] parse failure in ${file}: ${errorMessage(err)}`);
400
- }
401
- }
402
- // Evict after dispatch — keeps peak heap proportional to one file, not
403
- // the whole repo, on large monorepos.
404
- astCache.delete(file);
405
- sourceCache.delete(file);
406
- walkProgress.tick();
407
- }
408
- walkProgress.done();
409
- // ── Resolve phase: engine walks the populated graph ──────────────────────
410
- // Produces EngineOccurrence[] for every component reference in the graph:
411
- // external packages, workspace siblings, local symbols, and relative-
412
- // specifier imports alike.
413
- //
414
- // tagIndex serves tag-based emitters (parser-vue, parser-lit,
415
- // parser-html). External lookups try the warmed lazy
416
- // resolver first (populated by `.warm()`'s declared-dep eager-load and by
417
- // side-effect-import priming, both re-export-aware — sees barrels the
418
- // static CemIndex glob can't reach, e.g. nested node_modules leaves),
419
- // falling back to CemIndex.byTag for tags whose package was never imported
420
- // or warmed. Local lookups go through the workspace-WC entries in
421
- // LocalDefinitionIndex.byTag.
422
- // The engine emits through graphKeyCollector, which rebases each graph-key
423
- // filePath to outputRoot before handing the diagnostic to the shared collector.
424
- const tagIndex = {
425
- externalByTag: (tag) => {
426
- const warmed = warmedResolver.lookupByTag(tag);
427
- if (warmed && warmed.source.type === "external") {
428
- return { packageName: externalPackageOf(warmed.source) };
429
- }
430
- const hit = cemIndex.byTag.get(tag);
431
- if (!hit)
432
- return undefined;
433
- return { packageName: hit.packageName };
434
- },
435
- localByTag: (tag) => {
436
- const hit = localIndex.byTag.get(tag);
437
- if (!hit || hit.componentId.source.type !== "local")
438
- return undefined;
439
- return { filePath: hit.componentId.source.filePath, exportName: hit.exportName };
440
- },
441
- };
442
- // Bounded re-export resolver pins an un-walked workspace member's identity to
443
- // its definition file so ids are scope-invariant between whole-repo and
444
- // single-app scans. Only invoked for out-of-graph members.
445
- const resolveLocalDefinition = createBoundedDefinitionResolver({
446
- moduleResolver: resolveImport,
447
- onWarning: (msg) => writer(`[scan] ${msg}\n`),
448
- });
449
- const resolveOpts = {
450
- // Follow external aggregator barrels to the leaf package that
451
- // defines the export. Graph keys are configDir-relative — rebase before
452
- // handing to the resolver's Node-resolution walk. Same-package hits are
453
- // ignored by the engine but still terminal-load the leaf CEM, which is
454
- // what makes `stampWraps` hit for undeclared-transitive direct imports.
455
- // Uses `lookupExternalLeaf` (not `resolveLazyManifest`) so these
456
- // engine-driven visits stay out of `snapshotComponents()` /
457
- // `snapshotManifestRefs()` — otherwise an undeclared package's full CEM
458
- // catalog gets broadcast as zero-occurrence components.
459
- resolveExternalLeaf: (fromFile, specifier, exportName) => {
460
- const absFrom = isAbsolute(fromFile) ? fromFile : resolve(cfg.configDir, fromFile);
461
- const hit = warmedResolver.lookupExternalLeaf(absFrom, specifier, exportName);
462
- if (!hit)
463
- return null;
464
- return { leafPackage: hit.leafPackage, modulePath: hit.modulePath, exportName: hit.exportName };
465
- },
466
- tagIndex,
467
- collector: graphKeyCollector,
468
- };
469
- // Workspace-membership seam: findOwningPackage is the ONE membership
470
- // predicate (docs/CONTEXT.md). Never re-derive "is this path first-party".
471
- // Hooks attach at build() time so both engine lane-classification sites
472
- // (direct-import branch and wrapper-folding's composition fallbacks)
473
- // reach them through the Graph.
474
- const graph = graphBuilder.build({
475
- workspaceMembership: (p) => findOwningPackage(workspaceGraph, p) !== null,
476
- resolveLocalDefinition,
477
- });
478
- const { occurrences: engineOccurrences, registry } = resolveGraph(graph, resolveOpts);
479
- const t1 = performance.now();
480
- // Drain the shared collector.
481
- const rawDiagnostics = collector.drain();
482
- if (!opts.quiet) {
483
- for (const line of diagnosticLogLines(rawDiagnostics))
484
- log.warn(line);
485
- }
486
- // Rebase absolute diagnostic filePaths to outputRoot-relative POSIX; relative
487
- // filePaths pass through unchanged. The warn logs above print the diagnostics
488
- // before this step, so absolute paths stay absolute there.
489
- const diagnostics = rawDiagnostics.map((d) => ({
490
- ...d,
491
- filePath: isAbsolute(d.filePath) ? posixPath(relative(outputRoot, d.filePath)) : d.filePath,
492
- }));
493
- const snapshot = warmedResolver
494
- .snapshotComponents()
495
- .map((c) => ({ component: c }));
496
- const manifests = warmedResolver.snapshotManifestRefs();
497
- // Registry entries are keyed by graph key (configDir-relative); the roster
498
- // lives in output space. Rebase once here through the same `rebase` every
499
- // other engine path uses, so the join compares like with like. Keyed on
500
- // both `symbol` and `exportName` since `hasLocal` must also answer by the
501
- // local symbol when the entry's `exportName` is an alias.
502
- const rebasedEntries = registry.localEntries().map((e) => ({ ...e, filePath: rebase(e.filePath) }));
503
- const rebasedKeys = new Set(rebasedEntries.flatMap((e) => [`${e.filePath}::${e.symbol}`, `${e.filePath}::${e.exportName}`]));
504
- const rosterRegistry = {
505
- hasLocal: (filePath, exportName) => rebasedKeys.has(`${filePath}::${exportName}`),
506
- localEntries: () => rebasedEntries,
507
- };
508
- const seeds = buildComponentSeeds(results, localIndex, outputRoot, workspaceGraph, rosterRegistry, meta.repo.id);
509
- // Occurrences from the Vue/Lit/HTML parsers (reactPlugin.parse is a no-op).
510
- const legacyOccurrences = parseResultsToOccurrences(results, outputRoot);
511
- // Convert EngineOccurrence → OutputOccurrence and merge with the parser output.
512
- // Engine occurrences carry rawComponentId (structured ComponentId); translate
513
- // to the stable short-hash string via computeStableIdFromComponentId, then
514
- // re-derive occurrenceId using the final hashed componentId (matches the
515
- // convention in occurrences.ts so rollup correlates correctly).
516
- //
517
- // Also accumulate seeds for engine-discovered components (external or local)
518
- // that weren't already seeded by the parser path or the snapshot.
519
- // Without seeds, these components won't appear in components[] and their ids
520
- // won't be in validIds — causing composition edges to them to be silently
521
- // dropped during applyCompositionRollup.
522
- const engineSeeds = new Map();
523
- const resolvedEngineOccurrences = engineOccurrences.map((eo) => {
524
- // Normalise local filePaths in rawComponentId / rawOwnerComponentId to
525
- // output-space POSIX so the stable hash matches the one produced for seeds
526
- // built from the local index (which uses output-space relative paths).
527
- const normalisedComponentId = normaliseEngineLocalId(eo.rawComponentId, cfg.configDir, rebase);
528
- const componentId = computeStableIdFromComponentId(normalisedComponentId);
529
- // Resolve rawOwnerComponentId → hashed ownerComponentId string (if present).
530
- // Must be computed BEFORE occurrenceId so it can participate in the hash
531
- // (distinguishes fanned-out occurrences that share the same JSX call site).
532
- const ownerComponentId = eo.rawOwnerComponentId !== undefined
533
- ? computeStableIdFromComponentId(normaliseEngineLocalId(eo.rawOwnerComponentId, cfg.configDir, rebase))
534
- : eo.ownerComponentId;
535
- // eo.filePath is a scanRoot-relative graph key — rebase to outputRoot for
536
- // both the occurrenceId hash and the emitted OutputOccurrence.filePath.
537
- const outFile = rebase(eo.filePath);
538
- const occurrenceId = computeOccurrenceId(componentId, outFile, eo.line, eo.column, ownerComponentId);
539
- // Normalise absolute `via`/`viaChain` specifiers (e.g. GlobalComponents
540
- // entries for in-repo Vue components — see global-components.ts) to
541
- // output-space POSIX through the same rebase used for local identity
542
- // paths above. Authored specifiers are never absolute, so the isAbsolute
543
- // guard only ever fires for these synthetic entries. `via` is always
544
- // `viaChain[0]` (see engine/index.ts) — derive it from the normalised
545
- // chain so the two stay in sync.
546
- const normalisedViaChain = eo.viaChain.map((v) => normaliseVia(v, cfg.configDir, rebase));
547
- const via = normalisedViaChain[0] ?? normaliseVia(eo.via, cfg.configDir, rebase);
548
- // Ensure a seed exists for this component so it appears in components[].
549
- // Local seeds are already seeded from localIndex (step 3 of buildComponentSeeds);
550
- // this covers external occurrences whose package has no manifest snapshot entry.
551
- if (!engineSeeds.has(componentId)) {
552
- const existingSeed = seeds.find((s) => s.id === componentId);
553
- if (!existingSeed) {
554
- const identity = computeOutputIdentity({
555
- componentId: normalisedComponentId,
556
- repoId: meta.repo.id,
557
- workspaceGraph,
558
- outputRoot,
559
- ...(eo.definition !== undefined ? { definitionLoc: eo.definition } : {}),
560
- });
561
- engineSeeds.set(componentId, { id: componentId, identity, manifest: null });
562
- }
563
- }
564
- return {
565
- componentId,
566
- filePath: outFile,
567
- line: eo.line,
568
- column: eo.column,
569
- via,
570
- viaChain: normalisedViaChain,
571
- props: eo.props,
572
- occurrenceId,
573
- depth: eo.depth,
574
- ...(ownerComponentId !== undefined ? { ownerComponentId } : {}),
575
- ...(eo.events && eo.events.length > 0 ? { events: eo.events } : {}),
576
- };
577
- });
578
- // Merge engine seeds into the primary seeds array (deduplication via existingSeed check above).
579
- const allSeeds = stampWraps([...seeds, ...engineSeeds.values()], (pkg, exportName) => warmedResolver.lookupReactWrapper(pkg, exportName));
580
- // Attach manifest enrichment to every observed seed — parser-observed and
581
- // engine-discovered alike — via the ONE enrichment seam. Must run after
582
- // engine seeds are merged in so engine-only-discovered components (Vue
583
- // tag-only usages, wrap targets) are eligible for enrichment too, and before
584
- // buildScanOutput so the manifest lands in the emitted artefact.
585
- attachManifestEnrichment(allSeeds, snapshot, workspaceGraph);
586
- const occurrences = [...legacyOccurrences, ...resolvedEngineOccurrences];
587
- const partialStats = {
588
- filesScanned: files.length,
589
- filesWithUsage: countFilesWithUsage(occurrences),
590
- scanDurationMs: Math.round(t1 - t0),
591
- };
592
- const rawScanOutput = buildScanOutput(meta, manifests, partialStats, allSeeds, occurrences, diagnostics);
593
- // Post-resolve aggregation: stamp logicalId on every component and
594
- // emit wraps relationship edges — no synthetic zero-occurrence rows.
595
- // Leaf-package-lane records for UNOBSERVED targets match no row and
596
- // drop — the relationship edge carries that provenance.
597
- const { components: withRelationships, relationships } = applyWrapRelationships(rawScanOutput.components);
598
- const aggregated = applyEntryPackages(withRelationships, entryPackageRecorder.drain());
599
- const scanOutput = {
600
- ...rawScanOutput,
601
- components: aggregated,
602
- relationships,
603
- stats: { ...rawScanOutput.stats, componentsDetected: aggregated.length },
604
- };
605
- // Both functions join their root arg with output-space (outputRoot-relative)
606
- // component/occurrence filePaths — must use outputRoot, not cfg.configDir,
607
- // or the join produces a path that doesn't exist on disk once the two roots
608
- // diverge (git-mtime lookups silently return empty; version resolution
609
- // walks up from the wrong directory).
610
- await stampGitMTime(outputRoot, scanOutput.components);
611
- await stampVersion(outputRoot, scanOutput.components, scanOutput.occurrences);
612
- // cfg.output is a physical write destination (relative to the scan
613
- // invocation / config dir), not an output-space artefact field — stays
614
- // configDir-relative.
615
- const outputPath = isAbsolute(cfg.output) ? cfg.output : resolve(cfg.configDir, cfg.output);
616
- await writeJson(scanOutput, outputPath);
617
- if (!opts.quiet) {
618
- printSummary(scanOutput, outputPath);
619
- }
620
- let upload = "skipped";
621
- if (opts.upload) {
622
- try {
623
- const { createAuthedUploader } = await import("../auth/upload-auth.js");
624
- const uploader = await createAuthedUploader({
625
- ...(opts.hostOverride !== undefined ? { flagHost: opts.hostOverride } : {}),
626
- ...(cfg.host !== undefined ? { configHost: cfg.host } : {}),
627
- });
628
- const result = await uploader.upload(JSON.stringify(scanOutput));
629
- upload = result.status === "inserted" ? "ok" : "exists";
630
- log.info(result.status === "inserted"
631
- ? `Uploaded scan ${result.scanId} → ${uploader.base}${result.url}`
632
- : `Scan ${result.scanId} already exists at ${uploader.base}${result.url}; no changes.`);
633
- }
634
- catch (err) {
635
- upload = "failed";
636
- const { formatAuthError } = await import("../auth/session.js");
637
- const authMsg = formatAuthError(err);
638
- const msg = authMsg ?? (err instanceof Error ? err.message : String(err));
639
- log.error(`Upload failed: ${msg}`);
640
- }
641
- }
642
- return { output: scanOutput, upload };
643
- }
644
- function countFilesWithUsage(occurrences) {
645
- const files = new Set();
646
- for (const o of occurrences)
647
- files.add(o.filePath);
648
- return files.size;
649
- }
650
- /**
651
- * Normalise a local-source ComponentId's filePath to output-space
652
- * (outputRoot-relative POSIX) so the stable hash matches the one produced
653
- * for seeds built from the local index.
654
- *
655
- * The engine emits one of two shapes: an absolute path (relative-specifier
656
- * import whose target didn't resolve to a graph key) or a scanRoot-relative
657
- * graph key (the common case — `graph.resolveToGraphKey` succeeds because
658
- * the target file is part of this scan; owner refs are always this shape,
659
- * per the FileGraph-key convention). Both are normalised to scanRoot-relative
660
- * first, then projected into output-space via `rebase` — `rebase` alone
661
- * isn't enough because it expects a scanRoot-relative input, not an
662
- * absolute one.
663
- */
664
- function normaliseEngineLocalId(c, configDir, rebase) {
665
- if (c.source.type !== "local")
666
- return c;
667
- const fp = c.source.filePath;
668
- const scanRel = isAbsolute(fp) ? posixPath(relative(configDir, fp)) : fp;
669
- return {
670
- ...c,
671
- source: { type: "local", filePath: rebase(scanRel) },
672
- };
673
- }
674
- /**
675
- * Rebase an absolute `via.specifier` (or `viaChain[n].specifier`) to
676
- * output-space POSIX, mirroring `normaliseEngineLocalId`'s treatment of
677
- * local ComponentId filePaths. The GlobalComponents reader
678
- * (scan/global-components.ts) emits absolute specifiers for in-repo
679
- * entries — parser-vue stamps them straight into `viaOverride.specifier` —
680
- * so without this the artefact leaks machine-local absolute paths.
681
- * Authored specifiers (explicit imports) are always bare/relative import
682
- * strings, never absolute, so the `isAbsolute` guard only ever fires for
683
- * these synthetic auto-import entries.
684
- */
685
- function normaliseVia(v, configDir, rebase) {
686
- if (!("specifier" in v) || v.specifier === undefined || !isAbsolute(v.specifier))
687
- return v;
688
- const scanRel = posixPath(relative(configDir, v.specifier));
689
- return { ...v, specifier: rebase(scanRel) };
690
- }
691
- //# sourceMappingURL=scan.js.map