@timber-js/app 0.1.29 → 0.1.31
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/client/index.js +138 -86
- package/dist/client/index.js.map +1 -1
- package/dist/client/link-status-provider.d.ts +4 -4
- package/dist/client/link-status-provider.d.ts.map +1 -1
- package/dist/client/pending-navigation-context.d.ts +32 -0
- package/dist/client/pending-navigation-context.d.ts.map +1 -0
- package/dist/client/router.d.ts +12 -0
- package/dist/client/router.d.ts.map +1 -1
- package/dist/client/transition-root.d.ts +33 -13
- package/dist/client/transition-root.d.ts.map +1 -1
- package/dist/client/use-navigation-pending.d.ts.map +1 -1
- package/dist/index.js +120 -7
- package/dist/index.js.map +1 -1
- package/dist/plugins/chunks.d.ts +17 -6
- package/dist/plugins/chunks.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/client/browser-entry.ts +26 -16
- package/src/client/link-status-provider.tsx +14 -24
- package/src/client/pending-navigation-context.ts +66 -0
- package/src/client/router.ts +127 -75
- package/src/client/transition-root.tsx +84 -20
- package/src/client/use-navigation-pending.ts +8 -17
- package/src/plugins/chunks.ts +145 -17
|
@@ -11,41 +11,61 @@
|
|
|
11
11
|
* a transition update. React keeps the old committed tree visible while
|
|
12
12
|
* any new Suspense boundaries in the transition resolve.
|
|
13
13
|
*
|
|
14
|
-
*
|
|
15
|
-
* the
|
|
16
|
-
*
|
|
14
|
+
* Also manages `pendingUrl` via `useOptimistic`. During a navigation
|
|
15
|
+
* transition, the optimistic value (the target URL) shows immediately
|
|
16
|
+
* while the transition is pending, and automatically reverts to null
|
|
17
|
+
* when the transition commits. This ensures useLinkStatus and
|
|
18
|
+
* useNavigationPending show the pending state immediately and clear
|
|
19
|
+
* atomically with the new tree — same pattern Next.js uses with
|
|
20
|
+
* useOptimistic per Link instance, adapted for timber's server-component
|
|
21
|
+
* Link with global click delegation.
|
|
17
22
|
*
|
|
18
23
|
* See design/05-streaming.md §"deferSuspenseFor"
|
|
24
|
+
* See design/19-client-navigation.md §"NavigationContext"
|
|
19
25
|
*/
|
|
20
26
|
import { type ReactNode } from 'react';
|
|
21
27
|
/**
|
|
22
28
|
* Root wrapper component that enables transition-based rendering.
|
|
23
29
|
*
|
|
24
|
-
* Renders
|
|
25
|
-
*
|
|
26
|
-
*
|
|
30
|
+
* Renders PendingNavigationProvider around children for the pending URL
|
|
31
|
+
* context. The DOM tree matches the server-rendered HTML during hydration
|
|
32
|
+
* (the provider renders no extra DOM elements).
|
|
27
33
|
*
|
|
28
34
|
* Usage in browser-entry.ts:
|
|
29
35
|
* const rootEl = createElement(TransitionRoot, { initial: wrapped });
|
|
30
36
|
* reactRoot = hydrateRoot(document, rootEl);
|
|
31
37
|
*
|
|
32
38
|
* Subsequent navigations:
|
|
39
|
+
* navigateTransition(url, async () => { fetch; return wrappedElement; });
|
|
40
|
+
*
|
|
41
|
+
* Non-navigation renders:
|
|
33
42
|
* transitionRender(newWrappedElement);
|
|
34
43
|
*/
|
|
35
44
|
export declare function TransitionRoot({ initial }: {
|
|
36
45
|
initial: ReactNode;
|
|
37
46
|
}): ReactNode;
|
|
38
47
|
/**
|
|
39
|
-
* Trigger a transition render
|
|
40
|
-
* visible while any new Suspense
|
|
41
|
-
*
|
|
42
|
-
* This is the function called by the router's renderRoot callback
|
|
43
|
-
* instead of reactRoot.render() directly.
|
|
48
|
+
* Trigger a transition render for non-navigation updates.
|
|
49
|
+
* React keeps the old committed tree visible while any new Suspense
|
|
50
|
+
* boundaries in the update resolve.
|
|
44
51
|
*
|
|
45
|
-
*
|
|
46
|
-
* happen in practice — TransitionRoot mounts during hydration).
|
|
52
|
+
* Used for: applyRevalidation, popstate replay with cached payload.
|
|
47
53
|
*/
|
|
48
54
|
export declare function transitionRender(element: ReactNode): void;
|
|
55
|
+
/**
|
|
56
|
+
* Run a full navigation inside a React transition with optimistic pending URL.
|
|
57
|
+
*
|
|
58
|
+
* The `perform` callback runs inside `startTransition` — it should fetch the
|
|
59
|
+
* RSC payload, update router state, and return the wrapped React element.
|
|
60
|
+
* The pending URL shows immediately (useOptimistic urgent update) and reverts
|
|
61
|
+
* to null when the transition commits (atomic with the new tree).
|
|
62
|
+
*
|
|
63
|
+
* Returns a Promise that resolves when the async work completes (note: the
|
|
64
|
+
* React transition may not have committed yet, but all state updates are done).
|
|
65
|
+
*
|
|
66
|
+
* Used for: navigate(), refresh(), popstate with fetch.
|
|
67
|
+
*/
|
|
68
|
+
export declare function navigateTransition(pendingUrl: string, perform: () => Promise<ReactNode>): Promise<void>;
|
|
49
69
|
/**
|
|
50
70
|
* Check if the TransitionRoot is mounted and ready for renders.
|
|
51
71
|
* Used by browser-entry.ts to guard against renders before hydration.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transition-root.d.ts","sourceRoot":"","sources":["../../src/client/transition-root.tsx"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"transition-root.d.ts","sourceRoot":"","sources":["../../src/client/transition-root.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAKL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAuBf;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,cAAc,CAAC,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,EAAE,SAAS,CAAA;CAAE,GAAG,SAAS,CA8B7E;AAID;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,SAAS,GAAG,IAAI,CAIzD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,OAAO,CAAC,SAAS,CAAC,GAChC,OAAO,CAAC,IAAI,CAAC,CAMf;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,IAAI,OAAO,CAE/C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-navigation-pending.d.ts","sourceRoot":"","sources":["../../src/client/use-navigation-pending.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"use-navigation-pending.d.ts","sourceRoot":"","sources":["../../src/client/use-navigation-pending.ts"],"names":[],"mappings":"AASA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,oBAAoB,IAAI,OAAO,CAI9C"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { r as setViteServer, t as formatSize } from "./_chunks/format-DNt20Kt8.js";
|
|
2
2
|
import { i as scanRoutes, n as generateRouteMap, t as collectInterceptionRewrites } from "./_chunks/interception-DGDIjDbR.js";
|
|
3
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
+
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
4
4
|
import { dirname, extname, join, resolve } from "node:path";
|
|
5
5
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
6
6
|
import { createRequire } from "node:module";
|
|
@@ -12748,7 +12748,8 @@ function detectDynamicFontCallAst(source, importedNames) {
|
|
|
12748
12748
|
} catch {
|
|
12749
12749
|
return null;
|
|
12750
12750
|
}
|
|
12751
|
-
|
|
12751
|
+
const nameSet = new Set(importedNames);
|
|
12752
|
+
return walkForDynamicCalls(ast, nameSet, source);
|
|
12752
12753
|
}
|
|
12753
12754
|
/**
|
|
12754
12755
|
* Recursively walk the AST looking for CallExpression nodes where
|
|
@@ -14067,14 +14068,119 @@ function timberReactProd() {
|
|
|
14067
14068
|
//#endregion
|
|
14068
14069
|
//#region src/plugins/chunks.ts
|
|
14069
14070
|
/**
|
|
14071
|
+
* timber-chunks — Vite sub-plugin for intelligent client chunk splitting.
|
|
14072
|
+
*
|
|
14073
|
+
* Splits client bundles into cache tiers based on update frequency:
|
|
14074
|
+
*
|
|
14075
|
+
* Tier 1: vendor-react — react, react-dom, scheduler (changes rarely)
|
|
14076
|
+
* Tier 2: vendor-timber — timber runtime, RSC runtime (changes per framework update)
|
|
14077
|
+
* Tier 3: vendor-app — user node_modules (changes on dependency updates)
|
|
14078
|
+
* Tier 4: shared-app — small shared app utilities/components (< 5KB source)
|
|
14079
|
+
* Tier 5: [route]-* — per-route page/layout chunks (default Rollup splitting)
|
|
14080
|
+
*
|
|
14081
|
+
* The shared-app tier prevents tiny utility modules (constants, helpers,
|
|
14082
|
+
* small UI components) from becoming individual chunks when shared across
|
|
14083
|
+
* routes. Without this, Rolldown creates per-module chunks for any code
|
|
14084
|
+
* shared between two or more entry points, producing many sub-1KB chunks.
|
|
14085
|
+
*
|
|
14086
|
+
* Server environments (RSC, SSR) are left to Vite's default chunking since
|
|
14087
|
+
* Cloudflare Workers load all code from a single deployment bundle with no
|
|
14088
|
+
* benefit from cache-tier separation.
|
|
14089
|
+
*
|
|
14090
|
+
* Design docs: 27-chunking-strategy.md
|
|
14091
|
+
*/
|
|
14092
|
+
/**
|
|
14093
|
+
* Source file size threshold for the shared-app chunk.
|
|
14094
|
+
* Modules under this size that aren't route files get merged into shared-app
|
|
14095
|
+
* instead of getting their own tiny chunks.
|
|
14096
|
+
*/
|
|
14097
|
+
var SMALL_MODULE_THRESHOLD = 5 * 1024;
|
|
14098
|
+
/**
|
|
14099
|
+
* Route convention file basenames (without extension).
|
|
14100
|
+
* These files define route segments and must stay in per-route chunks
|
|
14101
|
+
* to preserve route-based code splitting.
|
|
14102
|
+
*/
|
|
14103
|
+
var ROUTE_FILE_BASENAMES = new Set([
|
|
14104
|
+
"page",
|
|
14105
|
+
"layout",
|
|
14106
|
+
"loading",
|
|
14107
|
+
"error",
|
|
14108
|
+
"not-found",
|
|
14109
|
+
"template",
|
|
14110
|
+
"access",
|
|
14111
|
+
"middleware",
|
|
14112
|
+
"default",
|
|
14113
|
+
"route"
|
|
14114
|
+
]);
|
|
14115
|
+
/**
|
|
14116
|
+
* Cache for source file sizes to avoid repeated statSync calls.
|
|
14117
|
+
* Populated lazily during the build.
|
|
14118
|
+
*/
|
|
14119
|
+
var sizeCache = /* @__PURE__ */ new Map();
|
|
14120
|
+
/**
|
|
14121
|
+
* Get the source file size, with caching.
|
|
14122
|
+
* Returns Infinity for virtual modules or files that can't be stat'd.
|
|
14123
|
+
*/
|
|
14124
|
+
function getSourceSize(id) {
|
|
14125
|
+
const cached = sizeCache.get(id);
|
|
14126
|
+
if (cached !== void 0) return cached;
|
|
14127
|
+
try {
|
|
14128
|
+
const size = statSync(id).size;
|
|
14129
|
+
sizeCache.set(id, size);
|
|
14130
|
+
return size;
|
|
14131
|
+
} catch {
|
|
14132
|
+
sizeCache.set(id, Infinity);
|
|
14133
|
+
return Infinity;
|
|
14134
|
+
}
|
|
14135
|
+
}
|
|
14136
|
+
/**
|
|
14137
|
+
* Extract the basename without extension from a module ID.
|
|
14138
|
+
* e.g. '/project/app/dashboard/page.tsx' → 'page'
|
|
14139
|
+
*/
|
|
14140
|
+
function getBasename(id) {
|
|
14141
|
+
const lastSlash = id.lastIndexOf("/");
|
|
14142
|
+
const filename = lastSlash >= 0 ? id.substring(lastSlash + 1) : id;
|
|
14143
|
+
const dotIndex = filename.indexOf(".");
|
|
14144
|
+
return dotIndex >= 0 ? filename.substring(0, dotIndex) : filename;
|
|
14145
|
+
}
|
|
14146
|
+
/**
|
|
14147
|
+
* Check if a module is a React ecosystem package (tier 1).
|
|
14148
|
+
*/
|
|
14149
|
+
function isReactVendor(id) {
|
|
14150
|
+
return id.includes("node_modules/react-dom") || id.includes("node_modules/react/") || id.includes("node_modules/scheduler");
|
|
14151
|
+
}
|
|
14152
|
+
/**
|
|
14153
|
+
* Check if a module is part of the timber framework runtime (tier 2).
|
|
14154
|
+
*/
|
|
14155
|
+
function isTimberRuntime(id) {
|
|
14156
|
+
return id.includes("/timber-app/") || id.includes("react-server-dom") || id.includes("@vitejs/plugin-rsc");
|
|
14157
|
+
}
|
|
14158
|
+
/**
|
|
14159
|
+
* Check if a module is a user-installed node_modules dependency (tier 3).
|
|
14160
|
+
* Excludes React ecosystem and timber runtime packages which have their own tiers.
|
|
14161
|
+
*/
|
|
14162
|
+
function isUserVendor(id) {
|
|
14163
|
+
return id.includes("node_modules/") && !isReactVendor(id) && !isTimberRuntime(id);
|
|
14164
|
+
}
|
|
14165
|
+
/**
|
|
14166
|
+
* Check if a module is a route convention file that should stay per-route.
|
|
14167
|
+
*/
|
|
14168
|
+
function isRouteFile(id) {
|
|
14169
|
+
return ROUTE_FILE_BASENAMES.has(getBasename(id));
|
|
14170
|
+
}
|
|
14171
|
+
/**
|
|
14070
14172
|
* Categorize a module ID into a cache tier chunk name.
|
|
14071
14173
|
*
|
|
14072
|
-
* Returns a chunk name for vendor modules
|
|
14073
|
-
* Rollup's default splitting handle
|
|
14174
|
+
* Returns a chunk name for vendor modules and small shared app code,
|
|
14175
|
+
* or undefined to let Rollup's default splitting handle route code.
|
|
14074
14176
|
*/
|
|
14075
14177
|
function assignChunk(id) {
|
|
14076
|
-
if (
|
|
14077
|
-
if (
|
|
14178
|
+
if (isReactVendor(id)) return "vendor-react";
|
|
14179
|
+
if (isTimberRuntime(id)) return "vendor-timber";
|
|
14180
|
+
if (isUserVendor(id)) return "vendor-app";
|
|
14181
|
+
if (!id.includes("\0") && id.startsWith("/") && !isRouteFile(id)) {
|
|
14182
|
+
if (getSourceSize(id) < SMALL_MODULE_THRESHOLD) return "shared-app";
|
|
14183
|
+
}
|
|
14078
14184
|
}
|
|
14079
14185
|
/**
|
|
14080
14186
|
* Group timber's internal 'use client' modules into the vendor-timber chunk.
|
|
@@ -14082,10 +14188,17 @@ function assignChunk(id) {
|
|
|
14082
14188
|
* The RSC plugin creates separate entry points for each 'use client' module,
|
|
14083
14189
|
* which manualChunks can't merge. This function is passed as the RSC plugin's
|
|
14084
14190
|
* `clientChunks` callback to group timber internals into a single chunk.
|
|
14085
|
-
*
|
|
14191
|
+
*
|
|
14192
|
+
* User client components that are small (< 5KB) are grouped into shared-client
|
|
14193
|
+
* to prevent thin facade wrappers from becoming individual chunks. This handles
|
|
14194
|
+
* the RSC client reference facade problem where each 'use client' module gets
|
|
14195
|
+
* a ~100-300 byte re-export wrapper chunk.
|
|
14086
14196
|
*/
|
|
14087
14197
|
function assignClientChunk(meta) {
|
|
14088
14198
|
if (meta.id.includes("/timber-app/")) return "vendor-timber";
|
|
14199
|
+
if (!meta.id.includes("\0") && meta.id.startsWith("/")) {
|
|
14200
|
+
if (getSourceSize(meta.id) < SMALL_MODULE_THRESHOLD) return "shared-client";
|
|
14201
|
+
}
|
|
14089
14202
|
}
|
|
14090
14203
|
/**
|
|
14091
14204
|
* Create the timber-chunks Vite plugin.
|