@octanejs/tanstack-start 0.1.1 → 0.1.5

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 (43) hide show
  1. package/package.json +13 -8
  2. package/src/GenericHydrate.tsrx +396 -0
  3. package/src/GenericHydrate.tsrx.d.ts +5 -0
  4. package/src/Hydrate.tsrx +106 -0
  5. package/src/Hydrate.tsrx.d.ts +63 -0
  6. package/src/client-only-server-strip.js +461 -0
  7. package/src/hydration/generic.d.ts +18 -0
  8. package/src/hydration/generic.js +26 -0
  9. package/src/hydration/idle.d.ts +9 -0
  10. package/src/hydration/idle.js +10 -0
  11. package/src/hydration/load.tsrx +38 -0
  12. package/src/hydration/load.tsrx.d.ts +8 -0
  13. package/src/hydration/never.tsrx +71 -0
  14. package/src/hydration/never.tsrx.d.ts +6 -0
  15. package/src/hydration/visible.tsrx +123 -0
  16. package/src/hydration/visible.tsrx.d.ts +12 -0
  17. package/src/hydration.d.ts +20 -0
  18. package/src/hydration.js +8 -0
  19. package/src/index.d.ts +11 -0
  20. package/src/index.js +2 -0
  21. package/src/internal/router-generator/filesystem/physical/getRouteNodes.js +4 -6
  22. package/src/internal/router-generator/generator.js +3 -0
  23. package/src/internal/router-plugin/core/code-splitter/compilers.js +3 -6
  24. package/src/internal/router-plugin/core/config.d.ts +1 -3
  25. package/src/internal/router-plugin/core/router-code-splitter-plugin.js +6 -3
  26. package/src/internal/router-plugin/esbuild.d.ts +4 -10
  27. package/src/internal/router-plugin/vite.d.ts +4 -10
  28. package/src/internal/start-plugin-core/import-protection/analysis.js +13 -12
  29. package/src/internal/start-plugin-core/import-protection/constants.d.ts +0 -1
  30. package/src/internal/start-plugin-core/import-protection/constants.js +0 -3
  31. package/src/internal/start-plugin-core/schema.d.ts +4 -14
  32. package/src/internal/start-plugin-core/start-compiler/compiler.d.ts +1 -6
  33. package/src/internal/start-plugin-core/start-compiler/compiler.js +4 -4
  34. package/src/internal/start-plugin-core/start-compiler/utils.d.ts +7 -0
  35. package/src/internal/start-plugin-core/start-compiler/utils.js +7 -0
  36. package/src/internal/start-plugin-core/types.d.ts +1 -2
  37. package/src/internal/start-plugin-core/vite/import-protection-plugin/plugin.js +21 -21
  38. package/src/internal/start-plugin-core/vite/module-id.d.ts +6 -0
  39. package/src/internal/start-plugin-core/vite/module-id.js +23 -0
  40. package/src/internal/start-plugin-core/vite/schema.d.ts +3 -12
  41. package/src/internal/start-plugin-core/vite/start-compiler-plugin/plugin.js +9 -3
  42. package/src/plugin-vite.d.ts +9 -1
  43. package/src/plugin-vite.js +20 -1
