@tanstack/start-plugin-core 1.160.1 → 1.161.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/dist/esm/dev-server-plugin/plugin.js +1 -1
  2. package/dist/esm/dev-server-plugin/plugin.js.map +1 -1
  3. package/dist/esm/import-protection-plugin/defaults.d.ts +17 -0
  4. package/dist/esm/import-protection-plugin/defaults.js +36 -0
  5. package/dist/esm/import-protection-plugin/defaults.js.map +1 -0
  6. package/dist/esm/import-protection-plugin/matchers.d.ts +13 -0
  7. package/dist/esm/import-protection-plugin/matchers.js +31 -0
  8. package/dist/esm/import-protection-plugin/matchers.js.map +1 -0
  9. package/dist/esm/import-protection-plugin/plugin.d.ts +16 -0
  10. package/dist/esm/import-protection-plugin/plugin.js +699 -0
  11. package/dist/esm/import-protection-plugin/plugin.js.map +1 -0
  12. package/dist/esm/import-protection-plugin/postCompileUsage.d.ts +11 -0
  13. package/dist/esm/import-protection-plugin/postCompileUsage.js +177 -0
  14. package/dist/esm/import-protection-plugin/postCompileUsage.js.map +1 -0
  15. package/dist/esm/import-protection-plugin/rewriteDeniedImports.d.ts +27 -0
  16. package/dist/esm/import-protection-plugin/rewriteDeniedImports.js +51 -0
  17. package/dist/esm/import-protection-plugin/rewriteDeniedImports.js.map +1 -0
  18. package/dist/esm/import-protection-plugin/sourceLocation.d.ts +132 -0
  19. package/dist/esm/import-protection-plugin/sourceLocation.js +255 -0
  20. package/dist/esm/import-protection-plugin/sourceLocation.js.map +1 -0
  21. package/dist/esm/import-protection-plugin/trace.d.ts +67 -0
  22. package/dist/esm/import-protection-plugin/trace.js +204 -0
  23. package/dist/esm/import-protection-plugin/trace.js.map +1 -0
  24. package/dist/esm/import-protection-plugin/utils.d.ts +8 -0
  25. package/dist/esm/import-protection-plugin/utils.js +29 -0
  26. package/dist/esm/import-protection-plugin/utils.js.map +1 -0
  27. package/dist/esm/import-protection-plugin/virtualModules.d.ts +25 -0
  28. package/dist/esm/import-protection-plugin/virtualModules.js +235 -0
  29. package/dist/esm/import-protection-plugin/virtualModules.js.map +1 -0
  30. package/dist/esm/plugin.js +7 -0
  31. package/dist/esm/plugin.js.map +1 -1
  32. package/dist/esm/prerender.js +3 -3
  33. package/dist/esm/prerender.js.map +1 -1
  34. package/dist/esm/schema.d.ts +260 -0
  35. package/dist/esm/schema.js +35 -1
  36. package/dist/esm/schema.js.map +1 -1
  37. package/dist/esm/start-compiler-plugin/compiler.js +5 -1
  38. package/dist/esm/start-compiler-plugin/compiler.js.map +1 -1
  39. package/dist/esm/start-compiler-plugin/handleCreateServerFn.js +2 -2
  40. package/dist/esm/start-compiler-plugin/handleCreateServerFn.js.map +1 -1
  41. package/dist/esm/start-compiler-plugin/plugin.js.map +1 -1
  42. package/dist/esm/start-router-plugin/plugin.js +5 -5
  43. package/dist/esm/start-router-plugin/plugin.js.map +1 -1
  44. package/package.json +6 -3
  45. package/src/dev-server-plugin/plugin.ts +1 -1
  46. package/src/import-protection-plugin/defaults.ts +56 -0
  47. package/src/import-protection-plugin/matchers.ts +48 -0
  48. package/src/import-protection-plugin/plugin.ts +1173 -0
  49. package/src/import-protection-plugin/postCompileUsage.ts +266 -0
  50. package/src/import-protection-plugin/rewriteDeniedImports.ts +255 -0
  51. package/src/import-protection-plugin/sourceLocation.ts +524 -0
  52. package/src/import-protection-plugin/trace.ts +296 -0
  53. package/src/import-protection-plugin/utils.ts +32 -0
  54. package/src/import-protection-plugin/virtualModules.ts +300 -0
  55. package/src/plugin.ts +7 -0
  56. package/src/schema.ts +58 -0
  57. package/src/start-compiler-plugin/compiler.ts +12 -1
  58. package/src/start-compiler-plugin/plugin.ts +3 -3
