@tanstack/start-plugin-core 1.163.0 → 1.163.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.
@@ -13,6 +13,29 @@ const MOCK_RUNTIME_PREFIX = "tanstack-start-import-protection:mock-runtime:";
13
13
  const RESOLVED_MOCK_RUNTIME_PREFIX = resolveViteId(MOCK_RUNTIME_PREFIX);
14
14
  const MARKER_PREFIX = "tanstack-start-import-protection:marker:";
15
15
  const RESOLVED_MARKER_PREFIX = resolveViteId(MARKER_PREFIX);
16
+ const RESOLVED_MARKER_SERVER_ONLY = resolveViteId(`${MARKER_PREFIX}server-only`);
17
+ const RESOLVED_MARKER_CLIENT_ONLY = resolveViteId(`${MARKER_PREFIX}client-only`);
18
+ function resolvedMarkerVirtualModuleId(kind) {
19
+ return kind === "server" ? RESOLVED_MARKER_SERVER_ONLY : RESOLVED_MARKER_CLIENT_ONLY;
20
+ }
21
+ function getResolvedVirtualModuleMatchers() {
22
+ return RESOLVED_VIRTUAL_MODULE_MATCHERS;
23
+ }
24
+ const RESOLVED_VIRTUAL_MODULE_MATCHERS = [
25
+ RESOLVED_MOCK_MODULE_ID,
26
+ RESOLVED_MOCK_BUILD_PREFIX,
27
+ RESOLVED_MOCK_EDGE_PREFIX,
28
+ RESOLVED_MOCK_RUNTIME_PREFIX,
29
+ RESOLVED_MARKER_PREFIX
30
+ ];
31
+ function resolveInternalVirtualModuleId(source) {
32
+ if (source === MOCK_MODULE_ID) return RESOLVED_MOCK_MODULE_ID;
33
+ if (source.startsWith(MOCK_EDGE_PREFIX)) return resolveViteId(source);
34
+ if (source.startsWith(MOCK_RUNTIME_PREFIX)) return resolveViteId(source);
35
+ if (source.startsWith(MOCK_BUILD_PREFIX)) return resolveViteId(source);
36
+ if (source.startsWith(MARKER_PREFIX)) return resolveViteId(source);
37
+ return void 0;
38
+ }
16
39
  function toBase64Url(input) {
17
40
  return Buffer.from(input, "utf8").toString("base64url");
18
41
  }
@@ -98,7 +121,8 @@ function __report(action, accessPath) {
98
121
  return true;
99
122
  },` : "";
100
123
  return `
101
- ${preamble}function ${fnName}(name) {
124
+ ${preamble}/* @__NO_SIDE_EFFECTS__ */
125
+ function ${fnName}(name) {
102
126
  const fn = function () {};
103
127
  fn.prototype.name = name;
104
128
  const children = Object.create(null);
@@ -125,12 +149,12 @@ ${preamble}function ${fnName}(name) {
125
149
  });
126
150
  return proxy;
127
151
  }
128
- const mock = ${fnName}('mock');
152
+ const mock = /* @__PURE__ */ ${fnName}('mock');
129
153
  export default mock;
