@tanstack/start-plugin-core 1.160.2 → 1.161.1

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 +9 -6
  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 @@
1
+ {"version":3,"file":"plugin.js","sources":["../../../src/import-protection-plugin/plugin.ts"],"sourcesContent":["import * as path from 'pathe'\nimport { normalizePath } from 'vite'\n\nimport { resolveViteId } from '../utils'\nimport { VITE_ENVIRONMENT_NAMES } from '../constants'\nimport { ImportGraph, buildTrace, formatViolation } from './trace'\nimport {\n getDefaultImportProtectionRules,\n getMarkerSpecifiers,\n} from './defaults'\nimport { findPostCompileUsagePos } from './postCompileUsage'\nimport { compileMatchers, matchesAny } from './matchers'\nimport { dedupePatterns, normalizeFilePath } from './utils'\nimport { collectMockExportNamesBySource } from './rewriteDeniedImports'\nimport {\n MARKER_PREFIX,\n MOCK_EDGE_PREFIX,\n MOCK_MODULE_ID,\n MOCK_RUNTIME_PREFIX,\n RESOLVED_MARKER_PREFIX,\n RESOLVED_MOCK_EDGE_PREFIX,\n RESOLVED_MOCK_MODULE_ID,\n RESOLVED_MOCK_RUNTIME_PREFIX,\n loadMarkerModule,\n loadMockEdgeModule,\n loadMockRuntimeModule,\n loadSilentMockModule,\n makeMockEdgeModuleId,\n mockRuntimeModuleIdFromViolation,\n} from './virtualModules'\nimport {\n addTraceImportLocations,\n buildCodeSnippet,\n buildLineIndex,\n findImportStatementLocationFromTransformed,\n findPostCompileUsageLocation,\n pickOriginalCodeFromSourcesContent,\n} from './sourceLocation'\nimport type { PluginOption } from 'vite'\nimport type { CompiledMatcher } from './matchers'\nimport type { ViolationInfo } from './trace'\nimport type {\n SourceMapLike,\n TransformResult,\n TransformResultProvider,\n} from './sourceLocation'\nimport type {\n ImportProtectionBehavior,\n ImportProtectionOptions,\n} from '../schema'\nimport type { CompileStartFrameworkOptions, GetConfigFn } from '../types'\n\n// Re-export public API that tests and other consumers depend on.\nexport { RESOLVED_MOCK_MODULE_ID } from './virtualModules'\nexport { rewriteDeniedImports } from './rewriteDeniedImports'\nexport { dedupePatterns } from './utils'\nexport type { Pattern } from './utils'\n\n/**\n * Immutable plugin configuration — set once in `configResolved`, never mutated\n * per-env or per-request afterward.\n */\ninterface PluginConfig {\n enabled: boolean\n root: string\n command: 'build' | 'serve'\n srcDirectory: string\n framework: CompileStartFrameworkOptions\n\n effectiveBehavior: ImportProtectionBehavior\n mockAccess: 'error' | 'warn' | 'off'\n logMode: 'once' | 'always'\n maxTraceDepth: number\n\n compiledRules: {\n client: {\n specifiers: Array<CompiledMatcher>\n files: Array<CompiledMatcher>\n }\n server: {\n specifiers: Array<CompiledMatcher>\n files: Array<CompiledMatcher>\n }\n }\n includeMatchers: Array<CompiledMatcher>\n excludeMatchers: Array<CompiledMatcher>\n ignoreImporterMatchers: Array<CompiledMatcher>\n\n markerSpecifiers: { serverOnly: Set<string>; clientOnly: Set<string> }\n envTypeMap: Map<string, 'client' | 'server'>\n\n onViolation?: (info: ViolationInfo) => boolean | void\n}\n\n/**\n * Per-Vite-environment mutable state. One instance per environment name,\n * stored in `envStates: Map<string, EnvState>`.\n *\n * All caches that previously lived on `PluginState` with `${envName}:` key\n * prefixes now live here without any prefix.\n */\ninterface EnvState {\n graph: ImportGraph\n /** Specifiers that resolved to the mock module (for transform-time rewriting). */\n deniedSources: Set<string>\n /** Per-importer denied edges (for dev ESM mock modules). */\n deniedEdges: Map<string, Set<string>>\n /**\n * During `vite dev` in mock mode, we generate a per-importer mock module that\n * exports the names the importer expects.\n * Populated in the transform hook (no disk reads).\n */\n mockExportsByImporter: Map<string, Map<string, Array<string>>>\n\n /** Resolve cache. Key: `${normalizedImporter}:${source}` (no env prefix). */\n resolveCache: Map<string, string | null>\n /** Reverse index: file path → Set of resolveCache keys involving that file. */\n resolveCacheByFile: Map<string, Set<string>>\n\n /** Import location cache. Key: `${importerFile}::${source}`. */\n importLocCache: Map<\n string,\n { file?: string; line: number; column: number } | null\n >\n /** Reverse index: file path → Set of importLocCache keys for that file. */\n importLocByFile: Map<string, Set<string>>\n\n /** Deduplication of logged violations (no env prefix in key). */\n seenViolations: Set<string>\n\n /** Transform result cache (code + composed sourcemap + original source). */\n transformResultCache: Map<string, TransformResult>\n /** Reverse index: physical file path → Set of transformResultCache keys. */\n transformResultKeysByFile: Map<string, Set<string>>\n}\n\n/**\n * Intentionally cross-env shared mutable state.\n *\n * A file's `'use server'`/`'use client'` directive is inherent to the file\n * content, not the environment that happens to discover it first.\n */\ninterface SharedState {\n fileMarkerKind: Map<string, 'server' | 'client'>\n}\n\nexport interface ImportProtectionPluginOptions {\n getConfig: GetConfigFn\n framework: CompileStartFrameworkOptions\n environments: Array<{ name: string; type: 'client' | 'server' }>\n providerEnvName: string\n}\n\nexport function importProtectionPlugin(\n opts: ImportProtectionPluginOptions,\n): PluginOption {\n const config: PluginConfig = {\n enabled: true,\n root: '',\n command: 'build',\n srcDirectory: '',\n framework: opts.framework,\n effectiveBehavior: 'error',\n mockAccess: 'error',\n logMode: 'once',\n maxTraceDepth: 20,\n compiledRules: {\n client: { specifiers: [], files: [] },\n server: { specifiers: [], files: [] },\n },\n includeMatchers: [],\n excludeMatchers: [],\n ignoreImporterMatchers: [],\n markerSpecifiers: { serverOnly: new Set(), clientOnly: new Set() },\n envTypeMap: new Map(opts.environments.map((e) => [e.name, e.type])),\n onViolation: undefined,\n }\n\n const envStates = new Map<string, EnvState>()\n const shared: SharedState = { fileMarkerKind: new Map() }\n\n // ---------------------------------------------------------------------------\n // Internal helpers\n // ---------------------------------------------------------------------------\n\n /**\n * Create a per-env `importLocCache` whose `.set` method automatically\n * maintains the reverse index (`importLocByFile`) for O(1) invalidation\n * in `hotUpdate`.\n *\n * Cache keys have the format `${importerFile}::${source}`.\n */\n function createImportLocCache(\n env: EnvState,\n ): Map<string, { file?: string; line: number; column: number } | null> {\n const cache = new Map<\n string,\n { file?: string; line: number; column: number } | null\n >()\n const originalSet = cache.set.bind(cache)\n cache.set = function (key, value) {\n originalSet(key, value)\n const sepIdx = key.indexOf('::')\n if (sepIdx !== -1) {\n const file = key.slice(0, sepIdx)\n let fileKeys = env.importLocByFile.get(file)\n if (!fileKeys) {\n fileKeys = new Set()\n env.importLocByFile.set(file, fileKeys)\n }\n fileKeys.add(key)\n }\n return this\n }\n return cache\n }\n\n function getMockEdgeExports(\n env: EnvState,\n importerId: string,\n source: string,\n ): Array<string> {\n const importerFile = normalizeFilePath(importerId)\n return env.mockExportsByImporter.get(importerFile)?.get(source) ?? []\n }\n\n function getMarkerKindForFile(\n fileId: string,\n ): 'server' | 'client' | undefined {\n const file = normalizeFilePath(fileId)\n return shared.fileMarkerKind.get(file)\n }\n\n /**\n * Build a {@link TransformResultProvider} for the given environment.\n *\n * The provider reads from the transform result cache that is populated by\n * the `tanstack-start-core:import-protection-transform-cache` plugin's\n * transform hook.\n */\n function getTransformResultProvider(env: EnvState): TransformResultProvider {\n return {\n getTransformResult(id: string) {\n // Try the full normalized ID first (preserves query params like\n // ?tsr-split=component for virtual modules).\n const fullKey = normalizePath(id)\n const exact = env.transformResultCache.get(fullKey)\n if (exact) return exact\n\n // Fall back to the query-stripped path for modules looked up by\n // their physical file path (e.g. trace steps, modules without\n // query params).\n const strippedKey = normalizeFilePath(id)\n return strippedKey !== fullKey\n ? env.transformResultCache.get(strippedKey)\n : undefined\n },\n }\n }\n\n type ViolationReporter = {\n warn: (msg: string) => void\n error: (msg: string) => never\n }\n\n /**\n * Build a complete {@link ViolationInfo} with trace, location, and snippet.\n *\n * This is the single path that all violation types go through: specifier,\n * file, and marker. Centralizing it eliminates the duplicated sequences of\n * `buildTrace` → `addTraceImportLocations` → location lookup → annotate →\n * snippet that previously appeared 5 times in the codebase.\n */\n async function buildViolationInfo(\n provider: TransformResultProvider,\n env: EnvState,\n envName: string,\n envType: 'client' | 'server',\n importer: string,\n normalizedImporter: string,\n source: string,\n overrides: Omit<\n ViolationInfo,\n | 'env'\n | 'envType'\n | 'behavior'\n | 'specifier'\n | 'importer'\n | 'trace'\n | 'snippet'\n | 'importerLoc'\n >,\n ): Promise<ViolationInfo> {\n const trace = buildTrace(\n env.graph,\n normalizedImporter,\n config.maxTraceDepth,\n )\n await addTraceImportLocations(provider, trace, env.importLocCache)\n\n const loc =\n (await findPostCompileUsageLocation(\n provider,\n importer,\n source,\n findPostCompileUsagePos,\n )) ||\n (await findImportStatementLocationFromTransformed(\n provider,\n importer,\n source,\n env.importLocCache,\n ))\n\n // Annotate the last trace step with the denied import's specifier and\n // location so every trace step (including the leaf) gets file:line:col.\n if (trace.length > 0) {\n const last = trace[trace.length - 1]!\n if (!last.specifier) last.specifier = source\n if (loc && last.line == null) {\n last.line = loc.line\n last.column = loc.column\n }\n }\n\n const snippet = loc ? buildCodeSnippet(provider, importer, loc) : undefined\n\n return {\n env: envName,\n envType,\n behavior: config.effectiveBehavior,\n specifier: source,\n importer: normalizedImporter,\n ...(loc ? { importerLoc: loc } : {}),\n trace,\n snippet,\n ...overrides,\n }\n }\n\n async function maybeReportMarkerViolationFromResolvedImport(\n ctx: ViolationReporter,\n provider: TransformResultProvider,\n env: EnvState,\n envName: string,\n envType: 'client' | 'server',\n importer: string,\n source: string,\n resolvedId: string,\n relativePath: string,\n ): Promise<ReturnType<typeof handleViolation> | undefined> {\n const markerKind = getMarkerKindForFile(resolvedId)\n const violates =\n (envType === 'client' && markerKind === 'server') ||\n (envType === 'server' && markerKind === 'client')\n if (!violates) return undefined\n\n const normalizedImporter = normalizeFilePath(importer)\n\n const info = await buildViolationInfo(\n provider,\n env,\n envName,\n envType,\n importer,\n normalizedImporter,\n source,\n {\n type: 'marker',\n resolved: normalizeFilePath(resolvedId),\n message:\n markerKind === 'server'\n ? `Module \"${relativePath}\" is marked server-only but is imported in the client environment`\n : `Module \"${relativePath}\" is marked client-only but is imported in the server environment`,\n },\n )\n\n return handleViolation.call(ctx, env, info)\n }\n\n function buildMockEdgeModuleId(\n env: EnvState,\n importerId: string,\n source: string,\n runtimeId: string,\n ): string {\n const exports = getMockEdgeExports(env, importerId, source)\n return makeMockEdgeModuleId(exports, source, runtimeId)\n }\n\n function getEnvType(envName: string): 'client' | 'server' {\n return config.envTypeMap.get(envName) ?? 'server'\n }\n\n function getRulesForEnvironment(envName: string): {\n specifiers: Array<CompiledMatcher>\n files: Array<CompiledMatcher>\n } {\n const type = getEnvType(envName)\n return type === 'client'\n ? config.compiledRules.client\n : config.compiledRules.server\n }\n\n const environmentNames = new Set<string>([\n VITE_ENVIRONMENT_NAMES.client,\n VITE_ENVIRONMENT_NAMES.server,\n ])\n if (opts.providerEnvName !== VITE_ENVIRONMENT_NAMES.server) {\n environmentNames.add(opts.providerEnvName)\n }\n\n /** Get (or lazily create) the per-env state for the given environment name. */\n function getEnv(envName: string): EnvState {\n let envState = envStates.get(envName)\n if (!envState) {\n const importLocByFile = new Map<string, Set<string>>()\n envState = {\n graph: new ImportGraph(),\n deniedSources: new Set(),\n deniedEdges: new Map(),\n mockExportsByImporter: new Map(),\n resolveCache: new Map(),\n resolveCacheByFile: new Map(),\n importLocCache: new Map(), // placeholder, replaced below\n importLocByFile,\n seenViolations: new Set(),\n transformResultCache: new Map(),\n transformResultKeysByFile: new Map(),\n }\n // Install reverse-index-maintaining importLocCache\n envState.importLocCache = createImportLocCache(envState)\n envStates.set(envName, envState)\n }\n return envState\n }\n\n function shouldCheckImporter(importer: string): boolean {\n // Normalize for matching\n const relativePath = path.relative(config.root, importer)\n\n // Check exclude first\n if (\n config.excludeMatchers.length > 0 &&\n matchesAny(relativePath, config.excludeMatchers)\n ) {\n return false\n }\n\n // Check ignore importers\n if (\n config.ignoreImporterMatchers.length > 0 &&\n matchesAny(relativePath, config.ignoreImporterMatchers)\n ) {\n return false\n }\n\n // Check include\n if (config.includeMatchers.length > 0) {\n return !!matchesAny(relativePath, config.includeMatchers)\n }\n\n // Default: check if within srcDirectory\n if (config.srcDirectory) {\n return importer.startsWith(config.srcDirectory)\n }\n\n return true\n }\n\n function dedupeKey(\n type: string,\n importer: string,\n specifier: string,\n resolved?: string,\n ): string {\n return `${type}:${importer}:${specifier}:${resolved ?? ''}`\n }\n\n function hasSeen(env: EnvState, key: string): boolean {\n if (config.logMode === 'always') return false\n if (env.seenViolations.has(key)) return true\n env.seenViolations.add(key)\n return false\n }\n\n function getRelativePath(absolutePath: string): string {\n return normalizePath(path.relative(config.root, absolutePath))\n }\n\n // ---------------------------------------------------------------------------\n // Vite plugins\n // ---------------------------------------------------------------------------\n\n return [\n {\n name: 'tanstack-start-core:import-protection',\n enforce: 'pre',\n\n applyToEnvironment(env) {\n if (!config.enabled) return false\n // Start's environments are named `client` and `ssr` (not `server`), plus\n // an optional serverFn provider environment (eg `rsc`) when configured.\n return environmentNames.has(env.name)\n },\n\n configResolved(viteConfig) {\n config.root = viteConfig.root\n config.command = viteConfig.command\n\n const { startConfig, resolvedStartConfig } = opts.getConfig()\n config.srcDirectory = resolvedStartConfig.srcDirectory\n\n const userOpts: ImportProtectionOptions | undefined =\n startConfig.importProtection\n\n // Determine if plugin is enabled\n if (userOpts?.enabled === false) {\n config.enabled = false\n return\n }\n\n config.enabled = true\n\n // Determine effective behavior\n if (userOpts?.behavior) {\n if (typeof userOpts.behavior === 'string') {\n config.effectiveBehavior = userOpts.behavior\n } else {\n config.effectiveBehavior =\n viteConfig.command === 'serve'\n ? (userOpts.behavior.dev ?? 'mock')\n : (userOpts.behavior.build ?? 'error')\n }\n } else {\n // Defaults: dev='mock', build='error'\n config.effectiveBehavior =\n viteConfig.command === 'serve' ? 'mock' : 'error'\n }\n\n // Log mode\n config.logMode = userOpts?.log ?? 'once'\n\n // Mock runtime access diagnostics\n config.mockAccess = userOpts?.mockAccess ?? 'error'\n\n // Max trace depth\n config.maxTraceDepth = userOpts?.maxTraceDepth ?? 20\n\n // User callback\n config.onViolation = userOpts?.onViolation as\n | ((info: ViolationInfo) => boolean | void)\n | undefined\n\n // Get default rules\n const defaults = getDefaultImportProtectionRules(opts.framework)\n\n // Merge user rules with defaults and compile matchers per env.\n // IMPORTANT: client specifier denies for Start server entrypoints must\n // always include the framework defaults even when the user provides a\n // custom list.\n const clientSpecifiers = dedupePatterns([\n ...defaults.client.specifiers,\n ...(userOpts?.client?.specifiers ?? []),\n ])\n\n // For file patterns, user config overrides defaults.\n const clientFiles = userOpts?.client?.files\n ? [...userOpts.client.files]\n : [...defaults.client.files]\n const serverSpecifiers = userOpts?.server?.specifiers\n ? dedupePatterns([...userOpts.server.specifiers])\n : dedupePatterns([...defaults.server.specifiers])\n const serverFiles = userOpts?.server?.files\n ? [...userOpts.server.files]\n : [...defaults.server.files]\n\n config.compiledRules.client = {\n specifiers: compileMatchers(clientSpecifiers),\n files: compileMatchers(clientFiles),\n }\n config.compiledRules.server = {\n specifiers: compileMatchers(serverSpecifiers),\n files: compileMatchers(serverFiles),\n }\n\n // Include/exclude\n if (userOpts?.include) {\n config.includeMatchers = compileMatchers(userOpts.include)\n }\n if (userOpts?.exclude) {\n config.excludeMatchers = compileMatchers(userOpts.exclude)\n }\n if (userOpts?.ignoreImporters) {\n config.ignoreImporterMatchers = compileMatchers(\n userOpts.ignoreImporters,\n )\n }\n\n // Marker specifiers\n const markers = getMarkerSpecifiers(opts.framework)\n config.markerSpecifiers = {\n serverOnly: new Set(markers.serverOnly),\n clientOnly: new Set(markers.clientOnly),\n }\n\n // Use known Start env entrypoints as trace roots.\n // This makes traces deterministic and prevents 1-line traces.\n for (const envDef of opts.environments) {\n const envState = getEnv(envDef.name)\n\n if (resolvedStartConfig.routerFilePath) {\n envState.graph.addEntry(\n normalizePath(resolvedStartConfig.routerFilePath),\n )\n }\n if (resolvedStartConfig.startFilePath) {\n envState.graph.addEntry(\n normalizePath(resolvedStartConfig.startFilePath),\n )\n }\n }\n },\n\n buildStart() {\n if (!config.enabled) return\n // Clear per-env caches\n for (const envState of envStates.values()) {\n envState.resolveCache.clear()\n envState.resolveCacheByFile.clear()\n envState.importLocCache.clear()\n envState.importLocByFile.clear()\n envState.seenViolations.clear()\n envState.transformResultCache.clear()\n envState.transformResultKeysByFile.clear()\n envState.graph.clear()\n envState.deniedSources.clear()\n envState.deniedEdges.clear()\n envState.mockExportsByImporter.clear()\n }\n\n // Clear shared state\n shared.fileMarkerKind.clear()\n\n // Re-add known entries after clearing.\n for (const envDef of opts.environments) {\n const envState = getEnv(envDef.name)\n const { resolvedStartConfig } = opts.getConfig()\n if (resolvedStartConfig.routerFilePath) {\n envState.graph.addEntry(\n normalizePath(resolvedStartConfig.routerFilePath),\n )\n }\n if (resolvedStartConfig.startFilePath) {\n envState.graph.addEntry(\n normalizePath(resolvedStartConfig.startFilePath),\n )\n }\n }\n },\n\n hotUpdate(ctx) {\n if (!config.enabled) return\n // Invalidate caches for updated files\n for (const mod of ctx.modules) {\n if (mod.id) {\n const id = mod.id\n const importerFile = normalizeFilePath(id)\n shared.fileMarkerKind.delete(importerFile)\n\n // Invalidate per-env caches\n for (const envState of envStates.values()) {\n // Invalidate cached import locations using reverse index\n const locKeys = envState.importLocByFile.get(importerFile)\n if (locKeys) {\n for (const key of locKeys) {\n envState.importLocCache.delete(key)\n }\n envState.importLocByFile.delete(importerFile)\n }\n\n // Invalidate resolve cache using reverse index\n const resolveKeys = envState.resolveCacheByFile.get(importerFile)\n if (resolveKeys) {\n for (const key of resolveKeys) {\n envState.resolveCache.delete(key)\n }\n envState.resolveCacheByFile.delete(importerFile)\n }\n\n // Invalidate graph edges\n envState.graph.invalidate(importerFile)\n envState.deniedEdges.delete(importerFile)\n envState.mockExportsByImporter.delete(importerFile)\n\n // Invalidate transform result cache for this file.\n const transformKeys =\n envState.transformResultKeysByFile.get(importerFile)\n if (transformKeys) {\n for (const key of transformKeys) {\n envState.transformResultCache.delete(key)\n }\n envState.transformResultKeysByFile.delete(importerFile)\n } else {\n // Fallback: at least clear the physical-file entry.\n envState.transformResultCache.delete(importerFile)\n }\n }\n }\n }\n },\n\n async resolveId(source, importer, _options) {\n if (!config.enabled) return undefined\n const envName = this.environment.name\n const env = getEnv(envName)\n const envType = getEnvType(envName)\n const provider = getTransformResultProvider(env)\n\n // Internal virtual modules must resolve in dev.\n if (source === MOCK_MODULE_ID) {\n return RESOLVED_MOCK_MODULE_ID\n }\n if (source.startsWith(MOCK_EDGE_PREFIX)) {\n return resolveViteId(source)\n }\n if (source.startsWith(MOCK_RUNTIME_PREFIX)) {\n return resolveViteId(source)\n }\n if (source.startsWith(MARKER_PREFIX)) {\n return resolveViteId(source)\n }\n\n // Skip if no importer (entry points)\n if (!importer) {\n // Track entry-ish modules so traces can terminate.\n // Vite may pass virtual ids here; normalize but keep them.\n env.graph.addEntry(source)\n return undefined\n }\n\n // Skip virtual modules\n if (source.startsWith('\\0') || source.startsWith('virtual:')) {\n return undefined\n }\n\n // Check if this is a marker import\n if (config.markerSpecifiers.serverOnly.has(source)) {\n // Record importer as server-only\n const resolvedImporter = normalizeFilePath(importer)\n const existing = shared.fileMarkerKind.get(resolvedImporter)\n if (existing && existing !== 'server') {\n this.error(\n `[import-protection] File \"${getRelativePath(resolvedImporter)}\" has both server-only and client-only markers. This is not allowed.`,\n )\n }\n shared.fileMarkerKind.set(resolvedImporter, 'server')\n\n // If we're in the client environment, this is a violation\n if (envType === 'client') {\n const info = await buildViolationInfo(\n provider,\n env,\n envName,\n envType,\n importer,\n resolvedImporter,\n source,\n {\n type: 'marker',\n message: `Module \"${getRelativePath(resolvedImporter)}\" is marked server-only but is imported in the client environment`,\n },\n )\n handleViolation.call(this, env, info)\n }\n\n // Return virtual empty module\n return resolveViteId(`${MARKER_PREFIX}server-only`)\n }\n\n if (config.markerSpecifiers.clientOnly.has(source)) {\n const resolvedImporter = normalizeFilePath(importer)\n const existing = shared.fileMarkerKind.get(resolvedImporter)\n if (existing && existing !== 'client') {\n this.error(\n `[import-protection] File \"${getRelativePath(resolvedImporter)}\" has both server-only and client-only markers. This is not allowed.`,\n )\n }\n shared.fileMarkerKind.set(resolvedImporter, 'client')\n\n if (envType === 'server') {\n const info = await buildViolationInfo(\n provider,\n env,\n envName,\n envType,\n importer,\n resolvedImporter,\n source,\n {\n type: 'marker',\n message: `Module \"${getRelativePath(resolvedImporter)}\" is marked client-only but is imported in the server environment`,\n },\n )\n handleViolation.call(this, env, info)\n }\n\n return resolveViteId(`${MARKER_PREFIX}client-only`)\n }\n\n // Check if the importer is within our scope\n const normalizedImporter = normalizeFilePath(importer)\n if (!shouldCheckImporter(normalizedImporter)) {\n return undefined\n }\n\n const matchers = getRulesForEnvironment(envName)\n\n // 1. Specifier-based denial (fast, no resolution needed)\n const specifierMatch = matchesAny(source, matchers.specifiers)\n if (specifierMatch) {\n env.graph.addEdge(source, normalizedImporter, source)\n const info = await buildViolationInfo(\n provider,\n env,\n envName,\n envType,\n importer,\n normalizedImporter,\n source,\n {\n type: 'specifier',\n pattern: specifierMatch.pattern,\n message: `Import \"${source}\" is denied in the \"${envName}\" environment`,\n },\n )\n return handleViolation.call(this, env, info)\n }\n\n // 2. Resolve the import (cached) — needed for file-based denial,\n // marker checks, and graph edge tracking.\n const cacheKey = `${normalizedImporter}:${source}`\n let resolved: string | null\n\n if (env.resolveCache.has(cacheKey)) {\n resolved = env.resolveCache.get(cacheKey) || null\n } else {\n const result = await this.resolve(source, importer, {\n skipSelf: true,\n })\n resolved = result ? normalizeFilePath(result.id) : null\n env.resolveCache.set(cacheKey, resolved)\n\n // Maintain reverse index for O(1) hotUpdate invalidation.\n // Index by the importer so that when a file changes, all resolve\n // cache entries where it was the importer are cleared.\n let fileKeys = env.resolveCacheByFile.get(normalizedImporter)\n if (!fileKeys) {\n fileKeys = new Set()\n env.resolveCacheByFile.set(normalizedImporter, fileKeys)\n }\n fileKeys.add(cacheKey)\n }\n\n if (resolved) {\n const relativePath = getRelativePath(resolved)\n\n // Always record the edge for trace building, even when not denied.\n env.graph.addEdge(resolved, normalizedImporter, source)\n\n // File-based denial check\n const fileMatch =\n matchers.files.length > 0\n ? matchesAny(relativePath, matchers.files)\n : undefined\n\n if (fileMatch) {\n const info = await buildViolationInfo(\n provider,\n env,\n envName,\n envType,\n importer,\n normalizedImporter,\n source,\n {\n type: 'file',\n pattern: fileMatch.pattern,\n resolved,\n message: `Import \"${source}\" (resolved to \"${relativePath}\") is denied in the \"${envName}\" environment`,\n },\n )\n return handleViolation.call(this, env, info)\n }\n\n // Marker restrictions apply regardless of explicit deny rules.\n const markerRes = await maybeReportMarkerViolationFromResolvedImport(\n this,\n provider,\n env,\n envName,\n envType,\n importer,\n source,\n resolved,\n relativePath,\n )\n if (markerRes !== undefined) {\n return markerRes\n }\n }\n\n return undefined\n },\n\n load: {\n filter: {\n id: new RegExp(\n `(${RESOLVED_MOCK_MODULE_ID.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}|${RESOLVED_MARKER_PREFIX.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}|${RESOLVED_MOCK_EDGE_PREFIX.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}|${RESOLVED_MOCK_RUNTIME_PREFIX.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')})`,\n ),\n },\n handler(id) {\n if (!config.enabled) return undefined\n if (id === RESOLVED_MOCK_MODULE_ID) {\n return loadSilentMockModule()\n }\n\n if (id.startsWith(RESOLVED_MOCK_EDGE_PREFIX)) {\n return loadMockEdgeModule(\n id.slice(RESOLVED_MOCK_EDGE_PREFIX.length),\n )\n }\n\n if (id.startsWith(RESOLVED_MOCK_RUNTIME_PREFIX)) {\n return loadMockRuntimeModule(\n id.slice(RESOLVED_MOCK_RUNTIME_PREFIX.length),\n )\n }\n\n if (id.startsWith(RESOLVED_MARKER_PREFIX)) {\n return loadMarkerModule()\n }\n\n return undefined\n },\n },\n },\n {\n // This plugin runs WITHOUT `enforce` so it executes after all\n // `enforce: 'pre'` transform hooks (including the Start compiler).\n // It captures the transformed code + composed sourcemap for every module\n // so that the `resolveId` hook (in the main plugin above) can look up\n // the importer's transform result and map violation locations back to\n // original source.\n //\n // Why not use `ctx.load()` in `resolveId`?\n // - Vite dev: `this.load()` returns a ModuleInfo proxy that throws on\n // `.code` access — code is not exposed.\n // - Rollup build: `ModuleInfo` has `.code` but NOT `.map`, so we\n // can't map generated positions back to original source.\n //\n // By caching in the transform hook we get both code and the composed\n // sourcemap that chains all the way back to the original file.\n //\n // Performance: only files under `srcDirectory` are cached because only\n // those can be importers in a violation. Third-party code in\n // node_modules is never checked.\n name: 'tanstack-start-core:import-protection-transform-cache',\n\n applyToEnvironment(env) {\n if (!config.enabled) return false\n return environmentNames.has(env.name)\n },\n\n transform: {\n filter: {\n id: {\n include: [/\\.[cm]?[tj]sx?($|\\?)/],\n },\n },\n handler(code, id) {\n if (!config.enabled) return undefined\n const envName = this.environment.name\n const file = normalizeFilePath(id)\n\n // Only cache files that could ever be checked as an importer.\n // This reuses the same include/exclude/ignoreImporters predicate as\n // the main import-protection resolveId hook.\n if (!shouldCheckImporter(file)) {\n return undefined\n }\n\n // getCombinedSourcemap() returns the composed sourcemap of all\n // transform hooks that ran before this one. It includes\n // sourcesContent so we can extract original source later.\n let map: SourceMapLike | undefined\n try {\n map = this.getCombinedSourcemap()\n } catch {\n // No sourcemap available (e.g. virtual modules or modules\n // that no prior plugin produced a map for).\n map = undefined\n }\n\n // Extract the original source from sourcesContent right here.\n // Composed sourcemaps can contain multiple sources; try to pick the\n // entry that best matches this importer.\n let originalCode: string | undefined\n if (map?.sourcesContent) {\n originalCode = pickOriginalCodeFromSourcesContent(\n map,\n file,\n config.root,\n )\n }\n\n // Precompute a line index for fast index->line/col conversions.\n const lineIndex = buildLineIndex(code)\n\n // Key by the full normalized module ID including query params\n // (e.g. \"src/routes/index.tsx?tsr-split=component\") so that\n // virtual modules derived from the same physical file each get\n // their own cache entry.\n const cacheKey = normalizePath(id)\n const envState = getEnv(envName)\n envState.transformResultCache.set(cacheKey, {\n code,\n map,\n originalCode,\n lineIndex,\n })\n\n // Maintain reverse index so hotUpdate invalidation is O(keys for file).\n let keySet = envState.transformResultKeysByFile.get(file)\n if (!keySet) {\n keySet = new Set<string>()\n envState.transformResultKeysByFile.set(file, keySet)\n }\n keySet.add(cacheKey)\n\n // Also store/update the stripped-path entry so that lookups by\n // physical file path (e.g. from trace steps in the import graph,\n // which normalize away query params) still find a result.\n // The last variant transformed wins, which is acceptable — trace\n // lookups are best-effort for line numbers.\n if (cacheKey !== file) {\n envState.transformResultCache.set(file, {\n code,\n map,\n originalCode,\n lineIndex,\n })\n keySet.add(file)\n }\n\n // Return nothing — we don't modify the code.\n return undefined\n },\n },\n },\n {\n // Separate plugin so the transform can be enabled/disabled per-environment.\n name: 'tanstack-start-core:import-protection-mock-rewrite',\n enforce: 'pre',\n\n // Only needed during dev. In build, we rely on Rollup's syntheticNamedExports.\n apply: 'serve',\n\n applyToEnvironment(env) {\n if (!config.enabled) return false\n // Only needed in mock mode — when not mocking, there is nothing to\n // record. applyToEnvironment runs after configResolved, so\n // config.effectiveBehavior is already set.\n if (config.effectiveBehavior !== 'mock') return false\n // We record expected named exports per importer in all Start Vite\n // environments during dev so mock-edge modules can provide explicit\n // ESM named exports.\n return environmentNames.has(env.name)\n },\n\n transform: {\n filter: {\n id: {\n include: [/\\.[cm]?[tj]sx?($|\\?)/],\n },\n },\n handler(code, id) {\n if (!config.enabled) return undefined\n const envName = this.environment.name\n const envState = envStates.get(envName)\n if (!envState) return undefined\n\n // Record export names per source for this importer so we can generate\n // dev mock-edge modules without any disk reads.\n try {\n const importerFile = normalizeFilePath(id)\n envState.mockExportsByImporter.set(\n importerFile,\n collectMockExportNamesBySource(code),\n )\n } catch {\n // Best-effort only\n }\n\n // Note: we no longer rewrite imports here.\n // Dev uses per-importer mock-edge modules in resolveId so native ESM\n // has explicit named exports, and runtime diagnostics are handled by\n // the mock runtime proxy when those mocks are actually invoked.\n return undefined\n },\n },\n },\n ] satisfies Array<PluginOption>\n\n // ---------------------------------------------------------------------------\n // Violation handling\n // ---------------------------------------------------------------------------\n\n function handleViolation(\n this: { warn: (msg: string) => void; error: (msg: string) => never },\n env: EnvState,\n info: ViolationInfo,\n ): { id: string; syntheticNamedExports: boolean } | string | undefined {\n const key = dedupeKey(\n info.type,\n info.importer,\n info.specifier,\n info.resolved,\n )\n\n // Call user callback\n if (config.onViolation) {\n const result = config.onViolation(info)\n if (result === false) {\n return undefined\n }\n }\n\n const seen = hasSeen(env, key)\n\n if (config.effectiveBehavior === 'error') {\n if (!seen) this.error(formatViolation(info, config.root))\n return undefined\n }\n\n // Mock mode: log once, but always return the mock module.\n if (!seen) {\n this.warn(formatViolation(info, config.root))\n }\n\n env.deniedSources.add(info.specifier)\n let edgeSet = env.deniedEdges.get(info.importer)\n if (!edgeSet) {\n edgeSet = new Set<string>()\n env.deniedEdges.set(info.importer, edgeSet)\n }\n edgeSet.add(info.specifier)\n\n if (config.command === 'serve') {\n const runtimeId = mockRuntimeModuleIdFromViolation(\n info,\n config.mockAccess,\n config.root,\n )\n return resolveViteId(\n buildMockEdgeModuleId(env, info.importer, info.specifier, runtimeId),\n )\n }\n\n // Build: Rollup can synthesize named exports.\n return { id: RESOLVED_MOCK_MODULE_ID, syntheticNamedExports: true }\n }\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ export type UsagePos = {
2
+ line: number;
3
+ column0: number;
4
+ };
5
+ /**
6
+ * Given transformed code, returns the first "meaningful" usage position for an
7
+ * import from `source` that survives compilation.
8
+ *
9
+ * The returned column is 0-based (Babel loc semantics).
10
+ */
11
+ export declare function findPostCompileUsagePos(code: string, source: string): UsagePos | undefined;
@@ -0,0 +1,177 @@
1
+ import * as t from "@babel/types";
2
+ import { parseAst } from "@tanstack/router-utils";
3
+ function collectPatternBindings(node, out) {
4
+ if (!node) return;
5
+ if (t.isIdentifier(node)) {
6
+ out.add(node.name);
7
+ return;
8
+ }
9
+ if (t.isRestElement(node)) {
10
+ collectPatternBindings(node.argument, out);
11
+ return;
12
+ }
13
+ if (t.isAssignmentPattern(node)) {
14
+ collectPatternBindings(node.left, out);
15
+ return;
16
+ }
17
+ if (t.isObjectPattern(node)) {
18
+ for (const prop of node.properties) {
19
+ if (t.isRestElement(prop)) {
20
+ collectPatternBindings(prop.argument, out);
21
+ } else if (t.isObjectProperty(prop)) {
22
+ collectPatternBindings(prop.value, out);
23
+ }
24
+ }
25
+ return;
26
+ }
27
+ if (t.isArrayPattern(node)) {
28
+ for (const el of node.elements) {
29
+ collectPatternBindings(el, out);
30
+ }
31
+ return;
32
+ }
33
+ }
34
+ function isBindingPosition(node, parent) {
35
+ if (!parent) return false;
36
+ if (t.isFunctionDeclaration(parent) && parent.id === node) return true;
37
+ if (t.isFunctionExpression(parent) && parent.id === node) return true;
38
+ if (t.isClassDeclaration(parent) && parent.id === node) return true;
39
+ if (t.isClassExpression(parent) && parent.id === node) return true;
40
+ if (t.isVariableDeclarator(parent) && parent.id === node) return true;
41
+ if (t.isImportSpecifier(parent) && parent.local === node) return true;
42
+ if (t.isImportDefaultSpecifier(parent) && parent.local === node) return true;
43
+ if (t.isImportNamespaceSpecifier(parent) && parent.local === node) return true;
44
+ if (t.isObjectProperty(parent) && parent.key === node && !parent.computed && // In `{ foo }`, the identifier is also a value reference and must count as
45
+ // usage. Babel represents this as `shorthand: true`.
46
+ !parent.shorthand)
47
+ return true;
48
+ if (t.isObjectMethod(parent) && parent.key === node && !parent.computed)
49
+ return true;
50
+ if (t.isExportSpecifier(parent) && parent.exported === node) return true;
51
+ return false;
52
+ }
53
+ function isPreferredUsage(node, parent) {
54
+ if (!parent) return false;
55
+ if (t.isCallExpression(parent) && parent.callee === node) return true;
56
+ if (t.isNewExpression(parent) && parent.callee === node) return true;
57
+ if (t.isMemberExpression(parent) && parent.object === node) return true;
58
+ return false;
59
+ }
60
+ function isScopeNode(node) {
61
+ return t.isProgram(node) || t.isFunctionDeclaration(node) || t.isFunctionExpression(node) || t.isArrowFunctionExpression(node) || t.isBlockStatement(node) || t.isCatchClause(node);
62
+ }
63
+ function isFunctionScopeNode(node) {
64
+ return t.isProgram(node) || t.isFunctionDeclaration(node) || t.isFunctionExpression(node) || t.isArrowFunctionExpression(node);
65
+ }
66
+ function collectScopeBindings(node, out) {
67
+ if (t.isFunctionDeclaration(node) || t.isFunctionExpression(node) || t.isArrowFunctionExpression(node)) {
68
+ for (const p of node.params) {
69
+ collectPatternBindings(p, out);
70
+ }
71
+ return;
72
+ }
73
+ if (t.isCatchClause(node)) {
74
+ collectPatternBindings(node.param, out);
75
+ return;
76
+ }
77
+ }
78
+ function findPostCompileUsagePos(code, source) {
79
+ const ast = parseAst({ code });
80
+ const imported = /* @__PURE__ */ new Set();
81
+ for (const node of ast.program.body) {
82
+ if (t.isImportDeclaration(node) && node.source.value === source) {
83
+ if (node.importKind === "type") continue;
84
+ for (const s of node.specifiers) {
85
+ if (t.isImportSpecifier(s) && s.importKind === "type") continue;
86
+ imported.add(s.local.name);
87
+ }
88
+ }
89
+ }
90
+ if (imported.size === 0) return void 0;
91
+ let preferred;
92
+ let anyUsage;
93
+ const scopes = [{ bindings: /* @__PURE__ */ new Set(), isFnScope: true }];
94
+ function isShadowed(name) {
95
+ for (let i = scopes.length - 1; i >= 1; i--) {
96
+ if (scopes[i].bindings.has(name)) return true;
97
+ }
98
+ return false;
99
+ }
100
+ function record(node, kind) {
101
+ const loc = node.loc?.start;
102
+ if (!loc) return;
103
+ const pos = { line: loc.line, column0: loc.column };
104
+ if (kind === "preferred") {
105
+ preferred ||= pos;
106
+ } else {
107
+ anyUsage ||= pos;
108
+ }
109
+ }
110
+ function pushScope(node) {
111
+ const bindings = /* @__PURE__ */ new Set();
112
+ collectScopeBindings(node, bindings);
113
+ scopes.push({ bindings, isFnScope: isFunctionScopeNode(node) });
114
+ }
115
+ function popScope() {
116
+ scopes.pop();
117
+ }
118
+ function nearestFnScope() {
119
+ for (let i = scopes.length - 1; i >= 0; i--) {
120
+ if (scopes[i].isFnScope) return scopes[i];
121
+ }
122
+ return scopes[0];
123
+ }
124
+ function walk(node, parent) {
125
+ if (!node) return;
126
+ if (preferred && anyUsage) return;
127
+ if (Array.isArray(node)) {
128
+ for (const n of node) walk(n, parent);
129
+ return;
130
+ }
131
+ const astNode = node;
132
+ if (t.isImportDeclaration(astNode)) return;
133
+ const enterScope = isScopeNode(astNode);
134
+ if (enterScope) {
135
+ pushScope(astNode);
136
+ }
137
+ if (t.isFunctionDeclaration(astNode) && astNode.id) {
138
+ scopes[scopes.length - 2]?.bindings.add(astNode.id.name);
139
+ }
140
+ if (t.isClassDeclaration(astNode) && astNode.id) {
141
+ scopes[scopes.length - 2]?.bindings.add(astNode.id.name);
142
+ }
143
+ if (t.isVariableDeclarator(astNode)) {
144
+ const isVar = t.isVariableDeclaration(parent) && parent.kind === "var";
145
+ const target = isVar ? nearestFnScope().bindings : scopes[scopes.length - 1].bindings;
146
+ collectPatternBindings(astNode.id, target);
147
+ }
148
+ if (t.isIdentifier(astNode) && imported.has(astNode.name)) {
149
+ if (!isBindingPosition(astNode, parent) && !isShadowed(astNode.name)) {
150
+ if (isPreferredUsage(astNode, parent)) {
151
+ record(astNode, "preferred");
152
+ } else {
153
+ record(astNode, "any");
154
+ }
155
+ }
156
+ }
157
+ const record_ = astNode;
158
+ for (const key of Object.keys(record_)) {
159
+ const value = record_[key];
160
+ if (!value) continue;
161
+ if (key === "loc" || key === "start" || key === "end") continue;
162
+ if (key === "parent") continue;
163
+ if (typeof value === "string" || typeof value === "number") continue;
164
+ walk(value, astNode);
165
+ if (preferred && anyUsage) break;
166
+ }
167
+ if (enterScope) {
168
+ popScope();
169
+ }
170
+ }
171
+ walk(ast.program, null);
172
+ return preferred ?? anyUsage;
173
+ }
174
+ export {
175
+ findPostCompileUsagePos
176
+ };
177
+ //# sourceMappingURL=postCompileUsage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postCompileUsage.js","sources":["../../../src/import-protection-plugin/postCompileUsage.ts"],"sourcesContent":["import * as t from '@babel/types'\nimport { parseAst } from '@tanstack/router-utils'\n\nexport type UsagePos = { line: number; column0: number }\n\nfunction collectPatternBindings(\n node: t.Node | null | undefined,\n out: Set<string>,\n): void {\n if (!node) return\n if (t.isIdentifier(node)) {\n out.add(node.name)\n return\n }\n if (t.isRestElement(node)) {\n collectPatternBindings(node.argument, out)\n return\n }\n if (t.isAssignmentPattern(node)) {\n collectPatternBindings(node.left, out)\n return\n }\n if (t.isObjectPattern(node)) {\n for (const prop of node.properties) {\n if (t.isRestElement(prop)) {\n collectPatternBindings(prop.argument, out)\n } else if (t.isObjectProperty(prop)) {\n collectPatternBindings(prop.value as t.Node, out)\n }\n }\n return\n }\n if (t.isArrayPattern(node)) {\n for (const el of node.elements) {\n collectPatternBindings(el, out)\n }\n return\n }\n}\n\nfunction isBindingPosition(node: t.Node, parent: t.Node | null): boolean {\n if (!parent) return false\n if (t.isFunctionDeclaration(parent) && parent.id === node) return true\n if (t.isFunctionExpression(parent) && parent.id === node) return true\n if (t.isClassDeclaration(parent) && parent.id === node) return true\n if (t.isClassExpression(parent) && parent.id === node) return true\n if (t.isVariableDeclarator(parent) && parent.id === node) return true\n if (t.isImportSpecifier(parent) && parent.local === node) return true\n if (t.isImportDefaultSpecifier(parent) && parent.local === node) return true\n if (t.isImportNamespaceSpecifier(parent) && parent.local === node) return true\n if (\n t.isObjectProperty(parent) &&\n parent.key === node &&\n !parent.computed &&\n // In `{ foo }`, the identifier is also a value reference and must count as\n // usage. Babel represents this as `shorthand: true`.\n !parent.shorthand\n )\n return true\n if (t.isObjectMethod(parent) && parent.key === node && !parent.computed)\n return true\n if (t.isExportSpecifier(parent) && parent.exported === node) return true\n return false\n}\n\nfunction isPreferredUsage(node: t.Node, parent: t.Node | null): boolean {\n if (!parent) return false\n if (t.isCallExpression(parent) && parent.callee === node) return true\n if (t.isNewExpression(parent) && parent.callee === node) return true\n if (t.isMemberExpression(parent) && parent.object === node) return true\n return false\n}\n\nfunction isScopeNode(node: t.Node): boolean {\n return (\n t.isProgram(node) ||\n t.isFunctionDeclaration(node) ||\n t.isFunctionExpression(node) ||\n t.isArrowFunctionExpression(node) ||\n t.isBlockStatement(node) ||\n t.isCatchClause(node)\n )\n}\n\n/** `var` hoists to the nearest function or program scope, not block scopes. */\nfunction isFunctionScopeNode(node: t.Node): boolean {\n return (\n t.isProgram(node) ||\n t.isFunctionDeclaration(node) ||\n t.isFunctionExpression(node) ||\n t.isArrowFunctionExpression(node)\n )\n}\n\nfunction collectScopeBindings(node: t.Node, out: Set<string>): void {\n if (\n t.isFunctionDeclaration(node) ||\n t.isFunctionExpression(node) ||\n t.isArrowFunctionExpression(node)\n ) {\n for (const p of node.params) {\n collectPatternBindings(p, out)\n }\n return\n }\n\n if (t.isCatchClause(node)) {\n collectPatternBindings(node.param, out)\n return\n }\n}\n\n/**\n * Given transformed code, returns the first \"meaningful\" usage position for an\n * import from `source` that survives compilation.\n *\n * The returned column is 0-based (Babel loc semantics).\n */\nexport function findPostCompileUsagePos(\n code: string,\n source: string,\n): UsagePos | undefined {\n const ast = parseAst({ code })\n\n // 1) Determine local names bound from this specifier\n const imported = new Set<string>()\n for (const node of ast.program.body) {\n if (t.isImportDeclaration(node) && node.source.value === source) {\n if (node.importKind === 'type') continue\n for (const s of node.specifiers) {\n if (t.isImportSpecifier(s) && s.importKind === 'type') continue\n imported.add(s.local.name)\n }\n }\n }\n if (imported.size === 0) return undefined\n\n let preferred: UsagePos | undefined\n let anyUsage: UsagePos | undefined\n\n // Scope stack (module scope at index 0).\n // Each entry tracks bindings and whether it is a function/program scope\n // (needed for `var` hoisting).\n interface ScopeEntry {\n bindings: Set<string>\n isFnScope: boolean\n }\n const scopes: Array<ScopeEntry> = [{ bindings: new Set(), isFnScope: true }]\n\n function isShadowed(name: string): boolean {\n // Check inner scopes only\n for (let i = scopes.length - 1; i >= 1; i--) {\n if (scopes[i]!.bindings.has(name)) return true\n }\n return false\n }\n\n function record(node: t.Node, kind: 'preferred' | 'any') {\n const loc = node.loc?.start\n if (!loc) return\n const pos: UsagePos = { line: loc.line, column0: loc.column }\n if (kind === 'preferred') {\n preferred ||= pos\n } else {\n anyUsage ||= pos\n }\n }\n\n function pushScope(node: t.Node): void {\n const bindings = new Set<string>()\n collectScopeBindings(node, bindings)\n scopes.push({ bindings, isFnScope: isFunctionScopeNode(node) })\n }\n\n function popScope(): void {\n scopes.pop()\n }\n\n /** Find the nearest function/program scope entry in the stack. */\n function nearestFnScope(): ScopeEntry {\n for (let i = scopes.length - 1; i >= 0; i--) {\n if (scopes[i]!.isFnScope) return scopes[i]!\n }\n // Should never happen (index 0 is always a function scope).\n return scopes[0]!\n }\n\n // The walker accepts AST nodes, arrays (from node children like\n // `body`, `params`, etc.), or null/undefined for optional children.\n type Walkable =\n | t.Node\n | ReadonlyArray<t.Node | null | undefined>\n | null\n | undefined\n\n function walk(node: Walkable, parent: t.Node | null) {\n if (!node) return\n if (preferred && anyUsage) return\n\n if (Array.isArray(node)) {\n for (const n of node) walk(n, parent)\n return\n }\n\n // After the array check + early return, node is guaranteed to be t.Node.\n // TypeScript doesn't narrow ReadonlyArray from the union, so we assert.\n const astNode = node as t.Node\n\n // Skip import declarations entirely\n if (t.isImportDeclaration(astNode)) return\n\n const enterScope = isScopeNode(astNode)\n if (enterScope) {\n pushScope(astNode)\n }\n\n // Add lexical bindings for variable declarations and class/function decls.\n // Note: function/class *declaration* identifiers bind in the parent scope,\n // so we register them before walking children.\n if (t.isFunctionDeclaration(astNode) && astNode.id) {\n scopes[scopes.length - 2]?.bindings.add(astNode.id.name)\n }\n if (t.isClassDeclaration(astNode) && astNode.id) {\n scopes[scopes.length - 2]?.bindings.add(astNode.id.name)\n }\n if (t.isVariableDeclarator(astNode)) {\n // `var` hoists to the nearest function/program scope, not block scope.\n const isVar = t.isVariableDeclaration(parent) && parent.kind === 'var'\n const target = isVar\n ? nearestFnScope().bindings\n : scopes[scopes.length - 1]!.bindings\n collectPatternBindings(astNode.id, target)\n }\n\n if (t.isIdentifier(astNode) && imported.has(astNode.name)) {\n if (!isBindingPosition(astNode, parent) && !isShadowed(astNode.name)) {\n if (isPreferredUsage(astNode, parent)) {\n record(astNode, 'preferred')\n } else {\n record(astNode, 'any')\n }\n }\n }\n\n // Iterate child properties of this AST node. We use a Record cast since\n // Babel node types don't expose an index signature, but we need to walk\n // all child properties generically.\n const record_ = astNode as unknown as Record<string, unknown>\n for (const key of Object.keys(record_)) {\n const value = record_[key]\n if (!value) continue\n if (key === 'loc' || key === 'start' || key === 'end') continue\n if (key === 'parent') continue\n if (typeof value === 'string' || typeof value === 'number') continue\n walk(value as Walkable, astNode)\n if (preferred && anyUsage) break\n }\n\n if (enterScope) {\n popScope()\n }\n }\n\n walk(ast.program, null)\n return preferred ?? anyUsage\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ export declare function isValidExportName(name: string): boolean;
2
+ /**
3
+ * Best-effort static analysis of an importer's source to determine which
4
+ * named exports are needed per specifier, to keep native ESM valid in dev.
5
+ */
6
+ export declare function collectMockExportNamesBySource(code: string): Map<string, Array<string>>;
7
+ /**
8
+ * Rewrite static imports/re-exports from denied sources using Babel AST transforms.
9
+ *
10
+ * Transforms:
11
+ * import { a as b, c } from 'denied'
12
+ * Into:
13
+ * import __tss_deny_0 from 'tanstack-start-import-protection:mock'
14
+ * const b = __tss_deny_0.a
15
+ * const c = __tss_deny_0.c
16
+ *
17
+ * Also handles:
18
+ * import def from 'denied' -> import def from mock
19
+ * import * as ns from 'denied' -> import ns from mock
20
+ * export { x } from 'denied' -> export const x = mock.x
21
+ * export * from 'denied' -> removed
22
+ * export { x as y } from 'denied' -> export const y = mock.x
23
+ */
24
+ export declare function rewriteDeniedImports(code: string, id: string, deniedSources: Set<string>, getMockModuleId?: (source: string) => string): {
25
+ code: string;
26
+ map?: object | null;
27
+ } | undefined;
@@ -0,0 +1,51 @@
1
+ import * as t from "@babel/types";
2
+ import { parseAst } from "@tanstack/router-utils";
3
+ function isValidExportName(name) {
4
+ if (name === "default") return false;
5
+ return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name);
6
+ }
7
+ function collectMockExportNamesBySource(code) {
8
+ const ast = parseAst({ code });
9
+ const namesBySource = /* @__PURE__ */ new Map();
10
+ const add = (source, name) => {
11
+ if (!isValidExportName(name)) return;
12
+ let set = namesBySource.get(source);
13
+ if (!set) {
14
+ set = /* @__PURE__ */ new Set();
15
+ namesBySource.set(source, set);
16
+ }
17
+ set.add(name);
18
+ };
19
+ for (const node of ast.program.body) {
20
+ if (t.isImportDeclaration(node)) {
21
+ if (node.importKind === "type") continue;
22
+ const source = node.source.value;
23
+ for (const s of node.specifiers) {
24
+ if (!t.isImportSpecifier(s)) continue;
25
+ if (s.importKind === "type") continue;
26
+ const importedName = t.isIdentifier(s.imported) ? s.imported.name : s.imported.value;
27
+ if (importedName === "default") continue;
28
+ add(source, importedName);
29
+ }
30
+ }
31
+ if (t.isExportNamedDeclaration(node) && node.source?.value) {
32
+ if (node.exportKind === "type") continue;
33
+ const source = node.source.value;
34
+ for (const s of node.specifiers) {
35
+ if (!t.isExportSpecifier(s)) continue;
36
+ if (s.exportKind === "type") continue;
37
+ add(source, s.local.name);
38
+ }
39
+ }
40
+ }
41
+ const out = /* @__PURE__ */ new Map();
42
+ for (const [source, set] of namesBySource) {
43
+ out.set(source, Array.from(set).sort());
44
+ }
45
+ return out;
46
+ }
47
+ export {
48
+ collectMockExportNamesBySource,
49
+ isValidExportName
50
+ };
51
+ //# sourceMappingURL=rewriteDeniedImports.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rewriteDeniedImports.js","sources":["../../../src/import-protection-plugin/rewriteDeniedImports.ts"],"sourcesContent":["import * as t from '@babel/types'\nimport { generateFromAst, parseAst } from '@tanstack/router-utils'\n\nimport { MOCK_MODULE_ID } from './virtualModules'\n\n// ---------------------------------------------------------------------------\n// Export name collection (for dev mock-edge modules)\n// ---------------------------------------------------------------------------\n\nexport function isValidExportName(name: string): boolean {\n if (name === 'default') return false\n return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name)\n}\n\n/**\n * Best-effort static analysis of an importer's source to determine which\n * named exports are needed per specifier, to keep native ESM valid in dev.\n */\nexport function collectMockExportNamesBySource(\n code: string,\n): Map<string, Array<string>> {\n const ast = parseAst({ code })\n\n const namesBySource = new Map<string, Set<string>>()\n const add = (source: string, name: string) => {\n if (!isValidExportName(name)) return\n let set = namesBySource.get(source)\n if (!set) {\n set = new Set<string>()\n namesBySource.set(source, set)\n }\n set.add(name)\n }\n\n for (const node of ast.program.body) {\n if (t.isImportDeclaration(node)) {\n if (node.importKind === 'type') continue\n const source = node.source.value\n for (const s of node.specifiers) {\n if (!t.isImportSpecifier(s)) continue\n if (s.importKind === 'type') continue\n const importedName = t.isIdentifier(s.imported)\n ? s.imported.name\n : s.imported.value\n // `import { default as x } from 'm'` only requires a default export.\n if (importedName === 'default') continue\n add(source, importedName)\n }\n }\n\n if (t.isExportNamedDeclaration(node) && node.source?.value) {\n if (node.exportKind === 'type') continue\n const source = node.source.value\n for (const s of node.specifiers) {\n if (!t.isExportSpecifier(s)) continue\n if (s.exportKind === 'type') continue\n add(source, s.local.name)\n }\n }\n }\n\n const out = new Map<string, Array<string>>()\n for (const [source, set] of namesBySource) {\n out.set(source, Array.from(set).sort())\n }\n return out\n}\n\n// ---------------------------------------------------------------------------\n// AST-based import rewriting\n// ---------------------------------------------------------------------------\n\n/**\n * Rewrite static imports/re-exports from denied sources using Babel AST transforms.\n *\n * Transforms:\n * import { a as b, c } from 'denied'\n * Into:\n * import __tss_deny_0 from 'tanstack-start-import-protection:mock'\n * const b = __tss_deny_0.a\n * const c = __tss_deny_0.c\n *\n * Also handles:\n * import def from 'denied' -> import def from mock\n * import * as ns from 'denied' -> import ns from mock\n * export { x } from 'denied' -> export const x = mock.x\n * export * from 'denied' -> removed\n * export { x as y } from 'denied' -> export const y = mock.x\n */\nexport function rewriteDeniedImports(\n code: string,\n id: string,\n deniedSources: Set<string>,\n getMockModuleId: (source: string) => string = () => MOCK_MODULE_ID,\n): { code: string; map?: object | null } | undefined {\n const ast = parseAst({ code })\n let modified = false\n let mockCounter = 0\n\n // Walk program body in reverse so splice indices stay valid\n for (let i = ast.program.body.length - 1; i >= 0; i--) {\n const node = ast.program.body[i]!\n\n // --- import declarations ---\n if (t.isImportDeclaration(node)) {\n // Skip type-only imports\n if (node.importKind === 'type') continue\n if (!deniedSources.has(node.source.value)) continue\n\n const mockVar = `__tss_deny_${mockCounter++}`\n const replacements: Array<t.Statement> = []\n\n // import __tss_deny_N from '<mock>'\n replacements.push(\n t.importDeclaration(\n [t.importDefaultSpecifier(t.identifier(mockVar))],\n t.stringLiteral(getMockModuleId(node.source.value)),\n ),\n )\n\n for (const specifier of node.specifiers) {\n if (t.isImportDefaultSpecifier(specifier)) {\n // import def from 'denied' -> const def = __tss_deny_N\n replacements.push(\n t.variableDeclaration('const', [\n t.variableDeclarator(\n t.identifier(specifier.local.name),\n t.identifier(mockVar),\n ),\n ]),\n )\n } else if (t.isImportNamespaceSpecifier(specifier)) {\n // import * as ns from 'denied' -> const ns = __tss_deny_N\n replacements.push(\n t.variableDeclaration('const', [\n t.variableDeclarator(\n t.identifier(specifier.local.name),\n t.identifier(mockVar),\n ),\n ]),\n )\n } else if (t.isImportSpecifier(specifier)) {\n // Skip type-only specifiers\n if (specifier.importKind === 'type') continue\n // import { a as b } from 'denied' -> const b = __tss_deny_N.a\n const importedName = t.isIdentifier(specifier.imported)\n ? specifier.imported.name\n : specifier.imported.value\n replacements.push(\n t.variableDeclaration('const', [\n t.variableDeclarator(\n t.identifier(specifier.local.name),\n t.memberExpression(\n t.identifier(mockVar),\n t.identifier(importedName),\n ),\n ),\n ]),\n )\n }\n }\n\n ast.program.body.splice(i, 1, ...replacements)\n modified = true\n continue\n }\n\n // --- export { x } from 'denied' ---\n if (t.isExportNamedDeclaration(node) && node.source) {\n if (node.exportKind === 'type') continue\n if (!deniedSources.has(node.source.value)) continue\n\n const mockVar = `__tss_deny_${mockCounter++}`\n const replacements: Array<t.Statement> = []\n\n // import __tss_deny_N from '<mock>'\n replacements.push(\n t.importDeclaration(\n [t.importDefaultSpecifier(t.identifier(mockVar))],\n t.stringLiteral(getMockModuleId(node.source.value)),\n ),\n )\n\n // For each re-exported specifier, create an exported const\n const exportSpecifiers: Array<{\n localName: string\n exportedName: string\n }> = []\n for (const specifier of node.specifiers) {\n if (t.isExportSpecifier(specifier)) {\n if (specifier.exportKind === 'type') continue\n const localName = specifier.local.name\n const exportedName = t.isIdentifier(specifier.exported)\n ? specifier.exported.name\n : specifier.exported.value\n\n const internalVar = `__tss_reexport_${localName}`\n // const __tss_reexport_x = __tss_deny_N.x\n replacements.push(\n t.variableDeclaration('const', [\n t.variableDeclarator(\n t.identifier(internalVar),\n t.memberExpression(\n t.identifier(mockVar),\n t.identifier(localName),\n ),\n ),\n ]),\n )\n exportSpecifiers.push({ localName: internalVar, exportedName })\n }\n }\n\n // export { __tss_reexport_x as x, ... }\n if (exportSpecifiers.length > 0) {\n replacements.push(\n t.exportNamedDeclaration(\n null,\n exportSpecifiers.map((s) =>\n t.exportSpecifier(\n t.identifier(s.localName),\n t.identifier(s.exportedName),\n ),\n ),\n ),\n )\n }\n\n ast.program.body.splice(i, 1, ...replacements)\n modified = true\n continue\n }\n\n // --- export * from 'denied' ---\n if (t.isExportAllDeclaration(node)) {\n if (node.exportKind === 'type') continue\n if (!deniedSources.has(node.source.value)) continue\n\n // Remove the star re-export entirely\n ast.program.body.splice(i, 1)\n modified = true\n continue\n }\n }\n\n if (!modified) return undefined\n\n const result = generateFromAst(ast, {\n sourceMaps: true,\n sourceFileName: id,\n filename: id,\n })\n\n return { code: result.code, map: result.map }\n}\n"],"names":[],"mappings":";;AASO,SAAS,kBAAkB,MAAuB;AACvD,MAAI,SAAS,UAAW,QAAO;AAC/B,SAAO,6BAA6B,KAAK,IAAI;AAC/C;AAMO,SAAS,+BACd,MAC4B;AAC5B,QAAM,MAAM,SAAS,EAAE,MAAM;AAE7B,QAAM,oCAAoB,IAAA;AAC1B,QAAM,MAAM,CAAC,QAAgB,SAAiB;AAC5C,QAAI,CAAC,kBAAkB,IAAI,EAAG;AAC9B,QAAI,MAAM,cAAc,IAAI,MAAM;AAClC,QAAI,CAAC,KAAK;AACR,gCAAU,IAAA;AACV,oBAAc,IAAI,QAAQ,GAAG;AAAA,IAC/B;AACA,QAAI,IAAI,IAAI;AAAA,EACd;AAEA,aAAW,QAAQ,IAAI,QAAQ,MAAM;AACnC,QAAI,EAAE,oBAAoB,IAAI,GAAG;AAC/B,UAAI,KAAK,eAAe,OAAQ;AAChC,YAAM,SAAS,KAAK,OAAO;AAC3B,iBAAW,KAAK,KAAK,YAAY;AAC/B,YAAI,CAAC,EAAE,kBAAkB,CAAC,EAAG;AAC7B,YAAI,EAAE,eAAe,OAAQ;AAC7B,cAAM,eAAe,EAAE,aAAa,EAAE,QAAQ,IAC1C,EAAE,SAAS,OACX,EAAE,SAAS;AAEf,YAAI,iBAAiB,UAAW;AAChC,YAAI,QAAQ,YAAY;AAAA,MAC1B;AAAA,IACF;AAEA,QAAI,EAAE,yBAAyB,IAAI,KAAK,KAAK,QAAQ,OAAO;AAC1D,UAAI,KAAK,eAAe,OAAQ;AAChC,YAAM,SAAS,KAAK,OAAO;AAC3B,iBAAW,KAAK,KAAK,YAAY;AAC/B,YAAI,CAAC,EAAE,kBAAkB,CAAC,EAAG;AAC7B,YAAI,EAAE,eAAe,OAAQ;AAC7B,YAAI,QAAQ,EAAE,MAAM,IAAI;AAAA,MAC1B;AAAA,IACF;AAAA,EACF;AAEA,QAAM,0BAAU,IAAA;AAChB,aAAW,CAAC,QAAQ,GAAG,KAAK,eAAe;AACzC,QAAI,IAAI,QAAQ,MAAM,KAAK,GAAG,EAAE,MAAM;AAAA,EACxC;AACA,SAAO;AACT;"}
@@ -0,0 +1,132 @@
1
+ import { Loc } from './trace.js';
2
+ /**
3
+ * Minimal source-map shape used throughout the import-protection plugin.
4
+ *
5
+ * Structurally compatible with both Rollup's `SourceMap` (version: number)
6
+ * and the `source-map` package's `RawSourceMap` (version: string).
7
+ */
8
+ export interface SourceMapLike {
9
+ file?: string;
10
+ sourceRoot?: string;
11
+ version: number | string;
12
+ sources: Array<string>;
13
+ names: Array<string>;
14
+ sourcesContent?: Array<string | null>;
15
+ mappings: string;
16
+ }
17
+ /**
18
+ * A cached transform result for a single module.
19
+ *
20
+ * - `code` – fully-transformed source (after all plugins).
21
+ * - `map` – composed sourcemap (chains back to the original file).
22
+ * - `originalCode` – the untransformed source, extracted from the
23
+ * sourcemap's `sourcesContent[0]` during the transform
24
+ * hook. Used by {@link buildCodeSnippet} so we never
25
+ * have to re-derive it via a flaky `sourceContentFor`
26
+ * lookup at display time.
27
+ */
28
+ export interface TransformResult {
29
+ code: string;
30
+ map: SourceMapLike | undefined;
31
+ originalCode: string | undefined;
32
+ /** Precomputed line index for `code` (index → line/col). */
33
+ lineIndex?: LineIndex;
34
+ }
35
+ /**
36
+ * Provides the transformed code and composed sourcemap for a module.
37
+ *
38
+ * During `resolveId`, Vite's `this.load()` does NOT return code/map in dev
39
+ * mode (the ModuleInfo proxy throws on `.code` access). Even in build mode,
40
+ * Rollup's `ModuleInfo` has `.code` but not `.map`.
41
+ *
42
+ * Instead, we populate this cache from a late-running transform hook that
43
+ * stores `{ code, map, originalCode }` for every module as it passes through
44
+ * the pipeline. By the time `resolveId` fires for an import, the importer
45
+ * has already been fully transformed, so the cache always has the data we
46
+ * need.
47
+ *
48
+ * The `id` parameter is the **raw** module ID (may include Vite query
49
+ * parameters like `?tsr-split=component`). Implementations should look up
50
+ * with the full ID first, then fall back to the query-stripped path so that
51
+ * virtual-module variants are resolved correctly without losing the base-file
52
+ * fallback.
53
+ */
54
+ export interface TransformResultProvider {
55
+ getTransformResult: (id: string) => TransformResult | undefined;
56
+ }
57
+ export type LineIndex = {
58
+ offsets: Array<number>;
59
+ };
60
+ export declare function buildLineIndex(code: string): LineIndex;
61
+ /**
62
+ * Pick the most-likely original source text for `importerFile`.
63
+ *
64
+ * Sourcemaps can contain multiple sources (composed maps), so `sourcesContent[0]`
65
+ * is not guaranteed to represent the importer.
66
+ */
67
+ export declare function pickOriginalCodeFromSourcesContent(map: SourceMapLike | undefined, importerFile: string, root: string): string | undefined;
68
+ export declare function mapGeneratedToOriginal(map: SourceMapLike | undefined, generated: {
69
+ line: number;
70
+ column0: number;
71
+ }, fallbackFile: string): Promise<Loc>;
72
+ export declare function findFirstImportSpecifierIndex(code: string, source: string): number;
73
+ /**
74
+ * Find the location of an import statement in a transformed module.
75
+ *
76
+ * Looks up the module's transformed code + composed sourcemap from the
77
+ * {@link TransformResultProvider}, finds the import specifier in the
78
+ * transformed code, and maps back to the original source via the sourcemap.
79
+ *
80
+ * Results are cached in `importLocCache`.
81
+ */
82
+ export declare function findImportStatementLocationFromTransformed(provider: TransformResultProvider, importerId: string, source: string, importLocCache: Map<string, {
83
+ file?: string;
84
+ line: number;
85
+ column: number;
86
+ } | null>): Promise<Loc | undefined>;
87
+ /**
88
+ * Find the first post-compile usage location for a denied import specifier.
89
+ *
90
+ * Best-effort: looks up the module's transformed output from the
91
+ * {@link TransformResultProvider}, finds the first non-import usage of
92
+ * an imported binding, and maps back to original source via sourcemap.
93
+ */
94
+ export declare function findPostCompileUsageLocation(provider: TransformResultProvider, importerId: string, source: string, findPostCompileUsagePos: (code: string, source: string) => {
95
+ line: number;
96
+ column0: number;
97
+ } | undefined): Promise<Loc | undefined>;
98
+ /**
99
+ * Annotate each trace hop with the location of the import that created the
100
+ * edge (file:line:col). Skips steps that already have a location.
101
+ */
102
+ export declare function addTraceImportLocations(provider: TransformResultProvider, trace: Array<{
103
+ file: string;
104
+ specifier?: string;
105
+ line?: number;
106
+ column?: number;
107
+ }>, importLocCache: Map<string, {
108
+ file?: string;
109
+ line: number;
110
+ column: number;
111
+ } | null>): Promise<void>;
112
+ export interface CodeSnippet {
113
+ /** Source lines with line numbers, e.g. `[" 6 | import { getSecret } from './secret.server'", ...]` */
114
+ lines: Array<string>;
115
+ /** The highlighted line (1-indexed original line number) */
116
+ highlightLine: number;
117
+ /** Clickable file:line reference */
118
+ location: string;
119
+ }
120
+ /**
121
+ * Build a vitest-style code snippet showing the lines surrounding a location.
122
+ *
123
+ * Uses the `originalCode` stored in the transform result cache (extracted from
124
+ * `sourcesContent[0]` of the composed sourcemap at transform time). This is
125
+ * reliable regardless of how the sourcemap names its sources.
126
+ *
127
+ * Falls back to the transformed code only when `originalCode` is unavailable
128
+ * (e.g. a virtual module with no sourcemap).
129
+ *
130
+ * @param contextLines Number of lines to show above/below the target line (default 2).
131
+ */
132
+ export declare function buildCodeSnippet(provider: TransformResultProvider, moduleId: string, loc: Loc, contextLines?: number): CodeSnippet | undefined;