@tanstack/start-plugin-core 1.166.11 → 1.166.13

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 (87) hide show
  1. package/dist/esm/build-sitemap.js +94 -123
  2. package/dist/esm/build-sitemap.js.map +1 -1
  3. package/dist/esm/constants.js +15 -20
  4. package/dist/esm/constants.js.map +1 -1
  5. package/dist/esm/dev-server-plugin/dev-styles.js +137 -150
  6. package/dist/esm/dev-server-plugin/dev-styles.js.map +1 -1
  7. package/dist/esm/dev-server-plugin/extract-html-scripts.js +16 -15
  8. package/dist/esm/dev-server-plugin/extract-html-scripts.js.map +1 -1
  9. package/dist/esm/dev-server-plugin/plugin.js +125 -195
  10. package/dist/esm/dev-server-plugin/plugin.js.map +1 -1
  11. package/dist/esm/import-protection-plugin/ast.js +6 -5
  12. package/dist/esm/import-protection-plugin/ast.js.map +1 -1
  13. package/dist/esm/import-protection-plugin/constants.js +20 -22
  14. package/dist/esm/import-protection-plugin/constants.js.map +1 -1
  15. package/dist/esm/import-protection-plugin/defaults.js +35 -25
  16. package/dist/esm/import-protection-plugin/defaults.js.map +1 -1
  17. package/dist/esm/import-protection-plugin/extensionlessAbsoluteIdResolver.js +93 -92
  18. package/dist/esm/import-protection-plugin/extensionlessAbsoluteIdResolver.js.map +1 -1
  19. package/dist/esm/import-protection-plugin/matchers.js +23 -24
  20. package/dist/esm/import-protection-plugin/matchers.js.map +1 -1
  21. package/dist/esm/import-protection-plugin/plugin.js +1045 -1361
  22. package/dist/esm/import-protection-plugin/plugin.js.map +1 -1
  23. package/dist/esm/import-protection-plugin/postCompileUsage.js +58 -55
  24. package/dist/esm/import-protection-plugin/postCompileUsage.js.map +1 -1
  25. package/dist/esm/import-protection-plugin/rewriteDeniedImports.js +187 -259
  26. package/dist/esm/import-protection-plugin/rewriteDeniedImports.js.map +1 -1
  27. package/dist/esm/import-protection-plugin/sourceLocation.js +238 -248
  28. package/dist/esm/import-protection-plugin/sourceLocation.js.map +1 -1
  29. package/dist/esm/import-protection-plugin/trace.js +173 -184
  30. package/dist/esm/import-protection-plugin/trace.js.map +1 -1
  31. package/dist/esm/import-protection-plugin/utils.js +132 -111
  32. package/dist/esm/import-protection-plugin/utils.js.map +1 -1
  33. package/dist/esm/import-protection-plugin/virtualModules.js +214 -194
  34. package/dist/esm/import-protection-plugin/virtualModules.js.map +1 -1
  35. package/dist/esm/index.js +2 -7
  36. package/dist/esm/load-env-plugin/plugin.js +12 -11
  37. package/dist/esm/load-env-plugin/plugin.js.map +1 -1
  38. package/dist/esm/output-directory.js +10 -10
  39. package/dist/esm/output-directory.js.map +1 -1
  40. package/dist/esm/plugin.js +275 -355
  41. package/dist/esm/plugin.js.map +1 -1
  42. package/dist/esm/post-server-build.js +39 -53
  43. package/dist/esm/post-server-build.js.map +1 -1
  44. package/dist/esm/prerender.js +177 -239
  45. package/dist/esm/prerender.js.map +1 -1
  46. package/dist/esm/preview-server-plugin/plugin.js +41 -44
  47. package/dist/esm/preview-server-plugin/plugin.js.map +1 -1
  48. package/dist/esm/queue.js +115 -126
  49. package/dist/esm/queue.js.map +1 -1
  50. package/dist/esm/resolve-entries.js +31 -32
  51. package/dist/esm/resolve-entries.js.map +1 -1
  52. package/dist/esm/schema.js +156 -179
  53. package/dist/esm/schema.js.map +1 -1
  54. package/dist/esm/start-compiler-plugin/compiler.js +655 -812
  55. package/dist/esm/start-compiler-plugin/compiler.js.map +1 -1
  56. package/dist/esm/start-compiler-plugin/handleClientOnlyJSX.js +25 -8
  57. package/dist/esm/start-compiler-plugin/handleClientOnlyJSX.js.map +1 -1
  58. package/dist/esm/start-compiler-plugin/handleCreateIsomorphicFn.js +22 -19
  59. package/dist/esm/start-compiler-plugin/handleCreateIsomorphicFn.js.map +1 -1
  60. package/dist/esm/start-compiler-plugin/handleCreateMiddleware.js +20 -22
  61. package/dist/esm/start-compiler-plugin/handleCreateMiddleware.js.map +1 -1
  62. package/dist/esm/start-compiler-plugin/handleCreateServerFn.js +187 -255
  63. package/dist/esm/start-compiler-plugin/handleCreateServerFn.js.map +1 -1
  64. package/dist/esm/start-compiler-plugin/handleEnvOnly.js +23 -33
  65. package/dist/esm/start-compiler-plugin/handleEnvOnly.js.map +1 -1
  66. package/dist/esm/start-compiler-plugin/plugin.js +247 -291
  67. package/dist/esm/start-compiler-plugin/plugin.js.map +1 -1
  68. package/dist/esm/start-compiler-plugin/utils.js +27 -27
  69. package/dist/esm/start-compiler-plugin/utils.js.map +1 -1
  70. package/dist/esm/start-manifest-plugin/manifestBuilder.js +272 -378
  71. package/dist/esm/start-manifest-plugin/manifestBuilder.js.map +1 -1
  72. package/dist/esm/start-manifest-plugin/plugin.js +35 -44
  73. package/dist/esm/start-manifest-plugin/plugin.js.map +1 -1
  74. package/dist/esm/start-router-plugin/constants.js +6 -5
  75. package/dist/esm/start-router-plugin/constants.js.map +1 -1
  76. package/dist/esm/start-router-plugin/generator-plugins/prerender-routes-plugin.js +24 -19
  77. package/dist/esm/start-router-plugin/generator-plugins/prerender-routes-plugin.js.map +1 -1
  78. package/dist/esm/start-router-plugin/generator-plugins/routes-manifest-plugin.js +28 -29
  79. package/dist/esm/start-router-plugin/generator-plugins/routes-manifest-plugin.js.map +1 -1
  80. package/dist/esm/start-router-plugin/plugin.js +146 -199
  81. package/dist/esm/start-router-plugin/plugin.js.map +1 -1
  82. package/dist/esm/start-router-plugin/pruneServerOnlySubtrees.js +32 -31
  83. package/dist/esm/start-router-plugin/pruneServerOnlySubtrees.js.map +1 -1
  84. package/dist/esm/utils.js +14 -14
  85. package/dist/esm/utils.js.map +1 -1
  86. package/package.json +7 -7
  87. package/dist/esm/index.js.map +0 -1