@@ -0,0 +1,699 @@
1
+ import * as path from "pathe";
2
+ import { normalizePath } from "vite";
3
+ import { resolveViteId } from "../utils.js";
4
+ import { VITE_ENVIRONMENT_NAMES } from "../constants.js";
5
+ import { formatViolation, ImportGraph, buildTrace } from "./trace.js";
6
+ import { getDefaultImportProtectionRules, getMarkerSpecifiers } from "./defaults.js";
7
+ import { findPostCompileUsagePos } from "./postCompileUsage.js";
8
+ import { matchesAny, compileMatchers } from "./matchers.js";
9
+ import { normalizeFilePath, dedupePatterns } from "./utils.js";
10
+ import { collectMockExportNamesBySource } from "./rewriteDeniedImports.js";
11
+ import { RESOLVED_MOCK_MODULE_ID, loadSilentMockModule, RESOLVED_MOCK_EDGE_PREFIX, loadMockEdgeModule, RESOLVED_MOCK_RUNTIME_PREFIX, loadMockRuntimeModule, RESOLVED_MARKER_PREFIX, loadMarkerModule, MOCK_MODULE_ID, MOCK_EDGE_PREFIX, MOCK_RUNTIME_PREFIX, MARKER_PREFIX, mockRuntimeModuleIdFromViolation, makeMockEdgeModuleId } from "./virtualModules.js";
12
+ import { pickOriginalCodeFromSourcesContent, buildLineIndex, addTraceImportLocations, findPostCompileUsageLocation, findImportStatementLocationFromTransformed, buildCodeSnippet } from "./sourceLocation.js";
13
+ function importProtectionPlugin(opts) {
14
+ const config = {
15
+ enabled: true,
16
+ root: "",
17
+ command: "build",
18
+ srcDirectory: "",
19
+ framework: opts.framework,
20
+ effectiveBehavior: "error",
21
+ mockAccess: "error",
22
+ logMode: "once",
23
+ maxTraceDepth: 20,
24
+ compiledRules: {
25
+ client: { specifiers: [], files: [] },
26
+ server: { specifiers: [], files: [] }
27
+ },
28
+ includeMatchers: [],
29
+ excludeMatchers: [],
30
+ ignoreImporterMatchers: [],
31
+ markerSpecifiers: { serverOnly: /* @__PURE__ */ new Set(), clientOnly: /* @__PURE__ */ new Set() },
32
+ envTypeMap: new Map(opts.environments.map((e) => [e.name, e.type])),
33
+ onViolation: void 0
34
+ };
35
+ const envStates = /* @__PURE__ */ new Map();
36
+ const shared = { fileMarkerKind: /* @__PURE__ */ new Map() };
37
+ function createImportLocCache(env) {
38
+ const cache = /* @__PURE__ */ new Map();
39
+ const originalSet = cache.set.bind(cache);
40
+ cache.set = function(key, value) {
41
+ originalSet(key, value);
42
+ const sepIdx = key.indexOf("::");
43
+ if (sepIdx !== -1) {
44
+ const file = key.slice(0, sepIdx);
45
+ let fileKeys = env.importLocByFile.get(file);
46
+ if (!fileKeys) {
47
+ fileKeys = /* @__PURE__ */ new Set();
48
+ env.importLocByFile.set(file, fileKeys);
49
+ }
50
+ fileKeys.add(key);
51
+ }
52
+ return this;
53
+ };
54
+ return cache;
55
+ }
56
+ function getMockEdgeExports(env, importerId, source) {
57
+ const importerFile = normalizeFilePath(importerId);
58
+ return env.mockExportsByImporter.get(importerFile)?.get(source) ?? [];
59
+ }
60
+ function getMarkerKindForFile(fileId) {
61
+ const file = normalizeFilePath(fileId);
62
+ return shared.fileMarkerKind.get(file);
63
+ }
64
+ function getTransformResultProvider(env) {
65
+ return {
66
+ getTransformResult(id) {
67
+ const fullKey = normalizePath(id);
68
+ const exact = env.transformResultCache.get(fullKey);
69
+ if (exact) return exact;
70
+ const strippedKey = normalizeFilePath(id);
71
+ return strippedKey !== fullKey ? env.transformResultCache.get(strippedKey) : void 0;
72
+ }
73
+ };
74
+ }
75
+ async function buildViolationInfo(provider, env, envName, envType, importer, normalizedImporter, source, overrides) {
76
+ const trace = buildTrace(
77
+ env.graph,
78
+ normalizedImporter,
79
+ config.maxTraceDepth
80
+ );
81
+ await addTraceImportLocations(provider, trace, env.importLocCache);
82
+ const loc = await findPostCompileUsageLocation(
83
+ provider,
84
+ importer,
85
+ source,
86
+ findPostCompileUsagePos
87
+ ) || await findImportStatementLocationFromTransformed(
88
+ provider,
89
+ importer,
90
+ source,
91
+ env.importLocCache
92
+ );
93
+ if (trace.length > 0) {
94
+ const last = trace[trace.length - 1];
95
+ if (!last.specifier) last.specifier = source;
96
+ if (loc && last.line == null) {
97
+ last.line = loc.line;
98
+ last.column = loc.column;
99
+ }
100
+ }
101
+ const snippet = loc ? buildCodeSnippet(provider, importer, loc) : void 0;
102
+ return {
103
+ env: envName,
104
+ envType,
105
+ behavior: config.effectiveBehavior,
106
+ specifier: source,
107
+ importer: normalizedImporter,
108
+ ...loc ? { importerLoc: loc } : {},
109
+ trace,
110
+ snippet,
111
+ ...overrides
112
+ };
113
+ }
114
+ async function maybeReportMarkerViolationFromResolvedImport(ctx, provider, env, envName, envType, importer, source, resolvedId, relativePath) {
115
+ const markerKind = getMarkerKindForFile(resolvedId);
116
+ const violates = envType === "client" && markerKind === "server" || envType === "server" && markerKind === "client";
117
+ if (!violates) return void 0;
118
+ const normalizedImporter = normalizeFilePath(importer);
119
+ const info = await buildViolationInfo(
120
+ provider,
121
+ env,
122
+ envName,
123
+ envType,
124
+ importer,
125
+ normalizedImporter,
126
+ source,
127
+ {
128
+ type: "marker",
129
+ resolved: normalizeFilePath(resolvedId),
130
+ message: 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`
131
+ }
132
+ );
133
+ return handleViolation.call(ctx, env, info);
134
+ }
135
+ function buildMockEdgeModuleId(env, importerId, source, runtimeId) {
136
+ const exports = getMockEdgeExports(env, importerId, source);
137
+ return makeMockEdgeModuleId(exports, source, runtimeId);
138
+ }
139
+ function getEnvType(envName) {
140
+ return config.envTypeMap.get(envName) ?? "server";
141
+ }
142
+ function getRulesForEnvironment(envName) {
143
+ const type = getEnvType(envName);
144
+ return type === "client" ? config.compiledRules.client : config.compiledRules.server;
145
+ }
146
+ const environmentNames = /* @__PURE__ */ new Set([
147
+ VITE_ENVIRONMENT_NAMES.client,
148
+ VITE_ENVIRONMENT_NAMES.server
149
+ ]);
150
+ if (opts.providerEnvName !== VITE_ENVIRONMENT_NAMES.server) {
151
+ environmentNames.add(opts.providerEnvName);
152
+ }
153
+ function getEnv(envName) {
154
+ let envState = envStates.get(envName);
155
+ if (!envState) {
156
+ const importLocByFile = /* @__PURE__ */ new Map();
157
+ envState = {
158
+ graph: new ImportGraph(),
159
+ deniedSources: /* @__PURE__ */ new Set(),
160
+ deniedEdges: /* @__PURE__ */ new Map(),
161
+ mockExportsByImporter: /* @__PURE__ */ new Map(),
162
+ resolveCache: /* @__PURE__ */ new Map(),
163
+ resolveCacheByFile: /* @__PURE__ */ new Map(),
164
+ importLocCache: /* @__PURE__ */ new Map(),
165
+ // placeholder, replaced below
166
+ importLocByFile,
167
+ seenViolations: /* @__PURE__ */ new Set(),
168
+ transformResultCache: /* @__PURE__ */ new Map(),
169
+ transformResultKeysByFile: /* @__PURE__ */ new Map()
170
+ };
171
+ envState.importLocCache = createImportLocCache(envState);
172
+ envStates.set(envName, envState);
173
+ }
174
+ return envState;
175
+ }
176
+ function shouldCheckImporter(importer) {
177
+ const relativePath = path.relative(config.root, importer);
178
+ if (config.excludeMatchers.length > 0 && matchesAny(relativePath, config.excludeMatchers)) {
179
+ return false;
180
+ }
181
+ if (config.ignoreImporterMatchers.length > 0 && matchesAny(relativePath, config.ignoreImporterMatchers)) {
182
+ return false;
183
+ }
184
+ if (config.includeMatchers.length > 0) {
185
+ return !!matchesAny(relativePath, config.includeMatchers);
186
+ }
187
+ if (config.srcDirectory) {
188
+ return importer.startsWith(config.srcDirectory);
189
+ }
190
+ return true;
191
+ }
192
+ function dedupeKey(type, importer, specifier, resolved) {
193
+ return `${type}:${importer}:${specifier}:${resolved ?? ""}`;
194
+ }
195
+ function hasSeen(env, key) {
196
+ if (config.logMode === "always") return false;
197
+ if (env.seenViolations.has(key)) return true;
198
+ env.seenViolations.add(key);
199
+ return false;
200
+ }
201
+ function getRelativePath(absolutePath) {
202
+ return normalizePath(path.relative(config.root, absolutePath));
203
+ }
204
+ return [
205
+ {
206
+ name: "tanstack-start-core:import-protection",
207
+ enforce: "pre",
208
+ applyToEnvironment(env) {
209
+ if (!config.enabled) return false;
210
+ return environmentNames.has(env.name);
211
+ },
212
+ configResolved(viteConfig) {
213
+ config.root = viteConfig.root;
214
+ config.command = viteConfig.command;
215
+ const { startConfig, resolvedStartConfig } = opts.getConfig();
216
+ config.srcDirectory = resolvedStartConfig.srcDirectory;
217
+ const userOpts = startConfig.importProtection;
218
+ if (userOpts?.enabled === false) {
219
+ config.enabled = false;
220
+ return;
221
+ }
222
+ config.enabled = true;
223
+ if (userOpts?.behavior) {
224
+ if (typeof userOpts.behavior === "string") {
225
+ config.effectiveBehavior = userOpts.behavior;
226
+ } else {
227
+ config.effectiveBehavior = viteConfig.command === "serve" ? userOpts.behavior.dev ?? "mock" : userOpts.behavior.build ?? "error";
228
+ }
229
+ } else {
230
+ config.effectiveBehavior = viteConfig.command === "serve" ? "mock" : "error";
231
+ }
232
+ config.logMode = userOpts?.log ?? "once";
233
+ config.mockAccess = userOpts?.mockAccess ?? "error";
234
+ config.maxTraceDepth = userOpts?.maxTraceDepth ?? 20;
235
+ config.onViolation = userOpts?.onViolation;
236
+ const defaults = getDefaultImportProtectionRules(opts.framework);
237
+ const clientSpecifiers = dedupePatterns([
238
+ ...defaults.client.specifiers,
239
+ ...userOpts?.client?.specifiers ?? []
240
+ ]);
241
+ const clientFiles = userOpts?.client?.files ? [...userOpts.client.files] : [...defaults.client.files];
242
+ const serverSpecifiers = userOpts?.server?.specifiers ? dedupePatterns([...userOpts.server.specifiers]) : dedupePatterns([...defaults.server.specifiers]);
243
+ const serverFiles = userOpts?.server?.files ? [...userOpts.server.files] : [...defaults.server.files];
244
+ config.compiledRules.client = {
245
+ specifiers: compileMatchers(clientSpecifiers),
246
+ files: compileMatchers(clientFiles)
247
+ };
248
+ config.compiledRules.server = {
249
+ specifiers: compileMatchers(serverSpecifiers),
250
+ files: compileMatchers(serverFiles)
251
+ };
252
+ if (userOpts?.include) {
253
+ config.includeMatchers = compileMatchers(userOpts.include);
254
+ }
255
+ if (userOpts?.exclude) {
256
+ config.excludeMatchers = compileMatchers(userOpts.exclude);
257
+ }
258
+ if (userOpts?.ignoreImporters) {
259
+ config.ignoreImporterMatchers = compileMatchers(
260
+ userOpts.ignoreImporters
261
+ );
262
+ }
263
+ const markers = getMarkerSpecifiers(opts.framework);
264
+ config.markerSpecifiers = {
265
+ serverOnly: new Set(markers.serverOnly),
266
+ clientOnly: new Set(markers.clientOnly)
267
+ };
268
+ for (const envDef of opts.environments) {
269
+ const envState = getEnv(envDef.name);
270
+ if (resolvedStartConfig.routerFilePath) {
271
+ envState.graph.addEntry(
272
+ normalizePath(resolvedStartConfig.routerFilePath)
273
+ );
274
+ }
275
+ if (resolvedStartConfig.startFilePath) {
276
+ envState.graph.addEntry(
277
+ normalizePath(resolvedStartConfig.startFilePath)
278
+ );
279
+ }
280
+ }
281
+ },
282
+ buildStart() {
283
+ if (!config.enabled) return;
284
+ for (const envState of envStates.values()) {
285
+ envState.resolveCache.clear();
286
+ envState.resolveCacheByFile.clear();
287
+ envState.importLocCache.clear();
288
+ envState.importLocByFile.clear();
289
+ envState.seenViolations.clear();
290
+ envState.transformResultCache.clear();
291
+ envState.transformResultKeysByFile.clear();
292
+ envState.graph.clear();
293
+ envState.deniedSources.clear();
294
+ envState.deniedEdges.clear();
295
+ envState.mockExportsByImporter.clear();
296
+ }
297
+ shared.fileMarkerKind.clear();
298
+ for (const envDef of opts.environments) {
299
+ const envState = getEnv(envDef.name);
300
+ const { resolvedStartConfig } = opts.getConfig();
301
+ if (resolvedStartConfig.routerFilePath) {
302
+ envState.graph.addEntry(
303
+ normalizePath(resolvedStartConfig.routerFilePath)
304
+ );
305
+ }
306
+ if (resolvedStartConfig.startFilePath) {
307
+ envState.graph.addEntry(
308
+ normalizePath(resolvedStartConfig.startFilePath)
309
+ );
310
+ }
311
+ }
312
+ },
313
+ hotUpdate(ctx) {
314
+ if (!config.enabled) return;
315
+ for (const mod of ctx.modules) {
316
+ if (mod.id) {
317
+ const id = mod.id;
318
+ const importerFile = normalizeFilePath(id);
319
+ shared.fileMarkerKind.delete(importerFile);
320
+ for (const envState of envStates.values()) {
321
+ const locKeys = envState.importLocByFile.get(importerFile);
322
+ if (locKeys) {
323
+ for (const key of locKeys) {
324
+ envState.importLocCache.delete(key);
325
+ }
326
+ envState.importLocByFile.delete(importerFile);
327
+ }
328
+ const resolveKeys = envState.resolveCacheByFile.get(importerFile);
329
+ if (resolveKeys) {
330
+ for (const key of resolveKeys) {
331
+ envState.resolveCache.delete(key);
332
+ }
333
+ envState.resolveCacheByFile.delete(importerFile);
334
+ }
335
+ envState.graph.invalidate(importerFile);
336
+ envState.deniedEdges.delete(importerFile);
337
+ envState.mockExportsByImporter.delete(importerFile);
338
+ const transformKeys = envState.transformResultKeysByFile.get(importerFile);
339
+ if (transformKeys) {
340
+ for (const key of transformKeys) {
341
+ envState.transformResultCache.delete(key);
342
+ }
343
+ envState.transformResultKeysByFile.delete(importerFile);
344
+ } else {
345
+ envState.transformResultCache.delete(importerFile);
346
+ }
347
+ }
348
+ }
349
+ }
350
+ },
351
+ async resolveId(source, importer, _options) {
352
+ if (!config.enabled) return void 0;
353
+ const envName = this.environment.name;
354
+ const env = getEnv(envName);
355
+ const envType = getEnvType(envName);
356
+ const provider = getTransformResultProvider(env);
357
+ if (source === MOCK_MODULE_ID) {
358
+ return RESOLVED_MOCK_MODULE_ID;
359
+ }
360
+ if (source.startsWith(MOCK_EDGE_PREFIX)) {
361
+ return resolveViteId(source);
362
+ }
363
+ if (source.startsWith(MOCK_RUNTIME_PREFIX)) {
364
+ return resolveViteId(source);
365
+ }
366
+ if (source.startsWith(MARKER_PREFIX)) {
367
+ return resolveViteId(source);
368
+ }
369
+ if (!importer) {
370
+ env.graph.addEntry(source);
371
+ return void 0;
372
+ }
373
+ if (source.startsWith("\0") || source.startsWith("virtual:")) {
374
+ return void 0;
375
+ }
376
+ if (config.markerSpecifiers.serverOnly.has(source)) {
377
+ const resolvedImporter = normalizeFilePath(importer);
378
+ const existing = shared.fileMarkerKind.get(resolvedImporter);
379
+ if (existing && existing !== "server") {
380
+ this.error(
381
+ `[import-protection] File "${getRelativePath(resolvedImporter)}" has both server-only and client-only markers. This is not allowed.`
382
+ );
383
+ }
384
+ shared.fileMarkerKind.set(resolvedImporter, "server");
385
+ if (envType === "client") {
386
+ const info = await buildViolationInfo(
387
+ provider,
388
+ env,
389
+ envName,
390
+ envType,
391
+ importer,
392
+ resolvedImporter,
393
+ source,
394
+ {
395
+ type: "marker",
396
+ message: `Module "${getRelativePath(resolvedImporter)}" is marked server-only but is imported in the client environment`
397
+ }
398
+ );
399
+ handleViolation.call(this, env, info);
400
+ }
401
+ return resolveViteId(`${MARKER_PREFIX}server-only`);
402
+ }
403
+ if (config.markerSpecifiers.clientOnly.has(source)) {
404
+ const resolvedImporter = normalizeFilePath(importer);
405
+ const existing = shared.fileMarkerKind.get(resolvedImporter);
406
+ if (existing && existing !== "client") {
407
+ this.error(
408
+ `[import-protection] File "${getRelativePath(resolvedImporter)}" has both server-only and client-only markers. This is not allowed.`
409
+ );
410
+ }
411
+ shared.fileMarkerKind.set(resolvedImporter, "client");
412
+ if (envType === "server") {
413
+ const info = await buildViolationInfo(
414
+ provider,
415
+ env,
416
+ envName,
417
+ envType,
418
+ importer,
419
+ resolvedImporter,
420
+ source,
421
+ {
422
+ type: "marker",
423
+ message: `Module "${getRelativePath(resolvedImporter)}" is marked client-only but is imported in the server environment`
424
+ }
425
+ );
426
+ handleViolation.call(this, env, info);
427
+ }
428
+ return resolveViteId(`${MARKER_PREFIX}client-only`);
429
+ }
430
+ const normalizedImporter = normalizeFilePath(importer);
431
+ if (!shouldCheckImporter(normalizedImporter)) {
432
+ return void 0;
433
+ }
434
+ const matchers = getRulesForEnvironment(envName);
435
+ const specifierMatch = matchesAny(source, matchers.specifiers);
436
+ if (specifierMatch) {
437
+ env.graph.addEdge(source, normalizedImporter, source);
438
+ const info = await buildViolationInfo(
439
+ provider,
440
+ env,
441
+ envName,
442
+ envType,
443
+ importer,
444
+ normalizedImporter,
445
+ source,
446
+ {
447
+ type: "specifier",
448
+ pattern: specifierMatch.pattern,
449
+ message: `Import "${source}" is denied in the "${envName}" environment`
450
+ }
451
+ );
452
+ return handleViolation.call(this, env, info);
453
+ }
454
+ const cacheKey = `${normalizedImporter}:${source}`;
455
+ let resolved;
456
+ if (env.resolveCache.has(cacheKey)) {
457
+ resolved = env.resolveCache.get(cacheKey) || null;
458
+ } else {
459
+ const result = await this.resolve(source, importer, {
460
+ skipSelf: true
461
+ });
462
+ resolved = result ? normalizeFilePath(result.id) : null;
463
+ env.resolveCache.set(cacheKey, resolved);
464
+ let fileKeys = env.resolveCacheByFile.get(normalizedImporter);
465
+ if (!fileKeys) {
466
+ fileKeys = /* @__PURE__ */ new Set();
467
+ env.resolveCacheByFile.set(normalizedImporter, fileKeys);
468
+ }
469
+ fileKeys.add(cacheKey);
470
+ }
471
+ if (resolved) {
472
+ const relativePath = getRelativePath(resolved);
473
+ env.graph.addEdge(resolved, normalizedImporter, source);
474
+ const fileMatch = matchers.files.length > 0 ? matchesAny(relativePath, matchers.files) : void 0;
475
+ if (fileMatch) {
476
+ const info = await buildViolationInfo(
477
+ provider,
478
+ env,
479
+ envName,
480
+ envType,
481
+ importer,
482
+ normalizedImporter,
483
+ source,
484
+ {
485
+ type: "file",
486
+ pattern: fileMatch.pattern,
487
+ resolved,
488
+ message: `Import "${source}" (resolved to "${relativePath}") is denied in the "${envName}" environment`
489
+ }
490
+ );
491
+ return handleViolation.call(this, env, info);
492
+ }
493
+ const markerRes = await maybeReportMarkerViolationFromResolvedImport(
494
+ this,
495
+ provider,
496
+ env,
497
+ envName,
498
+ envType,
499
+ importer,
500
+ source,
501
+ resolved,
502
+ relativePath
503
+ );
504
+ if (markerRes !== void 0) {
505
+ return markerRes;
506
+ }
507
+ }
508
+ return void 0;
509
+ },
510
+ load: {
511
+ filter: {
512
+ id: new RegExp(
513
+ `(${RESOLVED_MOCK_MODULE_ID.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}|${RESOLVED_MARKER_PREFIX.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}|${RESOLVED_MOCK_EDGE_PREFIX.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}|${RESOLVED_MOCK_RUNTIME_PREFIX.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")})`
514
+ )
515
+ },
516
+ handler(id) {
517
+ if (!config.enabled) return void 0;
518
+ if (id === RESOLVED_MOCK_MODULE_ID) {
519
+ return loadSilentMockModule();
520
+ }
521
+ if (id.startsWith(RESOLVED_MOCK_EDGE_PREFIX)) {
522
+ return loadMockEdgeModule(
523
+ id.slice(RESOLVED_MOCK_EDGE_PREFIX.length)
524
+ );
525
+ }
526
+ if (id.startsWith(RESOLVED_MOCK_RUNTIME_PREFIX)) {
527
+ return loadMockRuntimeModule(
528
+ id.slice(RESOLVED_MOCK_RUNTIME_PREFIX.length)
529
+ );
530
+ }
531
+ if (id.startsWith(RESOLVED_MARKER_PREFIX)) {
532
+ return loadMarkerModule();
533
+ }
534
+ return void 0;
535
+ }
536
+ }
537
+ },
538
+ {
539
+ // This plugin runs WITHOUT `enforce` so it executes after all
540
+ // `enforce: 'pre'` transform hooks (including the Start compiler).
541
+ // It captures the transformed code + composed sourcemap for every module
542
+ // so that the `resolveId` hook (in the main plugin above) can look up
543
+ // the importer's transform result and map violation locations back to
544
+ // original source.
545
+ //
546
+ // Why not use `ctx.load()` in `resolveId`?
547
+ // - Vite dev: `this.load()` returns a ModuleInfo proxy that throws on
548
+ // `.code` access — code is not exposed.
549
+ // - Rollup build: `ModuleInfo` has `.code` but NOT `.map`, so we
550
+ // can't map generated positions back to original source.
551
+ //
552
+ // By caching in the transform hook we get both code and the composed
553
+ // sourcemap that chains all the way back to the original file.
554
+ //
555
+ // Performance: only files under `srcDirectory` are cached because only
556
+ // those can be importers in a violation. Third-party code in
557
+ // node_modules is never checked.
558
+ name: "tanstack-start-core:import-protection-transform-cache",
559
+ applyToEnvironment(env) {
560
+ if (!config.enabled) return false;
561
+ return environmentNames.has(env.name);
562
+ },
563
+ transform: {
564
+ filter: {
565
+ id: {
566
+ include: [/\.[cm]?[tj]sx?($|\?)/]
567
+ }
568
+ },
569
+ handler(code, id) {
570
+ if (!config.enabled) return void 0;
571
+ const envName = this.environment.name;
572
+ const file = normalizeFilePath(id);
573
+ if (!shouldCheckImporter(file)) {
574
+ return void 0;
575
+ }
576
+ let map;
577
+ try {
578
+ map = this.getCombinedSourcemap();
579
+ } catch {
580
+ map = void 0;
581
+ }
582
+ let originalCode;
583
+ if (map?.sourcesContent) {
584
+ originalCode = pickOriginalCodeFromSourcesContent(
585
+ map,
586
+ file,
587
+ config.root
588
+ );
589
+ }
590
+ const lineIndex = buildLineIndex(code);
591
+ const cacheKey = normalizePath(id);
592
+ const envState = getEnv(envName);
593
+ envState.transformResultCache.set(cacheKey, {
594
+ code,
595
+ map,
596
+ originalCode,
597
+ lineIndex
598
+ });
599
+ let keySet = envState.transformResultKeysByFile.get(file);
600
+ if (!keySet) {
601
+ keySet = /* @__PURE__ */ new Set();
602
+ envState.transformResultKeysByFile.set(file, keySet);
603
+ }
604
+ keySet.add(cacheKey);
605
+ if (cacheKey !== file) {
606
+ envState.transformResultCache.set(file, {
607
+ code,
608
+ map,
609
+ originalCode,
610
+ lineIndex
611
+ });
612
+ keySet.add(file);
613
+ }
614
+ return void 0;
615
+ }
616
+ }
617
+ },
618
+ {
619
+ // Separate plugin so the transform can be enabled/disabled per-environment.
620
+ name: "tanstack-start-core:import-protection-mock-rewrite",
621
+ enforce: "pre",
622
+ // Only needed during dev. In build, we rely on Rollup's syntheticNamedExports.
623
+ apply: "serve",
624
+ applyToEnvironment(env) {
625
+ if (!config.enabled) return false;
626
+ if (config.effectiveBehavior !== "mock") return false;
627
+ return environmentNames.has(env.name);
628
+ },
629
+ transform: {
630
+ filter: {
631
+ id: {
632
+ include: [/\.[cm]?[tj]sx?($|\?)/]
633
+ }
634
+ },
635
+ handler(code, id) {
636
+ if (!config.enabled) return void 0;
637
+ const envName = this.environment.name;
638
+ const envState = envStates.get(envName);
639
+ if (!envState) return void 0;
640
+ try {
641
+ const importerFile = normalizeFilePath(id);
642
+ envState.mockExportsByImporter.set(
643
+ importerFile,
644
+ collectMockExportNamesBySource(code)
645
+ );
646
+ } catch {
647
+ }
648
+ return void 0;
649
+ }
650
+ }
651
+ }
652
+ ];
653
+ function handleViolation(env, info) {
654
+ const key = dedupeKey(
655
+ info.type,
656
+ info.importer,
657
+ info.specifier,
658
+ info.resolved
659
+ );
660
+ if (config.onViolation) {
661
+ const result = config.onViolation(info);
662
+ if (result === false) {
663
+ return void 0;
664
+ }
665
+ }
666
+ const seen = hasSeen(env, key);
667
+ if (config.effectiveBehavior === "error") {
668
+ if (!seen) this.error(formatViolation(info, config.root));
669
+ return void 0;
670
+ }
671
+ if (!seen) {
672
+ this.warn(formatViolation(info, config.root));
673
+ }
674
+ env.deniedSources.add(info.specifier);
675
+ let edgeSet = env.deniedEdges.get(info.importer);
676
+ if (!edgeSet) {
677
+ edgeSet = /* @__PURE__ */ new Set();
678
+ env.deniedEdges.set(info.importer, edgeSet);
679
+ }
680
+ edgeSet.add(info.specifier);
681
+ if (config.command === "serve") {
682
+ const runtimeId = mockRuntimeModuleIdFromViolation(
683
+ info,
684
+ config.mockAccess,
685
+ config.root
686
+ );
687
+ return resolveViteId(
688
+ buildMockEdgeModuleId(env, info.importer, info.specifier, runtimeId)
689
+ );
690
+ }
691
+ return { id: RESOLVED_MOCK_MODULE_ID, syntheticNamedExports: true };
692
+ }
693
+ }
694
+ export {
695
+ RESOLVED_MOCK_MODULE_ID,
696
+ dedupePatterns,
697
+ importProtectionPlugin
698
+ };
699
+ //# sourceMappingURL=plugin.js.map