130
154
  `;
131
155
  }
132
156
  function loadSilentMockModule() {
133
- return { syntheticNamedExports: true, code: generateMockCode() };
157
+ return { code: generateMockCode() };
134
158
  }
135
159
  function loadMockEdgeModule(encodedPayload) {
136
160
  let payload;
@@ -186,23 +210,40 @@ const MARKER_MODULE_RESULT = { code: "export {}" };
186
210
  function loadMarkerModule() {
187
211
  return MARKER_MODULE_RESULT;
188
212
  }
213
+ function loadResolvedVirtualModule(id) {
214
+ if (id === RESOLVED_MOCK_MODULE_ID) {
215
+ return loadSilentMockModule();
216
+ }
217
+ if (id.startsWith(RESOLVED_MOCK_BUILD_PREFIX)) {
218
+ return loadSilentMockModule();
219
+ }
220
+ if (id.startsWith(RESOLVED_MOCK_EDGE_PREFIX)) {
221
+ return loadMockEdgeModule(id.slice(RESOLVED_MOCK_EDGE_PREFIX.length));
222
+ }
223
+ if (id.startsWith(RESOLVED_MOCK_RUNTIME_PREFIX)) {
224
+ return loadMockRuntimeModule(id.slice(RESOLVED_MOCK_RUNTIME_PREFIX.length));
225
+ }
226
+ if (id.startsWith(RESOLVED_MARKER_PREFIX)) {
227
+ return loadMarkerModule();
228
+ }
229
+ return void 0;
230
+ }
189
231
  export {
190
232
  MARKER_PREFIX,
191
233
  MOCK_BUILD_PREFIX,
192
234
  MOCK_EDGE_PREFIX,
193
235
  MOCK_MODULE_ID,
194
236
  MOCK_RUNTIME_PREFIX,
195
- RESOLVED_MARKER_PREFIX,
196
- RESOLVED_MOCK_BUILD_PREFIX,
197
- RESOLVED_MOCK_EDGE_PREFIX,
198
- RESOLVED_MOCK_MODULE_ID,
199
- RESOLVED_MOCK_RUNTIME_PREFIX,
200
237
  RUNTIME_SUGGESTION_TEXT,
238
+ getResolvedVirtualModuleMatchers,
201
239
  loadMarkerModule,
202
240
  loadMockEdgeModule,
203
241
  loadMockRuntimeModule,
242
+ loadResolvedVirtualModule,
204
243
  loadSilentMockModule,
205
244
  makeMockEdgeModuleId,
206
- mockRuntimeModuleIdFromViolation
245
+ mockRuntimeModuleIdFromViolation,
246
+ resolveInternalVirtualModuleId,
247
+ resolvedMarkerVirtualModuleId
207
248
  };
208
249
  //# sourceMappingURL=virtualModules.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"virtualModules.js","sources":["../../../src/import-protection-plugin/virtualModules.ts"],"sourcesContent":["import { resolveViteId } from '../utils'\nimport { VITE_ENVIRONMENT_NAMES } from '../constants'\nimport { isValidExportName } from './rewriteDeniedImports'\nimport { CLIENT_ENV_SUGGESTIONS } from './trace'\nimport { relativizePath } from './utils'\nimport type { ViolationInfo } from './trace'\n\nexport const MOCK_MODULE_ID = 'tanstack-start-import-protection:mock'\nexport const RESOLVED_MOCK_MODULE_ID = resolveViteId(MOCK_MODULE_ID)\n\n/**\n * Per-violation mock prefix used in build+error mode.\n * Each deferred violation gets a unique ID so we can check which ones\n * survived tree-shaking in `generateBundle`.\n */\nexport const MOCK_BUILD_PREFIX = 'tanstack-start-import-protection:mock:build:'\nexport const RESOLVED_MOCK_BUILD_PREFIX = resolveViteId(MOCK_BUILD_PREFIX)\n\nexport const MOCK_EDGE_PREFIX = 'tanstack-start-import-protection:mock-edge:'\nexport const RESOLVED_MOCK_EDGE_PREFIX = resolveViteId(MOCK_EDGE_PREFIX)\n\nexport const MOCK_RUNTIME_PREFIX =\n 'tanstack-start-import-protection:mock-runtime:'\nexport const RESOLVED_MOCK_RUNTIME_PREFIX = resolveViteId(MOCK_RUNTIME_PREFIX)\n\nexport const MARKER_PREFIX = 'tanstack-start-import-protection:marker:'\nexport const RESOLVED_MARKER_PREFIX = resolveViteId(MARKER_PREFIX)\n\nfunction toBase64Url(input: string): string {\n return Buffer.from(input, 'utf8').toString('base64url')\n}\n\nfunction fromBase64Url(input: string): string {\n return Buffer.from(input, 'base64url').toString('utf8')\n}\n\ntype MockAccessMode = 'error' | 'warn' | 'off'\n\n/**\n * Compact runtime suggestion text for browser console, derived from\n * {@link CLIENT_ENV_SUGGESTIONS} so there's a single source of truth.\n */\nexport const RUNTIME_SUGGESTION_TEXT =\n 'Fix: ' +\n CLIENT_ENV_SUGGESTIONS.join('. ') +\n '. To disable these runtime diagnostics, set importProtection.mockAccess: \"off\".'\n\nexport function mockRuntimeModuleIdFromViolation(\n info: ViolationInfo,\n mode: MockAccessMode,\n root: string,\n): string {\n if (mode === 'off') return MOCK_MODULE_ID\n if (info.env !== VITE_ENVIRONMENT_NAMES.client) return MOCK_MODULE_ID\n\n const rel = (p: string) => relativizePath(p, root)\n const trace = info.trace.map((s) => {\n const file = rel(s.file)\n if (s.line == null) return file\n return `${file}:${s.line}:${s.column ?? 1}`\n })\n\n const payload = {\n env: info.env,\n importer: info.importer,\n specifier: info.specifier,\n trace,\n mode,\n }\n return `${MOCK_RUNTIME_PREFIX}${toBase64Url(JSON.stringify(payload))}`\n}\n\nexport function makeMockEdgeModuleId(\n exports: Array<string>,\n source: string,\n runtimeId: string,\n): string {\n const payload = { source, exports, runtimeId }\n return `${MOCK_EDGE_PREFIX}${toBase64Url(JSON.stringify(payload))}`\n}\n\n/**\n * Generate a recursive Proxy-based mock module.\n *\n * When `diagnostics` is provided, the generated code includes a `__report`\n * function that logs runtime warnings/errors when the mock is actually used\n * (property access for primitive coercion, calls, construction, sets).\n *\n * When `diagnostics` is omitted, the mock is completely silent — suitable\n * for the shared `MOCK_MODULE_ID` that uses `syntheticNamedExports`.\n */\nfunction generateMockCode(diagnostics?: {\n meta: {\n env: string\n importer: string\n specifier: string\n trace: Array<unknown>\n }\n mode: 'error' | 'warn' | 'off'\n}): string {\n const fnName = diagnostics ? '__createMock' : 'createMock'\n const hasDiag = !!diagnostics\n\n const preamble = hasDiag\n ? `const __meta = ${JSON.stringify(diagnostics.meta)};\nconst __mode = ${JSON.stringify(diagnostics.mode)};\n\nconst __seen = new Set();\nfunction __report(action, accessPath) {\n if (__mode === 'off') return;\n const key = action + ':' + accessPath;\n if (__seen.has(key)) return;\n __seen.add(key);\n\n const traceLines = Array.isArray(__meta.trace) && __meta.trace.length\n ? \"\\\\n\\\\nTrace:\\\\n\" + __meta.trace.map((t, i) => ' ' + (i + 1) + '. ' + String(t)).join('\\\\n')\n : '';\n\n const msg =\n '[import-protection] Mocked import used in dev client\\\\n\\\\n' +\n 'Denied import: \"' + __meta.specifier + '\"\\\\n' +\n 'Importer: ' + __meta.importer + '\\\\n' +\n 'Access: ' + accessPath + ' (' + action + ')' +\n traceLines +\n '\\\\n\\\\n' + ${JSON.stringify(RUNTIME_SUGGESTION_TEXT)};\n\n const err = new Error(msg);\n if (__mode === 'warn') {\n console.warn(err);\n } else {\n console.error(err);\n }\n}\n`\n : ''\n\n // Diagnostic-only traps for primitive coercion, set\n const diagGetTraps = hasDiag\n ? `\n if (prop === Symbol.toPrimitive) {\n return () => {\n __report('toPrimitive', name);\n return '[import-protection mock]';\n };\n }\n if (prop === 'toString' || prop === 'valueOf' || prop === 'toJSON') {\n return () => {\n __report(String(prop), name);\n return '[import-protection mock]';\n };\n }`\n : ''\n\n const applyBody = hasDiag\n ? `__report('call', name + '()');\n return ${fnName}(name + '()');`\n : `return ${fnName}(name + '()');`\n\n const constructBody = hasDiag\n ? `__report('construct', 'new ' + name);\n return ${fnName}('new ' + name);`\n : `return ${fnName}('new ' + name);`\n\n const setTrap = hasDiag\n ? `\n set(_target, prop) {\n __report('set', name + '.' + String(prop));\n return true;\n },`\n : ''\n\n return `\n${preamble}function ${fnName}(name) {\n const fn = function () {};\n fn.prototype.name = name;\n const children = Object.create(null);\n const proxy = new Proxy(fn, {\n get(_target, prop) {\n if (prop === '__esModule') return true;\n if (prop === 'default') return proxy;\n if (prop === 'caller') return null;\n if (prop === 'then') return (f) => Promise.resolve(f(proxy));\n if (prop === 'catch') return () => Promise.resolve(proxy);\n if (prop === 'finally') return (f) => { f(); return Promise.resolve(proxy); };${diagGetTraps}\n if (typeof prop === 'symbol') return undefined;\n if (!(prop in children)) {\n children[prop] = ${fnName}(name + '.' + prop);\n }\n return children[prop];\n },\n apply() {\n ${applyBody}\n },\n construct() {\n ${constructBody}\n },${setTrap}\n });\n return proxy;\n}\nconst mock = ${fnName}('mock');\nexport default mock;\n`\n}\n\nexport function loadSilentMockModule(): {\n syntheticNamedExports: boolean\n code: string\n} {\n return { syntheticNamedExports: true, code: generateMockCode() }\n}\n\nexport function loadMockEdgeModule(encodedPayload: string): { code: string } {\n let payload: { exports?: Array<string>; runtimeId?: string }\n try {\n payload = JSON.parse(fromBase64Url(encodedPayload)) as typeof payload\n } catch {\n payload = { exports: [] }\n }\n const names: Array<string> = Array.isArray(payload.exports)\n ? payload.exports.filter(\n (n): n is string =>\n typeof n === 'string' && n.length > 0 && n !== 'default',\n )\n : []\n\n const runtimeId: string =\n typeof payload.runtimeId === 'string' && payload.runtimeId.length > 0\n ? payload.runtimeId\n : MOCK_MODULE_ID\n\n const exportLines: Array<string> = []\n const stringExports: Array<{ alias: string; name: string }> = []\n\n for (let i = 0; i < names.length; i++) {\n const n = names[i]!\n if (isValidExportName(n)) {\n exportLines.push(`export const ${n} = mock.${n};`)\n } else {\n // ES2022 string-keyed export: use a temp var + re-export with string literal\n const alias = `__tss_str_${i}`\n exportLines.push(`const ${alias} = mock[${JSON.stringify(n)}];`)\n stringExports.push({ alias, name: n })\n }\n }\n\n if (stringExports.length > 0) {\n const reexports = stringExports\n .map((s) => `${s.alias} as ${JSON.stringify(s.name)}`)\n .join(', ')\n exportLines.push(`export { ${reexports} };`)\n }\n\n return {\n code: `import mock from ${JSON.stringify(runtimeId)};\n${exportLines.join('\\n')}\nexport default mock;\n`,\n }\n}\n\nexport function loadMockRuntimeModule(encodedPayload: string): {\n code: string\n} {\n let payload: {\n mode?: string\n env?: string\n importer?: string\n specifier?: string\n trace?: Array<unknown>\n }\n try {\n payload = JSON.parse(fromBase64Url(encodedPayload)) as typeof payload\n } catch {\n payload = {}\n }\n\n const mode: 'error' | 'warn' | 'off' =\n payload.mode === 'warn' || payload.mode === 'off' ? payload.mode : 'error'\n\n const meta = {\n env: String(payload.env ?? ''),\n importer: String(payload.importer ?? ''),\n specifier: String(payload.specifier ?? ''),\n trace: Array.isArray(payload.trace) ? payload.trace : [],\n }\n\n return { code: generateMockCode({ meta, mode }) }\n}\n\nconst MARKER_MODULE_RESULT = { code: 'export {}' } as const\n\nexport function loadMarkerModule(): { code: string } {\n return MARKER_MODULE_RESULT\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"virtualModules.js","sources":["../../../src/import-protection-plugin/virtualModules.ts"],"sourcesContent":["import { resolveViteId } from '../utils'\nimport { VITE_ENVIRONMENT_NAMES } from '../constants'\nimport { isValidExportName } from './rewriteDeniedImports'\nimport { CLIENT_ENV_SUGGESTIONS } from './trace'\nimport { relativizePath } from './utils'\nimport type { ViolationInfo } from './trace'\n\nexport const MOCK_MODULE_ID = 'tanstack-start-import-protection:mock'\nconst RESOLVED_MOCK_MODULE_ID = resolveViteId(MOCK_MODULE_ID)\n\n/**\n * Per-violation mock prefix used in build+error mode.\n * Each deferred violation gets a unique ID so we can check which ones\n * survived tree-shaking in `generateBundle`.\n */\nexport const MOCK_BUILD_PREFIX = 'tanstack-start-import-protection:mock:build:'\nconst RESOLVED_MOCK_BUILD_PREFIX = resolveViteId(MOCK_BUILD_PREFIX)\n\nexport const MOCK_EDGE_PREFIX = 'tanstack-start-import-protection:mock-edge:'\nconst RESOLVED_MOCK_EDGE_PREFIX = resolveViteId(MOCK_EDGE_PREFIX)\n\nexport const MOCK_RUNTIME_PREFIX =\n 'tanstack-start-import-protection:mock-runtime:'\nconst RESOLVED_MOCK_RUNTIME_PREFIX = resolveViteId(MOCK_RUNTIME_PREFIX)\n\nexport const MARKER_PREFIX = 'tanstack-start-import-protection:marker:'\nconst RESOLVED_MARKER_PREFIX = resolveViteId(MARKER_PREFIX)\n\nconst RESOLVED_MARKER_SERVER_ONLY = resolveViteId(`${MARKER_PREFIX}server-only`)\nconst RESOLVED_MARKER_CLIENT_ONLY = resolveViteId(`${MARKER_PREFIX}client-only`)\n\nexport function resolvedMarkerVirtualModuleId(\n kind: 'server' | 'client',\n): string {\n return kind === 'server'\n ? RESOLVED_MARKER_SERVER_ONLY\n : RESOLVED_MARKER_CLIENT_ONLY\n}\n\n/**\n * Convenience list for plugin `load` filters/handlers.\n *\n * Vite/Rollup call `load(id)` with the *resolved* virtual id (prefixed by `\\0`).\n * `resolveId(source)` sees the *unresolved* id/prefix (without `\\0`).\n */\nexport function getResolvedVirtualModuleMatchers(): ReadonlyArray<string> {\n return RESOLVED_VIRTUAL_MODULE_MATCHERS\n}\n\nconst RESOLVED_VIRTUAL_MODULE_MATCHERS = [\n RESOLVED_MOCK_MODULE_ID,\n RESOLVED_MOCK_BUILD_PREFIX,\n RESOLVED_MOCK_EDGE_PREFIX,\n RESOLVED_MOCK_RUNTIME_PREFIX,\n RESOLVED_MARKER_PREFIX,\n] as const\n\n/**\n * Resolve import-protection's internal virtual module IDs.\n *\n * `resolveId(source)` sees *unresolved* ids/prefixes (no `\\0`).\n * Returning a resolved id (with `\\0`) ensures Vite/Rollup route it to `load`.\n */\nexport function resolveInternalVirtualModuleId(\n source: string,\n): string | undefined {\n if (source === MOCK_MODULE_ID) return RESOLVED_MOCK_MODULE_ID\n if (source.startsWith(MOCK_EDGE_PREFIX)) return resolveViteId(source)\n if (source.startsWith(MOCK_RUNTIME_PREFIX)) return resolveViteId(source)\n if (source.startsWith(MOCK_BUILD_PREFIX)) return resolveViteId(source)\n if (source.startsWith(MARKER_PREFIX)) return resolveViteId(source)\n return undefined\n}\n\nfunction toBase64Url(input: string): string {\n return Buffer.from(input, 'utf8').toString('base64url')\n}\n\nfunction fromBase64Url(input: string): string {\n return Buffer.from(input, 'base64url').toString('utf8')\n}\n\ntype MockAccessMode = 'error' | 'warn' | 'off'\n\n/**\n * Compact runtime suggestion text for browser console, derived from\n * {@link CLIENT_ENV_SUGGESTIONS} so there's a single source of truth.\n */\nexport const RUNTIME_SUGGESTION_TEXT =\n 'Fix: ' +\n CLIENT_ENV_SUGGESTIONS.join('. ') +\n '. To disable these runtime diagnostics, set importProtection.mockAccess: \"off\".'\n\nexport function mockRuntimeModuleIdFromViolation(\n info: ViolationInfo,\n mode: MockAccessMode,\n root: string,\n): string {\n if (mode === 'off') return MOCK_MODULE_ID\n if (info.env !== VITE_ENVIRONMENT_NAMES.client) return MOCK_MODULE_ID\n\n const rel = (p: string) => relativizePath(p, root)\n const trace = info.trace.map((s) => {\n const file = rel(s.file)\n if (s.line == null) return file\n return `${file}:${s.line}:${s.column ?? 1}`\n })\n\n const payload = {\n env: info.env,\n importer: info.importer,\n specifier: info.specifier,\n trace,\n mode,\n }\n return `${MOCK_RUNTIME_PREFIX}${toBase64Url(JSON.stringify(payload))}`\n}\n\nexport function makeMockEdgeModuleId(\n exports: Array<string>,\n source: string,\n runtimeId: string,\n): string {\n const payload = { source, exports, runtimeId }\n return `${MOCK_EDGE_PREFIX}${toBase64Url(JSON.stringify(payload))}`\n}\n\n/**\n * Generate a recursive Proxy-based mock module.\n *\n * When `diagnostics` is provided, the generated code includes a `__report`\n * function that logs runtime warnings/errors when the mock is actually used\n * (property access for primitive coercion, calls, construction, sets).\n *\n * When `diagnostics` is omitted, the mock is completely silent — suitable\n * for base mock modules (e.g. `MOCK_MODULE_ID` or per-violation build mocks)\n * that are consumed by mock-edge modules providing explicit named exports.\n */\nfunction generateMockCode(diagnostics?: {\n meta: {\n env: string\n importer: string\n specifier: string\n trace: Array<unknown>\n }\n mode: 'error' | 'warn' | 'off'\n}): string {\n const fnName = diagnostics ? '__createMock' : 'createMock'\n const hasDiag = !!diagnostics\n\n const preamble = hasDiag\n ? `const __meta = ${JSON.stringify(diagnostics.meta)};\nconst __mode = ${JSON.stringify(diagnostics.mode)};\n\nconst __seen = new Set();\nfunction __report(action, accessPath) {\n if (__mode === 'off') return;\n const key = action + ':' + accessPath;\n if (__seen.has(key)) return;\n __seen.add(key);\n\n const traceLines = Array.isArray(__meta.trace) && __meta.trace.length\n ? \"\\\\n\\\\nTrace:\\\\n\" + __meta.trace.map((t, i) => ' ' + (i + 1) + '. ' + String(t)).join('\\\\n')\n : '';\n\n const msg =\n '[import-protection] Mocked import used in dev client\\\\n\\\\n' +\n 'Denied import: \"' + __meta.specifier + '\"\\\\n' +\n 'Importer: ' + __meta.importer + '\\\\n' +\n 'Access: ' + accessPath + ' (' + action + ')' +\n traceLines +\n '\\\\n\\\\n' + ${JSON.stringify(RUNTIME_SUGGESTION_TEXT)};\n\n const err = new Error(msg);\n if (__mode === 'warn') {\n console.warn(err);\n } else {\n console.error(err);\n }\n}\n`\n : ''\n\n // Diagnostic-only traps for primitive coercion, set\n const diagGetTraps = hasDiag\n ? `\n if (prop === Symbol.toPrimitive) {\n return () => {\n __report('toPrimitive', name);\n return '[import-protection mock]';\n };\n }\n if (prop === 'toString' || prop === 'valueOf' || prop === 'toJSON') {\n return () => {\n __report(String(prop), name);\n return '[import-protection mock]';\n };\n }`\n : ''\n\n const applyBody = hasDiag\n ? `__report('call', name + '()');\n return ${fnName}(name + '()');`\n : `return ${fnName}(name + '()');`\n\n const constructBody = hasDiag\n ? `__report('construct', 'new ' + name);\n return ${fnName}('new ' + name);`\n : `return ${fnName}('new ' + name);`\n\n const setTrap = hasDiag\n ? `\n set(_target, prop) {\n __report('set', name + '.' + String(prop));\n return true;\n },`\n : ''\n\n return `\n${preamble}/* @__NO_SIDE_EFFECTS__ */\nfunction ${fnName}(name) {\n const fn = function () {};\n fn.prototype.name = name;\n const children = Object.create(null);\n const proxy = new Proxy(fn, {\n get(_target, prop) {\n if (prop === '__esModule') return true;\n if (prop === 'default') return proxy;\n if (prop === 'caller') return null;\n if (prop === 'then') return (f) => Promise.resolve(f(proxy));\n if (prop === 'catch') return () => Promise.resolve(proxy);\n if (prop === 'finally') return (f) => { f(); return Promise.resolve(proxy); };${diagGetTraps}\n if (typeof prop === 'symbol') return undefined;\n if (!(prop in children)) {\n children[prop] = ${fnName}(name + '.' + prop);\n }\n return children[prop];\n },\n apply() {\n ${applyBody}\n },\n construct() {\n ${constructBody}\n },${setTrap}\n });\n return proxy;\n}\nconst mock = /* @__PURE__ */ ${fnName}('mock');\nexport default mock;\n`\n}\n\nexport function loadSilentMockModule(): { code: string } {\n return { code: generateMockCode() }\n}\n\nexport function loadMockEdgeModule(encodedPayload: string): { code: string } {\n let payload: { exports?: Array<string>; runtimeId?: string }\n try {\n payload = JSON.parse(fromBase64Url(encodedPayload)) as typeof payload\n } catch {\n payload = { exports: [] }\n }\n const names: Array<string> = Array.isArray(payload.exports)\n ? payload.exports.filter(\n (n): n is string =>\n typeof n === 'string' && n.length > 0 && n !== 'default',\n )\n : []\n\n const runtimeId: string =\n typeof payload.runtimeId === 'string' && payload.runtimeId.length > 0\n ? payload.runtimeId\n : MOCK_MODULE_ID\n\n const exportLines: Array<string> = []\n const stringExports: Array<{ alias: string; name: string }> = []\n\n for (let i = 0; i < names.length; i++) {\n const n = names[i]!\n if (isValidExportName(n)) {\n exportLines.push(`export const ${n} = mock.${n};`)\n } else {\n // ES2022 string-keyed export: use a temp var + re-export with string literal\n const alias = `__tss_str_${i}`\n exportLines.push(`const ${alias} = mock[${JSON.stringify(n)}];`)\n stringExports.push({ alias, name: n })\n }\n }\n\n if (stringExports.length > 0) {\n const reexports = stringExports\n .map((s) => `${s.alias} as ${JSON.stringify(s.name)}`)\n .join(', ')\n exportLines.push(`export { ${reexports} };`)\n }\n\n return {\n code: `import mock from ${JSON.stringify(runtimeId)};\n${exportLines.join('\\n')}\nexport default mock;\n`,\n }\n}\n\nexport function loadMockRuntimeModule(encodedPayload: string): {\n code: string\n} {\n let payload: {\n mode?: string\n env?: string\n importer?: string\n specifier?: string\n trace?: Array<unknown>\n }\n try {\n payload = JSON.parse(fromBase64Url(encodedPayload)) as typeof payload\n } catch {\n payload = {}\n }\n\n const mode: 'error' | 'warn' | 'off' =\n payload.mode === 'warn' || payload.mode === 'off' ? payload.mode : 'error'\n\n const meta = {\n env: String(payload.env ?? ''),\n importer: String(payload.importer ?? ''),\n specifier: String(payload.specifier ?? ''),\n trace: Array.isArray(payload.trace) ? payload.trace : [],\n }\n\n return { code: generateMockCode({ meta, mode }) }\n}\n\nconst MARKER_MODULE_RESULT = { code: 'export {}' } as const\n\nexport function loadMarkerModule(): { code: string } {\n return MARKER_MODULE_RESULT\n}\n\nexport function loadResolvedVirtualModule(\n id: string,\n): { code: string } | undefined {\n if (id === RESOLVED_MOCK_MODULE_ID) {\n return loadSilentMockModule()\n }\n\n // Per-violation build mock modules — same silent mock code\n if (id.startsWith(RESOLVED_MOCK_BUILD_PREFIX)) {\n return loadSilentMockModule()\n }\n\n if (id.startsWith(RESOLVED_MOCK_EDGE_PREFIX)) {\n return loadMockEdgeModule(id.slice(RESOLVED_MOCK_EDGE_PREFIX.length))\n }\n\n if (id.startsWith(RESOLVED_MOCK_RUNTIME_PREFIX)) {\n return loadMockRuntimeModule(id.slice(RESOLVED_MOCK_RUNTIME_PREFIX.length))\n }\n\n if (id.startsWith(RESOLVED_MARKER_PREFIX)) {\n return loadMarkerModule()\n }\n\n return undefined\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/start-plugin-core",
3
- "version": "1.163.0",
3
+ "version": "1.163.1",
4
4
  "description": "Modern and scalable routing for React applications",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -61,10 +61,10 @@
61
61
  "xmlbuilder2": "^4.0.3",
62
62
  "zod": "^3.24.2",
63
63
  "@tanstack/router-core": "1.162.9",
64
+ "@tanstack/router-plugin": "1.162.9",
64
65
  "@tanstack/router-generator": "1.162.9",
65
66
  "@tanstack/router-utils": "1.161.4",
66
67
  "@tanstack/start-client-core": "1.162.9",
67
- "@tanstack/router-plugin": "1.162.9",
68
68
  "@tanstack/start-server-core": "1.162.9"
69
69
  },
70
70
  "devDependencies": {
@@ -78,9 +78,9 @@ analysing a module's exports. These are tracked via `serverFnLookupModules` and
78
78
  ### Central functions
79
79
 
80
80
  - **`handleViolation()`**: Formats + reports (or silences) the violation. Returns
81
- a mock module ID (or `{ id, syntheticNamedExports }` in build) so `resolveId`
82
- can substitute the offending import. May also return `undefined` (suppressed by
83
- `onViolation` or silent+error in dev) or throw via `ctx.error()` (dev+error).
81
+ a mock-edge module ID (string) so `resolveId` can substitute the offending
82
+ import. May also return `undefined` (suppressed by `onViolation` or
83
+ silent+error in dev) or throw via `ctx.error()` (dev+error).
84
84
  - **`reportOrDeferViolation()`**: Dispatch layer. Either defers (stores for later
85
85
  verification) or reports immediately, depending on `shouldDefer`.
86
86
 
@@ -138,8 +138,11 @@ In dev, each violation gets a **per-importer mock edge module** that:
138
138
  - Delegates to a **runtime mock module** that contains a recursive Proxy and
139
139
  optional runtime diagnostics (console warnings when mocked values are used).
140
140
 
141
- This differs from build mode, where `syntheticNamedExports: true` lets Rollup
142
- handle named export resolution from the silent mock.
141
+ This differs from build mode, where each violation gets a **per-violation mock
142
+ edge module** wrapping a unique base mock module
143
+ (`\0tanstack-start-import-protection:mock:build:N`). The edge module re-exports
144
+ the named exports the importer expects, just like in dev, ensuring compatibility
145
+ with both Rollup and Rolldown (which doesn't support `syntheticNamedExports`).
143
146
 
144
147
  ## Build Mode Strategy
145
148
 
@@ -148,15 +151,19 @@ handle named export resolution from the silent mock.
148
151
  Both mock and error build modes follow the same pattern:
149
152
 
150
153
  1. **`resolveId`**: Call `handleViolation({ silent: true })`. Generate a
151
- **unique per-violation mock module ID** (`\0tanstack-start-import-protection:mock:build:N`).
152
- Store the violation + mock ID in `env.deferredBuildViolations`. Return the
153
- mock ID so Rollup substitutes the offending import.
154
+ **unique per-violation mock-edge module** that wraps a base mock module
155
+ (`\0tanstack-start-import-protection:mock:build:N`) and provides explicit
156
+ named exports matching the importer's import bindings. Store the violation +
157
+ mock-edge ID in `env.deferredBuildViolations`. Return the mock-edge ID so the
158
+ bundler substitutes the offending import.
154
159
 
155
- 2. **`load`**: Return a silent Proxy-based mock module (same code as
156
- `RESOLVED_MOCK_MODULE_ID`) with `syntheticNamedExports: true`.
160
+ 2. **`load`**: For the base mock module, return a silent Proxy-based mock. For
161
+ the mock-edge module, return code that imports from the base mock and
162
+ re-exports the expected named bindings (e.g. `export const Foo = mock.Foo`).
157
163
 
158
- 3. **Tree-shaking**: Rollup processes the bundle normally. If no binding from
159
- the mock module is actually used at runtime, the mock module is eliminated.
164
+ 3. **Tree-shaking**: The bundler processes the bundle normally. If no binding from
165
+ the mock-edge module is actually used at runtime, both the edge and base
166
+ modules are eliminated.
160
167
 
161
168
  4. **`generateBundle`**: Inspect the output chunks. For each deferred violation,
162
169
  check whether its unique mock module ID appears in any chunk's `modules`.
@@ -171,15 +178,16 @@ Both mock and error build modes follow the same pattern:
171
178
  The original `RESOLVED_MOCK_MODULE_ID` is a single shared virtual module used
172
179
  for all mock-mode violations. If multiple violations are deferred, we need to
173
180
  know _which specific ones_ survived tree-shaking. A shared ID would tell us
174
- "something survived" but not which violation it corresponds to. The unique IDs
175
- (`...mock:build:0`, `...mock:build:1`, etc.) provide this granularity.
181
+ "something survived" but not which violation it corresponds to. Each violation
182
+ gets a unique mock-edge module (wrapping a unique base mock
183
+ `...mock:build:0`, `...mock:build:1`, etc.) to provide this granularity.
176
184
 
177
185
  ### Why mocking doesn't affect tree-shaking
178
186
 
179
187
  From the consumer's perspective, the import bindings are identical whether they
180
- point to the real module or the mock. Rollup tree-shakes based on binding usage,
181
- not module content. If a binding from the barrel's re-export of `.server` is
182
- unused after the Start compiler strips server fn handlers, tree-shaking
188
+ point to the real module or the mock. The bundler tree-shakes based on binding
189
+ usage, not module content. If a binding from the barrel's re-export of `.server`
190
+ is unused after the Start compiler strips server fn handlers, tree-shaking
183
191
  eliminates it regardless of whether it points to real DB code or a Proxy mock.
184
192
 
185
193
  ### Per-environment operation
@@ -21,21 +21,13 @@ import {
21
21
  } from './utils'
22
22
  import { collectMockExportNamesBySource } from './rewriteDeniedImports'
23
23
  import {
24
- MARKER_PREFIX,
25
- MOCK_EDGE_PREFIX,
26
- MOCK_MODULE_ID,
27
- MOCK_RUNTIME_PREFIX,
28
- RESOLVED_MARKER_PREFIX,
29
- RESOLVED_MOCK_BUILD_PREFIX,
30
- RESOLVED_MOCK_EDGE_PREFIX,
31
- RESOLVED_MOCK_MODULE_ID,
32
- RESOLVED_MOCK_RUNTIME_PREFIX,
33
- loadMarkerModule,
34
- loadMockEdgeModule,
35
- loadMockRuntimeModule,
36
- loadSilentMockModule,
24
+ MOCK_BUILD_PREFIX,
25
+ getResolvedVirtualModuleMatchers,
26
+ loadResolvedVirtualModule,
37
27
  makeMockEdgeModuleId,
38
28
  mockRuntimeModuleIdFromViolation,
29
+ resolveInternalVirtualModuleId,
30
+ resolvedMarkerVirtualModuleId,
39
31
  } from './virtualModules'
40
32
  import {
41
33
  ImportLocCache,
@@ -62,9 +54,6 @@ import type {
62
54
  import type { CompileStartFrameworkOptions, GetConfigFn } from '../types'
63
55
 
64
56
  const SERVER_FN_LOOKUP_QUERY = '?' + SERVER_FN_LOOKUP
65
- const RESOLVED_MARKER_SERVER_ONLY = resolveViteId(`${MARKER_PREFIX}server-only`)
66
- const RESOLVED_MARKER_CLIENT_ONLY = resolveViteId(`${MARKER_PREFIX}client-only`)
67
-
68
57
  const IMPORT_PROTECTION_DEBUG =
69
58
  process.env.TSR_IMPORT_PROTECTION_DEBUG === '1' ||
70
59
  process.env.TSR_IMPORT_PROTECTION_DEBUG === 'true'
@@ -82,7 +71,6 @@ function matchesDebugFilter(...values: Array<string>): boolean {
82
71
  return values.some((v) => v.includes(IMPORT_PROTECTION_DEBUG_FILTER))
83
72
  }
84
73
 
85
- export { RESOLVED_MOCK_MODULE_ID } from './virtualModules'
86
74
  export { rewriteDeniedImports } from './rewriteDeniedImports'
87
75
  export { dedupePatterns, extractImportSources } from './utils'
88
76
  export type { Pattern } from './utils'
@@ -216,6 +204,16 @@ interface DeferredBuildViolation {
216
204
  info: ViolationInfo
217
205
  /** Unique mock module ID assigned to this violation. */
218
206
  mockModuleId: string
207
+
208
+ /**
209
+ * Module ID to check for tree-shaking survival in `generateBundle`.
210
+ *
211
+ * For most violations we check the unique mock module ID.
212
+ * For `marker` violations the import is a bare side-effect import that often
213
+ * gets optimized away regardless of whether the importer survives, so we
214
+ * instead check whether the importer module itself survived.
215
+ */
216
+ checkModuleId?: string
219
217
  }
220
218
 
221
219
  /**
@@ -818,27 +816,18 @@ export function importProtectionPlugin(
818
816
  env: EnvState,
819
817
  importerFile: string,
820
818
  info: ViolationInfo,
821
- mockReturnValue:
822
- | { id: string; syntheticNamedExports: boolean }
823
- | string
824
- | undefined,
819
+ mockReturnValue: string | undefined,
825
820
  ): void {
826
821
  getOrCreate(env.pendingViolations, importerFile, () => []).push({
827
822
  info,
828
- mockReturnValue:
829
- typeof mockReturnValue === 'string'
830
- ? mockReturnValue
831
- : (mockReturnValue?.id ?? ''),
823
+ mockReturnValue: mockReturnValue ?? '',
832
824
  })
833
825
  }
834
826
 
835
827
  /** Counter for generating unique per-violation mock module IDs in build mode. */
836
828
  let buildViolationCounter = 0
837
829
 
838
- type HandleViolationResult =
839
- | { id: string; syntheticNamedExports: boolean }
840
- | string
841
- | undefined
830
+ type HandleViolationResult = string | undefined
842
831
 
843
832
  async function handleViolation(
844
833
  ctx: ViolationReporter,
@@ -909,8 +898,22 @@ export function importProtectionPlugin(
909
898
 
910
899
  // Build: unique per-violation mock IDs so generateBundle can check
911
900
  // which violations survived tree-shaking (both mock and error mode).
912
- const mockId = `${RESOLVED_MOCK_BUILD_PREFIX}${buildViolationCounter++}`
913
- return { id: mockId, syntheticNamedExports: true }
901
+ // We wrap the base mock in a mock-edge module that provides explicit
902
+ // named exports Rolldown doesn't support Rollup's
903
+ // syntheticNamedExports, so without this named imports like
904
+ // `import { Foo } from "denied-pkg"` would fail with MISSING_EXPORT.
905
+ //
906
+ // Use the unresolved MOCK_BUILD_PREFIX (without \0) as the runtimeId
907
+ // so the mock-edge module's `import mock from "..."` goes through
908
+ // resolveId, which adds the \0 prefix. Using the resolved ID directly
909
+ // would cause Rollup/Vite to skip resolveId and fail to find the module.
910
+ const baseMockId = `${MOCK_BUILD_PREFIX}${buildViolationCounter++}`
911
+ const importerFile = normalizeFilePath(info.importer)
912
+ const exports =
913
+ env.mockExportsByImporter.get(importerFile)?.get(info.specifier) ?? []
914
+ return resolveViteId(
915
+ makeMockEdgeModuleId(exports, info.specifier, baseMockId),
916
+ )
914
917
  }
915
918
 
916
919
  /**
@@ -940,9 +943,14 @@ export function importProtectionPlugin(
940
943
 
941
944
  if (config.command === 'build') {
942
945
  // Build mode: store for generateBundle tree-shaking check.
943
- // The unique mock ID is inside `result`.
944
- const mockId = typeof result === 'string' ? result : (result?.id ?? '')
945
- env.deferredBuildViolations.push({ info, mockModuleId: mockId })
946
+ // The mock-edge module ID is returned as a plain string.
947
+ const mockId = result ?? ''
948
+ env.deferredBuildViolations.push({
949
+ info,
950
+ mockModuleId: mockId,
951
+ // For marker violations, check importer survival instead of mock.
952
+ checkModuleId: info.type === 'marker' ? info.importer : undefined,
953
+ })
946
954
  } else {
947
955
  // Dev mock: store for graph-reachability check.
948
956
  deferViolation(env, importerFile, info, result)
@@ -1182,18 +1190,8 @@ export function importProtectionPlugin(
1182
1190
  }
1183
1191
 
1184
1192
  // Internal virtual modules
1185
- if (source === MOCK_MODULE_ID) {
1186
- return RESOLVED_MOCK_MODULE_ID
1187
- }
1188
- if (source.startsWith(MOCK_EDGE_PREFIX)) {
1189
- return resolveViteId(source)
1190
- }
1191
- if (source.startsWith(MOCK_RUNTIME_PREFIX)) {
1192
- return resolveViteId(source)
1193
- }
1194
- if (source.startsWith(MARKER_PREFIX)) {
1195
- return resolveViteId(source)
1196
- }
1193
+ const internalVirtualId = resolveInternalVirtualModuleId(source)
1194
+ if (internalVirtualId) return internalVirtualId
1197
1195
 
1198
1196
  if (!importer) {
1199
1197
  env.graph.addEntry(source)
@@ -1287,8 +1285,8 @@ export function importProtectionPlugin(
1287
1285
  }
1288
1286
 
1289
1287
  return markerKind === 'server'
1290
- ? RESOLVED_MARKER_SERVER_ONLY
1291
- : RESOLVED_MARKER_CLIENT_ONLY
1288
+ ? resolvedMarkerVirtualModuleId('server')
1289
+ : resolvedMarkerVirtualModuleId('client')
1292
1290
  }
1293
1291
 
1294
1292
  // Check if the importer is within our scope
@@ -1427,15 +1425,7 @@ export function importProtectionPlugin(
1427
1425
  load: {
1428
1426
  filter: {
1429
1427
  id: new RegExp(
1430
- [
1431
- RESOLVED_MOCK_MODULE_ID,
1432
- RESOLVED_MOCK_BUILD_PREFIX,
1433
- RESOLVED_MARKER_PREFIX,
1434
- RESOLVED_MOCK_EDGE_PREFIX,
1435
- RESOLVED_MOCK_RUNTIME_PREFIX,
1436
- ]
1437
- .map(escapeRegExp)
1438
- .join('|'),
1428
+ getResolvedVirtualModuleMatchers().map(escapeRegExp).join('|'),
1439
1429
  ),
1440
1430
  },
1441
1431
  handler(id) {
@@ -1448,32 +1438,7 @@ export function importProtectionPlugin(
1448
1438
  }
1449
1439
  }
1450
1440
 
1451
- if (id === RESOLVED_MOCK_MODULE_ID) {
1452
- return loadSilentMockModule()
1453
- }
1454
-
1455
- // Per-violation build mock modules — same silent mock code
1456
- if (id.startsWith(RESOLVED_MOCK_BUILD_PREFIX)) {
1457
- return loadSilentMockModule()
1458
- }
1459
-
1460
- if (id.startsWith(RESOLVED_MOCK_EDGE_PREFIX)) {
1461
- return loadMockEdgeModule(
1462
- id.slice(RESOLVED_MOCK_EDGE_PREFIX.length),
1463
- )
1464
- }
1465
-
1466
- if (id.startsWith(RESOLVED_MOCK_RUNTIME_PREFIX)) {
1467
- return loadMockRuntimeModule(
1468
- id.slice(RESOLVED_MOCK_RUNTIME_PREFIX.length),
1469
- )
1470
- }
1471
-
1472
- if (id.startsWith(RESOLVED_MARKER_PREFIX)) {
1473
- return loadMarkerModule()
1474
- }
1475
-
1476
- return undefined
1441
+ return loadResolvedVirtualModule(id)
1477
1442
  },
1478
1443
  },
1479
1444
 
@@ -1492,11 +1457,16 @@ export function importProtectionPlugin(
1492
1457
  }
1493
1458
  }
1494
1459
 
1495
- // Check each deferred violation: if its unique mock module survived
1460
+ // Check each deferred violation: if its check module survived
1496
1461
  // in the bundle, the import was NOT tree-shaken — real leak.
1497
1462
  const realViolations: Array<ViolationInfo> = []
1498
- for (const { info, mockModuleId } of env.deferredBuildViolations) {
1499
- if (!survivingModules.has(mockModuleId)) continue
1463
+ for (const {
1464
+ info,
1465
+ mockModuleId,
1466
+ checkModuleId,
1467
+ } of env.deferredBuildViolations) {
1468
+ const checkId = checkModuleId ?? mockModuleId
1469
+ if (!survivingModules.has(checkId)) continue
1500
1470
 
1501
1471
  if (config.onViolation) {
1502
1472
  const result = await config.onViolation(info)
@@ -1648,18 +1618,17 @@ export function importProtectionPlugin(
1648
1618
  name: 'tanstack-start-core:import-protection-mock-rewrite',
1649
1619
  enforce: 'pre',
1650
1620
 
1651
- // Only needed during dev. In build, we rely on Rollup's syntheticNamedExports.
1652
- apply: 'serve',
1653
-
1654
1621
  applyToEnvironment(env) {
1655
1622
  if (!config.enabled) return false
1656
- // Only needed in mock mode when not mocking, there is nothing to
1657
- // record. applyToEnvironment runs after configResolved, so
1658
- // config.effectiveBehavior is already set.
1659
- if (config.effectiveBehavior !== 'mock') return false
1660
- // We record expected named exports per importer in all Start Vite
1661
- // environments during dev so mock-edge modules can provide explicit
1662
- // ESM named exports.
1623
+ // We record expected named exports per importer so mock-edge modules
1624
+ // can provide explicit ESM named exports. This is needed in both dev
1625
+ // and build: native ESM (dev) requires real named exports, and
1626
+ // Rolldown (used in Vite 6+) doesn't support Rollup's
1627
+ // syntheticNamedExports flag which was previously relied upon in build.
1628
+ //
1629
+ // In build+error mode we still emit mock modules for deferred
1630
+ // violations (checked at generateBundle time), so we always need the
1631
+ // export name data when import protection is active.
1663
1632
  return environmentNames.has(env.name)
1664
1633
  },
1665
1634