@@ -1,273 +1,263 @@
1
- import { SourceMapConsumer } from "source-map";
2
- import * as path from "pathe";
1
+ import { getOrCreate, normalizeFilePath } from "./utils.js";
3
2
  import { findPostCompileUsagePos } from "./postCompileUsage.js";
4
- import { normalizeFilePath, getOrCreate } from "./utils.js";
3
+ import * as path$1 from "pathe";
4
+ import { SourceMapConsumer } from "source-map";
5
+ //#region src/import-protection-plugin/sourceLocation.ts
5
6
  function buildLineIndex(code) {
6
- const offsets = [0];
7
- for (let i = 0; i < code.length; i++) {
8
- if (code.charCodeAt(i) === 10) {
9
- offsets.push(i + 1);
10
- }
11
- }
12
- return { offsets };
7
+ const offsets = [0];
8
+ for (let i = 0; i < code.length; i++) if (code.charCodeAt(i) === 10) offsets.push(i + 1);
9
+ return { offsets };
13
10
  }
14
11
  function upperBound(values, x) {
15
- let lo = 0;
16
- let hi = values.length;
17
- while (lo < hi) {
18
- const mid = lo + hi >> 1;
19
- if (values[mid] <= x) lo = mid + 1;
20
- else hi = mid;
21
- }
22
- return lo;
12
+ let lo = 0;
13
+ let hi = values.length;
14
+ while (lo < hi) {
15
+ const mid = lo + hi >> 1;
16
+ if (values[mid] <= x) lo = mid + 1;
17
+ else hi = mid;
18
+ }
19
+ return lo;
23
20
  }
24
21
  function indexToLineColWithIndex(lineIndex, idx) {
25
- const offsets = lineIndex.offsets;
26
- const ub = upperBound(offsets, idx);
27
- const lineIdx = Math.max(0, ub - 1);
28
- const line = lineIdx + 1;
29
- const lineStart = offsets[lineIdx] ?? 0;
30
- return { line, column0: Math.max(0, idx - lineStart) };
22
+ const offsets = lineIndex.offsets;
23
+ const ub = upperBound(offsets, idx);
24
+ const lineIdx = Math.max(0, ub - 1);
25
+ const line = lineIdx + 1;
26
+ const lineStart = offsets[lineIdx] ?? 0;
27
+ return {
28
+ line,
29
+ column0: Math.max(0, idx - lineStart)
30
+ };
31
31
  }
32
+ /**
33
+ * Pick the most-likely original source text for `importerFile` from
34
+ * a sourcemap that may contain multiple sources.
35
+ */
32
36
  function pickOriginalCodeFromSourcesContent(map, importerFile, root) {
33
- if (!map?.sourcesContent || map.sources.length === 0) {
34
- return void 0;
35
- }
36
- const file = normalizeFilePath(importerFile);
37
- const sourceRoot = map.sourceRoot;
38
- const fileSeg = file.split("/").filter(Boolean);
39
- const resolveBase = sourceRoot ? path.resolve(root, sourceRoot) : root;
40
- let bestIdx = -1;
41
- let bestScore = -1;
42
- for (let i = 0; i < map.sources.length; i++) {
43
- const content = map.sourcesContent[i];
44
- if (typeof content !== "string") continue;
45
- const src = map.sources[i] ?? "";
46
- const normalizedSrc = normalizeFilePath(src);
47
- if (normalizedSrc === file) {
48
- return content;
49
- }
50
- let resolved;
51
- if (!src) {
52
- resolved = "";
53
- } else if (path.isAbsolute(src)) {
54
- resolved = normalizeFilePath(src);
55
- } else {
56
- resolved = normalizeFilePath(path.resolve(resolveBase, src));
57
- }
58
- if (resolved === file) {
59
- return content;
60
- }
61
- const normalizedSrcSeg = normalizedSrc.split("/").filter(Boolean);
62
- const resolvedSeg = resolved !== normalizedSrc ? resolved.split("/").filter(Boolean) : normalizedSrcSeg;
63
- const score = Math.max(
64
- segmentSuffixScore(normalizedSrcSeg, fileSeg),
65
- segmentSuffixScore(resolvedSeg, fileSeg)
66
- );
67
- if (score > bestScore) {
68
- bestScore = score;
69
- bestIdx = i;
70
- }
71
- }
72
- if (bestIdx !== -1 && bestScore >= 1) {
73
- return map.sourcesContent[bestIdx] ?? void 0;
74
- }
75
- return map.sourcesContent[0] ?? void 0;
37
+ if (!map?.sourcesContent || map.sources.length === 0) return;
38
+ const file = normalizeFilePath(importerFile);
39
+ const sourceRoot = map.sourceRoot;
40
+ const fileSeg = file.split("/").filter(Boolean);
41
+ const resolveBase = sourceRoot ? path$1.resolve(root, sourceRoot) : root;
42
+ let bestIdx = -1;
43
+ let bestScore = -1;
44
+ for (let i = 0; i < map.sources.length; i++) {
45
+ const content = map.sourcesContent[i];
46
+ if (typeof content !== "string") continue;
47
+ const src = map.sources[i] ?? "";
48
+ const normalizedSrc = normalizeFilePath(src);
49
+ if (normalizedSrc === file) return content;
50
+ let resolved;
51
+ if (!src) resolved = "";
52
+ else if (path$1.isAbsolute(src)) resolved = normalizeFilePath(src);
53
+ else resolved = normalizeFilePath(path$1.resolve(resolveBase, src));
54
+ if (resolved === file) return content;
55
+ const normalizedSrcSeg = normalizedSrc.split("/").filter(Boolean);
56
+ const resolvedSeg = resolved !== normalizedSrc ? resolved.split("/").filter(Boolean) : normalizedSrcSeg;
57
+ const score = Math.max(segmentSuffixScore(normalizedSrcSeg, fileSeg), segmentSuffixScore(resolvedSeg, fileSeg));
58
+ if (score > bestScore) {
59
+ bestScore = score;
60
+ bestIdx = i;
61
+ }
62
+ }
63
+ if (bestIdx !== -1 && bestScore >= 1) return map.sourcesContent[bestIdx] ?? void 0;
64
+ return map.sourcesContent[0] ?? void 0;
76
65
  }
66
+ /** Count matching path segments from the end of `aSeg` against `bSeg`. */
77
67
  function segmentSuffixScore(aSeg, bSeg) {
78
- let score = 0;
79
- for (let i = aSeg.length - 1, j = bSeg.length - 1; i >= 0 && j >= 0; i--, j--) {
80
- if (aSeg[i] !== bSeg[j]) break;
81
- score++;
82
- }
83
- return score;
68
+ let score = 0;
69
+ for (let i = aSeg.length - 1, j = bSeg.length - 1; i >= 0 && j >= 0; i--, j--) {
70
+ if (aSeg[i] !== bSeg[j]) break;
71
+ score++;
72
+ }
73
+ return score;
84
74
  }