@@ -0,0 +1,123 @@
1
+ // visible hydration strategy — port of @tanstack/react-start-client's
2
+ // hydration/visible.tsx. Fully self-contained fast path (no shared gate
3
+ // registry): a per-instance promise gate resolved by an IntersectionObserver.
4
+ // `reactUse` feature-detection is dropped — octane's `use` always exists.
5
+ //
6
+ // OCTANE ADAPTATION: upstream's VisibleHydrate is bare-called as a method
7
+ // (`props.when._h(props)`) with the strategy as `this`, running its hooks on
8
+ // the caller's fiber. Octane's compiled components take the
9
+ // `(props, __s, __extra)` ABI and cannot be bare-called, so octane's `Hydrate`
10
+ // renders `_h` as a child component and VisibleHydrate derives its strategy
11
+ // from `props.when` instead of `this` (resolving a function-valued `when` per
12
+ // render, exactly as upstream's Hydrate does).
13
+ import { Suspense, use, useEffect, useRef, useState } from 'octane';
14
+ import type { OctaneNode } from 'octane';
15
+ import { isServer } from '@tanstack/router-core/isServer';
16
+ import type {
17
+ HydrationPrefetchStrategy,
18
+ VisibleHydrationOptions,
19
+ } from '@tanstack/start-client-core/hydration';
20
+ import type { HydrateProps, InternalHydrateProps, OctaneHydrationStrategy } from '../Hydrate.tsrx';
21
+
22
+ type VisibleGate = {
23
+ p: Promise<void>;
24
+ r: boolean;
25
+ s: () => void;
26
+ };
27
+
28
+ function HydrationBoundary(props: { g: VisibleGate; o?: () => void; children?: OctaneNode }) {
29
+ const { g, o } = props;
30
+
31
+ if (!g.r) {
32
+ use(g.p);
33
+ }
34
+
35
+ useEffect(() => {
36
+ o?.();
37
+ }, [o]);
38
+
39
+ return props.children;
40
+ }
41
+
42
+ export function VisibleHydrate(props: HydrateProps) @{
43
+ const when = props.when;
44
+ const strategy = (typeof when === 'function' ? when() : when) as OctaneHydrationStrategy<
45
+ 'visible',
46
+ true
47
+ >;
48
+ const prefetchStrategy = props.prefetch;
49
+ const preload = (props as InternalHydrateProps).p;
50
+ const markerRef = useRef<HTMLDivElement | null>(null);
51
+ const [gate] = useState<VisibleGate>(() => {
52
+ let resolvePromise: () => void;
53
+ const nextGate: VisibleGate = {
54
+ p: new Promise<void>((resolve) => {
55
+ resolvePromise = resolve;
56
+ }),
57
+ r: false,
58
+ s: () => {
59
+ nextGate.r = true;
60
+ resolvePromise();
61
+ },
62
+ };
63
+ if (isServer ?? typeof window === 'undefined') {
64
+ nextGate.s();
65
+ }
66
+
67
+ return nextGate;
68
+ });
69
+
70
+ useEffect(() => {
71
+ if (!preload || typeof prefetchStrategy === 'function') {
72
+ return;
73
+ }
74
+
75
+ return prefetchStrategy?._s?.({
76
+ element: markerRef.current,
77
+ prefetch: preload,
78
+ });
79
+ }, [prefetchStrategy, preload]);
80
+
81
+ useEffect(() => {
82
+ if (gate.r) return;
83
+
84
+ return strategy._s?.({
85
+ element: markerRef.current,
86
+ gate: gate as never,
87
+ });
88
+ }, [gate, strategy]);
89
+
90
+ <div ref={markerRef}>
91
+ <Suspense fallback={props.fallback}>
92
+ <HydrationBoundary g={gate} o={props.onHydrated}>{props.children}</HydrationBoundary>
93
+ </Suspense>
94
+ </div>
95
+ }
96
+
97
+ /* @__NO_SIDE_EFFECTS__ */
98
+ export function visible(
99
+ options?: VisibleHydrationOptions,
100
+ ): OctaneHydrationStrategy<'visible', true> & HydrationPrefetchStrategy<'visible'> {
101
+ const rootMargin = options?.rootMargin ?? '600px';
102
+ const threshold = options?.threshold ?? 0;
103
+
104
+ return {
105
+ _s: ({ element, gate, prefetch }) => {
106
+ const callback = prefetch || (gate as never as VisibleGate).s;
107
+
108
+ if (!element) {
109
+ callback();
110
+ return;
111
+ }
112
+
113
+ const observer = new IntersectionObserver((entries) => {
114
+ if (!entries[0]!.isIntersecting) return;
115
+ observer.disconnect();
116
+ callback();
117
+ }, { rootMargin, threshold });
118
+ observer.observe(element);
119
+ return () => observer.disconnect();
120
+ },
121
+ _h: VisibleHydrate,
122
+ };
123
+ }
@@ -0,0 +1,12 @@
1
+ import type { OctaneNode } from 'octane';
2
+ import type {
3
+ HydrationPrefetchStrategy,
4
+ VisibleHydrationOptions,
5
+ } from '@tanstack/start-client-core/hydration';
6
+ import type { HydrateProps, OctaneHydrationStrategy } from '../Hydrate.tsrx';
7
+
8
+ export declare function VisibleHydrate(props: HydrateProps): OctaneNode;
9
+
10
+ export declare function visible(
11
+ options?: VisibleHydrationOptions,
12
+ ): OctaneHydrationStrategy<'visible', true> & HydrationPrefetchStrategy<'visible'>;
@@ -0,0 +1,20 @@
1
+ export { condition, interaction, media } from './hydration/generic.js';
2
+ export { idle } from './hydration/idle.js';
3
+ export { load } from './hydration/load.tsrx';
4
+ export { never } from './hydration/never.tsrx';
5
+ export { visible } from './hydration/visible.tsrx';
6
+ export type {
7
+ HydrationCondition,
8
+ HydrationInteractionEvent,
9
+ HydrationInteractionEvents,
10
+ IdleHydrationOptions,
11
+ HydrationPrefetchContext,
12
+ HydrationPrefetchFunction,
13
+ HydrationPrefetchWhen,
14
+ HydrationPrefetchStrategy,
15
+ HydrationPrefetchWaitReason,
16
+ HydrationStrategyTypes,
17
+ HydrationWhen,
18
+ VisibleHydrationOptions,
19
+ } from '@tanstack/start-client-core/hydration';
20
+ export type { HydrationStrategy, OctaneHydrationStrategy } from './Hydrate.tsrx';
@@ -0,0 +1,8 @@
1
+ // `@octanejs/tanstack-start/hydration` — port of @tanstack/react-start's
2
+ // hydration.ts subpath: the hydration strategy factories consumed by
3
+ // `<Hydrate when={...}>`.
4
+ export { condition, interaction, media } from './hydration/generic.js';
5
+ export { idle } from './hydration/idle.js';
6
+ export { load } from './hydration/load.tsrx';
7
+ export { never } from './hydration/never.tsrx';
8
+ export { visible } from './hydration/visible.tsrx';
package/src/index.d.ts CHANGED
@@ -1,5 +1,16 @@
1
1
  export { useServerFn } from './use-server-fn.js';
