@tanstack/start-plugin-core 1.166.12 → 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.
- package/dist/esm/build-sitemap.js +94 -123
- package/dist/esm/build-sitemap.js.map +1 -1
- package/dist/esm/constants.js +15 -20
- package/dist/esm/constants.js.map +1 -1
- package/dist/esm/dev-server-plugin/dev-styles.js +137 -150
- package/dist/esm/dev-server-plugin/dev-styles.js.map +1 -1
- package/dist/esm/dev-server-plugin/extract-html-scripts.js +16 -15
- package/dist/esm/dev-server-plugin/extract-html-scripts.js.map +1 -1
- package/dist/esm/dev-server-plugin/plugin.js +125 -195
- package/dist/esm/dev-server-plugin/plugin.js.map +1 -1
- package/dist/esm/import-protection-plugin/ast.js +6 -5
- package/dist/esm/import-protection-plugin/ast.js.map +1 -1
- package/dist/esm/import-protection-plugin/constants.js +20 -22
- package/dist/esm/import-protection-plugin/constants.js.map +1 -1
- package/dist/esm/import-protection-plugin/defaults.js +35 -25
- package/dist/esm/import-protection-plugin/defaults.js.map +1 -1
- package/dist/esm/import-protection-plugin/extensionlessAbsoluteIdResolver.js +93 -92
- package/dist/esm/import-protection-plugin/extensionlessAbsoluteIdResolver.js.map +1 -1
- package/dist/esm/import-protection-plugin/matchers.js +23 -24
- package/dist/esm/import-protection-plugin/matchers.js.map +1 -1
- package/dist/esm/import-protection-plugin/plugin.js +1045 -1361
- package/dist/esm/import-protection-plugin/plugin.js.map +1 -1
- package/dist/esm/import-protection-plugin/postCompileUsage.js +58 -55
- package/dist/esm/import-protection-plugin/postCompileUsage.js.map +1 -1
- package/dist/esm/import-protection-plugin/rewriteDeniedImports.js +187 -259
- package/dist/esm/import-protection-plugin/rewriteDeniedImports.js.map +1 -1
- package/dist/esm/import-protection-plugin/sourceLocation.js +238 -248
- package/dist/esm/import-protection-plugin/sourceLocation.js.map +1 -1
- package/dist/esm/import-protection-plugin/trace.js +173 -184
- package/dist/esm/import-protection-plugin/trace.js.map +1 -1
- package/dist/esm/import-protection-plugin/utils.js +132 -111
- package/dist/esm/import-protection-plugin/utils.js.map +1 -1
- package/dist/esm/import-protection-plugin/virtualModules.js +216 -196
- package/dist/esm/import-protection-plugin/virtualModules.js.map +1 -1
- package/dist/esm/index.js +2 -7
- package/dist/esm/load-env-plugin/plugin.js +12 -11
- package/dist/esm/load-env-plugin/plugin.js.map +1 -1
- package/dist/esm/output-directory.js +10 -10
- package/dist/esm/output-directory.js.map +1 -1
- package/dist/esm/plugin.js +275 -355
- package/dist/esm/plugin.js.map +1 -1
- package/dist/esm/post-server-build.js +39 -53
- package/dist/esm/post-server-build.js.map +1 -1
- package/dist/esm/prerender.js +177 -239
- package/dist/esm/prerender.js.map +1 -1
- package/dist/esm/preview-server-plugin/plugin.js +41 -44
- package/dist/esm/preview-server-plugin/plugin.js.map +1 -1
- package/dist/esm/queue.js +115 -126
- package/dist/esm/queue.js.map +1 -1
- package/dist/esm/resolve-entries.js +31 -32
- package/dist/esm/resolve-entries.js.map +1 -1
- package/dist/esm/schema.js +156 -179
- package/dist/esm/schema.js.map +1 -1
- package/dist/esm/start-compiler-plugin/compiler.js +655 -812
- package/dist/esm/start-compiler-plugin/compiler.js.map +1 -1
- package/dist/esm/start-compiler-plugin/handleClientOnlyJSX.js +25 -8
- package/dist/esm/start-compiler-plugin/handleClientOnlyJSX.js.map +1 -1
- package/dist/esm/start-compiler-plugin/handleCreateIsomorphicFn.js +22 -19
- package/dist/esm/start-compiler-plugin/handleCreateIsomorphicFn.js.map +1 -1
- package/dist/esm/start-compiler-plugin/handleCreateMiddleware.js +20 -22
- package/dist/esm/start-compiler-plugin/handleCreateMiddleware.js.map +1 -1
- package/dist/esm/start-compiler-plugin/handleCreateServerFn.js +187 -255
- package/dist/esm/start-compiler-plugin/handleCreateServerFn.js.map +1 -1
- package/dist/esm/start-compiler-plugin/handleEnvOnly.js +23 -33
- package/dist/esm/start-compiler-plugin/handleEnvOnly.js.map +1 -1
- package/dist/esm/start-compiler-plugin/plugin.js +247 -291
- package/dist/esm/start-compiler-plugin/plugin.js.map +1 -1
- package/dist/esm/start-compiler-plugin/utils.js +27 -27
- package/dist/esm/start-compiler-plugin/utils.js.map +1 -1
- package/dist/esm/start-manifest-plugin/manifestBuilder.js +272 -378
- package/dist/esm/start-manifest-plugin/manifestBuilder.js.map +1 -1
- package/dist/esm/start-manifest-plugin/plugin.js +35 -44
- package/dist/esm/start-manifest-plugin/plugin.js.map +1 -1
- package/dist/esm/start-router-plugin/constants.js +6 -5
- package/dist/esm/start-router-plugin/constants.js.map +1 -1
- package/dist/esm/start-router-plugin/generator-plugins/prerender-routes-plugin.js +24 -19
- package/dist/esm/start-router-plugin/generator-plugins/prerender-routes-plugin.js.map +1 -1
- package/dist/esm/start-router-plugin/generator-plugins/routes-manifest-plugin.js +28 -29
- package/dist/esm/start-router-plugin/generator-plugins/routes-manifest-plugin.js.map +1 -1
- package/dist/esm/start-router-plugin/plugin.js +146 -199
- package/dist/esm/start-router-plugin/plugin.js.map +1 -1
- package/dist/esm/start-router-plugin/pruneServerOnlySubtrees.js +32 -31
- package/dist/esm/start-router-plugin/pruneServerOnlySubtrees.js.map +1 -1
- package/dist/esm/utils.js +14 -14
- package/dist/esm/utils.js.map +1 -1
- package/package.json +7 -7
- package/dist/esm/index.js.map +0 -1
|
@@ -1,1367 +1,1051 @@
|
|
|
1
|
-
import { normalizePath } from "vite";
|
|
2
1
|
import { resolveViteId } from "../utils.js";
|
|
3
2
|
import { VITE_ENVIRONMENT_NAMES } from "../constants.js";
|
|
4
|
-
import {
|
|
3
|
+
import { IMPORT_PROTECTION_DEBUG, SERVER_FN_LOOKUP_QUERY, VITE_BROWSER_VIRTUAL_PREFIX } from "./constants.js";
|
|
4
|
+
import { buildResolutionCandidates, buildSourceCandidates, canonicalizeResolvedId, clearNormalizeFilePathCache, debugLog, dedupePatterns, escapeRegExp, extractImportSources, getOrCreate, isInsideDirectory, matchesDebugFilter, normalizeFilePath, relativizePath, shouldDeferViolation } from "./utils.js";
|
|
5
|
+
import { ImportGraph, buildTrace, formatViolation } from "./trace.js";
|
|
5
6
|
import { getDefaultImportProtectionRules, getMarkerSpecifiers } from "./defaults.js";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { collectNamedExports, rewriteDeniedImports
|
|
9
|
-
import { loadResolvedVirtualModule, getResolvedVirtualModuleMatchers, resolveInternalVirtualModuleId, resolvedMarkerVirtualModuleId, generateSelfContainedMockModule, mockRuntimeModuleIdFromViolation, generateDevSelfDenialModule, MOCK_BUILD_PREFIX, makeMockEdgeModuleId } from "./virtualModules.js";
|
|
7
|
+
import { compileMatchers, matchesAny } from "./matchers.js";
|
|
8
|
+
import { MOCK_BUILD_PREFIX, generateDevSelfDenialModule, generateSelfContainedMockModule, getResolvedVirtualModuleMatchers, loadResolvedVirtualModule, makeMockEdgeModuleId, mockRuntimeModuleIdFromViolation, resolveInternalVirtualModuleId, resolvedMarkerVirtualModuleId } from "./virtualModules.js";
|
|
9
|
+
import { collectMockExportNamesBySource, collectNamedExports, rewriteDeniedImports } from "./rewriteDeniedImports.js";
|
|
10
10
|
import { ExtensionlessAbsoluteIdResolver } from "./extensionlessAbsoluteIdResolver.js";
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
11
|
+
import { ImportLocCache, addTraceImportLocations, buildCodeSnippet, buildLineIndex, findImportStatementLocationFromTransformed, findPostCompileUsageLocation, pickOriginalCodeFromSourcesContent } from "./sourceLocation.js";
|
|
12
|
+
import { normalizePath } from "vite";
|
|
13
|
+
//#region src/import-protection-plugin/plugin.ts
|
|
13
14
|
function importProtectionPlugin(opts) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
);
|
|
1047
|
-
if (markerInfo) {
|
|
1048
|
-
return reportOrDeferViolation(
|
|
1049
|
-
this,
|
|
1050
|
-
env,
|
|
1051
|
-
normalizedImporter,
|
|
1052
|
-
importer,
|
|
1053
|
-
markerInfo,
|
|
1054
|
-
shouldDefer,
|
|
1055
|
-
isPreTransformResolve
|
|
1056
|
-
);
|
|
1057
|
-
}
|
|
1058
|
-
}
|
|
1059
|
-
}
|
|
1060
|
-
return void 0;
|
|
1061
|
-
},
|
|
1062
|
-
load: {
|
|
1063
|
-
filter: {
|
|
1064
|
-
id: new RegExp(
|
|
1065
|
-
getResolvedVirtualModuleMatchers().map(escapeRegExp).join("|")
|
|
1066
|
-
)
|
|
1067
|
-
},
|
|
1068
|
-
handler(id) {
|
|
1069
|
-
if (IMPORT_PROTECTION_DEBUG) {
|
|
1070
|
-
if (matchesDebugFilter(id)) {
|
|
1071
|
-
debugLog("load:handler", {
|
|
1072
|
-
env: this.environment.name,
|
|
1073
|
-
id: normalizePath(id)
|
|
1074
|
-
});
|
|
1075
|
-
}
|
|
1076
|
-
}
|
|
1077
|
-
return loadResolvedVirtualModule(id);
|
|
1078
|
-
}
|
|
1079
|
-
},
|
|
1080
|
-
async generateBundle(_options, bundle) {
|
|
1081
|
-
const envName = this.environment.name;
|
|
1082
|
-
const env = envStates.get(envName);
|
|
1083
|
-
if (!env || env.deferredBuildViolations.length === 0) return;
|
|
1084
|
-
const candidateCache = /* @__PURE__ */ new Map();
|
|
1085
|
-
const toModuleIdCandidates = (id) => {
|
|
1086
|
-
let cached = candidateCache.get(id);
|
|
1087
|
-
if (cached) return cached;
|
|
1088
|
-
const out = /* @__PURE__ */ new Set();
|
|
1089
|
-
const normalized = normalizeFilePath(id);
|
|
1090
|
-
out.add(id);
|
|
1091
|
-
out.add(normalized);
|
|
1092
|
-
out.add(relativizePath(normalized, config.root));
|
|
1093
|
-
if (normalized.startsWith(VITE_BROWSER_VIRTUAL_PREFIX)) {
|
|
1094
|
-
const internal = `\0${normalized.slice(VITE_BROWSER_VIRTUAL_PREFIX.length)}`;
|
|
1095
|
-
out.add(internal);
|
|
1096
|
-
out.add(relativizePath(normalizeFilePath(internal), config.root));
|
|
1097
|
-
}
|
|
1098
|
-
if (normalized.startsWith("\0")) {
|
|
1099
|
-
const browser = `${VITE_BROWSER_VIRTUAL_PREFIX}${normalized.slice(1)}`;
|
|
1100
|
-
out.add(browser);
|
|
1101
|
-
out.add(relativizePath(normalizeFilePath(browser), config.root));
|
|
1102
|
-
}
|
|
1103
|
-
cached = Array.from(out);
|
|
1104
|
-
candidateCache.set(id, cached);
|
|
1105
|
-
return cached;
|
|
1106
|
-
};
|
|
1107
|
-
const survivingModules = /* @__PURE__ */ new Set();
|
|
1108
|
-
for (const chunk of Object.values(bundle)) {
|
|
1109
|
-
if (chunk.type === "chunk") {
|
|
1110
|
-
for (const moduleId of Object.keys(chunk.modules)) {
|
|
1111
|
-
for (const candidate of toModuleIdCandidates(moduleId)) {
|
|
1112
|
-
survivingModules.add(candidate);
|
|
1113
|
-
}
|
|
1114
|
-
}
|
|
1115
|
-
}
|
|
1116
|
-
}
|
|
1117
|
-
const didModuleSurvive = (moduleId) => toModuleIdCandidates(moduleId).some(
|
|
1118
|
-
(candidate) => survivingModules.has(candidate)
|
|
1119
|
-
);
|
|
1120
|
-
const realViolations = [];
|
|
1121
|
-
for (const {
|
|
1122
|
-
info,
|
|
1123
|
-
mockModuleId,
|
|
1124
|
-
checkModuleId
|
|
1125
|
-
} of env.deferredBuildViolations) {
|
|
1126
|
-
let survived;
|
|
1127
|
-
if (checkModuleId != null) {
|
|
1128
|
-
const importerVariantIds = /* @__PURE__ */ new Set([info.importer]);
|
|
1129
|
-
const importerKeys = env.transformResultKeysByFile.get(
|
|
1130
|
-
normalizeFilePath(info.importer)
|
|
1131
|
-
);
|
|
1132
|
-
if (importerKeys) {
|
|
1133
|
-
for (const key of importerKeys) {
|
|
1134
|
-
importerVariantIds.add(key);
|
|
1135
|
-
}
|
|
1136
|
-
}
|
|
1137
|
-
survived = false;
|
|
1138
|
-
for (const importerId of importerVariantIds) {
|
|
1139
|
-
if (didModuleSurvive(importerId)) {
|
|
1140
|
-
survived = true;
|
|
1141
|
-
break;
|
|
1142
|
-
}
|
|
1143
|
-
}
|
|
1144
|
-
} else {
|
|
1145
|
-
survived = didModuleSurvive(mockModuleId);
|
|
1146
|
-
}
|
|
1147
|
-
if (!survived) continue;
|
|
1148
|
-
if (config.onViolation) {
|
|
1149
|
-
const result = await config.onViolation(info);
|
|
1150
|
-
if (result === false) continue;
|
|
1151
|
-
}
|
|
1152
|
-
realViolations.push(info);
|
|
1153
|
-
}
|
|
1154
|
-
if (realViolations.length === 0) return;
|
|
1155
|
-
if (config.effectiveBehavior === "error") {
|
|
1156
|
-
this.error(formatViolation(realViolations[0], config.root));
|
|
1157
|
-
} else {
|
|
1158
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1159
|
-
for (const info of realViolations) {
|
|
1160
|
-
const key = dedupeKey(info);
|
|
1161
|
-
if (!seen.has(key)) {
|
|
1162
|
-
seen.add(key);
|
|
1163
|
-
this.warn(formatViolation(info, config.root));
|
|
1164
|
-
}
|
|
1165
|
-
}
|
|
1166
|
-
}
|
|
1167
|
-
}
|
|
1168
|
-
},
|
|
1169
|
-
{
|
|
1170
|
-
// Captures transformed code + composed sourcemap for location mapping.
|
|
1171
|
-
// Runs after all `enforce: 'pre'` hooks (including the Start compiler).
|
|
1172
|
-
// Only files under `srcDirectory` are cached.
|
|
1173
|
-
name: "tanstack-start-core:import-protection-transform-cache",
|
|
1174
|
-
applyToEnvironment(env) {
|
|
1175
|
-
if (!config.enabled) return false;
|
|
1176
|
-
return environmentNames.has(env.name);
|
|
1177
|
-
},
|
|
1178
|
-
transform: {
|
|
1179
|
-
filter: {
|
|
1180
|
-
id: {
|
|
1181
|
-
include: [/\.[cm]?[tj]sx?($|\?)/]
|
|
1182
|
-
}
|
|
1183
|
-
},
|
|
1184
|
-
async handler(code, id) {
|
|
1185
|
-
const envName = this.environment.name;
|
|
1186
|
-
const file = normalizeFilePath(id);
|
|
1187
|
-
const envType = getEnvType(envName);
|
|
1188
|
-
const matchers = getRulesForEnvironment(envName);
|
|
1189
|
-
const isBuild = config.command === "build";
|
|
1190
|
-
if (IMPORT_PROTECTION_DEBUG) {
|
|
1191
|
-
if (matchesDebugFilter(file)) {
|
|
1192
|
-
debugLog("transform-cache", {
|
|
1193
|
-
env: envName,
|
|
1194
|
-
id: normalizePath(id),
|
|
1195
|
-
file
|
|
1196
|
-
});
|
|
1197
|
-
}
|
|
1198
|
-
}
|
|
1199
|
-
if (!shouldCheckImporter(file)) {
|
|
1200
|
-
return void 0;
|
|
1201
|
-
}
|
|
1202
|
-
const selfFileMatch = checkFileDenial(getRelativePath(file), matchers);
|
|
1203
|
-
if (selfFileMatch) {
|
|
1204
|
-
let exportNames = [];
|
|
1205
|
-
try {
|
|
1206
|
-
exportNames = collectNamedExports(code);
|
|
1207
|
-
} catch {
|
|
1208
|
-
}
|
|
1209
|
-
if (isBuild) {
|
|
1210
|
-
return generateSelfContainedMockModule(exportNames);
|
|
1211
|
-
}
|
|
1212
|
-
const runtimeId = mockRuntimeModuleIdFromViolation(
|
|
1213
|
-
{
|
|
1214
|
-
env: envType,
|
|
1215
|
-
behavior: config.effectiveBehavior === "error" ? "error" : "mock",
|
|
1216
|
-
importer: file,
|
|
1217
|
-
specifier: relativizePath(file, config.root),
|
|
1218
|
-
pattern: selfFileMatch.pattern,
|
|
1219
|
-
message: `File "${relativizePath(file, config.root)}" is denied in the ${envType} environment`,
|
|
1220
|
-
trace: []
|
|
1221
|
-
},
|
|
1222
|
-
config.mockAccess,
|
|
1223
|
-
config.root
|
|
1224
|
-
);
|
|
1225
|
-
return generateDevSelfDenialModule(exportNames, runtimeId);
|
|
1226
|
-
}
|
|
1227
|
-
let map;
|
|
1228
|
-
try {
|
|
1229
|
-
map = this.getCombinedSourcemap();
|
|
1230
|
-
} catch {
|
|
1231
|
-
map = void 0;
|
|
1232
|
-
}
|
|
1233
|
-
let originalCode;
|
|
1234
|
-
if (map?.sourcesContent) {
|
|
1235
|
-
originalCode = pickOriginalCodeFromSourcesContent(
|
|
1236
|
-
map,
|
|
1237
|
-
file,
|
|
1238
|
-
config.root
|
|
1239
|
-
);
|
|
1240
|
-
}
|
|
1241
|
-
const lineIndex = buildLineIndex(code);
|
|
1242
|
-
const cacheKey = normalizePath(id);
|
|
1243
|
-
const envState = getEnv(envName);
|
|
1244
|
-
const isServerFnLookup = id.includes(SERVER_FN_LOOKUP_QUERY);
|
|
1245
|
-
if (isServerFnLookup) {
|
|
1246
|
-
envState.serverFnLookupModules.add(file);
|
|
1247
|
-
}
|
|
1248
|
-
const result = {
|
|
1249
|
-
code,
|
|
1250
|
-
map,
|
|
1251
|
-
originalCode,
|
|
1252
|
-
lineIndex
|
|
1253
|
-
};
|
|
1254
|
-
cacheTransformResult(envState, file, cacheKey, result);
|
|
1255
|
-
if (isBuild) return void 0;
|
|
1256
|
-
const isDevMock = config.effectiveBehavior === "mock";
|
|
1257
|
-
const importSources = extractImportSources(code);
|
|
1258
|
-
const resolvedChildren = /* @__PURE__ */ new Set();
|
|
1259
|
-
const deniedSourceReplacements = /* @__PURE__ */ new Map();
|
|
1260
|
-
for (const src of importSources) {
|
|
1261
|
-
try {
|
|
1262
|
-
const resolved = await this.resolve(src, id, { skipSelf: true });
|
|
1263
|
-
if (resolved && !resolved.external) {
|
|
1264
|
-
const resolvedPath = canonicalizeResolvedId(
|
|
1265
|
-
resolved.id,
|
|
1266
|
-
config.root,
|
|
1267
|
-
resolveExtensionlessAbsoluteId
|
|
1268
|
-
);
|
|
1269
|
-
resolvedChildren.add(resolvedPath);
|
|
1270
|
-
if (resolved.id.includes("tanstack-start-import-protection:")) {
|
|
1271
|
-
let physicalPath;
|
|
1272
|
-
const pending = envState.pendingViolations.get(file);
|
|
1273
|
-
if (pending) {
|
|
1274
|
-
const match = pending.find(
|
|
1275
|
-
(pv) => pv.info.specifier === src && pv.info.resolved
|
|
1276
|
-
);
|
|
1277
|
-
if (match) physicalPath = match.info.resolved;
|
|
1278
|
-
}
|
|
1279
|
-
if (physicalPath && physicalPath !== resolvedPath) {
|
|
1280
|
-
resolvedChildren.add(physicalPath);
|
|
1281
|
-
envState.graph.addEdge(physicalPath, file, src);
|
|
1282
|
-
}
|
|
1283
|
-
}
|
|
1284
|
-
envState.graph.addEdge(resolvedPath, file, src);
|
|
1285
|
-
if (isDevMock) {
|
|
1286
|
-
const relativePath = getRelativePath(resolvedPath);
|
|
1287
|
-
const fileMatch = checkFileDenial(relativePath, matchers);
|
|
1288
|
-
if (fileMatch) {
|
|
1289
|
-
const info = await buildFileViolationInfo(
|
|
1290
|
-
envState.transformResultProvider,
|
|
1291
|
-
envState,
|
|
1292
|
-
envName,
|
|
1293
|
-
envType,
|
|
1294
|
-
id,
|
|
1295
|
-
file,
|
|
1296
|
-
src,
|
|
1297
|
-
resolvedPath,
|
|
1298
|
-
fileMatch.pattern
|
|
1299
|
-
);
|
|
1300
|
-
const replacement = await reportOrDeferViolation(
|
|
1301
|
-
this,
|
|
1302
|
-
envState,
|
|
1303
|
-
file,
|
|
1304
|
-
id,
|
|
1305
|
-
info,
|
|
1306
|
-
isDevMock,
|
|
1307
|
-
isServerFnLookup
|
|
1308
|
-
);
|
|
1309
|
-
if (replacement) {
|
|
1310
|
-
deniedSourceReplacements.set(
|
|
1311
|
-
src,
|
|
1312
|
-
replacement.startsWith("\0") ? VITE_BROWSER_VIRTUAL_PREFIX + replacement.slice(1) : replacement
|
|
1313
|
-
);
|
|
1314
|
-
}
|
|
1315
|
-
}
|
|
1316
|
-
}
|
|
1317
|
-
}
|
|
1318
|
-
} catch {
|
|
1319
|
-
}
|
|
1320
|
-
}
|
|
1321
|
-
envState.postTransformImports.set(cacheKey, resolvedChildren);
|
|
1322
|
-
if (cacheKey !== file && !isServerFnLookup) {
|
|
1323
|
-
envState.postTransformImports.set(file, resolvedChildren);
|
|
1324
|
-
}
|
|
1325
|
-
await processPendingViolations(envState, this.warn.bind(this));
|
|
1326
|
-
if (deniedSourceReplacements.size > 0) {
|
|
1327
|
-
try {
|
|
1328
|
-
const rewritten = rewriteDeniedImports(
|
|
1329
|
-
code,
|
|
1330
|
-
id,
|
|
1331
|
-
new Set(deniedSourceReplacements.keys()),
|
|
1332
|
-
(source) => deniedSourceReplacements.get(source) ?? source
|
|
1333
|
-
);
|
|
1334
|
-
if (!rewritten) {
|
|
1335
|
-
return void 0;
|
|
1336
|
-
}
|
|
1337
|
-
const normalizedMap = rewritten.map ? {
|
|
1338
|
-
...rewritten.map,
|
|
1339
|
-
version: Number(rewritten.map.version),
|
|
1340
|
-
sourcesContent: rewritten.map.sourcesContent?.map(
|
|
1341
|
-
(s) => s ?? ""
|
|
1342
|
-
) ?? []
|
|
1343
|
-
} : {
|
|
1344
|
-
version: 3,
|
|
1345
|
-
file: id,
|
|
1346
|
-
names: [],
|
|
1347
|
-
sources: [id],
|
|
1348
|
-
sourcesContent: [code],
|
|
1349
|
-
mappings: ""
|
|
1350
|
-
};
|
|
1351
|
-
return {
|
|
1352
|
-
code: rewritten.code,
|
|
1353
|
-
map: normalizedMap
|
|
1354
|
-
};
|
|
1355
|
-
} catch {
|
|
1356
|
-
}
|
|
1357
|
-
}
|
|
1358
|
-
return void 0;
|
|
1359
|
-
}
|
|
1360
|
-
}
|
|
1361
|
-
}
|
|
1362
|
-
];
|
|
15
|
+
let devServer = null;
|
|
16
|
+
const extensionlessIdResolver = new ExtensionlessAbsoluteIdResolver();
|
|
17
|
+
const resolveExtensionlessAbsoluteId = (id) => extensionlessIdResolver.resolve(id);
|
|
18
|
+
const importPatternCache = /* @__PURE__ */ new Map();
|
|
19
|
+
function findFirstImportSpecifierIndex(code, source) {
|
|
20
|
+
let patterns = importPatternCache.get(source);
|
|
21
|
+
if (!patterns) {
|
|
22
|
+
const escaped = escapeRegExp(source);
|
|
23
|
+
patterns = [
|
|
24
|
+
new RegExp(`\\bimport\\s+(['"])${escaped}\\1`),
|
|
25
|
+
new RegExp(`\\bfrom\\s+(['"])${escaped}\\1`),
|
|
26
|
+
new RegExp(`\\bimport\\s*\\(\\s*(['"])${escaped}\\1\\s*\\)`)
|
|
27
|
+
];
|
|
28
|
+
importPatternCache.set(source, patterns);
|
|
29
|
+
}
|
|
30
|
+
let best = -1;
|
|
31
|
+
for (const re of patterns) {
|
|
32
|
+
const m = re.exec(code);
|
|
33
|
+
if (!m) continue;
|
|
34
|
+
const idx = m.index + m[0].indexOf(source);
|
|
35
|
+
if (idx === -1) continue;
|
|
36
|
+
if (best === -1 || idx < best) best = idx;
|
|
37
|
+
}
|
|
38
|
+
return best;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Build an import trace using Vite's per-environment module graph, which
|
|
42
|
+
* is authoritative even on warm starts when the plugin's own ImportGraph
|
|
43
|
+
* may be incomplete (Vite skips resolveId for cached modules).
|
|
44
|
+
*/
|
|
45
|
+
function buildTraceFromModuleGraph(envName, env, targetFile) {
|
|
46
|
+
if (!devServer) return null;
|
|
47
|
+
const environment = devServer.environments[envName];
|
|
48
|
+
if (!environment) return null;
|
|
49
|
+
const file = normalizeFilePath(targetFile);
|
|
50
|
+
const start = environment.moduleGraph.getModuleById(file);
|
|
51
|
+
if (!start) return null;
|
|
52
|
+
const nodeIds = /* @__PURE__ */ new Map();
|
|
53
|
+
function nodeId(n) {
|
|
54
|
+
let cached = nodeIds.get(n);
|
|
55
|
+
if (cached === void 0) {
|
|
56
|
+
cached = n.id ? normalizeFilePath(n.id) : n.url ? normalizeFilePath(n.url) : "";
|
|
57
|
+
nodeIds.set(n, cached);
|
|
58
|
+
}
|
|
59
|
+
return cached;
|
|
60
|
+
}
|
|
61
|
+
const queue = [start];
|
|
62
|
+
const visited = new Set([start]);
|
|
63
|
+
const parent = /* @__PURE__ */ new Map();
|
|
64
|
+
let entryRoot = null;
|
|
65
|
+
let fallbackRoot = null;
|
|
66
|
+
let qi = 0;
|
|
67
|
+
while (qi < queue.length) {
|
|
68
|
+
const node = queue[qi++];
|
|
69
|
+
const id = nodeId(node);
|
|
70
|
+
if (id && env.graph.entries.has(id)) {
|
|
71
|
+
entryRoot = node;
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
const importers = node.importers;
|
|
75
|
+
if (importers.size === 0) {
|
|
76
|
+
if (!fallbackRoot) fallbackRoot = node;
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
for (const imp of importers) {
|
|
80
|
+
if (visited.has(imp)) continue;
|
|
81
|
+
visited.add(imp);
|
|
82
|
+
parent.set(imp, node);
|
|
83
|
+
queue.push(imp);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const root = entryRoot ?? fallbackRoot;
|
|
87
|
+
if (!root) return null;
|
|
88
|
+
const chain = [];
|
|
89
|
+
let cur = root;
|
|
90
|
+
for (let i = 0; i < config.maxTraceDepth + 2 && cur; i++) {
|
|
91
|
+
chain.push(cur);
|
|
92
|
+
if (cur === start) break;
|
|
93
|
+
cur = parent.get(cur);
|
|
94
|
+
}
|
|
95
|
+
const steps = [];
|
|
96
|
+
for (let i = 0; i < chain.length; i++) {
|
|
97
|
+
const id = nodeId(chain[i]);
|
|
98
|
+
if (!id) continue;
|
|
99
|
+
let specifier;
|
|
100
|
+
if (i + 1 < chain.length) {
|
|
101
|
+
const nextId = nodeId(chain[i + 1]);
|
|
102
|
+
if (nextId) specifier = env.graph.reverseEdges.get(nextId)?.get(id);
|
|
103
|
+
}
|
|
104
|
+
steps.push(specifier ? {
|
|
105
|
+
file: id,
|
|
106
|
+
specifier
|
|
107
|
+
} : { file: id });
|
|
108
|
+
}
|
|
109
|
+
return steps.length ? steps : null;
|
|
110
|
+
}
|
|
111
|
+
const config = {
|
|
112
|
+
enabled: true,
|
|
113
|
+
root: "",
|
|
114
|
+
command: "build",
|
|
115
|
+
srcDirectory: "",
|
|
116
|
+
framework: opts.framework,
|
|
117
|
+
effectiveBehavior: "error",
|
|
118
|
+
mockAccess: "error",
|
|
119
|
+
logMode: "once",
|
|
120
|
+
maxTraceDepth: 20,
|
|
121
|
+
compiledRules: {
|
|
122
|
+
client: {
|
|
123
|
+
specifiers: [],
|
|
124
|
+
files: [],
|
|
125
|
+
excludeFiles: []
|
|
126
|
+
},
|
|
127
|
+
server: {
|
|
128
|
+
specifiers: [],
|
|
129
|
+
files: [],
|
|
130
|
+
excludeFiles: []
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
includeMatchers: [],
|
|
134
|
+
excludeMatchers: [],
|
|
135
|
+
ignoreImporterMatchers: [],
|
|
136
|
+
markerSpecifiers: {
|
|
137
|
+
serverOnly: /* @__PURE__ */ new Set(),
|
|
138
|
+
clientOnly: /* @__PURE__ */ new Set()
|
|
139
|
+
},
|
|
140
|
+
envTypeMap: new Map(opts.environments.map((e) => [e.name, e.type])),
|
|
141
|
+
onViolation: void 0
|
|
142
|
+
};
|
|
143
|
+
const envStates = /* @__PURE__ */ new Map();
|
|
144
|
+
const shared = { fileMarkerKind: /* @__PURE__ */ new Map() };
|
|
145
|
+
/**
|
|
146
|
+
* Build the best available trace for a module and enrich each step with
|
|
147
|
+
* line/column locations. Tries the plugin's own ImportGraph first, then
|
|
148
|
+
* Vite's moduleGraph (authoritative on warm start), keeping whichever is
|
|
149
|
+
* longer. Annotates the last step with the denied specifier + location.
|
|
150
|
+
*
|
|
151
|
+
* Shared by {@link buildViolationInfo} and {@link processPendingViolations}.
|
|
152
|
+
*/
|
|
153
|
+
async function rebuildAndAnnotateTrace(provider, env, envName, normalizedImporter, specifier, importerLoc, traceOverride) {
|
|
154
|
+
let trace = traceOverride ?? buildTrace(env.graph, normalizedImporter, config.maxTraceDepth);
|
|
155
|
+
if (config.command === "serve") {
|
|
156
|
+
const mgTrace = buildTraceFromModuleGraph(envName, env, normalizedImporter);
|
|
157
|
+
if (mgTrace && mgTrace.length > trace.length) trace = mgTrace;
|
|
158
|
+
}
|
|
159
|
+
await addTraceImportLocations(provider, trace, env.importLocCache, findFirstImportSpecifierIndex);
|
|
160
|
+
if (trace.length > 0) {
|
|
161
|
+
const last = trace[trace.length - 1];
|
|
162
|
+
if (!last.specifier) last.specifier = specifier;
|
|
163
|
+
if (importerLoc && last.line == null) {
|
|
164
|
+
last.line = importerLoc.line;
|
|
165
|
+
last.column = importerLoc.column;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return trace;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Build a complete {@link ViolationInfo} with trace, location, and snippet.
|
|
172
|
+
*
|
|
173
|
+
* This is the single path that all violation types go through: specifier,
|
|
174
|
+
* file, and marker.
|
|
175
|
+
*/
|
|
176
|
+
async function buildViolationInfo(provider, env, envName, envType, importer, normalizedImporter, source, overrides, traceOverride) {
|
|
177
|
+
const loc = await resolveImporterLocation(provider, env, importer, buildSourceCandidates(source, "resolved" in overrides && typeof overrides.resolved === "string" ? overrides.resolved : void 0, config.root));
|
|
178
|
+
const trace = await rebuildAndAnnotateTrace(provider, env, envName, normalizedImporter, source, loc, traceOverride);
|
|
179
|
+
const snippet = loc ? buildCodeSnippet(provider, importer, loc) : void 0;
|
|
180
|
+
return {
|
|
181
|
+
env: envName,
|
|
182
|
+
envType,
|
|
183
|
+
behavior: config.effectiveBehavior,
|
|
184
|
+
specifier: source,
|
|
185
|
+
importer: normalizedImporter,
|
|
186
|
+
...loc ? { importerLoc: loc } : {},
|
|
187
|
+
trace,
|
|
188
|
+
snippet,
|
|
189
|
+
...overrides
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
async function resolveImporterLocation(provider, env, importer, sourceCandidates) {
|
|
193
|
+
for (const candidate of sourceCandidates) {
|
|
194
|
+
const loc = await findPostCompileUsageLocation(provider, importer, candidate) || await findImportStatementLocationFromTransformed(provider, importer, candidate, env.importLocCache, findFirstImportSpecifierIndex);
|
|
195
|
+
if (loc) return loc;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Check if a resolved import violates marker restrictions (e.g. importing
|
|
200
|
+
* a server-only module in the client env). If so, build and return the
|
|
201
|
+
* {@link ViolationInfo} — the caller is responsible for reporting/deferring.
|
|
202
|
+
*
|
|
203
|
+
* Returns `undefined` when the resolved import has no marker conflict.
|
|
204
|
+
*/
|
|
205
|
+
async function buildMarkerViolationFromResolvedImport(provider, env, envName, envType, importer, source, resolvedId, relativePath, traceOverride) {
|
|
206
|
+
const normalizedResolvedId = normalizeFilePath(resolvedId);
|
|
207
|
+
const markerKind = shared.fileMarkerKind.get(normalizedResolvedId);
|
|
208
|
+
if (!(envType === "client" && markerKind === "server" || envType === "server" && markerKind === "client")) return void 0;
|
|
209
|
+
return buildViolationInfo(provider, env, envName, envType, importer, normalizeFilePath(importer), source, {
|
|
210
|
+
type: "marker",
|
|
211
|
+
resolved: normalizedResolvedId,
|
|
212
|
+
message: buildMarkerViolationMessage(relativePath, markerKind)
|
|
213
|
+
}, traceOverride);
|
|
214
|
+
}
|
|
215
|
+
function buildMarkerViolationMessage(relativePath, markerKind) {
|
|
216
|
+
return markerKind === "server" ? `Module "${relativePath}" is marked server-only but is imported in the client environment` : `Module "${relativePath}" is marked client-only but is imported in the server environment`;
|
|
217
|
+
}
|
|
218
|
+
async function buildFileViolationInfo(provider, env, envName, envType, importer, normalizedImporter, source, resolvedPath, pattern, traceOverride) {
|
|
219
|
+
return buildViolationInfo(provider, env, envName, envType, importer, normalizedImporter, source, {
|
|
220
|
+
type: "file",
|
|
221
|
+
pattern,
|
|
222
|
+
resolved: resolvedPath,
|
|
223
|
+
message: `Import "${source}" (resolved to "${getRelativePath(resolvedPath)}") is denied in the ${envType} environment`
|
|
224
|
+
}, traceOverride);
|
|
225
|
+
}
|
|
226
|
+
function getEnvType(envName) {
|
|
227
|
+
return config.envTypeMap.get(envName) ?? "server";
|
|
228
|
+
}
|
|
229
|
+
function getRulesForEnvironment(envName) {
|
|
230
|
+
return getEnvType(envName) === "client" ? config.compiledRules.client : config.compiledRules.server;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Check if a relative path matches any denied file pattern for the given
|
|
234
|
+
* environment, respecting `excludeFiles`. Returns the matching pattern
|
|
235
|
+
* or `undefined` if the file is not denied.
|
|
236
|
+
*/
|
|
237
|
+
function checkFileDenial(relativePath, matchers) {
|
|
238
|
+
if (matchers.excludeFiles.length > 0 && matchesAny(relativePath, matchers.excludeFiles)) return;
|
|
239
|
+
return matchers.files.length > 0 ? matchesAny(relativePath, matchers.files) : void 0;
|
|
240
|
+
}
|
|
241
|
+
const environmentNames = new Set([VITE_ENVIRONMENT_NAMES.client, VITE_ENVIRONMENT_NAMES.server]);
|
|
242
|
+
if (opts.providerEnvName !== VITE_ENVIRONMENT_NAMES.server) environmentNames.add(opts.providerEnvName);
|
|
243
|
+
/** Get (or lazily create) the per-env state for the given environment name. */
|
|
244
|
+
function getEnv(envName) {
|
|
245
|
+
let envState = envStates.get(envName);
|
|
246
|
+
if (!envState) {
|
|
247
|
+
const transformResultCache = /* @__PURE__ */ new Map();
|
|
248
|
+
envState = {
|
|
249
|
+
graph: new ImportGraph(),
|
|
250
|
+
mockExportsByImporter: /* @__PURE__ */ new Map(),
|
|
251
|
+
resolveCache: /* @__PURE__ */ new Map(),
|
|
252
|
+
resolveCacheByFile: /* @__PURE__ */ new Map(),
|
|
253
|
+
importLocCache: new ImportLocCache(),
|
|
254
|
+
seenViolations: /* @__PURE__ */ new Set(),
|
|
255
|
+
transformResultCache,
|
|
256
|
+
transformResultKeysByFile: /* @__PURE__ */ new Map(),
|
|
257
|
+
transformResultProvider: { getTransformResult(id) {
|
|
258
|
+
const fullKey = normalizePath(id);
|
|
259
|
+
const exact = transformResultCache.get(fullKey);
|
|
260
|
+
if (exact) return exact;
|
|
261
|
+
const strippedKey = normalizeFilePath(id);
|
|
262
|
+
return strippedKey !== fullKey ? transformResultCache.get(strippedKey) : void 0;
|
|
263
|
+
} },
|
|
264
|
+
postTransformImports: /* @__PURE__ */ new Map(),
|
|
265
|
+
serverFnLookupModules: /* @__PURE__ */ new Set(),
|
|
266
|
+
pendingViolations: /* @__PURE__ */ new Map(),
|
|
267
|
+
deferredBuildViolations: []
|
|
268
|
+
};
|
|
269
|
+
envStates.set(envName, envState);
|
|
270
|
+
}
|
|
271
|
+
return envState;
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Search a parsed export-names map for an entry matching any of the
|
|
275
|
+
* specifier candidates. Returns matching names or empty array.
|
|
276
|
+
*/
|
|
277
|
+
function findExportsInMap(exportMap, candidates) {
|
|
278
|
+
for (const candidate of candidates) {
|
|
279
|
+
const hit = exportMap.get(candidate);
|
|
280
|
+
if (hit && hit.length > 0) return hit;
|
|
281
|
+
}
|
|
282
|
+
return [];
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Build deduped resolution candidates for a module ID, including the
|
|
286
|
+
* extensionless absolute path when the ID looks like a file path.
|
|
287
|
+
*/
|
|
288
|
+
function buildIdCandidates(id, extra) {
|
|
289
|
+
const set = new Set(buildResolutionCandidates(id));
|
|
290
|
+
if (extra) {
|
|
291
|
+
for (const c of buildResolutionCandidates(extra)) set.add(c);
|
|
292
|
+
set.add(resolveExtensionlessAbsoluteId(extra));
|
|
293
|
+
}
|
|
294
|
+
return Array.from(set);
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Resolve which named exports the importer needs from a denied specifier,
|
|
298
|
+
* so mock-edge modules can provide explicit ESM named exports.
|
|
299
|
+
*
|
|
300
|
+
* Tries multiple strategies: cached export maps, AST parsing, and
|
|
301
|
+
* resolver-based comparison.
|
|
302
|
+
*/
|
|
303
|
+
async function resolveExportsForDeniedSpecifier(env, ctx, info, importerIdHint) {
|
|
304
|
+
const importerFile = normalizeFilePath(info.importer);
|
|
305
|
+
const specifierCandidates = buildIdCandidates(info.specifier, info.resolved);
|
|
306
|
+
let parsedBySource = env.mockExportsByImporter.get(importerFile);
|
|
307
|
+
if (!parsedBySource) {
|
|
308
|
+
const importerCode = env.transformResultProvider.getTransformResult(importerFile)?.code ?? (importerIdHint && ctx.getModuleInfo ? ctx.getModuleInfo(importerIdHint)?.code ?? void 0 : void 0);
|
|
309
|
+
if (typeof importerCode !== "string" || importerCode.length === 0) return [];
|
|
310
|
+
try {
|
|
311
|
+
parsedBySource = collectMockExportNamesBySource(importerCode);
|
|
312
|
+
await recordMockExportsForImporter(env, importerFile, parsedBySource, async (src) => {
|
|
313
|
+
const cacheKey = `${importerFile}:${src}`;
|
|
314
|
+
if (env.resolveCache.has(cacheKey)) return env.resolveCache.get(cacheKey) ?? void 0;
|
|
315
|
+
if (!ctx.resolve) return void 0;
|
|
316
|
+
const resolved = await ctx.resolve(src, info.importer, { skipSelf: true });
|
|
317
|
+
if (!resolved || resolved.external) return void 0;
|
|
318
|
+
return resolved.id;
|
|
319
|
+
});
|
|
320
|
+
parsedBySource = env.mockExportsByImporter.get(importerFile) ?? parsedBySource;
|
|
321
|
+
} catch {
|
|
322
|
+
return [];
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
const direct = findExportsInMap(parsedBySource, specifierCandidates);
|
|
326
|
+
if (direct.length > 0) return direct;
|
|
327
|
+
const candidateSet = new Set(specifierCandidates);
|
|
328
|
+
for (const [sourceKey, names] of parsedBySource) {
|
|
329
|
+
if (!names.length) continue;
|
|
330
|
+
const resolvedId = await resolveSourceKey(env, ctx, importerFile, sourceKey, info.importer);
|
|
331
|
+
if (!resolvedId) continue;
|
|
332
|
+
const resolvedCandidates = buildIdCandidates(resolvedId);
|
|
333
|
+
resolvedCandidates.push(resolveExtensionlessAbsoluteId(resolvedId));
|
|
334
|
+
if (resolvedCandidates.some((v) => candidateSet.has(v))) return names;
|
|
335
|
+
}
|
|
336
|
+
return [];
|
|
337
|
+
}
|
|
338
|
+
/** Best-effort resolve a source key using the cache or ctx.resolve. */
|
|
339
|
+
async function resolveSourceKey(env, ctx, importerFile, sourceKey, importerId) {
|
|
340
|
+
const cacheKey = `${importerFile}:${sourceKey}`;
|
|
341
|
+
if (env.resolveCache.has(cacheKey)) return env.resolveCache.get(cacheKey) ?? void 0;
|
|
342
|
+
if (!ctx.resolve) return void 0;
|
|
343
|
+
try {
|
|
344
|
+
const resolved = await ctx.resolve(sourceKey, importerId, { skipSelf: true });
|
|
345
|
+
if (!resolved || resolved.external) return void 0;
|
|
346
|
+
return resolved.id;
|
|
347
|
+
} catch {
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
async function recordMockExportsForImporter(env, importerId, namesBySource, resolveSource) {
|
|
352
|
+
const importerFile = normalizeFilePath(importerId);
|
|
353
|
+
if (namesBySource.size === 0) return;
|
|
354
|
+
for (const [source, names] of namesBySource) try {
|
|
355
|
+
const resolvedId = await resolveSource(source);
|
|
356
|
+
if (!resolvedId) continue;
|
|
357
|
+
namesBySource.set(normalizeFilePath(resolvedId), names);
|
|
358
|
+
namesBySource.set(resolveExtensionlessAbsoluteId(resolvedId), names);
|
|
359
|
+
} catch {}
|
|
360
|
+
const existing = env.mockExportsByImporter.get(importerFile);
|
|
361
|
+
if (!existing) {
|
|
362
|
+
env.mockExportsByImporter.set(importerFile, namesBySource);
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
for (const [source, names] of namesBySource) {
|
|
366
|
+
const prev = existing.get(source);
|
|
367
|
+
if (!prev) {
|
|
368
|
+
existing.set(source, names);
|
|
369
|
+
continue;
|
|
370
|
+
}
|
|
371
|
+
const union = new Set([...prev, ...names]);
|
|
372
|
+
existing.set(source, Array.from(union).sort());
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
const shouldCheckImporterCache = /* @__PURE__ */ new Map();
|
|
376
|
+
function shouldCheckImporter(importer) {
|
|
377
|
+
let result = shouldCheckImporterCache.get(importer);
|
|
378
|
+
if (result !== void 0) return result;
|
|
379
|
+
const relativePath = relativizePath(importer, config.root);
|
|
380
|
+
if (config.excludeMatchers.length > 0 && matchesAny(relativePath, config.excludeMatchers) || config.ignoreImporterMatchers.length > 0 && matchesAny(relativePath, config.ignoreImporterMatchers)) result = false;
|
|
381
|
+
else if (config.includeMatchers.length > 0) result = !!matchesAny(relativePath, config.includeMatchers);
|
|
382
|
+
else if (config.srcDirectory) result = isInsideDirectory(importer, config.srcDirectory);
|
|
383
|
+
else result = true;
|
|
384
|
+
shouldCheckImporterCache.set(importer, result);
|
|
385
|
+
return result;
|
|
386
|
+
}
|
|
387
|
+
function dedupeKey(info) {
|
|
388
|
+
return `${info.type}:${info.importer}:${info.specifier}:${info.resolved ?? ""}`;
|
|
389
|
+
}
|
|
390
|
+
function hasSeen(env, key) {
|
|
391
|
+
if (config.logMode === "always") return false;
|
|
392
|
+
if (env.seenViolations.has(key)) return true;
|
|
393
|
+
env.seenViolations.add(key);
|
|
394
|
+
return false;
|
|
395
|
+
}
|
|
396
|
+
function getRelativePath(absolutePath) {
|
|
397
|
+
return relativizePath(normalizePath(absolutePath), config.root);
|
|
398
|
+
}
|
|
399
|
+
/** Reset all caches on an EnvState (called from buildStart). */
|
|
400
|
+
function clearEnvState(envState) {
|
|
401
|
+
envState.resolveCache.clear();
|
|
402
|
+
envState.resolveCacheByFile.clear();
|
|
403
|
+
envState.importLocCache.clear();
|
|
404
|
+
envState.seenViolations.clear();
|
|
405
|
+
envState.transformResultCache.clear();
|
|
406
|
+
envState.transformResultKeysByFile.clear();
|
|
407
|
+
envState.postTransformImports.clear();
|
|
408
|
+
envState.serverFnLookupModules.clear();
|
|
409
|
+
envState.pendingViolations.clear();
|
|
410
|
+
envState.deferredBuildViolations.length = 0;
|
|
411
|
+
envState.graph.clear();
|
|
412
|
+
envState.mockExportsByImporter.clear();
|
|
413
|
+
}
|
|
414
|
+
/** Invalidate all env-level caches that reference a specific file. */
|
|
415
|
+
function invalidateFileFromEnv(envState, file) {
|
|
416
|
+
envState.importLocCache.deleteByFile(file);
|
|
417
|
+
const resolveKeys = envState.resolveCacheByFile.get(file);
|
|
418
|
+
if (resolveKeys) {
|
|
419
|
+
for (const key of resolveKeys) envState.resolveCache.delete(key);
|
|
420
|
+
envState.resolveCacheByFile.delete(file);
|
|
421
|
+
}
|
|
422
|
+
envState.graph.invalidate(file);
|
|
423
|
+
envState.mockExportsByImporter.delete(file);
|
|
424
|
+
envState.serverFnLookupModules.delete(file);
|
|
425
|
+
envState.pendingViolations.delete(file);
|
|
426
|
+
const transformKeys = envState.transformResultKeysByFile.get(file);
|
|
427
|
+
if (transformKeys) {
|
|
428
|
+
for (const key of transformKeys) {
|
|
429
|
+
envState.transformResultCache.delete(key);
|
|
430
|
+
envState.postTransformImports.delete(key);
|
|
431
|
+
}
|
|
432
|
+
envState.transformResultKeysByFile.delete(file);
|
|
433
|
+
} else {
|
|
434
|
+
envState.transformResultCache.delete(file);
|
|
435
|
+
envState.postTransformImports.delete(file);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
/** Store a transform result under both the cacheKey and physical file path. */
|
|
439
|
+
function cacheTransformResult(envState, file, cacheKey, result) {
|
|
440
|
+
envState.transformResultCache.set(cacheKey, result);
|
|
441
|
+
const keySet = getOrCreate(envState.transformResultKeysByFile, file, () => /* @__PURE__ */ new Set());
|
|
442
|
+
keySet.add(cacheKey);
|
|
443
|
+
if (cacheKey !== file) {
|
|
444
|
+
envState.transformResultCache.set(file, result);
|
|
445
|
+
keySet.add(file);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
/** Register known Start entrypoints as trace roots for all environments. */
|
|
449
|
+
function registerEntries() {
|
|
450
|
+
const { resolvedStartConfig } = opts.getConfig();
|
|
451
|
+
for (const envDef of opts.environments) {
|
|
452
|
+
const envState = getEnv(envDef.name);
|
|
453
|
+
if (resolvedStartConfig.routerFilePath) envState.graph.addEntry(normalizePath(resolvedStartConfig.routerFilePath));
|
|
454
|
+
if (resolvedStartConfig.startFilePath) envState.graph.addEntry(normalizePath(resolvedStartConfig.startFilePath));
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Get the merged set of post-transform imports for a file, checking all
|
|
459
|
+
* code-split variants. Returns `null` if no post-transform data exists
|
|
460
|
+
* yet (transform hasn't run).
|
|
461
|
+
*
|
|
462
|
+
* Skips `SERVER_FN_LOOKUP` variants because they contain untransformed
|
|
463
|
+
* code — the Start compiler excludes them.
|
|
464
|
+
*/
|
|
465
|
+
function getPostTransformImports(env, file) {
|
|
466
|
+
const keySet = env.transformResultKeysByFile.get(file);
|
|
467
|
+
let merged = null;
|
|
468
|
+
if (keySet) for (const k of keySet) {
|
|
469
|
+
if (k.includes(SERVER_FN_LOOKUP_QUERY)) continue;
|
|
470
|
+
const imports = env.postTransformImports.get(k);
|
|
471
|
+
if (imports) if (!merged) merged = new Set(imports);
|
|
472
|
+
else for (const v of imports) merged.add(v);
|
|
473
|
+
}
|
|
474
|
+
if (!merged) {
|
|
475
|
+
const imports = env.postTransformImports.get(file);
|
|
476
|
+
if (imports) merged = new Set(imports);
|
|
477
|
+
}
|
|
478
|
+
return merged;
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* Check whether an import edge from `parent` to `target` survived
|
|
482
|
+
* post-transform compilation.
|
|
483
|
+
*
|
|
484
|
+
* Returns:
|
|
485
|
+
* - `'live'` — target appears in a non-lookup variant's post-transform imports
|
|
486
|
+
* - `'dead'` — post-transform data exists but target is absent (compiler stripped it)
|
|
487
|
+
* - `'pending'` — transform ran but import data not yet posted
|
|
488
|
+
* - `'no-data'` — transform never ran (warm-start cached module)
|
|
489
|
+
*/
|
|
490
|
+
function checkEdgeLiveness(env, parent, target) {
|
|
491
|
+
const keySet = env.transformResultKeysByFile.get(parent);
|
|
492
|
+
let anyVariantCached = false;
|
|
493
|
+
if (keySet) for (const k of keySet) {
|
|
494
|
+
if (k.includes(SERVER_FN_LOOKUP_QUERY)) continue;
|
|
495
|
+
const imports = env.postTransformImports.get(k);
|
|
496
|
+
if (imports) {
|
|
497
|
+
anyVariantCached = true;
|
|
498
|
+
if (imports.has(target)) return "live";
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
if (!anyVariantCached) {
|
|
502
|
+
const imports = env.postTransformImports.get(parent);
|
|
503
|
+
if (imports) return imports.has(target) ? "live" : "dead";
|
|
504
|
+
return env.transformResultCache.has(parent) || (keySet ? keySet.size > 0 : false) ? "pending" : "no-data";
|
|
505
|
+
}
|
|
506
|
+
return "dead";
|
|
507
|
+
}
|
|
508
|
+
function checkPostTransformReachability(env, file) {
|
|
509
|
+
const visited = /* @__PURE__ */ new Set();
|
|
510
|
+
const queue = [file];
|
|
511
|
+
let hasUnknownEdge = false;
|
|
512
|
+
let qi = 0;
|
|
513
|
+
while (qi < queue.length) {
|
|
514
|
+
const current = queue[qi++];
|
|
515
|
+
if (visited.has(current)) continue;
|
|
516
|
+
visited.add(current);
|
|
517
|
+
if (env.graph.entries.has(current)) return "reachable";
|
|
518
|
+
const importers = env.graph.reverseEdges.get(current);
|
|
519
|
+
if (!importers) continue;
|
|
520
|
+
for (const [parent] of importers) {
|
|
521
|
+
if (visited.has(parent)) continue;
|
|
522
|
+
const liveness = checkEdgeLiveness(env, parent, current);
|
|
523
|
+
if (liveness === "live" || liveness === "no-data") queue.push(parent);
|
|
524
|
+
else if (liveness === "pending") hasUnknownEdge = true;
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
return hasUnknownEdge ? "unknown" : "unreachable";
|
|
528
|
+
}
|
|
529
|
+
/**
|
|
530
|
+
* Filter pending violations using edge-survival data. Returns the subset
|
|
531
|
+
* of violations whose resolved import survived the Start compiler (or all
|
|
532
|
+
* violations when no post-transform data is available yet).
|
|
533
|
+
*
|
|
534
|
+
* Returns `undefined` when all violations were stripped or when we must wait
|
|
535
|
+
* for post-transform data before proceeding.
|
|
536
|
+
*/
|
|
537
|
+
function filterEdgeSurvival(env, file, violations) {
|
|
538
|
+
const postTransform = getPostTransformImports(env, file);
|
|
539
|
+
if (postTransform) {
|
|
540
|
+
const surviving = violations.filter((pv) => !pv.info.resolved || postTransform.has(pv.info.resolved));
|
|
541
|
+
if (surviving.length === 0) return "all-stripped";
|
|
542
|
+
env.pendingViolations.set(file, surviving);
|
|
543
|
+
return {
|
|
544
|
+
active: surviving,
|
|
545
|
+
edgeSurvivalApplied: true
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
if (violations.some((pv) => pv.fromPreTransformResolve)) return "await-transform";
|
|
549
|
+
return {
|
|
550
|
+
active: violations,
|
|
551
|
+
edgeSurvivalApplied: false
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* Process pending violations for the given environment. Called from the
|
|
556
|
+
* transform-cache hook after each module transform is cached, because new
|
|
557
|
+
* transform data may allow us to confirm or discard pending violations.
|
|
558
|
+
*
|
|
559
|
+
* @param warnFn - `this.warn` from the transform hook context
|
|
560
|
+
*/
|
|
561
|
+
async function processPendingViolations(env, warnFn) {
|
|
562
|
+
if (env.pendingViolations.size === 0) return;
|
|
563
|
+
const toDelete = [];
|
|
564
|
+
for (const [file, violations] of env.pendingViolations) {
|
|
565
|
+
const filtered = filterEdgeSurvival(env, file, violations);
|
|
566
|
+
if (filtered === "all-stripped") {
|
|
567
|
+
toDelete.push(file);
|
|
568
|
+
continue;
|
|
569
|
+
}
|
|
570
|
+
if (filtered === "await-transform") continue;
|
|
571
|
+
const { active, edgeSurvivalApplied } = filtered;
|
|
572
|
+
const status = env.graph.entries.size > 0 ? checkPostTransformReachability(env, file) : "unknown";
|
|
573
|
+
if (status === "reachable") {
|
|
574
|
+
for (const pv of active) await emitPendingViolation(env, warnFn, pv);
|
|
575
|
+
toDelete.push(file);
|
|
576
|
+
} else if (status === "unreachable") toDelete.push(file);
|
|
577
|
+
else if (config.command === "serve") {
|
|
578
|
+
let emittedAny = false;
|
|
579
|
+
for (const pv of active) {
|
|
580
|
+
if (pv.fromPreTransformResolve) continue;
|
|
581
|
+
if (edgeSurvivalApplied || pv.info.type === "file" && !!pv.info.resolved && isInsideDirectory(pv.info.resolved, config.srcDirectory)) emittedAny = await emitPendingViolation(env, warnFn, pv) || emittedAny;
|
|
582
|
+
}
|
|
583
|
+
if (emittedAny) toDelete.push(file);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
for (const file of toDelete) env.pendingViolations.delete(file);
|
|
587
|
+
}
|
|
588
|
+
async function emitPendingViolation(env, warnFn, pv) {
|
|
589
|
+
if (!pv.info.importerLoc) {
|
|
590
|
+
const sourceCandidates = buildSourceCandidates(pv.info.specifier, pv.info.resolved, config.root);
|
|
591
|
+
const loc = await resolveImporterLocation(env.transformResultProvider, env, pv.info.importer, sourceCandidates);
|
|
592
|
+
if (loc) {
|
|
593
|
+
pv.info.importerLoc = loc;
|
|
594
|
+
pv.info.snippet = buildCodeSnippet(env.transformResultProvider, pv.info.importer, loc);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
if (hasSeen(env, dedupeKey(pv.info))) return false;
|
|
598
|
+
const freshTrace = await rebuildAndAnnotateTrace(env.transformResultProvider, env, pv.info.env, pv.info.importer, pv.info.specifier, pv.info.importerLoc);
|
|
599
|
+
if (freshTrace.length > pv.info.trace.length) pv.info.trace = freshTrace;
|
|
600
|
+
if (config.onViolation) {
|
|
601
|
+
if (await config.onViolation(pv.info) === false) return false;
|
|
602
|
+
}
|
|
603
|
+
warnFn(formatViolation(pv.info, config.root));
|
|
604
|
+
return true;
|
|
605
|
+
}
|
|
606
|
+
/**
|
|
607
|
+
* Record a violation as pending for later confirmation via graph
|
|
608
|
+
* reachability. Called from `resolveId` when `shouldDefer` is true.
|
|
609
|
+
*/
|
|
610
|
+
function deferViolation(env, importerFile, info, isPreTransformResolve) {
|
|
611
|
+
getOrCreate(env.pendingViolations, importerFile, () => []).push({
|
|
612
|
+
info,
|
|
613
|
+
fromPreTransformResolve: isPreTransformResolve
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
/** Counter for generating unique per-violation mock module IDs in build mode. */
|
|
617
|
+
let buildViolationCounter = 0;
|
|
618
|
+
async function handleViolation(ctx, env, info, importerIdHint, violationOpts) {
|
|
619
|
+
if (!violationOpts?.silent) {
|
|
620
|
+
if (config.onViolation) {
|
|
621
|
+
if (await config.onViolation(info) === false) return void 0;
|
|
622
|
+
}
|
|
623
|
+
if (config.effectiveBehavior === "error") return ctx.error(formatViolation(info, config.root));
|
|
624
|
+
if (!hasSeen(env, dedupeKey(info))) ctx.warn(formatViolation(info, config.root));
|
|
625
|
+
} else if (config.effectiveBehavior === "error" && config.command !== "build") return;
|
|
626
|
+
if (info.type === "file") return info.resolved;
|
|
627
|
+
return resolveViteId(makeMockEdgeModuleId(await resolveExportsForDeniedSpecifier(env, ctx, info, importerIdHint), config.command === "serve" ? mockRuntimeModuleIdFromViolation(info, config.mockAccess, config.root) : `${MOCK_BUILD_PREFIX}${buildViolationCounter++}`));
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* Unified violation dispatch: either defers or reports immediately.
|
|
631
|
+
*
|
|
632
|
+
* When `shouldDefer` is true (dev mock + build modes), calls
|
|
633
|
+
* `handleViolation` silently to obtain the mock module ID, then stores
|
|
634
|
+
* the violation for later verification:
|
|
635
|
+
* - Dev mock mode: all violations are deferred to `pendingViolations`
|
|
636
|
+
* for edge-survival and graph-reachability checking via
|
|
637
|
+
* `processPendingViolations`.
|
|
638
|
+
* - Build mode (mock + error): defers to `deferredBuildViolations` for
|
|
639
|
+
* tree-shaking verification in `generateBundle`.
|
|
640
|
+
*
|
|
641
|
+
* Otherwise reports immediately (dev error mode). Pre-transform
|
|
642
|
+
* resolves are silenced in error mode because they fire before the
|
|
643
|
+
* compiler runs and there is no deferred verification path.
|
|
644
|
+
*
|
|
645
|
+
* Returns the mock module ID / resolve result from `handleViolation`.
|
|
646
|
+
*/
|
|
647
|
+
async function reportOrDeferViolation(ctx, env, importerFile, importerIdHint, info, shouldDefer, isPreTransformResolve) {
|
|
648
|
+
if (shouldDefer) {
|
|
649
|
+
const result = await handleViolation(ctx, env, info, importerIdHint, { silent: true });
|
|
650
|
+
if (config.command === "build") {
|
|
651
|
+
const mockId = result ?? "";
|
|
652
|
+
env.deferredBuildViolations.push({
|
|
653
|
+
info,
|
|
654
|
+
mockModuleId: mockId,
|
|
655
|
+
checkModuleId: info.type === "marker" ? info.importer : void 0
|
|
656
|
+
});
|
|
657
|
+
} else {
|
|
658
|
+
deferViolation(env, importerFile, info, isPreTransformResolve);
|
|
659
|
+
await processPendingViolations(env, ctx.warn.bind(ctx));
|
|
660
|
+
}
|
|
661
|
+
return result;
|
|
662
|
+
}
|
|
663
|
+
return handleViolation(ctx, env, info, importerIdHint, { silent: isPreTransformResolve });
|
|
664
|
+
}
|
|
665
|
+
return [{
|
|
666
|
+
name: "tanstack-start-core:import-protection",
|
|
667
|
+
enforce: "pre",
|
|
668
|
+
applyToEnvironment(env) {
|
|
669
|
+
if (!config.enabled) return false;
|
|
670
|
+
return environmentNames.has(env.name);
|
|
671
|
+
},
|
|
672
|
+
configResolved(viteConfig) {
|
|
673
|
+
config.root = viteConfig.root;
|
|
674
|
+
config.command = viteConfig.command;
|
|
675
|
+
const { startConfig, resolvedStartConfig } = opts.getConfig();
|
|
676
|
+
config.srcDirectory = resolvedStartConfig.srcDirectory;
|
|
677
|
+
const userOpts = startConfig.importProtection;
|
|
678
|
+
if (userOpts?.enabled === false) {
|
|
679
|
+
config.enabled = false;
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
config.enabled = true;
|
|
683
|
+
const behavior = userOpts?.behavior;
|
|
684
|
+
if (typeof behavior === "string") config.effectiveBehavior = behavior;
|
|
685
|
+
else config.effectiveBehavior = viteConfig.command === "serve" ? behavior?.dev ?? "mock" : behavior?.build ?? "error";
|
|
686
|
+
config.logMode = userOpts?.log ?? "once";
|
|
687
|
+
config.mockAccess = userOpts?.mockAccess ?? "error";
|
|
688
|
+
config.maxTraceDepth = userOpts?.maxTraceDepth ?? 20;
|
|
689
|
+
if (userOpts?.onViolation) {
|
|
690
|
+
const fn = userOpts.onViolation;
|
|
691
|
+
config.onViolation = (info) => fn(info);
|
|
692
|
+
}
|
|
693
|
+
const defaults = getDefaultImportProtectionRules();
|
|
694
|
+
const pick = (user, fallback) => user ? [...user] : [...fallback];
|
|
695
|
+
const clientSpecifiers = dedupePatterns([...defaults.client.specifiers, ...userOpts?.client?.specifiers ?? []]);
|
|
696
|
+
config.compiledRules.client = {
|
|
697
|
+
specifiers: compileMatchers(clientSpecifiers),
|
|
698
|
+
files: compileMatchers(pick(userOpts?.client?.files, defaults.client.files)),
|
|
699
|
+
excludeFiles: compileMatchers(pick(userOpts?.client?.excludeFiles, defaults.client.excludeFiles))
|
|
700
|
+
};
|
|
701
|
+
config.compiledRules.server = {
|
|
702
|
+
specifiers: compileMatchers(dedupePatterns(pick(userOpts?.server?.specifiers, defaults.server.specifiers))),
|
|
703
|
+
files: compileMatchers(pick(userOpts?.server?.files, defaults.server.files)),
|
|
704
|
+
excludeFiles: compileMatchers(pick(userOpts?.server?.excludeFiles, defaults.server.excludeFiles))
|
|
705
|
+
};
|
|
706
|
+
config.includeMatchers = compileMatchers(userOpts?.include ?? []);
|
|
707
|
+
config.excludeMatchers = compileMatchers(userOpts?.exclude ?? []);
|
|
708
|
+
config.ignoreImporterMatchers = compileMatchers(userOpts?.ignoreImporters ?? []);
|
|
709
|
+
const markers = getMarkerSpecifiers();
|
|
710
|
+
config.markerSpecifiers = {
|
|
711
|
+
serverOnly: new Set(markers.serverOnly),
|
|
712
|
+
clientOnly: new Set(markers.clientOnly)
|
|
713
|
+
};
|
|
714
|
+
},
|
|
715
|
+
configureServer(server) {
|
|
716
|
+
devServer = server;
|
|
717
|
+
},
|
|
718
|
+
buildStart() {
|
|
719
|
+
if (!config.enabled) return;
|
|
720
|
+
clearNormalizeFilePathCache();
|
|
721
|
+
extensionlessIdResolver.clear();
|
|
722
|
+
importPatternCache.clear();
|
|
723
|
+
shouldCheckImporterCache.clear();
|
|
724
|
+
for (const envState of envStates.values()) clearEnvState(envState);
|
|
725
|
+
shared.fileMarkerKind.clear();
|
|
726
|
+
registerEntries();
|
|
727
|
+
},
|
|
728
|
+
hotUpdate(ctx) {
|
|
729
|
+
if (!config.enabled) return;
|
|
730
|
+
for (const mod of ctx.modules) if (mod.id) {
|
|
731
|
+
const id = mod.id;
|
|
732
|
+
const importerFile = normalizeFilePath(id);
|
|
733
|
+
extensionlessIdResolver.invalidateByFile(importerFile);
|
|
734
|
+
shared.fileMarkerKind.delete(importerFile);
|
|
735
|
+
for (const envState of envStates.values()) invalidateFileFromEnv(envState, importerFile);
|
|
736
|
+
}
|
|
737
|
+
},
|
|
738
|
+
async resolveId(source, importer, _options) {
|
|
739
|
+
const envName = this.environment.name;
|
|
740
|
+
const env = getEnv(envName);
|
|
741
|
+
const envType = getEnvType(envName);
|
|
742
|
+
const provider = env.transformResultProvider;
|
|
743
|
+
const isScanResolve = !!_options.scan;
|
|
744
|
+
if (IMPORT_PROTECTION_DEBUG) {
|
|
745
|
+
const importerPath = importer ? normalizeFilePath(importer) : "(entry)";
|
|
746
|
+
const isEntryResolve = !importer;
|
|
747
|
+
if (process.env.TSR_IMPORT_PROTECTION_DEBUG_FILTER === "entry" ? isEntryResolve : matchesDebugFilter(source, importerPath)) debugLog("resolveId", {
|
|
748
|
+
env: envName,
|
|
749
|
+
envType,
|
|
750
|
+
source,
|
|
751
|
+
importer: importerPath,
|
|
752
|
+
isEntryResolve,
|
|
753
|
+
command: config.command
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
const internalVirtualId = resolveInternalVirtualModuleId(source);
|
|
757
|
+
if (internalVirtualId) return internalVirtualId;
|
|
758
|
+
if (!importer) {
|
|
759
|
+
env.graph.addEntry(source);
|
|
760
|
+
await processPendingViolations(env, this.warn.bind(this));
|
|
761
|
+
return;
|
|
762
|
+
}
|
|
763
|
+
if (source.startsWith("\0") || source.startsWith("virtual:")) return;
|
|
764
|
+
const normalizedImporter = normalizeFilePath(importer);
|
|
765
|
+
const isDirectLookup = importer.includes(SERVER_FN_LOOKUP_QUERY);
|
|
766
|
+
if (isDirectLookup) env.serverFnLookupModules.add(normalizedImporter);
|
|
767
|
+
const isPreTransformResolve = isDirectLookup || env.serverFnLookupModules.has(normalizedImporter) || isScanResolve;
|
|
768
|
+
const isDevMock = config.command === "serve" && config.effectiveBehavior === "mock";
|
|
769
|
+
const isBuild = config.command === "build";
|
|
770
|
+
const shouldDefer = shouldDeferViolation({
|
|
771
|
+
isBuild,
|
|
772
|
+
isDevMock
|
|
773
|
+
});
|
|
774
|
+
const resolveAgainstImporter = async () => {
|
|
775
|
+
const primary = await this.resolve(source, importer, { skipSelf: true });
|
|
776
|
+
if (primary) return canonicalizeResolvedId(primary.id, config.root, resolveExtensionlessAbsoluteId);
|
|
777
|
+
return null;
|
|
778
|
+
};
|
|
779
|
+
const markerKind = config.markerSpecifiers.serverOnly.has(source) ? "server" : config.markerSpecifiers.clientOnly.has(source) ? "client" : void 0;
|
|
780
|
+
if (markerKind) {
|
|
781
|
+
const existing = shared.fileMarkerKind.get(normalizedImporter);
|
|
782
|
+
if (existing && existing !== markerKind) this.error(`[import-protection] File "${getRelativePath(normalizedImporter)}" has both server-only and client-only markers. This is not allowed.`);
|
|
783
|
+
shared.fileMarkerKind.set(normalizedImporter, markerKind);
|
|
784
|
+
const violatesEnv = envType === "client" && markerKind === "server" || envType === "server" && markerKind === "client";
|
|
785
|
+
if (violatesEnv) {
|
|
786
|
+
const info = await buildViolationInfo(provider, env, envName, envType, importer, normalizedImporter, source, {
|
|
787
|
+
type: "marker",
|
|
788
|
+
message: buildMarkerViolationMessage(getRelativePath(normalizedImporter), markerKind)
|
|
789
|
+
});
|
|
790
|
+
const markerResult = await reportOrDeferViolation(this, env, normalizedImporter, importer, info, shouldDefer, isPreTransformResolve);
|
|
791
|
+
if (isBuild && markerResult != null) return markerResult;
|
|
792
|
+
}
|
|
793
|
+
const envRetroKey = `retro-marker:${normalizedImporter}`;
|
|
794
|
+
if (violatesEnv && !env.seenViolations.has(envRetroKey)) {
|
|
795
|
+
env.seenViolations.add(envRetroKey);
|
|
796
|
+
let retroDeferred = false;
|
|
797
|
+
const importersMap = env.graph.reverseEdges.get(normalizedImporter);
|
|
798
|
+
if (importersMap && importersMap.size > 0) for (const [importerFile, specifier] of importersMap) {
|
|
799
|
+
if (!specifier) continue;
|
|
800
|
+
if (!shouldCheckImporter(importerFile)) continue;
|
|
801
|
+
const markerInfo = await buildMarkerViolationFromResolvedImport(provider, env, envName, envType, importerFile, specifier, normalizedImporter, getRelativePath(normalizedImporter));
|
|
802
|
+
if (markerInfo) {
|
|
803
|
+
deferViolation(env, importerFile, markerInfo, isPreTransformResolve);
|
|
804
|
+
retroDeferred = true;
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
if (retroDeferred) await processPendingViolations(env, this.warn.bind(this));
|
|
808
|
+
}
|
|
809
|
+
return markerKind === "server" ? resolvedMarkerVirtualModuleId("server") : resolvedMarkerVirtualModuleId("client");
|
|
810
|
+
}
|
|
811
|
+
if (!shouldCheckImporter(normalizedImporter)) return;
|
|
812
|
+
const matchers = getRulesForEnvironment(envName);
|
|
813
|
+
const specifierMatch = matchesAny(source, matchers.specifiers);
|
|
814
|
+
if (specifierMatch) {
|
|
815
|
+
if (!isPreTransformResolve) env.graph.addEdge(source, normalizedImporter, source);
|
|
816
|
+
const info = await buildViolationInfo(provider, env, envName, envType, importer, normalizedImporter, source, {
|
|
817
|
+
type: "specifier",
|
|
818
|
+
pattern: specifierMatch.pattern,
|
|
819
|
+
message: `Import "${source}" is denied in the ${envType} environment`
|
|
820
|
+
});
|
|
821
|
+
if (shouldDefer && !info.resolved) try {
|
|
822
|
+
const resolvedForInfo = await resolveAgainstImporter();
|
|
823
|
+
if (resolvedForInfo) info.resolved = resolvedForInfo;
|
|
824
|
+
} catch {}
|
|
825
|
+
return reportOrDeferViolation(this, env, normalizedImporter, importer, info, shouldDefer, isPreTransformResolve);
|
|
826
|
+
}
|
|
827
|
+
const cacheKey = `${normalizedImporter}:${source}`;
|
|
828
|
+
let resolved;
|
|
829
|
+
if (env.resolveCache.has(cacheKey)) resolved = env.resolveCache.get(cacheKey) ?? null;
|
|
830
|
+
else {
|
|
831
|
+
resolved = await resolveAgainstImporter();
|
|
832
|
+
if (resolved !== null) {
|
|
833
|
+
env.resolveCache.set(cacheKey, resolved);
|
|
834
|
+
getOrCreate(env.resolveCacheByFile, normalizedImporter, () => /* @__PURE__ */ new Set()).add(cacheKey);
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
if (resolved) {
|
|
838
|
+
const relativePath = getRelativePath(resolved);
|
|
839
|
+
if (isPreTransformResolve && !isScanResolve) env.serverFnLookupModules.add(resolved);
|
|
840
|
+
if (!isPreTransformResolve) env.graph.addEdge(resolved, normalizedImporter, source);
|
|
841
|
+
if (!(matchers.excludeFiles.length > 0 && matchesAny(relativePath, matchers.excludeFiles))) {
|
|
842
|
+
const fileMatch = matchers.files.length > 0 ? matchesAny(relativePath, matchers.files) : void 0;
|
|
843
|
+
if (fileMatch) {
|
|
844
|
+
const info = await buildFileViolationInfo(provider, env, envName, envType, importer, normalizedImporter, source, resolved, fileMatch.pattern);
|
|
845
|
+
return reportOrDeferViolation(this, env, normalizedImporter, importer, info, shouldDefer, isPreTransformResolve);
|
|
846
|
+
}
|
|
847
|
+
const markerInfo = await buildMarkerViolationFromResolvedImport(provider, env, envName, envType, importer, source, resolved, relativePath);
|
|
848
|
+
if (markerInfo) return reportOrDeferViolation(this, env, normalizedImporter, importer, markerInfo, shouldDefer, isPreTransformResolve);
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
},
|
|
852
|
+
load: {
|
|
853
|
+
filter: { id: new RegExp(getResolvedVirtualModuleMatchers().map(escapeRegExp).join("|")) },
|
|
854
|
+
handler(id) {
|
|
855
|
+
if (IMPORT_PROTECTION_DEBUG) {
|
|
856
|
+
if (matchesDebugFilter(id)) debugLog("load:handler", {
|
|
857
|
+
env: this.environment.name,
|
|
858
|
+
id: normalizePath(id)
|
|
859
|
+
});
|
|
860
|
+
}
|
|
861
|
+
return loadResolvedVirtualModule(id);
|
|
862
|
+
}
|
|
863
|
+
},
|
|
864
|
+
async generateBundle(_options, bundle) {
|
|
865
|
+
const envName = this.environment.name;
|
|
866
|
+
const env = envStates.get(envName);
|
|
867
|
+
if (!env || env.deferredBuildViolations.length === 0) return;
|
|
868
|
+
const candidateCache = /* @__PURE__ */ new Map();
|
|
869
|
+
const toModuleIdCandidates = (id) => {
|
|
870
|
+
let cached = candidateCache.get(id);
|
|
871
|
+
if (cached) return cached;
|
|
872
|
+
const out = /* @__PURE__ */ new Set();
|
|
873
|
+
const normalized = normalizeFilePath(id);
|
|
874
|
+
out.add(id);
|
|
875
|
+
out.add(normalized);
|
|
876
|
+
out.add(relativizePath(normalized, config.root));
|
|
877
|
+
if (normalized.startsWith("/@id/__x00__")) {
|
|
878
|
+
const internal = `\0${normalized.slice(VITE_BROWSER_VIRTUAL_PREFIX.length)}`;
|
|
879
|
+
out.add(internal);
|
|
880
|
+
out.add(relativizePath(normalizeFilePath(internal), config.root));
|
|
881
|
+
}
|
|
882
|
+
if (normalized.startsWith("\0")) {
|
|
883
|
+
const browser = `${VITE_BROWSER_VIRTUAL_PREFIX}${normalized.slice(1)}`;
|
|
884
|
+
out.add(browser);
|
|
885
|
+
out.add(relativizePath(normalizeFilePath(browser), config.root));
|
|
886
|
+
}
|
|
887
|
+
cached = Array.from(out);
|
|
888
|
+
candidateCache.set(id, cached);
|
|
889
|
+
return cached;
|
|
890
|
+
};
|
|
891
|
+
const survivingModules = /* @__PURE__ */ new Set();
|
|
892
|
+
for (const chunk of Object.values(bundle)) if (chunk.type === "chunk") for (const moduleId of Object.keys(chunk.modules)) for (const candidate of toModuleIdCandidates(moduleId)) survivingModules.add(candidate);
|
|
893
|
+
const didModuleSurvive = (moduleId) => toModuleIdCandidates(moduleId).some((candidate) => survivingModules.has(candidate));
|
|
894
|
+
const realViolations = [];
|
|
895
|
+
for (const { info, mockModuleId, checkModuleId } of env.deferredBuildViolations) {
|
|
896
|
+
let survived;
|
|
897
|
+
if (checkModuleId != null) {
|
|
898
|
+
const importerVariantIds = new Set([info.importer]);
|
|
899
|
+
const importerKeys = env.transformResultKeysByFile.get(normalizeFilePath(info.importer));
|
|
900
|
+
if (importerKeys) for (const key of importerKeys) importerVariantIds.add(key);
|
|
901
|
+
survived = false;
|
|
902
|
+
for (const importerId of importerVariantIds) if (didModuleSurvive(importerId)) {
|
|
903
|
+
survived = true;
|
|
904
|
+
break;
|
|
905
|
+
}
|
|
906
|
+
} else survived = didModuleSurvive(mockModuleId);
|
|
907
|
+
if (!survived) continue;
|
|
908
|
+
if (config.onViolation) {
|
|
909
|
+
if (await config.onViolation(info) === false) continue;
|
|
910
|
+
}
|
|
911
|
+
realViolations.push(info);
|
|
912
|
+
}
|
|
913
|
+
if (realViolations.length === 0) return;
|
|
914
|
+
if (config.effectiveBehavior === "error") this.error(formatViolation(realViolations[0], config.root));
|
|
915
|
+
else {
|
|
916
|
+
const seen = /* @__PURE__ */ new Set();
|
|
917
|
+
for (const info of realViolations) {
|
|
918
|
+
const key = dedupeKey(info);
|
|
919
|
+
if (!seen.has(key)) {
|
|
920
|
+
seen.add(key);
|
|
921
|
+
this.warn(formatViolation(info, config.root));
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
}, {
|
|
927
|
+
name: "tanstack-start-core:import-protection-transform-cache",
|
|
928
|
+
applyToEnvironment(env) {
|
|
929
|
+
if (!config.enabled) return false;
|
|
930
|
+
return environmentNames.has(env.name);
|
|
931
|
+
},
|
|
932
|
+
transform: {
|
|
933
|
+
filter: { id: { include: [/\.[cm]?[tj]sx?($|\?)/] } },
|
|
934
|
+
async handler(code, id) {
|
|
935
|
+
const envName = this.environment.name;
|
|
936
|
+
const file = normalizeFilePath(id);
|
|
937
|
+
const envType = getEnvType(envName);
|
|
938
|
+
const matchers = getRulesForEnvironment(envName);
|
|
939
|
+
const isBuild = config.command === "build";
|
|
940
|
+
if (IMPORT_PROTECTION_DEBUG) {
|
|
941
|
+
if (matchesDebugFilter(file)) debugLog("transform-cache", {
|
|
942
|
+
env: envName,
|
|
943
|
+
id: normalizePath(id),
|
|
944
|
+
file
|
|
945
|
+
});
|
|
946
|
+
}
|
|
947
|
+
if (!shouldCheckImporter(file)) return;
|
|
948
|
+
const selfFileMatch = checkFileDenial(getRelativePath(file), matchers);
|
|
949
|
+
if (selfFileMatch) {
|
|
950
|
+
let exportNames = [];
|
|
951
|
+
try {
|
|
952
|
+
exportNames = collectNamedExports(code);
|
|
953
|
+
} catch {}
|
|
954
|
+
if (isBuild) return generateSelfContainedMockModule(exportNames);
|
|
955
|
+
const runtimeId = mockRuntimeModuleIdFromViolation({
|
|
956
|
+
type: "file",
|
|
957
|
+
env: envType,
|
|
958
|
+
envType,
|
|
959
|
+
behavior: config.effectiveBehavior === "error" ? "error" : "mock",
|
|
960
|
+
importer: file,
|
|
961
|
+
specifier: relativizePath(file, config.root),
|
|
962
|
+
resolved: file,
|
|
963
|
+
pattern: selfFileMatch.pattern,
|
|
964
|
+
message: `File "${relativizePath(file, config.root)}" is denied in the ${envType} environment`,
|
|
965
|
+
trace: []
|
|
966
|
+
}, config.mockAccess, config.root);
|
|
967
|
+
return generateDevSelfDenialModule(exportNames, runtimeId);
|
|
968
|
+
}
|
|
969
|
+
let map;
|
|
970
|
+
try {
|
|
971
|
+
map = this.getCombinedSourcemap();
|
|
972
|
+
} catch {
|
|
973
|
+
map = void 0;
|
|
974
|
+
}
|
|
975
|
+
let originalCode;
|
|
976
|
+
if (map?.sourcesContent) originalCode = pickOriginalCodeFromSourcesContent(map, file, config.root);
|
|
977
|
+
const lineIndex = buildLineIndex(code);
|
|
978
|
+
const cacheKey = normalizePath(id);
|
|
979
|
+
const envState = getEnv(envName);
|
|
980
|
+
const isServerFnLookup = id.includes(SERVER_FN_LOOKUP_QUERY);
|
|
981
|
+
if (isServerFnLookup) envState.serverFnLookupModules.add(file);
|
|
982
|
+
cacheTransformResult(envState, file, cacheKey, {
|
|
983
|
+
code,
|
|
984
|
+
map,
|
|
985
|
+
originalCode,
|
|
986
|
+
lineIndex
|
|
987
|
+
});
|
|
988
|
+
if (isBuild) return void 0;
|
|
989
|
+
const isDevMock = config.effectiveBehavior === "mock";
|
|
990
|
+
const importSources = extractImportSources(code);
|
|
991
|
+
const resolvedChildren = /* @__PURE__ */ new Set();
|
|
992
|
+
const deniedSourceReplacements = /* @__PURE__ */ new Map();
|
|
993
|
+
for (const src of importSources) try {
|
|
994
|
+
const resolved = await this.resolve(src, id, { skipSelf: true });
|
|
995
|
+
if (resolved && !resolved.external) {
|
|
996
|
+
const resolvedPath = canonicalizeResolvedId(resolved.id, config.root, resolveExtensionlessAbsoluteId);
|
|
997
|
+
resolvedChildren.add(resolvedPath);
|
|
998
|
+
if (resolved.id.includes("tanstack-start-import-protection:")) {
|
|
999
|
+
let physicalPath;
|
|
1000
|
+
const pending = envState.pendingViolations.get(file);
|
|
1001
|
+
if (pending) {
|
|
1002
|
+
const match = pending.find((pv) => pv.info.specifier === src && pv.info.resolved);
|
|
1003
|
+
if (match) physicalPath = match.info.resolved;
|
|
1004
|
+
}
|
|
1005
|
+
if (physicalPath && physicalPath !== resolvedPath) {
|
|
1006
|
+
resolvedChildren.add(physicalPath);
|
|
1007
|
+
envState.graph.addEdge(physicalPath, file, src);
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
envState.graph.addEdge(resolvedPath, file, src);
|
|
1011
|
+
if (isDevMock) {
|
|
1012
|
+
const fileMatch = checkFileDenial(getRelativePath(resolvedPath), matchers);
|
|
1013
|
+
if (fileMatch) {
|
|
1014
|
+
const info = await buildFileViolationInfo(envState.transformResultProvider, envState, envName, envType, id, file, src, resolvedPath, fileMatch.pattern);
|
|
1015
|
+
const replacement = await reportOrDeferViolation(this, envState, file, id, info, isDevMock, isServerFnLookup);
|
|
1016
|
+
if (replacement) deniedSourceReplacements.set(src, replacement.startsWith("\0") ? VITE_BROWSER_VIRTUAL_PREFIX + replacement.slice(1) : replacement);
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
} catch {}
|
|
1021
|
+
envState.postTransformImports.set(cacheKey, resolvedChildren);
|
|
1022
|
+
if (cacheKey !== file && !isServerFnLookup) envState.postTransformImports.set(file, resolvedChildren);
|
|
1023
|
+
await processPendingViolations(envState, this.warn.bind(this));
|
|
1024
|
+
if (deniedSourceReplacements.size > 0) try {
|
|
1025
|
+
const rewritten = rewriteDeniedImports(code, id, new Set(deniedSourceReplacements.keys()), (source) => deniedSourceReplacements.get(source) ?? source);
|
|
1026
|
+
if (!rewritten) return;
|
|
1027
|
+
const normalizedMap = rewritten.map ? {
|
|
1028
|
+
...rewritten.map,
|
|
1029
|
+
version: Number(rewritten.map.version),
|
|
1030
|
+
sourcesContent: rewritten.map.sourcesContent?.map((s) => s ?? "") ?? []
|
|
1031
|
+
} : {
|
|
1032
|
+
version: 3,
|
|
1033
|
+
file: id,
|
|
1034
|
+
names: [],
|
|
1035
|
+
sources: [id],
|
|
1036
|
+
sourcesContent: [code],
|
|
1037
|
+
mappings: ""
|
|
1038
|
+
};
|
|
1039
|
+
return {
|
|
1040
|
+
code: rewritten.code,
|
|
1041
|
+
map: normalizedMap
|
|
1042
|
+
};
|
|
1043
|
+
} catch {}
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
}];
|
|
1363
1047
|
}
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
//# sourceMappingURL=plugin.js.map
|
|
1048
|
+
//#endregion
|
|
1049
|
+
export { importProtectionPlugin };
|
|
1050
|
+
|
|
1051
|
+
//# sourceMappingURL=plugin.js.map
|