85
75
  async function mapGeneratedToOriginal(map, generated, fallbackFile) {
86
- const fallback = {
87
- file: fallbackFile,
88
- line: generated.line,
89
- column: generated.column0 + 1
90
- };
91
- if (!map) {
92
- return fallback;
93
- }
94
- const consumer = await getSourceMapConsumer(map);
95
- if (!consumer) return fallback;
96
- try {
97
- const orig = consumer.originalPositionFor({
98
- line: generated.line,
99
- column: generated.column0
100
- });
101
- if (orig.line != null && orig.column != null) {
102
- return {
103
- file: orig.source ? normalizeFilePath(orig.source) : fallbackFile,
104
- line: orig.line,
105
- column: orig.column + 1
106
- };
107
- }
108
- } catch {
109
- }
110
- return fallback;
76
+ const fallback = {
77
+ file: fallbackFile,
78
+ line: generated.line,
79
+ column: generated.column0 + 1
80
+ };
81
+ if (!map) return fallback;
82
+ const consumer = await getSourceMapConsumer(map);
83
+ if (!consumer) return fallback;
84
+ try {
85
+ const orig = consumer.originalPositionFor({
86
+ line: generated.line,
87
+ column: generated.column0
88
+ });
89
+ if (orig.line != null && orig.column != null) return {
90
+ file: orig.source ? normalizeFilePath(orig.source) : fallbackFile,
91
+ line: orig.line,
92
+ column: orig.column + 1
93
+ };
94
+ } catch {}
95
+ return fallback;
111
96
  }
112
- const consumerCache = /* @__PURE__ */ new WeakMap();
97
+ var consumerCache = /* @__PURE__ */ new WeakMap();
113
98
  function toRawSourceMap(map) {
114
- return {
115
- ...map,
116
- file: map.file ?? "",
117
- version: Number(map.version),
118
- sourcesContent: map.sourcesContent?.map((s) => s ?? "") ?? []
119
- };
99
+ return {
100
+ ...map,
101
+ file: map.file ?? "",
102
+ version: Number(map.version),
103
+ sourcesContent: map.sourcesContent?.map((s) => s ?? "") ?? []
104
+ };
120
105
  }
121
106
  async function getSourceMapConsumer(map) {
122
- const cached = consumerCache.get(map);
123
- if (cached) return cached;
124
- const promise = (async () => {
125
- try {
126
- return await new SourceMapConsumer(toRawSourceMap(map));
127
- } catch {
128
- return null;
129
- }
130
- })();
131
- consumerCache.set(map, promise);
132
- return promise;
133
- }
134
- class ImportLocCache {
135
- cache = /* @__PURE__ */ new Map();
136
- reverseIndex = /* @__PURE__ */ new Map();
137
- has(key) {
138
- return this.cache.has(key);
139
- }
140
- get(key) {
141
- return this.cache.get(key);
142
- }
143
- set(key, value) {
144
- this.cache.set(key, value);
145
- const file = key.slice(0, key.indexOf("::"));
146
- getOrCreate(this.reverseIndex, file, () => /* @__PURE__ */ new Set()).add(key);
147
- }
148
- clear() {
149
- this.cache.clear();
150
- this.reverseIndex.clear();
151
- }
152
- /** Remove all cache entries where the importer matches `file`. */
153
- deleteByFile(file) {
154
- const keys = this.reverseIndex.get(file);
155
- if (keys) {
156
- for (const key of keys) {
157
- this.cache.delete(key);
158
- }
159
- this.reverseIndex.delete(file);
160
- }
161
- }
107
+ const cached = consumerCache.get(map);
108
+ if (cached) return cached;
109
+ const promise = (async () => {
110
+ try {
111
+ return await new SourceMapConsumer(toRawSourceMap(map));
112
+ } catch {
113
+ return null;
114
+ }
115
+ })();
116
+ consumerCache.set(map, promise);
117
+ return promise;
162
118
  }
119
+ /**
120
+ * Cache for import statement locations with reverse index for O(1)
121
+ * invalidation by file. Keys: `${importerFile}::${source}`.
122
+ */
123
+ var ImportLocCache = class {
124
+ cache = /* @__PURE__ */ new Map();
125
+ reverseIndex = /* @__PURE__ */ new Map();
126
+ has(key) {
127
+ return this.cache.has(key);
128
+ }
129
+ get(key) {
130
+ return this.cache.get(key);
131
+ }
132
+ set(key, value) {
133
+ this.cache.set(key, value);
134
+ const file = key.slice(0, key.indexOf("::"));
135
+ getOrCreate(this.reverseIndex, file, () => /* @__PURE__ */ new Set()).add(key);
136
+ }
137
+ clear() {
138
+ this.cache.clear();
139
+ this.reverseIndex.clear();
140
+ }
141
+ /** Remove all cache entries where the importer matches `file`. */
142
+ deleteByFile(file) {
143
+ const keys = this.reverseIndex.get(file);
144
+ if (keys) {
145
+ for (const key of keys) this.cache.delete(key);
146
+ this.reverseIndex.delete(file);
147
+ }
148
+ }
149
+ };
150
+ /**
151
+ * Find the location of an import statement in a transformed module
152
+ * by searching the post-transform code and mapping back via sourcemap.
153
+ * Results are cached in `importLocCache`.
154
+ */
163
155
  async function findImportStatementLocationFromTransformed(provider, importerId, source, importLocCache, findImportSpecifierIndex) {
164
- const importerFile = normalizeFilePath(importerId);
165
- const cacheKey = `${importerFile}::${source}`;
166
- if (importLocCache.has(cacheKey)) {
167
- return importLocCache.get(cacheKey) ?? void 0;
168
- }
169
- try {
170
- const res = provider.getTransformResult(importerId);
171
- if (!res) {
172
- importLocCache.set(cacheKey, null);
173
- return void 0;
174
- }
175
- const { code, map } = res;
176
- const lineIndex = res.lineIndex ?? buildLineIndex(code);
177
- const idx = findImportSpecifierIndex(code, source);
178
- if (idx === -1) {
179
- importLocCache.set(cacheKey, null);
180
- return void 0;
181
- }
182
- const generated = indexToLineColWithIndex(lineIndex, idx);
183
- const loc = await mapGeneratedToOriginal(map, generated, importerFile);
184
- importLocCache.set(cacheKey, loc);
185
- return loc;
186
- } catch {
187
- importLocCache.set(cacheKey, null);
188
- return void 0;
189
- }
156
+ const importerFile = normalizeFilePath(importerId);
157
+ const cacheKey = `${importerFile}::${source}`;
158
+ if (importLocCache.has(cacheKey)) return importLocCache.get(cacheKey) ?? void 0;
159
+ try {
160
+ const res = provider.getTransformResult(importerId);
161
+ if (!res) {
162
+ importLocCache.set(cacheKey, null);
163
+ return;
164
+ }
165
+ const { code, map } = res;
166
+ const lineIndex = res.lineIndex ?? buildLineIndex(code);
167
+ const idx = findImportSpecifierIndex(code, source);
168
+ if (idx === -1) {
169
+ importLocCache.set(cacheKey, null);
170
+ return;
171
+ }
172
+ const loc = await mapGeneratedToOriginal(map, indexToLineColWithIndex(lineIndex, idx), importerFile);
173
+ importLocCache.set(cacheKey, loc);
174
+ return loc;
175
+ } catch {
176
+ importLocCache.set(cacheKey, null);
177
+ return;
178
+ }
190
179
  }
