@bleedingdev/modern-js-plugin-tanstack 3.5.0-ultramodern.3 → 3.5.0-ultramodern.30

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.
@@ -55,6 +55,7 @@ var __webpack_exports__ = {};
55
55
  collectCanonicalRoutesForEntry: ()=>external_tanstackTypes_js_namespaceObject.collectCanonicalRoutesForEntry,
56
56
  createTanstackRsbuildRouteSplittingProfile: ()=>external_routeSplitting_js_namespaceObject.createTanstackRsbuildRouteSplittingProfile,
57
57
  default: ()=>src_cli,
58
+ generateTanstackRouteArtifacts: ()=>generateTanstackRouteArtifacts,
58
59
  generateTanstackRouterTypesSourceForEntry: ()=>external_tanstackTypes_js_namespaceObject.generateTanstackRouterTypesSourceForEntry,
59
60
  resolveTanstackRouteCodeSplittingEnabled: ()=>external_routeSplitting_js_namespaceObject.resolveTanstackRouteCodeSplittingEnabled,
60
61
  tanstackRouterPlugin: ()=>tanstackRouterPlugin,
@@ -295,11 +296,35 @@ ${canonicalRoutesAugmentation}`;
295
296
  }
296
297
  };
297
298
  }
299
+ async function generateTanstackRouteArtifacts(opts) {
300
+ const [{ createRunOptions }, { cli }, utils] = await Promise.all([
301
+ import("@modern-js/app-tools/cli/run"),
302
+ import("@modern-js/plugin/cli"),
303
+ import("@modern-js/utils")
304
+ ]);
305
+ process.env.MODERN_ARGV = 'node modern build';
306
+ process.env.NODE_ENV = process.env.NODE_ENV || 'production';
307
+ const { findExists, CONFIG_FILE_EXTENSIONS } = utils;
308
+ const configFileBase = external_node_path_default().resolve(opts.appDirectory, 'modern.config');
309
+ const resolvedConfigFile = findExists(CONFIG_FILE_EXTENSIONS.map((extension)=>`${configFileBase}${extension}`));
310
+ if (!resolvedConfigFile) throw new Error(`[plugin-tanstack] Unable to locate a Modern.js config file (modern.config.{${CONFIG_FILE_EXTENSIONS.map((extension)=>extension.slice(1)).join(',')}}) in ${opts.appDirectory}. TanStack route generation requires the app config to resolve its plugins.`);
311
+ const runOptions = await createRunOptions({
312
+ cwd: opts.appDirectory,
313
+ configFile: configFileBase,
314
+ version: opts.version || process.env.MODERN_JS_VERSION || '0.0.0-routes-generate',
315
+ internalPlugins: utils.INTERNAL_RUNTIME_PLUGINS
316
+ });
317
+ await cli.init({
318
+ ...runOptions,
319
+ command: 'build'
320
+ });
321
+ }
298
322
  const src_cli = tanstackRouterPlugin;
299
323
  })();
300
324
  exports.collectCanonicalRoutesForEntry = __webpack_exports__.collectCanonicalRoutesForEntry;
301
325
  exports.createTanstackRsbuildRouteSplittingProfile = __webpack_exports__.createTanstackRsbuildRouteSplittingProfile;
302
326
  exports["default"] = __webpack_exports__["default"];
327
+ exports.generateTanstackRouteArtifacts = __webpack_exports__.generateTanstackRouteArtifacts;
303
328
  exports.generateTanstackRouterTypesSourceForEntry = __webpack_exports__.generateTanstackRouterTypesSourceForEntry;
304
329
  exports.resolveTanstackRouteCodeSplittingEnabled = __webpack_exports__.resolveTanstackRouteCodeSplittingEnabled;
305
330
  exports.tanstackRouterPlugin = __webpack_exports__.tanstackRouterPlugin;
@@ -309,6 +334,7 @@ for(var __rspack_i in __webpack_exports__)if (-1 === [
309
334
  "collectCanonicalRoutesForEntry",
310
335
  "createTanstackRsbuildRouteSplittingProfile",
311
336
  "default",
337
+ "generateTanstackRouteArtifacts",
312
338
  "generateTanstackRouterTypesSourceForEntry",
313
339
  "resolveTanstackRouteCodeSplittingEnabled",
314
340
  "tanstackRouterPlugin",
@@ -167,13 +167,14 @@ const tanstackRouterPlugin = (userConfig = {})=>{
167
167
  if (!modifiedRouteObjects.length) return;
168
168
  const { request, nonce, baseUrl, loaderFailureMode = 'errorBoundary' } = context.ssrContext;
169
169
  const _basename = '/' === baseUrl ? (0, external_utils_js_namespaceObject.urlJoin)(baseUrl, basename || '') : baseUrl;
170
- const initialHref = createGetSsrHref(request.raw);
171
- const isRSCNavigation = enableRsc && 'true' === request.raw.headers.get('x-rsc-tree');
170
+ const rawRequest = request.raw;
171
+ const initialHref = createGetSsrHref(rawRequest);
172
+ const isRSCNavigation = enableRsc && 'true' === rawRequest.headers.get('x-rsc-tree');
172
173
  const requestContext = (0, node_namespaceObject.createRequestContext)(context.ssrContext?.loaderContext);
173
174
  const controller = new AbortController();
174
- const ssrRequest = new Request(request.raw.url, {
175
+ const ssrRequest = new Request(rawRequest.url, {
175
176
  method: 'GET',
176
- headers: request.raw.headers,
177
+ headers: rawRequest.headers,
177
178
  signal: controller.signal
178
179
  });
179
180
  const routerContext = {
@@ -201,7 +202,7 @@ const tanstackRouterPlugin = (userConfig = {})=>{
201
202
  history,
202
203
  basepath: '/',
203
204
  rewrite,
204
- origin: new URL(request.raw.url).origin,
205
+ origin: new URL(rawRequest.url).origin,
205
206
  ssr: {
206
207
  nonce
207
208
  },
@@ -230,14 +231,14 @@ const tanstackRouterPlugin = (userConfig = {})=>{
230
231
  }
231
232
  const routerErrors = collectRouterErrors(tanstackRouter);
232
233
  if (routerErrors && 'clientRender' === loaderFailureMode) {
233
- context.ssrContext?.response.status(200);
234
+ context.ssrContext?.response?.status(200);
234
235
  try {
235
236
  serverRouter.serverSsr?.cleanup?.();
236
237
  } catch {}
237
238
  throw Object.values(routerErrors)[0];
238
239
  }
239
240
  await preloadMatchedRouteComponents(serverRouter);
240
- context.ssrContext?.response.status(tanstackRouter.state.statusCode);
241
+ context.ssrContext?.response?.status(tanstackRouter.state.statusCode);
241
242
  await serverRouter.serverSsr?.dehydrate?.();
242
243
  if (isRSCNavigation) {
243
244
  await waitForRouterSerialization(serverRouter);
@@ -40,10 +40,23 @@ function resolvePreloadFromPrefetch(prefetch, preload) {
40
40
  return 'viewport';
41
41
  }
42
42
  const LinkComponentImpl = (props)=>{
43
- const { prefetch, preload, ...rest } = props;
44
- return /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(react_router_namespaceObject.Link, {
43
+ const { prefetch, preload, 'aria-current': ariaCurrentProp, ref, children, ...rest } = props;
44
+ const hasAriaCurrentOverride = 'aria-current' in props;
45
+ const linkOptions = {
45
46
  ...rest,
46
47
  preload: resolvePreloadFromPrefetch(prefetch, preload)
48
+ };
49
+ const linkProps = (0, react_router_namespaceObject.useLinkProps)(linkOptions, ref);
50
+ const { disabled: _disabled, type: _type, ...anchorProps } = linkProps;
51
+ if (hasAriaCurrentOverride) if (false === ariaCurrentProp || void 0 === ariaCurrentProp) delete anchorProps['aria-current'];
52
+ else anchorProps['aria-current'] = ariaCurrentProp;
53
+ const resolvedChildren = 'function' == typeof children ? children({
54
+ isActive: 'active' === anchorProps['data-status'],
55
+ isTransitioning: 'transitioning' === anchorProps['data-transitioning']
56
+ }) : children;
57
+ return /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("a", {
58
+ ...anchorProps,
59
+ children: resolvedChildren
47
60
  });
48
61
  };
49
62
  const Link = LinkComponentImpl;
@@ -238,6 +238,29 @@ function tanstackRouterPlugin(options = {}) {
238
238
  }
239
239
  };
240
240
  }
241
+ async function generateTanstackRouteArtifacts(opts) {
242
+ const [{ createRunOptions }, { cli }, utils] = await Promise.all([
243
+ import("@modern-js/app-tools/cli/run"),
244
+ import("@modern-js/plugin/cli"),
245
+ import("@modern-js/utils")
246
+ ]);
247
+ process.env.MODERN_ARGV = 'node modern build';
248
+ process.env.NODE_ENV = process.env.NODE_ENV || 'production';
249
+ const { findExists, CONFIG_FILE_EXTENSIONS } = utils;
250
+ const configFileBase = node_path.resolve(opts.appDirectory, 'modern.config');
251
+ const resolvedConfigFile = findExists(CONFIG_FILE_EXTENSIONS.map((extension)=>`${configFileBase}${extension}`));
252
+ if (!resolvedConfigFile) throw new Error(`[plugin-tanstack] Unable to locate a Modern.js config file (modern.config.{${CONFIG_FILE_EXTENSIONS.map((extension)=>extension.slice(1)).join(',')}}) in ${opts.appDirectory}. TanStack route generation requires the app config to resolve its plugins.`);
253
+ const runOptions = await createRunOptions({
254
+ cwd: opts.appDirectory,
255
+ configFile: configFileBase,
256
+ version: opts.version || process.env.MODERN_JS_VERSION || '0.0.0-routes-generate',
257
+ internalPlugins: utils.INTERNAL_RUNTIME_PLUGINS
258
+ });
259
+ await cli.init({
260
+ ...runOptions,
261
+ command: 'build'
262
+ });
263
+ }
241
264
  const src_cli = tanstackRouterPlugin;
242
265
  export default src_cli;
243
- export { collectCanonicalRoutesForEntry, createTanstackRsbuildRouteSplittingProfile, generateTanstackRouterTypesSourceForEntry, resolveTanstackRouteCodeSplittingEnabled, tanstackRouterPlugin, writeTanstackRegisterFile, writeTanstackRouterTypesForEntries };
266
+ export { collectCanonicalRoutesForEntry, createTanstackRsbuildRouteSplittingProfile, generateTanstackRouteArtifacts, generateTanstackRouterTypesSourceForEntry, resolveTanstackRouteCodeSplittingEnabled, tanstackRouterPlugin, writeTanstackRegisterFile, writeTanstackRouterTypesForEntries };
@@ -134,13 +134,14 @@ const tanstackRouterPlugin = (userConfig = {})=>{
134
134
  if (!modifiedRouteObjects.length) return;
135
135
  const { request, nonce, baseUrl, loaderFailureMode = 'errorBoundary' } = context.ssrContext;
136
136
  const _basename = '/' === baseUrl ? urlJoin(baseUrl, basename || '') : baseUrl;
137
- const initialHref = createGetSsrHref(request.raw);
138
- const isRSCNavigation = enableRsc && 'true' === request.raw.headers.get('x-rsc-tree');
137
+ const rawRequest = request.raw;
138
+ const initialHref = createGetSsrHref(rawRequest);
139
+ const isRSCNavigation = enableRsc && 'true' === rawRequest.headers.get('x-rsc-tree');
139
140
  const requestContext = createRequestContext(context.ssrContext?.loaderContext);
140
141
  const controller = new AbortController();
141
- const ssrRequest = new Request(request.raw.url, {
142
+ const ssrRequest = new Request(rawRequest.url, {
142
143
  method: 'GET',
143
- headers: request.raw.headers,
144
+ headers: rawRequest.headers,
144
145
  signal: controller.signal
145
146
  });
146
147
  const routerContext = {
@@ -168,7 +169,7 @@ const tanstackRouterPlugin = (userConfig = {})=>{
168
169
  history,
169
170
  basepath: '/',
170
171
  rewrite,
171
- origin: new URL(request.raw.url).origin,
172
+ origin: new URL(rawRequest.url).origin,
172
173
  ssr: {
173
174
  nonce
174
175
  },
@@ -197,14 +198,14 @@ const tanstackRouterPlugin = (userConfig = {})=>{
197
198
  }
198
199
  const routerErrors = collectRouterErrors(tanstackRouter);
199
200
  if (routerErrors && 'clientRender' === loaderFailureMode) {
200
- context.ssrContext?.response.status(200);
201
+ context.ssrContext?.response?.status(200);
201
202
  try {
202
203
  serverRouter.serverSsr?.cleanup?.();
203
204
  } catch {}
204
205
  throw Object.values(routerErrors)[0];
205
206
  }
206
207
  await preloadMatchedRouteComponents(serverRouter);
207
- context.ssrContext?.response.status(tanstackRouter.state.statusCode);
208
+ context.ssrContext?.response?.status(tanstackRouter.state.statusCode);
208
209
  await serverRouter.serverSsr?.dehydrate?.();
209
210
  if (isRSCNavigation) {
210
211
  await waitForRouterSerialization(serverRouter);
@@ -1,5 +1,5 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
- import { Link } from "@tanstack/react-router";
2
+ import { useLinkProps } from "@tanstack/react-router";
3
3
  function resolvePreloadFromPrefetch(prefetch, preload) {
4
4
  if (void 0 !== preload) return preload;
5
5
  if ('none' === prefetch) return false;
@@ -7,12 +7,25 @@ function resolvePreloadFromPrefetch(prefetch, preload) {
7
7
  return 'viewport';
8
8
  }
9
9
  const LinkComponentImpl = (props)=>{
10
- const { prefetch, preload, ...rest } = props;
11
- return /*#__PURE__*/ jsx(Link, {
10
+ const { prefetch, preload, 'aria-current': ariaCurrentProp, ref, children, ...rest } = props;
11
+ const hasAriaCurrentOverride = 'aria-current' in props;
12
+ const linkOptions = {
12
13
  ...rest,
13
14
  preload: resolvePreloadFromPrefetch(prefetch, preload)
15
+ };
16
+ const linkProps = useLinkProps(linkOptions, ref);
17
+ const { disabled: _disabled, type: _type, ...anchorProps } = linkProps;
18
+ if (hasAriaCurrentOverride) if (false === ariaCurrentProp || void 0 === ariaCurrentProp) delete anchorProps['aria-current'];
19
+ else anchorProps['aria-current'] = ariaCurrentProp;
20
+ const resolvedChildren = 'function' == typeof children ? children({
21
+ isActive: 'active' === anchorProps['data-status'],
22
+ isTransitioning: 'transitioning' === anchorProps['data-transitioning']
23
+ }) : children;
24
+ return /*#__PURE__*/ jsx("a", {
25
+ ...anchorProps,
26
+ children: resolvedChildren
14
27
  });
15
28
  };
16
- const prefetchLink_Link = LinkComponentImpl;
29
+ const Link = LinkComponentImpl;
17
30
  const NavLink = LinkComponentImpl;
18
- export { NavLink, prefetchLink_Link as Link };
31
+ export { Link, NavLink };
@@ -242,6 +242,29 @@ function tanstackRouterPlugin(options = {}) {
242
242
  }
243
243
  };
244
244
  }
245
+ async function generateTanstackRouteArtifacts(opts) {
246
+ const [{ createRunOptions }, { cli }, utils] = await Promise.all([
247
+ import("@modern-js/app-tools/cli/run"),
248
+ import("@modern-js/plugin/cli"),
249
+ import("@modern-js/utils")
250
+ ]);
251
+ process.env.MODERN_ARGV = 'node modern build';
252
+ process.env.NODE_ENV = process.env.NODE_ENV || 'production';
253
+ const { findExists, CONFIG_FILE_EXTENSIONS } = utils;
254
+ const configFileBase = node_path.resolve(opts.appDirectory, 'modern.config');
255
+ const resolvedConfigFile = findExists(CONFIG_FILE_EXTENSIONS.map((extension)=>`${configFileBase}${extension}`));
256
+ if (!resolvedConfigFile) throw new Error(`[plugin-tanstack] Unable to locate a Modern.js config file (modern.config.{${CONFIG_FILE_EXTENSIONS.map((extension)=>extension.slice(1)).join(',')}}) in ${opts.appDirectory}. TanStack route generation requires the app config to resolve its plugins.`);
257
+ const runOptions = await createRunOptions({
258
+ cwd: opts.appDirectory,
259
+ configFile: configFileBase,
260
+ version: opts.version || process.env.MODERN_JS_VERSION || '0.0.0-routes-generate',
261
+ internalPlugins: utils.INTERNAL_RUNTIME_PLUGINS
262
+ });
263
+ await cli.init({
264
+ ...runOptions,
265
+ command: 'build'
266
+ });
267
+ }
245
268
  const src_cli = tanstackRouterPlugin;
246
269
  export default src_cli;
247
- export { collectCanonicalRoutesForEntry, createTanstackRsbuildRouteSplittingProfile, generateTanstackRouterTypesSourceForEntry, resolveTanstackRouteCodeSplittingEnabled, tanstackRouterPlugin, writeTanstackRegisterFile, writeTanstackRouterTypesForEntries };
270
+ export { collectCanonicalRoutesForEntry, createTanstackRsbuildRouteSplittingProfile, generateTanstackRouteArtifacts, generateTanstackRouterTypesSourceForEntry, resolveTanstackRouteCodeSplittingEnabled, tanstackRouterPlugin, writeTanstackRegisterFile, writeTanstackRouterTypesForEntries };
@@ -135,13 +135,14 @@ const tanstackRouterPlugin = (userConfig = {})=>{
135
135
  if (!modifiedRouteObjects.length) return;
136
136
  const { request, nonce, baseUrl, loaderFailureMode = 'errorBoundary' } = context.ssrContext;
137
137
  const _basename = '/' === baseUrl ? urlJoin(baseUrl, basename || '') : baseUrl;
138
- const initialHref = createGetSsrHref(request.raw);
139
- const isRSCNavigation = enableRsc && 'true' === request.raw.headers.get('x-rsc-tree');
138
+ const rawRequest = request.raw;
139
+ const initialHref = createGetSsrHref(rawRequest);
140
+ const isRSCNavigation = enableRsc && 'true' === rawRequest.headers.get('x-rsc-tree');
140
141
  const requestContext = createRequestContext(context.ssrContext?.loaderContext);
141
142
  const controller = new AbortController();
142
- const ssrRequest = new Request(request.raw.url, {
143
+ const ssrRequest = new Request(rawRequest.url, {
143
144
  method: 'GET',
144
- headers: request.raw.headers,
145
+ headers: rawRequest.headers,
145
146
  signal: controller.signal
146
147
  });
147
148
  const routerContext = {
@@ -169,7 +170,7 @@ const tanstackRouterPlugin = (userConfig = {})=>{
169
170
  history,
170
171
  basepath: '/',
171
172
  rewrite,
172
- origin: new URL(request.raw.url).origin,
173
+ origin: new URL(rawRequest.url).origin,
173
174
  ssr: {
174
175
  nonce
175
176
  },
@@ -198,14 +199,14 @@ const tanstackRouterPlugin = (userConfig = {})=>{
198
199
  }
199
200
  const routerErrors = collectRouterErrors(tanstackRouter);
200
201
  if (routerErrors && 'clientRender' === loaderFailureMode) {
201
- context.ssrContext?.response.status(200);
202
+ context.ssrContext?.response?.status(200);
202
203
  try {
203
204
  serverRouter.serverSsr?.cleanup?.();
204
205
  } catch {}
205
206
  throw Object.values(routerErrors)[0];
206
207
  }
207
208
  await preloadMatchedRouteComponents(serverRouter);
208
- context.ssrContext?.response.status(tanstackRouter.state.statusCode);
209
+ context.ssrContext?.response?.status(tanstackRouter.state.statusCode);
209
210
  await serverRouter.serverSsr?.dehydrate?.();
210
211
  if (isRSCNavigation) {
211
212
  await waitForRouterSerialization(serverRouter);
@@ -1,6 +1,6 @@
1
1
  import "node:module";
2
2
  import { jsx } from "react/jsx-runtime";
3
- import { Link } from "@tanstack/react-router";
3
+ import { useLinkProps } from "@tanstack/react-router";
4
4
  function resolvePreloadFromPrefetch(prefetch, preload) {
5
5
  if (void 0 !== preload) return preload;
6
6
  if ('none' === prefetch) return false;
@@ -8,12 +8,25 @@ function resolvePreloadFromPrefetch(prefetch, preload) {
8
8
  return 'viewport';
9
9
  }
10
10
  const LinkComponentImpl = (props)=>{
11
- const { prefetch, preload, ...rest } = props;
12
- return /*#__PURE__*/ jsx(Link, {
11
+ const { prefetch, preload, 'aria-current': ariaCurrentProp, ref, children, ...rest } = props;
12
+ const hasAriaCurrentOverride = 'aria-current' in props;
13
+ const linkOptions = {
13
14
  ...rest,
14
15
  preload: resolvePreloadFromPrefetch(prefetch, preload)
16
+ };
17
+ const linkProps = useLinkProps(linkOptions, ref);
18
+ const { disabled: _disabled, type: _type, ...anchorProps } = linkProps;
19
+ if (hasAriaCurrentOverride) if (false === ariaCurrentProp || void 0 === ariaCurrentProp) delete anchorProps['aria-current'];
20
+ else anchorProps['aria-current'] = ariaCurrentProp;
21
+ const resolvedChildren = 'function' == typeof children ? children({
22
+ isActive: 'active' === anchorProps['data-status'],
23
+ isTransitioning: 'transitioning' === anchorProps['data-transitioning']
24
+ }) : children;
25
+ return /*#__PURE__*/ jsx("a", {
26
+ ...anchorProps,
27
+ children: resolvedChildren
15
28
  });
16
29
  };
17
- const prefetchLink_Link = LinkComponentImpl;
30
+ const Link = LinkComponentImpl;
18
31
  const NavLink = LinkComponentImpl;
19
- export { NavLink, prefetchLink_Link as Link };
32
+ export { Link, NavLink };
@@ -30,4 +30,15 @@ export declare function writeTanstackRouterTypesForEntries(opts: {
30
30
  i18nPluginInstalled?: boolean;
31
31
  }): Promise<void>;
32
32
  export declare function tanstackRouterPlugin(options?: TanstackRouterPluginOptions): CliPlugin<AppTools>;
33
+ /**
34
+ * Headless regeneration of `router.gen.ts` / `register.gen.d.ts` for a generated
35
+ * app, without running a full dev/build. Drives the app-tools analyze `onPrepare`
36
+ * (which fires `generateEntryCode`) and returns before any bundling — the app's
37
+ * own registered plugin-tanstack writes the artifacts via its `generateEntryCode`
38
+ * subscriber. Reuses the exact hook dev/build use, so there is no scanning drift.
39
+ */
40
+ export declare function generateTanstackRouteArtifacts(opts: {
41
+ appDirectory: string;
42
+ version?: string;
43
+ }): Promise<void>;
33
44
  export default tanstackRouterPlugin;
@@ -1,5 +1,3 @@
1
- import type { Plugin, RuntimePluginExtends } from '@modern-js/plugin';
2
- import type { RuntimePluginAPI } from '@modern-js/plugin/runtime';
3
1
  import { type TInternalRuntimeContext } from '@modern-js/runtime/context';
4
2
  import type { RouterExtendsHooks } from './hooks';
5
3
  import type { RouterConfig } from './types';
@@ -8,14 +6,20 @@ export type TanstackRouterRuntimeConfig = {
8
6
  router?: Partial<RouterConfig>;
9
7
  [key: string]: unknown;
10
8
  };
11
- type TanstackRouterRuntimeExtends = Required<RuntimePluginExtends<TanstackRouterRuntimeConfig, TInternalRuntimeContext>> & {
12
- extendHooks: RouterExtendsHooks;
9
+ type RuntimeInterrupt = (value?: unknown) => unknown;
10
+ export type TanstackRouterPluginAPI = {
11
+ getRuntimeConfig: () => TanstackRouterRuntimeConfig;
12
+ getHooks: () => RouterExtendsHooks;
13
+ onBeforeRender: (listener: (context: TInternalRuntimeContext, interrupt: RuntimeInterrupt) => unknown) => void;
14
+ wrapRoot: (listener: (App: any) => any) => void;
15
+ [key: string]: any;
13
16
  };
14
- export type TanstackRouterPluginAPI = RuntimePluginAPI<TanstackRouterRuntimeExtends>;
15
- export type TanstackRouterRuntimePlugin = Plugin<TanstackRouterPluginAPI, TInternalRuntimeContext>;
16
- export declare function getMergedRouterConfig(api: TanstackRouterPluginAPI, userConfig: Partial<RouterConfig>): RouterConfig;
17
- export declare function getFinalRouteConfig(mergedConfig: RouterConfig): {
18
- routes: (import("@modern-js/types").NestedRoute<string | (() => JSX.Element)> | import("@modern-js/types").PageRoute)[] | undefined;
19
- globalApp: import("react").ComponentClass<{}, any> | import("react").FunctionComponent<{}> | undefined;
17
+ export type TanstackRouterRuntimePlugin = {
18
+ name?: string;
19
+ registryHooks?: RouterExtendsHooks;
20
+ setup?: (api: TanstackRouterPluginAPI) => unknown;
21
+ [key: string]: unknown;
20
22
  };
23
+ export declare function getMergedRouterConfig(api: TanstackRouterPluginAPI, userConfig: Partial<RouterConfig>): RouterConfig;
24
+ export declare function getFinalRouteConfig(mergedConfig: RouterConfig): RouterConfig['routesConfig'];
21
25
  export {};
@@ -1,8 +1,46 @@
1
- import type { RouterFramework } from '@modern-js/runtime/context';
2
1
  import type { RouteObject } from '@modern-js/runtime-utils/router';
3
- import type { NestedRoute, PageRoute } from '@modern-js/types';
4
2
  import type React from 'react';
5
- export type { BuiltInRouterFramework, InternalRouterRuntimeState, InternalRouterServerSnapshot, RouterFramework, RouterRouteMatchSnapshot, RouterServerPrepareResult, } from '@modern-js/runtime/context';
3
+ export type BuiltInRouterFramework = 'react-router' | 'tanstack';
4
+ export type RouterFramework = BuiltInRouterFramework | (string & {});
5
+ export type ModernRoute = {
6
+ type: 'nested' | 'page';
7
+ path?: string;
8
+ id?: string;
9
+ component?: React.ComponentType | string;
10
+ children?: ModernRoute[];
11
+ [key: string]: any;
12
+ };
13
+ export type RouterRouteMatchSnapshot = {
14
+ routeId: string;
15
+ assetRouteId?: string;
16
+ pathname?: string;
17
+ params?: Record<string, string>;
18
+ };
19
+ export type InternalRouterServerSnapshot = {
20
+ framework?: RouterFramework;
21
+ basename?: string;
22
+ statusCode?: number;
23
+ errors?: Record<string, unknown>;
24
+ routerData?: {
25
+ loaderData?: Record<string, unknown>;
26
+ errors?: Record<string, unknown>;
27
+ };
28
+ hydrationScript?: string;
29
+ hydrationScripts?: string[];
30
+ matchedRouteIds?: string[];
31
+ matches?: RouterRouteMatchSnapshot[];
32
+ };
33
+ export type InternalRouterRuntimeState = {
34
+ framework: RouterFramework;
35
+ basename?: string;
36
+ instance?: unknown;
37
+ hydrationScript?: string;
38
+ hydrationScripts?: string[];
39
+ matchedRouteIds?: string[];
40
+ matches?: RouterRouteMatchSnapshot[];
41
+ serverSnapshot?: InternalRouterServerSnapshot;
42
+ cleanup?: () => void | Promise<void>;
43
+ };
6
44
  /**
7
45
  * TanStack-specific router config. Unlike the react-router provider config,
8
46
  * this intentionally has no `oldVersion`/`future` fields — those are
@@ -12,7 +50,7 @@ export type RouterConfig = {
12
50
  framework?: RouterFramework;
13
51
  routesConfig: {
14
52
  globalApp?: React.ComponentType<any>;
15
- routes?: (NestedRoute | PageRoute)[];
53
+ routes?: ModernRoute[];
16
54
  };
17
55
  serverBase?: string[];
18
56
  supportHtml5History?: boolean;
@@ -1,10 +1,11 @@
1
1
  import type { RouteObject } from '@modern-js/runtime-utils/router';
2
- import type { NestedRoute, PageRoute, SSRMode } from '@modern-js/types';
3
2
  import React from 'react';
3
+ import type { ModernRoute } from './types';
4
+ export type SSRMode = 'string' | 'stream';
4
5
  type RouterConfig = {
5
6
  routesConfig: {
6
7
  globalApp?: React.ComponentType<any>;
7
- routes?: (NestedRoute | PageRoute)[];
8
+ routes?: ModernRoute[];
8
9
  };
9
10
  };
10
11
  type GlobalAppProps = {
@@ -21,7 +22,7 @@ export type ModernRouteObject = RouteObject & {
21
22
  default: React.ComponentType;
22
23
  }>;
23
24
  };
24
- export declare function getRouteObjects(routes: (NestedRoute | PageRoute)[], { globalApp, ssrMode, props, }: {
25
+ export declare function getRouteObjects(routes: ModernRoute[], { globalApp, ssrMode, props, }: {
25
26
  globalApp?: React.ComponentType<GlobalAppProps>;
26
27
  ssrMode?: SSRMode;
27
28
  props?: Record<string, unknown>;
package/package.json CHANGED
@@ -18,7 +18,7 @@
18
18
  "modern.js",
19
19
  "tanstack-router"
20
20
  ],
21
- "version": "3.5.0-ultramodern.3",
21
+ "version": "3.5.0-ultramodern.30",
22
22
  "engines": {
23
23
  "node": ">=20"
24
24
  },
@@ -77,13 +77,14 @@
77
77
  "@swc/helpers": "^0.5.23",
78
78
  "@tanstack/react-router": "1.170.16",
79
79
  "@tanstack/router-core": "1.171.13",
80
- "@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.5.0-ultramodern.3",
81
- "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.5.0-ultramodern.3",
82
- "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.5.0-ultramodern.3",
83
- "@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.5.0-ultramodern.3"
80
+ "@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.5.0-ultramodern.30",
81
+ "@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.5.0-ultramodern.30",
82
+ "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.5.0-ultramodern.30",
83
+ "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.5.0-ultramodern.30"
84
84
  },
85
85
  "peerDependencies": {
86
- "@modern-js/runtime": "3.5.0-ultramodern.3",
86
+ "@modern-js/app-tools": "3.5.0-ultramodern.30",
87
+ "@modern-js/runtime": "3.5.0-ultramodern.30",
87
88
  "react": "^19.2.7",
88
89
  "react-dom": "^19.2.7"
89
90
  },
@@ -98,8 +99,8 @@
98
99
  "@typescript/native-preview": "7.0.0-dev.20260628.1",
99
100
  "react": "^19.2.7",
100
101
  "react-dom": "^19.2.7",
101
- "@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.5.0-ultramodern.3",
102
- "@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.5.0-ultramodern.3",
102
+ "@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.5.0-ultramodern.30",
103
+ "@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.5.0-ultramodern.30",
103
104
  "@scripts/rstest-config": "2.66.0"
104
105
  },
105
106
  "sideEffects": [
package/rstest.config.mts CHANGED
@@ -25,6 +25,7 @@ export default {
25
25
  include: [
26
26
  'tests/router/cli.test.ts',
27
27
  'tests/router/fastDefaults.test.ts',
28
+ 'tests/router/generateRouteArtifacts.test.ts',
28
29
  'tests/router/hooks.test.ts',
29
30
  'tests/router/loaderBridge.test.ts',
30
31
  'tests/router/packageSurface.test.ts',
@@ -44,6 +45,7 @@ export default {
44
45
  'tests/router/dataMutation.test.tsx',
45
46
  'tests/router/hydrationBoundary.test.tsx',
46
47
  'tests/router/prefetchLink.test.tsx',
48
+ 'tests/router/prefetchLinkPreload.test.tsx',
47
49
  ],
48
50
  extends: commonConfig,
49
51
  }),
package/src/cli/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- // @effect-diagnostics asyncFunction:off nodeBuiltinImport:off strictBooleanExpressions:off
1
+ // @effect-diagnostics asyncFunction:off nodeBuiltinImport:off strictBooleanExpressions:off processEnv:off processEnvInEffect:off
2
2
  import path from 'node:path';
3
3
  import type {
4
4
  AppNormalizedConfig,
@@ -518,4 +518,66 @@ export function tanstackRouterPlugin(
518
518
  };
519
519
  }
520
520
 
521
+ /**
522
+ * Headless regeneration of `router.gen.ts` / `register.gen.d.ts` for a generated
523
+ * app, without running a full dev/build. Drives the app-tools analyze `onPrepare`
524
+ * (which fires `generateEntryCode`) and returns before any bundling — the app's
525
+ * own registered plugin-tanstack writes the artifacts via its `generateEntryCode`
526
+ * subscriber. Reuses the exact hook dev/build use, so there is no scanning drift.
527
+ */
528
+ export async function generateTanstackRouteArtifacts(opts: {
529
+ appDirectory: string;
530
+ version?: string;
531
+ }): Promise<void> {
532
+ const [{ createRunOptions }, { cli }, utils] = await Promise.all([
533
+ import('@modern-js/app-tools/cli/run'),
534
+ import('@modern-js/plugin/cli'),
535
+ import('@modern-js/utils'),
536
+ ]);
537
+ // Load-bearing, not decoration: app-tools' analyze.onPrepare only fires
538
+ // generateEntryCode (the hook that writes the route artifacts) when
539
+ // checkIsBuildCommands() is true, and getCommand() resolves the command
540
+ // from MODERN_ARGV before process.argv — cli.init's `command` option does
541
+ // not feed that check.
542
+ process.env.MODERN_ARGV = 'node modern build';
543
+ process.env.NODE_ENV = process.env.NODE_ENV || 'production';
544
+
545
+ // Resolve the app's Modern.js config file ourselves and pass it as an
546
+ // absolute base path. `createRunOptions` otherwise defaults its config lookup
547
+ // to `process.cwd()` (not `cwd`/appDirectory), so headless invocations that
548
+ // run from a different directory than the app — e.g. the create CLI spawning
549
+ // this from the workspace root — find no config, and `findExists` returns
550
+ // `false`. That `false` then flows into `cli.init` -> `getConfigFilePath` ->
551
+ // `path.isAbsolute(false)`, throwing the opaque
552
+ // `The "path" argument must be of type string. Received type boolean (false)`.
553
+ // Passing an absolute base is resolution-based: cwd no longer matters, and the
554
+ // downstream `getConfigFile` still appends the correct extension.
555
+ const { findExists, CONFIG_FILE_EXTENSIONS } = utils as {
556
+ findExists: (paths: string[]) => string | false;
557
+ CONFIG_FILE_EXTENSIONS: string[];
558
+ };
559
+ const configFileBase = path.resolve(opts.appDirectory, 'modern.config');
560
+ const resolvedConfigFile = findExists(
561
+ CONFIG_FILE_EXTENSIONS.map(extension => `${configFileBase}${extension}`),
562
+ );
563
+ if (!resolvedConfigFile) {
564
+ throw new Error(
565
+ `[plugin-tanstack] Unable to locate a Modern.js config file (modern.config.{${CONFIG_FILE_EXTENSIONS.map(
566
+ extension => extension.slice(1),
567
+ ).join(',')}}) in ${opts.appDirectory}. ` +
568
+ 'TanStack route generation requires the app config to resolve its plugins.',
569
+ );
570
+ }
571
+
572
+ const runOptions = await createRunOptions({
573
+ cwd: opts.appDirectory,
574
+ configFile: configFileBase,
575
+ version:
576
+ opts.version || process.env.MODERN_JS_VERSION || '0.0.0-routes-generate',
577
+ internalPlugins: (utils as { INTERNAL_RUNTIME_PLUGINS: unknown })
578
+ .INTERNAL_RUNTIME_PLUGINS as never,
579
+ });
580
+ await cli.init({ ...runOptions, command: 'build' });
581
+ }
582
+
521
583
  export default tanstackRouterPlugin;