2
2
  export * from '@tanstack/start-client-core';
3
+ export { Hydrate } from './Hydrate.tsrx';
4
+ export type {
5
+ HydrateOptions,
6
+ HydrateProps,
7
+ HydrationInteractionEvent,
8
+ HydrationInteractionEvents,
9
+ HydrationPrefetchStrategy,
10
+ HydrationStrategy,
11
+ HydrationWhen,
12
+ OctaneHydrationStrategy,
13
+ } from './Hydrate.tsrx';
3
14
  export {
4
15
  createClientOnlyFn,
5
16
  createCsrfMiddleware,
package/src/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { useServerFn } from './use-server-fn.js';
2
+ import { Hydrate } from './Hydrate.tsrx';
2
3
  import {
3
4
  createClientOnlyFn,
4
5
  createCsrfMiddleware,
@@ -11,6 +12,7 @@ import {
11
12
 
12
13
  export * from '@tanstack/start-client-core';
13
14
  export {
15
+ Hydrate,
14
16
  createClientOnlyFn,
15
17
  createCsrfMiddleware,
16
18
  createIsomorphicFn,
@@ -180,12 +180,10 @@ async function getRouteNodes(config, root, tokenRegexes) {
180
180
  }
181
181
  const lastOriginalSegment = originalRoutePath.split('/').filter(Boolean).pop() || '';
182
182
  const indexTokenCandidate = unwrapBracketWrappedSegment(lastOriginalSegment);
183
- if (
184
- !(
185
- lastOriginalSegment !== indexTokenCandidate &&
186
- indexTokenSegmentRegex.test(indexTokenCandidate)
187
- )
188
- ) {
183
+ if (!(
184
+ lastOriginalSegment !== indexTokenCandidate &&
185
+ indexTokenSegmentRegex.test(indexTokenCandidate)
186
+ )) {
189
187
  const updatedRouteSegments = routePath.split('/').filter(Boolean);
190
188
  const updatedLastRouteSegment =
191
189
  updatedRouteSegments[updatedRouteSegments.length - 1] || '';
@@ -240,6 +240,9 @@ var Generator = class Generator {
240
240
  } else {
241
241
  const unrecoverableErrors = errArray.filter((e) => !isRerun(e));
242
242
  this.runPromise = void 0;
243
+ if (process.env.OCTANE_DEBUG_GENERATOR) {
244
+ for (const e of unrecoverableErrors) console.error('[generator-debug]', e);
245
+ }
243
246
  throw new Error(unrecoverableErrors.map((e) => e.message).join());
244
247
  }
245
248
  }
@@ -836,12 +836,9 @@ function detectCodeSplitGroupingsFromRoute(opts) {
836
836
  programPath.traverse({
837
837
  CallExpression(path) {
838
838
  if (!t.isIdentifier(path.node.callee)) return;
839
- if (
840
- !(
841
- path.node.callee.name === 'createRoute' ||
842
- path.node.callee.name === 'createFileRoute'
843
- )
844
- )
839
+ if (!(
840
+ path.node.callee.name === 'createRoute' || path.node.callee.name === 'createFileRoute'
841
+ ))
845
842
  return;
846
843
  function babelHandleSplittingGroups(routeOptions) {
847
844
  if (t.isObjectExpression(routeOptions))
@@ -251,9 +251,7 @@ export declare const getConfig: (
251
251
  tmpDir: string;
252
252
  importRoutesUsingAbsolutePaths: boolean;
253
253
  virtualRouteConfig?:
254
- | string
255
- | import('@tanstack/virtual-file-routes').VirtualRootRoute
256
- | undefined;
254
+ string | import('@tanstack/virtual-file-routes').VirtualRootRoute | undefined;
257
255
  routeFilePrefix?: string | undefined;
258
256
  routeFileIgnorePattern?: string | undefined;
259
257
  pathParamsAllowedCharacters?: (':' | '$' | ';' | '@' | '&' | '=' | '+' | ',')[] | undefined;
@@ -34,7 +34,7 @@ function createRouterCodeSplitterPlugin(options = {}, routerPluginContext) {
34
34
  if (typeof options === 'function') userConfig = options();
35
35
  else userConfig = getConfig(options, ROOT);
36
36
  }
37
- const isProduction = process.env.NODE_ENV === 'production';
37
+ let isProduction = process.env.NODE_ENV === 'production';
38
38
  const sharedBindingsMap = /* @__PURE__ */ new Map();
39
39
  const getGlobalCodeSplitGroupings = () => {
40
40
  return userConfig.codeSplittingOptions?.defaultBehavior || defaultCodeSplitGroupings;
@@ -159,6 +159,7 @@ function createRouterCodeSplitterPlugin(options = {}, routerPluginContext) {
159
159
  },
160
160
  vite: {
161
161
  configResolved(config) {
162
+ isProduction = config.command === 'build';
162
163
  ROOT = config.root;
163
164
  initUserConfig();
164
165
  validateFrameworkPluginOrder({
@@ -173,11 +174,13 @@ function createRouterCodeSplitterPlugin(options = {}, routerPluginContext) {
173
174
  return true;
174
175
  },
175
176
  },
176
- rspack() {
177
+ rspack(compiler) {
178
+ isProduction = compiler.options.mode === 'production';
177
179
  ROOT = process.cwd();
178
180
  initUserConfig();
179
181
  },
180
- webpack() {
182
+ webpack(compiler) {
183
+ isProduction = compiler.options.mode === 'production';
181
184
  ROOT = process.cwd();
182
185
  initUserConfig();
183
186
  },
@@ -68,14 +68,11 @@ declare const TanStackRouterEsbuild: (
68
68
  tmpDir: string;
69
69
  importRoutesUsingAbsolutePaths: boolean;
70
70
  virtualRouteConfig?:
71
- | string
72
- | import('@tanstack/virtual-file-routes').VirtualRootRoute
73
- | undefined;
71
+ string | import('@tanstack/virtual-file-routes').VirtualRootRoute | undefined;
74
72
  routeFilePrefix?: string | undefined;
75
73
  routeFileIgnorePattern?: string | undefined;
76
74
  pathParamsAllowedCharacters?:
77
- | (':' | '$' | ';' | '@' | '&' | '=' | '+' | ',')[]
78
- | undefined;
75
+ (':' | '$' | ';' | '@' | '&' | '=' | '+' | ',')[] | undefined;
79
76
  routeTreeFileFooter?: string[] | (() => Array<string>) | undefined;
80
77
  autoCodeSplitting?: boolean | undefined;
81
78
  customScaffolding?:
@@ -143,14 +140,11 @@ declare const tanstackRouter: (
143
140
  tmpDir: string;
144
141
  importRoutesUsingAbsolutePaths: boolean;
145
142
  virtualRouteConfig?:
146
- | string
147
- | import('@tanstack/virtual-file-routes').VirtualRootRoute
148
- | undefined;
143
+ string | import('@tanstack/virtual-file-routes').VirtualRootRoute | undefined;
149
144
  routeFilePrefix?: string | undefined;
150
145
  routeFileIgnorePattern?: string | undefined;
151
146
  pathParamsAllowedCharacters?:
152
- | (':' | '$' | ';' | '@' | '&' | '=' | '+' | ',')[]
153
- | undefined;
147
+ (':' | '$' | ';' | '@' | '&' | '=' | '+' | ',')[] | undefined;
154
148
  routeTreeFileFooter?: string[] | (() => Array<string>) | undefined;
155
149
  autoCodeSplitting?: boolean | undefined;
156
150
  customScaffolding?:
@@ -68,14 +68,11 @@ declare const tanstackRouter: (
68
68
  tmpDir: string;
69
69
  importRoutesUsingAbsolutePaths: boolean;
70
70
  virtualRouteConfig?:
71
- | string
72
- | import('@tanstack/virtual-file-routes').VirtualRootRoute
73
- | undefined;
71
+ string | import('@tanstack/virtual-file-routes').VirtualRootRoute | undefined;
74
72
  routeFilePrefix?: string | undefined;
75
73
  routeFileIgnorePattern?: string | undefined;
76
74
  pathParamsAllowedCharacters?:
77
- | (':' | '$' | ';' | '@' | '&' | '=' | '+' | ',')[]
78
- | undefined;
75
+ (':' | '$' | ';' | '@' | '&' | '=' | '+' | ',')[] | undefined;
79
76
  routeTreeFileFooter?: string[] | (() => Array<string>) | undefined;
80
77
  autoCodeSplitting?: boolean | undefined;
81
78
  customScaffolding?:
@@ -146,14 +143,11 @@ declare const TanStackRouterVite: (
146
143
  tmpDir: string;
147
144
  importRoutesUsingAbsolutePaths: boolean;
148
145
  virtualRouteConfig?:
149
- | string
150
- | import('@tanstack/virtual-file-routes').VirtualRootRoute
151
- | undefined;
146
+ string | import('@tanstack/virtual-file-routes').VirtualRootRoute | undefined;
152
147
  routeFilePrefix?: string | undefined;
153
148
  routeFileIgnorePattern?: string | undefined;
154
149
  pathParamsAllowedCharacters?:
155
- | (':' | '$' | ';' | '@' | '&' | '=' | '+' | ',')[]
156
- | undefined;
150
+ (':' | '$' | ';' | '@' | '&' | '=' | '+' | ',')[] | undefined;
157
151
  routeTreeFileFooter?: string[] | (() => Array<string>) | undefined;
158
152
  autoCodeSplitting?: boolean | undefined;
159
153
  customScaffolding?:
@@ -72,21 +72,22 @@ function collectIdentifiersFromPattern$1(pattern, add) {
72
72
  function isValidExportName(name) {
73
73
  if (name === 'default' || name.length === 0) return false;
74
74
  const first = name.charCodeAt(0);
75
- if (
76
- !((first >= 65 && first <= 90) || (first >= 97 && first <= 122) || first === 95 || first === 36)
77
- )
75
+ if (!(
76
+ (first >= 65 && first <= 90) ||
77
+ (first >= 97 && first <= 122) ||
78
+ first === 95 ||
79
+ first === 36
80
+ ))
78
81
  return false;
79
82
  for (let i = 1; i < name.length; i++) {
80
83
  const ch = name.charCodeAt(i);
81
- if (
82
- !(
83
- (ch >= 65 && ch <= 90) ||
84
- (ch >= 97 && ch <= 122) ||
85
- (ch >= 48 && ch <= 57) ||
86
- ch === 95 ||
87
- ch === 36
88
- )
89
- )
84
+ if (!(
85
+ (ch >= 65 && ch <= 90) ||
86
+ (ch >= 97 && ch <= 122) ||
87
+ (ch >= 48 && ch <= 57) ||
88
+ ch === 95 ||
89
+ ch === 36
90
+ ))
90
91
  return false;
91
92
  }
92
93
  return true;
@@ -1,4 +1,3 @@
1
- export declare const SERVER_FN_LOOKUP_QUERY = '?server-fn-module-lookup';
2
1
  export declare const MOCK_MODULE_ID = 'tanstack-start-import-protection:mock';
3
2
  export declare const MOCK_BUILD_PREFIX = 'tanstack-start-import-protection:mock:build:';
4
3
  export declare const MOCK_EDGE_PREFIX = 'tanstack-start-import-protection:mock-edge:';
@@ -1,6 +1,4 @@
1
- import { SERVER_FN_LOOKUP } from '../constants.js';
2
1
  //#region src/import-protection/constants.ts
3
- var SERVER_FN_LOOKUP_QUERY = `?${SERVER_FN_LOOKUP}`;
4
2
  var MOCK_MODULE_ID = 'tanstack-start-import-protection:mock';
5
3
  var MOCK_BUILD_PREFIX = 'tanstack-start-import-protection:mock:build:';
6
4
  var MOCK_EDGE_PREFIX = 'tanstack-start-import-protection:mock-edge:';
@@ -34,6 +32,5 @@ export {
34
32
  MOCK_EDGE_PREFIX,
35
33
  MOCK_MODULE_ID,
36
34
  MOCK_RUNTIME_PREFIX,
37
- SERVER_FN_LOOKUP_QUERY,
38
35
  VITE_BROWSER_VIRTUAL_PREFIX,
39
36
  };
@@ -208,8 +208,7 @@ export declare function parseStartConfig(
208
208
  base: string;
209
209
  disableCsrfMiddlewareWarning: boolean;
210
210
  generateFunctionId?:
211
- | ((opts: { filename: string; functionName: string }) => string | undefined)
212
- | undefined;
211
+ ((opts: { filename: string; functionName: string }) => string | undefined) | undefined;
213
212
  };
214
213
  pages: {
215
214
  path: string;
@@ -218,14 +217,7 @@ export declare function parseStartConfig(
218
217
  exclude?: boolean | undefined;
219
218
  priority?: number | undefined;
220
219
  changefreq?:
221
- | 'never'
222
- | 'always'
223
- | 'hourly'
224
- | 'daily'
225
- | 'weekly'
226
- | 'monthly'
227
- | 'yearly'
228
- | undefined;
220
+ 'never' | 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | undefined;
229
221
  lastmod?: string | Date | undefined;
230
222
  alternateRefs?:
231
223
  | {
@@ -297,8 +289,7 @@ export declare function parseStartConfig(
297
289
  retryCount?: number | undefined;
298
290
  retryDelay?: number | undefined;
299
291
  onSuccess?:
300
- | ((result: { page: z.infer<typeof pageBaseSchema>; html: string }) => unknown)
301
- | undefined;
292
+ ((result: { page: z.infer<typeof pageBaseSchema>; html: string }) => unknown) | undefined;
302
293
  headers?: Record<string, string> | undefined;
303
294
  })
304
295
  | undefined;
@@ -333,8 +324,7 @@ export declare function parseStartConfig(
333
324
  | undefined;
334
325
  mockAccess?: 'error' | 'warn' | 'off' | undefined;
335
326
  onViolation?:
336
- | ((violation: unknown) => boolean | void | Promise<boolean | void>)
337
- | undefined;
327
+ ((violation: unknown) => boolean | void | Promise<boolean | void>) | undefined;
338
328
  include?: (string | RegExp)[] | undefined;
339
329
  exclude?: (string | RegExp)[] | undefined;
340
330
  client?:
@@ -12,12 +12,7 @@ type Binding = ModuleInfoBinding & {
12
12
  };
13
13
  type Kind = 'None' | `Root` | `Builder` | LookupKind;
14
14
  export type BuiltInLookupKind =
15
- | 'ServerFn'
16
- | 'Middleware'
17
- | 'IsomorphicFn'
18
- | 'ServerOnlyFn'
19
- | 'ClientOnlyFn'
20
- | 'ClientOnlyJSX';
15
+ 'ServerFn' | 'Middleware' | 'IsomorphicFn' | 'ServerOnlyFn' | 'ClientOnlyFn' | 'ClientOnlyJSX';
21
16
  export type ExternalLookupKind = `External:${string}`;
22
17
  export type LookupKind = BuiltInLookupKind | ExternalLookupKind;
23
18
  export declare function getExternalLookupKind(
@@ -850,7 +850,7 @@ var StartCompiler = class {
850
850
  const binding = (await this.getModuleInfo(id)).bindings.get(ident);
851
851
  if (!binding) return 'None';
852
852
  if (binding.resolvedKind) return binding.resolvedKind;
853
- const vKey = `${cleanId(id)}:${ident}`;
853
+ const vKey = `${id}:${ident}`;
854
854
  if (visited.has(vKey)) return 'None';
855
855
  visited.add(vKey);
856
856
  const resolvedKind = await this.resolveBindingKind(binding, id, visited);
@@ -904,7 +904,7 @@ var StartCompiler = class {
904
904
  if (isBuildMode) this.getExportResolutionCache(moduleInfo.id).set(exportName, null);
905
905
  }
906
906
  async resolveBindingTarget(resolution, visited = /* @__PURE__ */ new Set()) {
907
- const key = `${cleanId(resolution.moduleInfo.id)}:${resolution.localName}`;
907
+ const key = `${resolution.moduleInfo.id}:${resolution.localName}`;
908
908
  if (visited.has(key)) return;
909
909
  visited.add(key);
910
910
  if (resolution.binding.type !== 'import') return resolution;
@@ -936,7 +936,7 @@ var StartCompiler = class {
936
936
  const target = found ? ((await this.resolveBindingTarget(found)) ?? found) : void 0;
937
937
  if (
938
938
  target &&
939
- cleanId(resolved.moduleInfo.id) === cleanId(target.moduleInfo.id) &&
939
+ resolved.moduleInfo.id === target.moduleInfo.id &&
940
940
  resolved.localName === target.localName
941
941
  )
942
942
  return kind;
@@ -965,7 +965,7 @@ var StartCompiler = class {
965
965
  return knownKind;
966
966
  }
967
967
  if (found.binding.resolvedKind) return found.binding.resolvedKind;
968
- const vKey = `${cleanId(found.moduleInfo.id)}:${found.localName}`;
968
+ const vKey = `${found.moduleInfo.id}:${found.localName}`;
969
969
  if (visited.has(vKey)) return 'None';
970
970
  visited.add(vKey);
971
971
  const resolvedKind = await this.resolveBindingKind(found.binding, found.moduleInfo.id, visited);
@@ -14,6 +14,13 @@ export declare function codeFrameError(
14
14
  },
15
15
  message: string,
16
16
  ): Error;
17
+ /**
18
+ * Converts a bundler module ID to its physical-file identity for diagnostics,
19
+ * filesystem matching, and file-based invalidation.
20
+ *
21
+ * Do not use this for IDs passed to resolve/load hooks or as module cache keys:
22
+ * virtual prefixes and queries can be part of the module's semantic identity.
23
+ */
17
24
  export declare function cleanId(id: string): string;
18
25
  /**
19
26
  * Strips a method call by replacing it with its callee object.
@@ -15,6 +15,13 @@ function codeFrameError(code, loc, message) {
15
15
  );
16
16
  return new Error(frame);
17
17
  }
18
+ /**
19
+ * Converts a bundler module ID to its physical-file identity for diagnostics,
20
+ * filesystem matching, and file-based invalidation.
21
+ *
22
+ * Do not use this for IDs passed to resolve/load hooks or as module cache keys:
23
+ * virtual prefixes and queries can be part of the module's semantic identity.
24
+ */
18
25
  function cleanId(id) {
19
26
  if (id.startsWith('\0')) id = id.slice(1);
20
27
  const queryIndex = id.indexOf('?');
@@ -22,8 +22,7 @@ export type SerializationAdapterByRuntime = Partial<
22
22
  Record<SerializationRuntime, SerializationAdapterModuleRef>
23
23
  >;
24
24
  export type SerializationAdapterConfig =
25
- | SerializationAdapterModuleRef
26
- | SerializationAdapterByRuntime;
25
+ SerializationAdapterModuleRef | SerializationAdapterByRuntime;
27
26
  export type StartCompilerEnvironment = 'client' | 'server';
28
27
  export interface StartCompilerImportTransformImport {
29
28
  libName: string;
@@ -1,9 +1,8 @@
1
- import { VITE_ENVIRONMENT_NAMES } from '../../constants.js';
1
+ import { SERVER_FN_LOOKUP, TRANSFORM_ID_REGEX, VITE_ENVIRONMENT_NAMES } from '../../constants.js';
2
2
  import { escapeRegExp, resolveViteId } from '../../utils.js';
3
3
  import {
4
4
  IMPORT_PROTECTION_DEBUG,
5
5
  MOCK_BUILD_PREFIX,
6
- SERVER_FN_LOOKUP_QUERY,
7
6
  VITE_BROWSER_VIRTUAL_PREFIX,
8
7
  } from '../../import-protection/constants.js';
9
8
  import {
@@ -64,6 +63,7 @@ import {
64
63
  resolveInternalVirtualModuleId,
65
64
  resolvedMarkerVirtualModuleId,
66
65
  } from './virtualModules.js';
66
+ import { hasIdQueryFlag } from '../module-id.js';
67
67
  import { dirname, relative } from 'pathe';
68
68
  import { writeFileSync } from 'node:fs';
69
69
  import { normalizePath } from 'vite';
@@ -415,12 +415,10 @@ function importProtectionPlugin(opts) {
415
415
  ) {
416
416
  const normalizedResolvedId = normalizeFilePath(resolvedId);
417
417
  const markerKind = shared.fileMarkerKind.get(normalizedResolvedId);
418
- if (
419
- !(
420
- (envType === 'client' && markerKind === 'server') ||
421
- (envType === 'server' && markerKind === 'client')
422
- )
423
- )
418
+ if (!(
419
+ (envType === 'client' && markerKind === 'server') ||
420
+ (envType === 'server' && markerKind === 'client')
421
+ ))
424
422
  return void 0;
425
423
  return buildViolationInfo(
426
424
  envName,
@@ -696,7 +694,7 @@ function importProtectionPlugin(opts) {
696
694
  let merged = null;
697
695
  if (keySet)
698
696
  for (const k of keySet) {
699
- if (k.includes(SERVER_FN_LOOKUP_QUERY)) continue;
697
+ if (hasIdQueryFlag(k, SERVER_FN_LOOKUP)) continue;
700
698
  const imports = env.postTransformImports.get(k);
701
699
  if (imports)
702
700
  if (!merged) merged = new Set(imports);
@@ -723,7 +721,7 @@ function importProtectionPlugin(opts) {
723
721
  let anyVariantCached = false;
724
722
  if (keySet)
725
723
  for (const k of keySet) {
726
- if (k.includes(SERVER_FN_LOOKUP_QUERY)) continue;
724
+ if (hasIdQueryFlag(k, SERVER_FN_LOOKUP)) continue;
727
725
  const imports = env.postTransformImports.get(k);
728
726
  if (imports) {
729
727
  anyVariantCached = true;
@@ -1081,21 +1079,19 @@ function importProtectionPlugin(opts) {
1081
1079
  if (internalVirtualId) return internalVirtualId;
1082
1080
  if (!importer) {
1083
1081
  const normalizedSource = normalizeFilePath(source);
1084
- if (
1085
- !(
1086
- config.command === 'serve' &&
1087
- config.bundledDev &&
1088
- envType === 'client' &&
1089
- isInsideDirectory(normalizedSource, config.srcDirectory)
1090
- )
1091
- )
1082
+ if (!(
1083
+ config.command === 'serve' &&
1084
+ config.bundledDev &&
1085
+ envType === 'client' &&
1086
+ isInsideDirectory(normalizedSource, config.srcDirectory)
1087
+ ))
1092
1088
  env.graph.addEntry(source);
1093
1089
  await processPendingViolations(env, this.warn.bind(this));
1094
1090
  return;
1095
1091
  }
1096
1092
  if (source.startsWith('\0') || source.startsWith('virtual:')) return;
1097
1093
  const normalizedImporter = normalizeFilePath(importer);
1098
- const isDirectLookup = importer.includes(SERVER_FN_LOOKUP_QUERY);
1094
+ const isDirectLookup = hasIdQueryFlag(importer, SERVER_FN_LOOKUP);
1099
1095
  if (config.command === 'serve' && config.bundledDev && envType === 'client') {
1100
1096
  if (
1101
1097
  isInsideDirectory(normalizedImporter, normalizePath(`${config.srcDirectory}/routes`))
@@ -1389,7 +1385,11 @@ function importProtectionPlugin(opts) {
1389
1385
  return environmentNames.has(env.name);
1390
1386
  },
1391
1387
  transform: {
1392
- filter: { id: { include: [/\.[cm]?[tj]sx?($|\?)/] } },
1388
+ // TRANSFORM_ID_REGEX includes .tsrx the hand-rolled react-only
1389
+ // regex here silently skipped octane importers, so denied *.client.*
1390
+ // imports were never edge-rewritten/deferred and hard-errored at
1391
+ // resolve instead of tree-shake-verified like react's do.
1392
+ filter: { id: { include: [...TRANSFORM_ID_REGEX] } },
1393
1393
  async handler(code, id) {
1394
1394
  perf?.count('transform.calls');
1395
1395
  const envName = this.environment.name;
@@ -1432,7 +1432,7 @@ function importProtectionPlugin(opts) {
1432
1432
  }
1433
1433
  const cacheKey = normalizePath(id);
1434
1434
  const envState = getEnv(envName);
1435
- const isServerFnLookup = id.includes(SERVER_FN_LOOKUP_QUERY);
1435
+ const isServerFnLookup = hasIdQueryFlag(id, SERVER_FN_LOOKUP);
1436
1436
  if (isServerFnLookup) envState.serverFnLookupModules.add(file);
1437
1437
  const result = {
1438
1438
  code,