180
+ /**
181
+ * Find the first post-compile usage location for a denied import specifier.
182
+ * Best-effort: searches transformed code for non-import uses of imported
183
+ * bindings and maps back to original source via sourcemap.
184
+ */
191
185
  async function findPostCompileUsageLocation(provider, importerId, source) {
192
- try {
193
- const importerFile = normalizeFilePath(importerId);
194
- const res = provider.getTransformResult(importerId);
195
- if (!res) return void 0;
196
- const { code, map } = res;
197
- if (!res.lineIndex) {
198
- res.lineIndex = buildLineIndex(code);
199
- }
200
- const pos = findPostCompileUsagePos(code, source);
201
- if (!pos) return void 0;
202
- return await mapGeneratedToOriginal(map, pos, importerFile);
203
- } catch {
204
- return void 0;
205
- }
186
+ try {
187
+ const importerFile = normalizeFilePath(importerId);
188
+ const res = provider.getTransformResult(importerId);
189
+ if (!res) return void 0;
190
+ const { code, map } = res;
191
+ if (!res.lineIndex) res.lineIndex = buildLineIndex(code);
192
+ const pos = findPostCompileUsagePos(code, source);
193
+ if (!pos) return void 0;
194
+ return await mapGeneratedToOriginal(map, pos, importerFile);
195
+ } catch {
196
+ return;
197
+ }
206
198
  }
199
+ /**
200
+ * Annotate each trace hop with the location of the import that created the
201
+ * edge (file:line:col). Skips steps that already have a location.
202
+ */
207
203
  async function addTraceImportLocations(provider, trace, importLocCache, findImportSpecifierIndex) {
208
- for (const step of trace) {
209
- if (!step.specifier) continue;
210
- if (step.line != null && step.column != null) continue;
211
- const loc = await findImportStatementLocationFromTransformed(
212
- provider,
213
- step.file,
214
- step.specifier,
215
- importLocCache,
216
- findImportSpecifierIndex
217
- );
218
- if (!loc) continue;
219
- step.line = loc.line;
220
- step.column = loc.column;
221
- }
204
+ for (const step of trace) {
205
+ if (!step.specifier) continue;
206
+ if (step.line != null && step.column != null) continue;
207
+ const loc = await findImportStatementLocationFromTransformed(provider, step.file, step.specifier, importLocCache, findImportSpecifierIndex);
208
+ if (!loc) continue;
209
+ step.line = loc.line;
210
+ step.column = loc.column;
211
+ }
222
212
  }
213
+ /**
214
+ * Build a vitest-style code snippet showing lines surrounding a location.
215
+ *
216
+ * Prefers `originalCode` from the sourcemap's sourcesContent; falls back
217
+ * to transformed code when unavailable.
218
+ */
223
219
  function buildCodeSnippet(provider, moduleId, loc, contextLines = 2) {
224
- try {
225
- const importerFile = normalizeFilePath(moduleId);
226
- const res = provider.getTransformResult(moduleId);
227
- if (!res) return void 0;
228
- const sourceCode = res.originalCode ?? res.code;
229
- const targetLine = loc.line;
230
- const targetCol = loc.column;
231
- if (targetLine < 1) return void 0;
232
- const allLines = sourceCode.split("\n");
233
- for (let i = 0; i < allLines.length; i++) {
234
- const line = allLines[i];
235
- if (line.endsWith("\r")) allLines[i] = line.slice(0, -1);
236
- }
237
- const wantStart = Math.max(1, targetLine - contextLines);
238
- const wantEnd = Math.min(allLines.length, targetLine + contextLines);
239
- if (targetLine > allLines.length) return void 0;
240
- const lines = allLines.slice(wantStart - 1, wantEnd);
241
- const gutterWidth = String(wantEnd).length;
242
- const sourceFile = loc.file ?? importerFile;
243
- const snippetLines = [];
244
- for (let i = 0; i < lines.length; i++) {
245
- const ln = wantStart + i;
246
- const lineContent = lines[i];
247
- const lineNumStr = String(ln).padStart(gutterWidth, " ");
248
- const marker = ln === targetLine ? ">" : " ";
249
- snippetLines.push(` ${marker} ${lineNumStr} | ${lineContent}`);
250
- if (ln === targetLine && targetCol > 0) {
251
- const padding = " ".repeat(targetCol - 1);
252
- snippetLines.push(` ${" ".repeat(gutterWidth)} | ${padding}^`);
253
- }
254
- }
255
- return {
256
- lines: snippetLines,
257
- highlightLine: targetLine,
258
- location: `${sourceFile}:${targetLine}:${targetCol}`
259
- };
260
- } catch {
261
- return void 0;
262
- }
220
+ try {
221
+ const importerFile = normalizeFilePath(moduleId);
222
+ const res = provider.getTransformResult(moduleId);
223
+ if (!res) return void 0;
224
+ const sourceCode = res.originalCode ?? res.code;
225
+ const targetLine = loc.line;
226
+ const targetCol = loc.column;
227
+ if (targetLine < 1) return void 0;
228
+ const allLines = sourceCode.split("\n");
229
+ for (let i = 0; i < allLines.length; i++) {
230
+ const line = allLines[i];
231
+ if (line.endsWith("\r")) allLines[i] = line.slice(0, -1);
232
+ }
233
+ const wantStart = Math.max(1, targetLine - contextLines);
234
+ const wantEnd = Math.min(allLines.length, targetLine + contextLines);
235
+ if (targetLine > allLines.length) return void 0;
236
+ const lines = allLines.slice(wantStart - 1, wantEnd);
237
+ const gutterWidth = String(wantEnd).length;
238
+ const sourceFile = loc.file ?? importerFile;
239
+ const snippetLines = [];
240
+ for (let i = 0; i < lines.length; i++) {
241
+ const ln = wantStart + i;
242
+ const lineContent = lines[i];
243
+ const lineNumStr = String(ln).padStart(gutterWidth, " ");
244
+ const marker = ln === targetLine ? ">" : " ";
245
+ snippetLines.push(` ${marker} ${lineNumStr} | ${lineContent}`);
246
+ if (ln === targetLine && targetCol > 0) {
247
+ const padding = " ".repeat(targetCol - 1);
248
+ snippetLines.push(` ${" ".repeat(gutterWidth)} | ${padding}^`);
249
+ }
250
+ }
251
+ return {
252
+ lines: snippetLines,
253
+ highlightLine: targetLine,
254
+ location: `${sourceFile}:${targetLine}:${targetCol}`
255
+ };
256
+ } catch {
257
+ return;
258
+ }
263
259
  }
