@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.
- package/dist/cjs/cli/index.js +26 -0
- package/dist/cjs/runtime/plugin.node.js +8 -7
- package/dist/cjs/runtime/prefetchLink.js +15 -2
- package/dist/esm/cli/index.mjs +24 -1
- package/dist/esm/runtime/plugin.node.mjs +8 -7
- package/dist/esm/runtime/prefetchLink.mjs +18 -5
- package/dist/esm-node/cli/index.mjs +24 -1
- package/dist/esm-node/runtime/plugin.node.mjs +8 -7
- package/dist/esm-node/runtime/prefetchLink.mjs +18 -5
- package/dist/types/cli/index.d.ts +11 -0
- package/dist/types/runtime/pluginCore.d.ts +14 -10
- package/dist/types/runtime/types.d.ts +42 -4
- package/dist/types/runtime/utils.d.ts +4 -3
- package/package.json +9 -8
- package/rstest.config.mts +2 -0
- package/src/cli/index.ts +63 -1
- package/src/runtime/plugin.node.tsx +16 -7
- package/src/runtime/pluginCore.ts +23 -15
- package/src/runtime/prefetchLink.tsx +78 -15
- package/src/runtime/types.ts +46 -16
- package/src/runtime/utils.tsx +5 -3
- package/tests/router/cli.test.ts +7 -0
- package/tests/router/generateRouteArtifacts.test.ts +106 -0
- package/tests/router/prefetchLink.test.tsx +106 -59
- package/tests/router/prefetchLinkPreload.test.tsx +85 -0
package/dist/cjs/cli/index.js
CHANGED
|
@@ -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
|
|
171
|
-
const
|
|
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(
|
|
175
|
+
const ssrRequest = new Request(rawRequest.url, {
|
|
175
176
|
method: 'GET',
|
|
176
|
-
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(
|
|
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
|
|
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
|
|
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
|
-
|
|
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;
|
package/dist/esm/cli/index.mjs
CHANGED
|
@@ -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
|
|
138
|
-
const
|
|
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(
|
|
142
|
+
const ssrRequest = new Request(rawRequest.url, {
|
|
142
143
|
method: 'GET',
|
|
143
|
-
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(
|
|
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
|
|
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
|
|
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 {
|
|
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
|
-
|
|
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
|
|
29
|
+
const Link = LinkComponentImpl;
|
|
17
30
|
const NavLink = LinkComponentImpl;
|
|
18
|
-
export {
|
|
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
|
|
139
|
-
const
|
|
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(
|
|
143
|
+
const ssrRequest = new Request(rawRequest.url, {
|
|
143
144
|
method: 'GET',
|
|
144
|
-
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(
|
|
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
|
|
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
|
|
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 {
|
|
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
|
-
|
|
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
|
|
30
|
+
const Link = LinkComponentImpl;
|
|
18
31
|
const NavLink = LinkComponentImpl;
|
|
19
|
-
export {
|
|
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
|
|
12
|
-
|
|
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
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
|
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?:
|
|
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?:
|
|
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:
|
|
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.
|
|
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/
|
|
81
|
-
"@modern-js/
|
|
82
|
-
"@modern-js/
|
|
83
|
-
"@modern-js/
|
|
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/
|
|
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.
|
|
102
|
-
"@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.5.0-ultramodern.
|
|
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;
|