264
- export {
265
- ImportLocCache,
266
- addTraceImportLocations,
267
- buildCodeSnippet,
268
- buildLineIndex,
269
- findImportStatementLocationFromTransformed,
270
- findPostCompileUsageLocation,
271
- pickOriginalCodeFromSourcesContent
272
- };
273
- //# sourceMappingURL=sourceLocation.js.map
260
+ //#endregion
261
+ export { ImportLocCache, addTraceImportLocations, buildCodeSnippet, buildLineIndex, findImportStatementLocationFromTransformed, findPostCompileUsageLocation, pickOriginalCodeFromSourcesContent };
262
+
263
+ //# sourceMappingURL=sourceLocation.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sourceLocation.js","sources":["../../../src/import-protection-plugin/sourceLocation.ts"],"sourcesContent":["import { SourceMapConsumer } from 'source-map'\nimport * as path from 'pathe'\n\nimport { findPostCompileUsagePos } from './postCompileUsage'\nimport { getOrCreate, normalizeFilePath } from './utils'\nimport type { Loc } from './trace'\nimport type { RawSourceMap } from 'source-map'\n\n// Source-map type compatible with both Rollup's SourceMap and source-map's\n// RawSourceMap. Structural type avoids version: number vs string mismatch.\n\n/**\n * Minimal source-map shape used throughout the import-protection plugin.\n */\nexport interface SourceMapLike {\n file?: string\n sourceRoot?: string\n version: number | string\n sources: Array<string>\n names: Array<string>\n sourcesContent?: Array<string | null>\n mappings: string\n}\n\n// Transform result provider (replaces ctx.load() which doesn't work in dev)\nexport interface TransformResult {\n code: string\n map: SourceMapLike | undefined\n originalCode: string | undefined\n /** Precomputed line index for `code` (index → line/col). */\n lineIndex?: LineIndex\n}\n\n/**\n * Provides the transformed code and composed sourcemap for a module.\n *\n * Populated from a late-running transform hook. By the time `resolveId`\n * fires for an import, the importer has already been fully transformed.\n */\nexport interface TransformResultProvider {\n getTransformResult: (id: string) => TransformResult | undefined\n}\n\n// Index → line/column conversion\n\nexport type LineIndex = {\n offsets: Array<number>\n}\n\nexport function buildLineIndex(code: string): LineIndex {\n const offsets: Array<number> = [0]\n for (let i = 0; i < code.length; i++) {\n if (code.charCodeAt(i) === 10) {\n offsets.push(i + 1)\n }\n }\n return { offsets }\n}\n\nfunction upperBound(values: Array<number>, x: number): number {\n let lo = 0\n let hi = values.length\n while (lo < hi) {\n const mid = (lo + hi) >> 1\n if (values[mid]! <= x) lo = mid + 1\n else hi = mid\n }\n return lo\n}\n\nfunction indexToLineColWithIndex(\n lineIndex: LineIndex,\n idx: number,\n): { line: number; column0: number } {\n const offsets = lineIndex.offsets\n const ub = upperBound(offsets, idx)\n const lineIdx = Math.max(0, ub - 1)\n const line = lineIdx + 1\n\n const lineStart = offsets[lineIdx] ?? 0\n return { line, column0: Math.max(0, idx - lineStart) }\n}\n\n/**\n * Pick the most-likely original source text for `importerFile` from\n * a sourcemap that may contain multiple sources.\n */\nexport function pickOriginalCodeFromSourcesContent(\n map: SourceMapLike | undefined,\n importerFile: string,\n root: string,\n): string | undefined {\n if (!map?.sourcesContent || map.sources.length === 0) {\n return undefined\n }\n\n const file = normalizeFilePath(importerFile)\n const sourceRoot = map.sourceRoot\n const fileSeg = file.split('/').filter(Boolean)\n\n const resolveBase = sourceRoot ? path.resolve(root, sourceRoot) : root\n\n let bestIdx = -1\n let bestScore = -1\n\n for (let i = 0; i < map.sources.length; i++) {\n const content = map.sourcesContent[i]\n if (typeof content !== 'string') continue\n\n const src = map.sources[i] ?? ''\n\n const normalizedSrc = normalizeFilePath(src)\n if (normalizedSrc === file) {\n return content\n }\n\n let resolved: string\n if (!src) {\n resolved = ''\n } else if (path.isAbsolute(src)) {\n resolved = normalizeFilePath(src)\n } else {\n resolved = normalizeFilePath(path.resolve(resolveBase, src))\n }\n if (resolved === file) {\n return content\n }\n\n // Count matching path segments from the end.\n const normalizedSrcSeg = normalizedSrc.split('/').filter(Boolean)\n const resolvedSeg =\n resolved !== normalizedSrc\n ? resolved.split('/').filter(Boolean)\n : normalizedSrcSeg\n const score = Math.max(\n segmentSuffixScore(normalizedSrcSeg, fileSeg),\n segmentSuffixScore(resolvedSeg, fileSeg),\n )\n\n if (score > bestScore) {\n bestScore = score\n bestIdx = i\n }\n }\n\n if (bestIdx !== -1 && bestScore >= 1) {\n return map.sourcesContent[bestIdx] ?? undefined\n }\n\n return map.sourcesContent[0] ?? undefined\n}\n\n/** Count matching path segments from the end of `aSeg` against `bSeg`. */\nfunction segmentSuffixScore(aSeg: Array<string>, bSeg: Array<string>): number {\n let score = 0\n for (\n let i = aSeg.length - 1, j = bSeg.length - 1;\n i >= 0 && j >= 0;\n i--, j--\n ) {\n if (aSeg[i] !== bSeg[j]) break\n score++\n }\n return score\n}\n\nasync function mapGeneratedToOriginal(\n map: SourceMapLike | undefined,\n generated: { line: number; column0: number },\n fallbackFile: string,\n): Promise<Loc> {\n const fallback: Loc = {\n file: fallbackFile,\n line: generated.line,\n column: generated.column0 + 1,\n }\n\n if (!map) {\n return fallback\n }\n\n const consumer = await getSourceMapConsumer(map)\n if (!consumer) return fallback\n\n try {\n const orig = consumer.originalPositionFor({\n line: generated.line,\n column: generated.column0,\n })\n if (orig.line != null && orig.column != null) {\n return {\n file: orig.source ? normalizeFilePath(orig.source) : fallbackFile,\n line: orig.line,\n column: orig.column + 1,\n }\n }\n } catch {\n // Malformed sourcemap\n }\n\n return fallback\n}\n\nconst consumerCache = new WeakMap<object, Promise<SourceMapConsumer | null>>()\n\nfunction toRawSourceMap(map: SourceMapLike): RawSourceMap {\n return {\n ...map,\n file: map.file ?? '',\n version: Number(map.version),\n sourcesContent: map.sourcesContent?.map((s) => s ?? '') ?? [],\n }\n}\n\nasync function getSourceMapConsumer(\n map: SourceMapLike,\n): Promise<SourceMapConsumer | null> {\n const cached = consumerCache.get(map)\n if (cached) return cached\n\n const promise = (async () => {\n try {\n return await new SourceMapConsumer(toRawSourceMap(map))\n } catch {\n return null\n }\n })()\n\n consumerCache.set(map, promise)\n return promise\n}\n\nexport type ImportLocEntry = { file?: string; line: number; column: number }\n\n/**\n * Cache for import statement locations with reverse index for O(1)\n * invalidation by file. Keys: `${importerFile}::${source}`.\n */\nexport class ImportLocCache {\n private cache = new Map<string, ImportLocEntry | null>()\n private reverseIndex = new Map<string, Set<string>>()\n\n has(key: string): boolean {\n return this.cache.has(key)\n }\n\n get(key: string): ImportLocEntry | null | undefined {\n return this.cache.get(key)\n }\n\n set(key: string, value: ImportLocEntry | null): void {\n this.cache.set(key, value)\n const file = key.slice(0, key.indexOf('::'))\n getOrCreate(this.reverseIndex, file, () => new Set()).add(key)\n }\n\n clear(): void {\n this.cache.clear()\n this.reverseIndex.clear()\n }\n\n /** Remove all cache entries where the importer matches `file`. */\n deleteByFile(file: string): void {\n const keys = this.reverseIndex.get(file)\n if (keys) {\n for (const key of keys) {\n this.cache.delete(key)\n }\n this.reverseIndex.delete(file)\n }\n }\n}\n\nexport type FindImportSpecifierIndex = (code: string, source: string) => number\n\n/**\n * Find the location of an import statement in a transformed module\n * by searching the post-transform code and mapping back via sourcemap.\n * Results are cached in `importLocCache`.\n */\nexport async function findImportStatementLocationFromTransformed(\n provider: TransformResultProvider,\n importerId: string,\n source: string,\n importLocCache: ImportLocCache,\n findImportSpecifierIndex: FindImportSpecifierIndex,\n): Promise<Loc | undefined> {\n const importerFile = normalizeFilePath(importerId)\n const cacheKey = `${importerFile}::${source}`\n if (importLocCache.has(cacheKey)) {\n return importLocCache.get(cacheKey) ?? undefined\n }\n\n try {\n const res = provider.getTransformResult(importerId)\n if (!res) {\n importLocCache.set(cacheKey, null)\n return undefined\n }\n\n const { code, map } = res\n\n const lineIndex = res.lineIndex ?? buildLineIndex(code)\n\n const idx = findImportSpecifierIndex(code, source)\n if (idx === -1) {\n importLocCache.set(cacheKey, null)\n return undefined\n }\n\n const generated = indexToLineColWithIndex(lineIndex, idx)\n const loc = await mapGeneratedToOriginal(map, generated, importerFile)\n importLocCache.set(cacheKey, loc)\n return loc\n } catch {\n importLocCache.set(cacheKey, null)\n return undefined\n }\n}\n\n/**\n * Find the first post-compile usage location for a denied import specifier.\n * Best-effort: searches transformed code for non-import uses of imported\n * bindings and maps back to original source via sourcemap.\n */\nexport async function findPostCompileUsageLocation(\n provider: TransformResultProvider,\n importerId: string,\n source: string,\n): Promise<Loc | undefined> {\n try {\n const importerFile = normalizeFilePath(importerId)\n const res = provider.getTransformResult(importerId)\n if (!res) return undefined\n const { code, map } = res\n\n if (!res.lineIndex) {\n res.lineIndex = buildLineIndex(code)\n }\n\n const pos = findPostCompileUsagePos(code, source)\n if (!pos) return undefined\n\n return await mapGeneratedToOriginal(map, pos, importerFile)\n } catch {\n return undefined\n }\n}\n\n/**\n * Annotate each trace hop with the location of the import that created the\n * edge (file:line:col). Skips steps that already have a location.\n */\nexport async function addTraceImportLocations(\n provider: TransformResultProvider,\n trace: Array<{\n file: string\n specifier?: string\n line?: number\n column?: number\n }>,\n importLocCache: ImportLocCache,\n findImportSpecifierIndex: FindImportSpecifierIndex,\n): Promise<void> {\n for (const step of trace) {\n if (!step.specifier) continue\n if (step.line != null && step.column != null) continue\n const loc = await findImportStatementLocationFromTransformed(\n provider,\n step.file,\n step.specifier,\n importLocCache,\n findImportSpecifierIndex,\n )\n if (!loc) continue\n step.line = loc.line\n step.column = loc.column\n }\n}\n\n// Code snippet extraction (vitest-style context around a location)\n\nexport interface CodeSnippet {\n /** Source lines with line numbers, e.g. `[\" 6 | import { getSecret } from './secret.server'\", ...]` */\n lines: Array<string>\n /** The highlighted line (1-indexed original line number) */\n highlightLine: number\n /** Clickable file:line reference */\n location: string\n}\n\n/**\n * Build a vitest-style code snippet showing lines surrounding a location.\n *\n * Prefers `originalCode` from the sourcemap's sourcesContent; falls back\n * to transformed code when unavailable.\n */\nexport function buildCodeSnippet(\n provider: TransformResultProvider,\n moduleId: string,\n loc: Loc,\n contextLines: number = 2,\n): CodeSnippet | undefined {\n try {\n const importerFile = normalizeFilePath(moduleId)\n const res = provider.getTransformResult(moduleId)\n if (!res) return undefined\n\n const sourceCode = res.originalCode ?? res.code\n const targetLine = loc.line // 1-indexed\n const targetCol = loc.column // 1-indexed\n\n if (targetLine < 1) return undefined\n\n const allLines = sourceCode.split('\\n')\n // Strip trailing \\r from \\r\\n line endings\n for (let i = 0; i < allLines.length; i++) {\n const line = allLines[i]!\n if (line.endsWith('\\r')) allLines[i] = line.slice(0, -1)\n }\n\n const wantStart = Math.max(1, targetLine - contextLines)\n const wantEnd = Math.min(allLines.length, targetLine + contextLines)\n\n if (targetLine > allLines.length) return undefined\n\n const lines = allLines.slice(wantStart - 1, wantEnd)\n const gutterWidth = String(wantEnd).length\n\n const sourceFile = loc.file ?? importerFile\n const snippetLines: Array<string> = []\n for (let i = 0; i < lines.length; i++) {\n const ln = wantStart + i\n const lineContent = lines[i]!\n const lineNumStr = String(ln).padStart(gutterWidth, ' ')\n const marker = ln === targetLine ? '>' : ' '\n snippetLines.push(` ${marker} ${lineNumStr} | ${lineContent}`)\n\n if (ln === targetLine && targetCol > 0) {\n const padding = ' '.repeat(targetCol - 1)\n snippetLines.push(` ${' '.repeat(gutterWidth)} | ${padding}^`)\n }\n }\n\n return {\n lines: snippetLines,\n highlightLine: targetLine,\n location: `${sourceFile}:${targetLine}:${targetCol}`,\n }\n } catch {\n return undefined\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sourceLocation.js","names":[],"sources":["../../../src/import-protection-plugin/sourceLocation.ts"],"sourcesContent":["import { SourceMapConsumer } from 'source-map'\nimport * as path from 'pathe'\n\nimport { findPostCompileUsagePos } from './postCompileUsage'\nimport { getOrCreate, normalizeFilePath } from './utils'\nimport type { Loc } from './trace'\nimport type { RawSourceMap } from 'source-map'\n\n// Source-map type compatible with both Rollup's SourceMap and source-map's\n// RawSourceMap. Structural type avoids version: number vs string mismatch.\n\n/**\n * Minimal source-map shape used throughout the import-protection plugin.\n */\nexport interface SourceMapLike {\n file?: string\n sourceRoot?: string\n version: number | string\n sources: Array<string>\n names: Array<string>\n sourcesContent?: Array<string | null>\n mappings: string\n}\n\n// Transform result provider (replaces ctx.load() which doesn't work in dev)\nexport interface TransformResult {\n code: string\n map: SourceMapLike | undefined\n originalCode: string | undefined\n /** Precomputed line index for `code` (index → line/col). */\n lineIndex?: LineIndex\n}\n\n/**\n * Provides the transformed code and composed sourcemap for a module.\n *\n * Populated from a late-running transform hook. By the time `resolveId`\n * fires for an import, the importer has already been fully transformed.\n */\nexport interface TransformResultProvider {\n getTransformResult: (id: string) => TransformResult | undefined\n}\n\n// Index → line/column conversion\n\nexport type LineIndex = {\n offsets: Array<number>\n}\n\nexport function buildLineIndex(code: string): LineIndex {\n const offsets: Array<number> = [0]\n for (let i = 0; i < code.length; i++) {\n if (code.charCodeAt(i) === 10) {\n offsets.push(i + 1)\n }\n }\n return { offsets }\n}\n\nfunction upperBound(values: Array<number>, x: number): number {\n let lo = 0\n let hi = values.length\n while (lo < hi) {\n const mid = (lo + hi) >> 1\n if (values[mid]! <= x) lo = mid + 1\n else hi = mid\n }\n return lo\n}\n\nfunction indexToLineColWithIndex(\n lineIndex: LineIndex,\n idx: number,\n): { line: number; column0: number } {\n const offsets = lineIndex.offsets\n const ub = upperBound(offsets, idx)\n const lineIdx = Math.max(0, ub - 1)\n const line = lineIdx + 1\n\n const lineStart = offsets[lineIdx] ?? 0\n return { line, column0: Math.max(0, idx - lineStart) }\n}\n\n/**\n * Pick the most-likely original source text for `importerFile` from\n * a sourcemap that may contain multiple sources.\n */\nexport function pickOriginalCodeFromSourcesContent(\n map: SourceMapLike | undefined,\n importerFile: string,\n root: string,\n): string | undefined {\n if (!map?.sourcesContent || map.sources.length === 0) {\n return undefined\n }\n\n const file = normalizeFilePath(importerFile)\n const sourceRoot = map.sourceRoot\n const fileSeg = file.split('/').filter(Boolean)\n\n const resolveBase = sourceRoot ? path.resolve(root, sourceRoot) : root\n\n let bestIdx = -1\n let bestScore = -1\n\n for (let i = 0; i < map.sources.length; i++) {\n const content = map.sourcesContent[i]\n if (typeof content !== 'string') continue\n\n const src = map.sources[i] ?? ''\n\n const normalizedSrc = normalizeFilePath(src)\n if (normalizedSrc === file) {\n return content\n }\n\n let resolved: string\n if (!src) {\n resolved = ''\n } else if (path.isAbsolute(src)) {\n resolved = normalizeFilePath(src)\n } else {\n resolved = normalizeFilePath(path.resolve(resolveBase, src))\n }\n if (resolved === file) {\n return content\n }\n\n // Count matching path segments from the end.\n const normalizedSrcSeg = normalizedSrc.split('/').filter(Boolean)\n const resolvedSeg =\n resolved !== normalizedSrc\n ? resolved.split('/').filter(Boolean)\n : normalizedSrcSeg\n const score = Math.max(\n segmentSuffixScore(normalizedSrcSeg, fileSeg),\n segmentSuffixScore(resolvedSeg, fileSeg),\n )\n\n if (score > bestScore) {\n bestScore = score\n bestIdx = i\n }\n }\n\n if (bestIdx !== -1 && bestScore >= 1) {\n return map.sourcesContent[bestIdx] ?? undefined\n }\n\n return map.sourcesContent[0] ?? undefined\n}\n\n/** Count matching path segments from the end of `aSeg` against `bSeg`. */\nfunction segmentSuffixScore(aSeg: Array<string>, bSeg: Array<string>): number {\n let score = 0\n for (\n let i = aSeg.length - 1, j = bSeg.length - 1;\n i >= 0 && j >= 0;\n i--, j--\n ) {\n if (aSeg[i] !== bSeg[j]) break\n score++\n }\n return score\n}\n\nasync function mapGeneratedToOriginal(\n map: SourceMapLike | undefined,\n generated: { line: number; column0: number },\n fallbackFile: string,\n): Promise<Loc> {\n const fallback: Loc = {\n file: fallbackFile,\n line: generated.line,\n column: generated.column0 + 1,\n }\n\n if (!map) {\n return fallback\n }\n\n const consumer = await getSourceMapConsumer(map)\n if (!consumer) return fallback\n\n try {\n const orig = consumer.originalPositionFor({\n line: generated.line,\n column: generated.column0,\n })\n if (orig.line != null && orig.column != null) {\n return {\n file: orig.source ? normalizeFilePath(orig.source) : fallbackFile,\n line: orig.line,\n column: orig.column + 1,\n }\n }\n } catch {\n // Malformed sourcemap\n }\n\n return fallback\n}\n\nconst consumerCache = new WeakMap<object, Promise<SourceMapConsumer | null>>()\n\nfunction toRawSourceMap(map: SourceMapLike): RawSourceMap {\n return {\n ...map,\n file: map.file ?? '',\n version: Number(map.version),\n sourcesContent: map.sourcesContent?.map((s) => s ?? '') ?? [],\n }\n}\n\nasync function getSourceMapConsumer(\n map: SourceMapLike,\n): Promise<SourceMapConsumer | null> {\n const cached = consumerCache.get(map)\n if (cached) return cached\n\n const promise = (async () => {\n try {\n return await new SourceMapConsumer(toRawSourceMap(map))\n } catch {\n return null\n }\n })()\n\n consumerCache.set(map, promise)\n return promise\n}\n\nexport type ImportLocEntry = { file?: string; line: number; column: number }\n\n/**\n * Cache for import statement locations with reverse index for O(1)\n * invalidation by file. Keys: `${importerFile}::${source}`.\n */\nexport class ImportLocCache {\n private cache = new Map<string, ImportLocEntry | null>()\n private reverseIndex = new Map<string, Set<string>>()\n\n has(key: string): boolean {\n return this.cache.has(key)\n }\n\n get(key: string): ImportLocEntry | null | undefined {\n return this.cache.get(key)\n }\n\n set(key: string, value: ImportLocEntry | null): void {\n this.cache.set(key, value)\n const file = key.slice(0, key.indexOf('::'))\n getOrCreate(this.reverseIndex, file, () => new Set()).add(key)\n }\n\n clear(): void {\n this.cache.clear()\n this.reverseIndex.clear()\n }\n\n /** Remove all cache entries where the importer matches `file`. */\n deleteByFile(file: string): void {\n const keys = this.reverseIndex.get(file)\n if (keys) {\n for (const key of keys) {\n this.cache.delete(key)\n }\n this.reverseIndex.delete(file)\n }\n }\n}\n\nexport type FindImportSpecifierIndex = (code: string, source: string) => number\n\n/**\n * Find the location of an import statement in a transformed module\n * by searching the post-transform code and mapping back via sourcemap.\n * Results are cached in `importLocCache`.\n */\nexport async function findImportStatementLocationFromTransformed(\n provider: TransformResultProvider,\n importerId: string,\n source: string,\n importLocCache: ImportLocCache,\n findImportSpecifierIndex: FindImportSpecifierIndex,\n): Promise<Loc | undefined> {\n const importerFile = normalizeFilePath(importerId)\n const cacheKey = `${importerFile}::${source}`\n if (importLocCache.has(cacheKey)) {\n return importLocCache.get(cacheKey) ?? undefined\n }\n\n try {\n const res = provider.getTransformResult(importerId)\n if (!res) {\n importLocCache.set(cacheKey, null)\n return undefined\n }\n\n const { code, map } = res\n\n const lineIndex = res.lineIndex ?? buildLineIndex(code)\n\n const idx = findImportSpecifierIndex(code, source)\n if (idx === -1) {\n importLocCache.set(cacheKey, null)\n return undefined\n }\n\n const generated = indexToLineColWithIndex(lineIndex, idx)\n const loc = await mapGeneratedToOriginal(map, generated, importerFile)\n importLocCache.set(cacheKey, loc)\n return loc\n } catch {\n importLocCache.set(cacheKey, null)\n return undefined\n }\n}\n\n/**\n * Find the first post-compile usage location for a denied import specifier.\n * Best-effort: searches transformed code for non-import uses of imported\n * bindings and maps back to original source via sourcemap.\n */\nexport async function findPostCompileUsageLocation(\n provider: TransformResultProvider,\n importerId: string,\n source: string,\n): Promise<Loc | undefined> {\n try {\n const importerFile = normalizeFilePath(importerId)\n const res = provider.getTransformResult(importerId)\n if (!res) return undefined\n const { code, map } = res\n\n if (!res.lineIndex) {\n res.lineIndex = buildLineIndex(code)\n }\n\n const pos = findPostCompileUsagePos(code, source)\n if (!pos) return undefined\n\n return await mapGeneratedToOriginal(map, pos, importerFile)\n } catch {\n return undefined\n }\n}\n\n/**\n * Annotate each trace hop with the location of the import that created the\n * edge (file:line:col). Skips steps that already have a location.\n */\nexport async function addTraceImportLocations(\n provider: TransformResultProvider,\n trace: Array<{\n file: string\n specifier?: string\n line?: number\n column?: number\n }>,\n importLocCache: ImportLocCache,\n findImportSpecifierIndex: FindImportSpecifierIndex,\n): Promise<void> {\n for (const step of trace) {\n if (!step.specifier) continue\n if (step.line != null && step.column != null) continue\n const loc = await findImportStatementLocationFromTransformed(\n provider,\n step.file,\n step.specifier,\n importLocCache,\n findImportSpecifierIndex,\n )\n if (!loc) continue\n step.line = loc.line\n step.column = loc.column\n }\n}\n\n// Code snippet extraction (vitest-style context around a location)\n\nexport interface CodeSnippet {\n /** Source lines with line numbers, e.g. `[\" 6 | import { getSecret } from './secret.server'\", ...]` */\n lines: Array<string>\n /** The highlighted line (1-indexed original line number) */\n highlightLine: number\n /** Clickable file:line reference */\n location: string\n}\n\n/**\n * Build a vitest-style code snippet showing lines surrounding a location.\n *\n * Prefers `originalCode` from the sourcemap's sourcesContent; falls back\n * to transformed code when unavailable.\n */\nexport function buildCodeSnippet(\n provider: TransformResultProvider,\n moduleId: string,\n loc: Loc,\n contextLines: number = 2,\n): CodeSnippet | undefined {\n try {\n const importerFile = normalizeFilePath(moduleId)\n const res = provider.getTransformResult(moduleId)\n if (!res) return undefined\n\n const sourceCode = res.originalCode ?? res.code\n const targetLine = loc.line // 1-indexed\n const targetCol = loc.column // 1-indexed\n\n if (targetLine < 1) return undefined\n\n const allLines = sourceCode.split('\\n')\n // Strip trailing \\r from \\r\\n line endings\n for (let i = 0; i < allLines.length; i++) {\n const line = allLines[i]!\n if (line.endsWith('\\r')) allLines[i] = line.slice(0, -1)\n }\n\n const wantStart = Math.max(1, targetLine - contextLines)\n const wantEnd = Math.min(allLines.length, targetLine + contextLines)\n\n if (targetLine > allLines.length) return undefined\n\n const lines = allLines.slice(wantStart - 1, wantEnd)\n const gutterWidth = String(wantEnd).length\n\n const sourceFile = loc.file ?? importerFile\n const snippetLines: Array<string> = []\n for (let i = 0; i < lines.length; i++) {\n const ln = wantStart + i\n const lineContent = lines[i]!\n const lineNumStr = String(ln).padStart(gutterWidth, ' ')\n const marker = ln === targetLine ? '>' : ' '\n snippetLines.push(` ${marker} ${lineNumStr} | ${lineContent}`)\n\n if (ln === targetLine && targetCol > 0) {\n const padding = ' '.repeat(targetCol - 1)\n snippetLines.push(` ${' '.repeat(gutterWidth)} | ${padding}^`)\n }\n }\n\n return {\n lines: snippetLines,\n highlightLine: targetLine,\n location: `${sourceFile}:${targetLine}:${targetCol}`,\n }\n } catch {\n return undefined\n }\n}\n"],"mappings":"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"}