@rangojs/router 0.0.0-experimental.122 → 0.0.0-experimental.125
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/bin/rango.js +10 -6
- package/dist/testing/vitest.js +82 -0
- package/dist/vite/index.js +55 -48
- package/package.json +61 -21
- package/skills/caching/SKILL.md +2 -1
- package/skills/hooks/SKILL.md +40 -29
- package/skills/host-router/SKILL.md +16 -2
- package/skills/intercept/SKILL.md +4 -2
- package/skills/layout/SKILL.md +11 -6
- package/skills/loader/SKILL.md +6 -2
- package/skills/middleware/SKILL.md +4 -2
- package/skills/migrate-nextjs/SKILL.md +3 -1
- package/skills/parallel/SKILL.md +9 -4
- package/skills/rango/SKILL.md +12 -0
- package/skills/route/SKILL.md +10 -2
- package/skills/testing/SKILL.md +129 -0
- package/skills/testing/bindings.md +89 -0
- package/skills/testing/cache-prerender.md +98 -0
- package/skills/testing/client-components.md +122 -0
- package/skills/testing/e2e-parity.md +125 -0
- package/skills/testing/flight.md +89 -0
- package/skills/testing/handles.md +129 -0
- package/skills/testing/loader.md +128 -0
- package/skills/testing/middleware.md +99 -0
- package/skills/testing/render-handler.md +118 -0
- package/skills/testing/response-routes.md +95 -0
- package/skills/testing/reverse-and-types.md +84 -0
- package/skills/testing/server-actions.md +107 -0
- package/skills/testing/server-tree.md +128 -0
- package/skills/testing/setup.md +120 -0
- package/src/__internal.ts +0 -65
- package/src/browser/action-coordinator.ts +1 -1
- package/src/browser/action-fence.ts +47 -0
- package/src/browser/cookie-name.ts +140 -0
- package/src/browser/event-controller.ts +1 -83
- package/src/browser/invalidate-client-cache.ts +52 -0
- package/src/browser/navigation-bridge.ts +14 -1
- package/src/browser/navigation-client.ts +14 -1
- package/src/browser/navigation-store-handle.ts +38 -0
- package/src/browser/navigation-store.ts +26 -51
- package/src/browser/navigation-transaction.ts +0 -32
- package/src/browser/partial-update.ts +1 -83
- package/src/browser/prefetch/cache.ts +6 -45
- package/src/browser/prefetch/fetch.ts +7 -0
- package/src/browser/prefetch/queue.ts +6 -3
- package/src/browser/rango-state.ts +157 -99
- package/src/browser/react/Link.tsx +0 -2
- package/src/browser/react/NavigationProvider.tsx +2 -1
- package/src/browser/react/ScrollRestoration.tsx +10 -6
- package/src/browser/react/filter-segment-order.ts +0 -2
- package/src/browser/react/index.ts +0 -51
- package/src/browser/react/location-state-shared.ts +0 -13
- package/src/browser/react/location-state.ts +0 -1
- package/src/browser/react/use-action.ts +6 -15
- package/src/browser/react/use-handle.ts +0 -5
- package/src/browser/react/use-link-status.ts +0 -4
- package/src/browser/react/use-navigation.ts +0 -3
- package/src/browser/react/use-params.ts +0 -2
- package/src/browser/react/use-search-params.ts +0 -5
- package/src/browser/react/use-segments.ts +0 -13
- package/src/browser/rsc-router.tsx +12 -4
- package/src/browser/server-action-bridge.ts +77 -15
- package/src/browser/types.ts +7 -2
- package/src/browser/validate-redirect-origin.ts +4 -5
- package/src/build/route-trie.ts +3 -0
- package/src/build/route-types/param-extraction.ts +6 -3
- package/src/build/route-types/router-processing.ts +0 -8
- package/src/cache/cache-policy.ts +0 -54
- package/src/cache/cache-runtime.ts +27 -24
- package/src/cache/cache-scope.ts +0 -27
- package/src/cache/cache-tag.ts +0 -37
- package/src/cache/cf/cf-cache-store.ts +94 -46
- package/src/cache/cf/index.ts +0 -24
- package/src/cache/document-cache.ts +11 -36
- package/src/cache/handle-snapshot.ts +0 -40
- package/src/cache/index.ts +0 -27
- package/src/cache/memory-segment-store.ts +2 -48
- package/src/cache/profile-registry.ts +7 -3
- package/src/cache/read-through-swr.ts +41 -11
- package/src/cache/segment-codec.ts +0 -16
- package/src/cache/types.ts +0 -98
- package/src/client.rsc.tsx +1 -22
- package/src/client.tsx +14 -38
- package/src/component-utils.ts +19 -0
- package/src/deps/ssr.ts +0 -1
- package/src/handle.ts +28 -18
- package/src/handles/MetaTags.tsx +0 -14
- package/src/handles/meta.ts +0 -39
- package/src/host/cookie-handler.ts +0 -36
- package/src/host/errors.ts +0 -24
- package/src/host/index.ts +6 -0
- package/src/host/pattern-matcher.ts +7 -50
- package/src/host/router.ts +1 -65
- package/src/host/testing.ts +40 -27
- package/src/host/types.ts +6 -2
- package/src/href-client.ts +0 -4
- package/src/index.rsc.ts +42 -3
- package/src/index.ts +31 -1
- package/src/internal-debug.ts +2 -4
- package/src/loader.rsc.ts +19 -9
- package/src/loader.ts +12 -4
- package/src/network-error-thrower.tsx +1 -6
- package/src/outlet-provider.tsx +1 -5
- package/src/prerender/param-hash.ts +10 -11
- package/src/prerender/store.ts +23 -30
- package/src/prerender.ts +58 -3
- package/src/root-error-boundary.tsx +1 -19
- package/src/route-content-wrapper.tsx +1 -44
- package/src/route-definition/dsl-helpers.ts +7 -19
- package/src/route-definition/helpers-types.ts +3 -3
- package/src/route-definition/redirect.ts +11 -1
- package/src/route-map-builder.ts +0 -16
- package/src/router/basename.ts +14 -0
- package/src/router/content-negotiation.ts +0 -13
- package/src/router/error-handling.ts +12 -16
- package/src/router/find-match.ts +4 -30
- package/src/router/intercept-resolution.ts +10 -1
- package/src/router/lazy-includes.ts +1 -57
- package/src/router/loader-resolution.ts +3 -2
- package/src/router/logging.ts +0 -6
- package/src/router/manifest.ts +1 -25
- package/src/router/match-api.ts +0 -20
- package/src/router/match-context.ts +0 -22
- package/src/router/match-handlers.ts +57 -58
- package/src/router/match-middleware/background-revalidation.ts +0 -7
- package/src/router/match-middleware/cache-lookup.ts +1 -54
- package/src/router/match-middleware/cache-store.ts +0 -31
- package/src/router/match-middleware/intercept-resolution.ts +0 -22
- package/src/router/match-middleware/segment-resolution.ts +0 -21
- package/src/router/match-pipelines.ts +1 -42
- package/src/router/match-result.ts +1 -52
- package/src/router/metrics.ts +0 -34
- package/src/router/middleware-cookies.ts +0 -13
- package/src/router/middleware-types.ts +0 -115
- package/src/router/middleware.ts +7 -30
- package/src/router/navigation-snapshot.ts +0 -51
- package/src/router/params-util.ts +23 -0
- package/src/router/pattern-matching.ts +1 -33
- package/src/router/prerender-match.ts +33 -45
- package/src/router/request-classification.ts +1 -38
- package/src/router/revalidation.ts +5 -58
- package/src/router/router-context.ts +0 -26
- package/src/router/router-interfaces.ts +7 -0
- package/src/router/router-options.ts +30 -0
- package/src/router/segment-resolution/fresh.ts +25 -57
- package/src/router/segment-resolution/helpers.ts +34 -0
- package/src/router/segment-resolution/loader-cache.ts +10 -13
- package/src/router/segment-resolution/revalidation.ts +5 -42
- package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
- package/src/router/segment-resolution.ts +4 -1
- package/src/router/state-cookie-name.ts +33 -0
- package/src/router/telemetry-otel.ts +0 -20
- package/src/router/telemetry.ts +96 -19
- package/src/router/timeout.ts +0 -20
- package/src/router/trie-matching.ts +63 -40
- package/src/router/types.ts +1 -63
- package/src/router/url-params.ts +0 -5
- package/src/router.ts +40 -9
- package/src/rsc/handler.ts +14 -2
- package/src/rsc/helpers.ts +34 -0
- package/src/rsc/origin-guard.ts +0 -12
- package/src/rsc/progressive-enhancement.ts +4 -1
- package/src/rsc/rsc-rendering.ts +4 -7
- package/src/rsc/runtime-warnings.ts +14 -0
- package/src/rsc/server-action.ts +30 -28
- package/src/rsc/types.ts +2 -1
- package/src/runtime-env.ts +18 -0
- package/src/search-params.ts +0 -16
- package/src/segment-loader-promise.ts +14 -2
- package/src/segment-system.tsx +79 -88
- package/src/server/cookie-store.ts +52 -1
- package/src/server/handle-store.ts +7 -24
- package/src/server/loader-registry.ts +5 -24
- package/src/server/request-context.ts +74 -77
- package/src/ssr/index.tsx +14 -14
- package/src/static-handler.ts +10 -13
- package/src/testing/cache-status.ts +119 -0
- package/src/testing/collect-handle.ts +40 -0
- package/src/testing/dispatch.ts +581 -0
- package/src/testing/dom.entry.ts +22 -0
- package/src/testing/e2e/fixture.ts +188 -0
- package/src/testing/e2e/index.ts +127 -0
- package/src/testing/e2e/matchers.ts +35 -0
- package/src/testing/e2e/page-helpers.ts +272 -0
- package/src/testing/e2e/parity.ts +387 -0
- package/src/testing/e2e/server.ts +195 -0
- package/src/testing/flight-matchers.ts +97 -0
- package/src/testing/flight-normalize.ts +11 -0
- package/src/testing/flight-runtime.d.ts +57 -0
- package/src/testing/flight-tree.ts +682 -0
- package/src/testing/flight.entry.ts +52 -0
- package/src/testing/flight.ts +186 -0
- package/src/testing/generated-routes.ts +183 -0
- package/src/testing/index.ts +98 -0
- package/src/testing/internal/context.ts +348 -0
- package/src/testing/internal/flight-client-globals.ts +30 -0
- package/src/testing/internal/seed-vars.ts +54 -0
- package/src/testing/render-handler.ts +311 -0
- package/src/testing/render-route.tsx +504 -0
- package/src/testing/run-loader.ts +378 -0
- package/src/testing/run-middleware.ts +205 -0
- package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
- package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
- package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
- package/src/testing/vitest-stubs/version.ts +5 -0
- package/src/testing/vitest.ts +305 -0
- package/src/theme/ThemeProvider.tsx +0 -52
- package/src/theme/ThemeScript.tsx +0 -6
- package/src/theme/constants.ts +0 -12
- package/src/theme/index.ts +0 -7
- package/src/theme/theme-context.ts +1 -5
- package/src/theme/theme-script.ts +0 -14
- package/src/theme/use-theme.ts +0 -3
- package/src/types/boundaries.ts +0 -35
- package/src/types/error-types.ts +25 -89
- package/src/types/global-namespace.ts +15 -15
- package/src/types/handler-context.ts +16 -13
- package/src/types/index.ts +0 -10
- package/src/types/request-scope.ts +0 -19
- package/src/types/route-config.ts +6 -50
- package/src/types/route-entry.ts +0 -6
- package/src/types/segments.ts +0 -13
- package/src/urls/include-helper.ts +0 -4
- package/src/urls/index.ts +0 -6
- package/src/urls/path-helper-types.ts +2 -2
- package/src/urls/path-helper.ts +0 -54
- package/src/urls/urls-function.ts +0 -13
- package/src/use-loader.tsx +0 -186
- package/src/vite/discovery/bundle-postprocess.ts +2 -1
- package/src/vite/discovery/discover-routers.ts +6 -7
- package/src/vite/discovery/virtual-module-codegen.ts +1 -11
- package/src/vite/plugin-types.ts +3 -1
- package/src/vite/plugins/cjs-to-esm.ts +0 -11
- package/src/vite/plugins/client-ref-dedup.ts +0 -11
- package/src/vite/plugins/client-ref-hashing.ts +0 -10
- package/src/vite/plugins/cloudflare-protocol-stub.ts +0 -20
- package/src/vite/plugins/expose-action-id.ts +2 -73
- package/src/vite/plugins/expose-id-utils.ts +0 -55
- package/src/vite/plugins/expose-ids/export-analysis.ts +0 -38
- package/src/vite/plugins/expose-ids/handler-transform.ts +0 -15
- package/src/vite/plugins/expose-ids/loader-transform.ts +0 -15
- package/src/vite/plugins/expose-ids/router-transform.ts +0 -13
- package/src/vite/plugins/expose-internal-ids.ts +10 -0
- package/src/vite/plugins/performance-tracks.ts +0 -3
- package/src/vite/plugins/use-cache-transform.ts +0 -36
- package/src/vite/plugins/version-injector.ts +0 -20
- package/src/vite/plugins/version-plugin.ts +1 -49
- package/src/vite/plugins/virtual-entries.ts +0 -15
- package/src/vite/rango.ts +1 -108
- package/src/vite/router-discovery.ts +2 -1
- package/src/vite/utils/ast-handler-extract.ts +0 -16
- package/src/vite/utils/bundle-analysis.ts +6 -13
- package/src/vite/utils/client-chunks.ts +0 -6
- package/src/vite/utils/forward-user-plugins.ts +0 -22
- package/src/vite/utils/manifest-utils.ts +0 -4
- package/src/vite/utils/package-resolution.ts +1 -73
- package/src/vite/utils/prerender-utils.ts +0 -35
- package/src/vite/utils/shared-utils.ts +3 -35
- package/src/browser/react/use-client-cache.ts +0 -58
- package/src/browser/shallow.ts +0 -40
|
@@ -9,12 +9,6 @@ import {
|
|
|
9
9
|
import { codeMatchIndices } from "../../../build/route-types/source-scan.js";
|
|
10
10
|
import type { CreateExportBinding } from "./types.js";
|
|
11
11
|
|
|
12
|
-
/**
|
|
13
|
-
* Check whether every non-type export in `code` is accounted for by the given
|
|
14
|
-
* bindings. Returns false if any export exists that is not one of the known
|
|
15
|
-
* create* call locals/exports, allowing callers to bail out for mixed-export
|
|
16
|
-
* files.
|
|
17
|
-
*/
|
|
18
12
|
export function isExportOnlyFile(
|
|
19
13
|
code: string,
|
|
20
14
|
bindings: CreateExportBinding[],
|
|
@@ -28,10 +22,8 @@ export function isExportOnlyFile(
|
|
|
28
22
|
for (const e of b.exportNames) knownExports.add(e);
|
|
29
23
|
}
|
|
30
24
|
|
|
31
|
-
// Bail on star re-exports (unknown exports)
|
|
32
25
|
if (/export\s*\*/.test(code)) return false;
|
|
33
26
|
|
|
34
|
-
// Check `export const/let/var/function/class/default X` declarations
|
|
35
27
|
const declExportPattern =
|
|
36
28
|
/export\s+(const|let|var|function|class|default)\s+(\w+)/g;
|
|
37
29
|
let match: RegExpExecArray | null;
|
|
@@ -39,8 +31,6 @@ export function isExportOnlyFile(
|
|
|
39
31
|
if (!knownExports.has(match[2])) return false;
|
|
40
32
|
}
|
|
41
33
|
|
|
42
|
-
// Check `export { X }` and `export { X as Y }` specifiers: the local name
|
|
43
|
-
// must reference a known create* binding.
|
|
44
34
|
const specExportPattern = /export\s*\{([^}]+)\}/g;
|
|
45
35
|
while ((match = specExportPattern.exec(code)) !== null) {
|
|
46
36
|
const specifiers = match[1]
|
|
@@ -67,9 +57,6 @@ function createCallPattern(fnNames: string[]): RegExp {
|
|
|
67
57
|
);
|
|
68
58
|
}
|
|
69
59
|
|
|
70
|
-
// Counts real create*() call sites, ignoring occurrences inside comments and
|
|
71
|
-
// string literals. Used by the unsupported-shape warning heuristic and the
|
|
72
|
-
// inline-extraction pre-check.
|
|
73
60
|
export function countCreateCallsForNames(
|
|
74
61
|
code: string,
|
|
75
62
|
fnNames: string[],
|
|
@@ -77,7 +64,6 @@ export function countCreateCallsForNames(
|
|
|
77
64
|
return codeMatchIndices(code, createCallPattern(fnNames)).length;
|
|
78
65
|
}
|
|
79
66
|
|
|
80
|
-
/** Convert a 0-based byte offset to a 1-based { line, column }. */
|
|
81
67
|
export function offsetToLineColumn(
|
|
82
68
|
code: string,
|
|
83
69
|
index: number,
|
|
@@ -94,14 +80,6 @@ export function offsetToLineColumn(
|
|
|
94
80
|
return { line, column: index - lineStart + 1 };
|
|
95
81
|
}
|
|
96
82
|
|
|
97
|
-
/**
|
|
98
|
-
* Locate every real create*() call site (comment/string-free) that is NOT one
|
|
99
|
-
* of the supported, id-injectable export bindings, returning each as a 1-based
|
|
100
|
-
* { line, column }. The empty result means every call is in a supported shape.
|
|
101
|
-
* Both binding-collection paths anchor `callExprStart` at the start of the
|
|
102
|
-
* create* identifier — exactly where this pattern matches — so the set
|
|
103
|
-
* difference is exact.
|
|
104
|
-
*/
|
|
105
83
|
export function findUnsupportedCreateCallSites(
|
|
106
84
|
code: string,
|
|
107
85
|
fnNames: string[],
|
|
@@ -158,16 +136,6 @@ export function getCalledIdentifierFromCall(callExpr: any): string | null {
|
|
|
158
136
|
return null;
|
|
159
137
|
}
|
|
160
138
|
|
|
161
|
-
/**
|
|
162
|
-
* plugin-react's dev Fast Refresh wraps exports whose function body uses
|
|
163
|
-
* hook-like calls in a signature-registration call. A loader/handle that calls
|
|
164
|
-
* `ctx.use(...)` trips this heuristic, so `export const X = createLoader(...)`
|
|
165
|
-
* becomes `export const X = _s(createLoader(...), "<sig>", true)` — the create*
|
|
166
|
-
* call is the first argument of an unrelated wrapper call. Unwrap a single such
|
|
167
|
-
* layer so ID injection still targets the inner create* call. The `$$id`
|
|
168
|
-
* assignment is appended after the whole statement (against the export local),
|
|
169
|
-
* which is unaffected by the wrapper since `_s(x)` returns `x`.
|
|
170
|
-
*/
|
|
171
139
|
function unwrapSignatureWrappedCall(init: any, fnNameSet: Set<string>): any {
|
|
172
140
|
if (init?.type !== "CallExpression") return init;
|
|
173
141
|
const directId = getCalledIdentifierFromCall(init);
|
|
@@ -331,10 +299,6 @@ export function collectCreateExportBindings(
|
|
|
331
299
|
}
|
|
332
300
|
}
|
|
333
301
|
|
|
334
|
-
// When the JS parser misidentifies TypeScript generics (e.g.,
|
|
335
|
-
// createLocationState<{ text: string }>({...})) as binary expressions,
|
|
336
|
-
// the AST path finds 0 bindings even though calls exist. Fall back to
|
|
337
|
-
// regex-based detection which handles generics via <[^>]*> matching.
|
|
338
302
|
if (bindings.length === 0) {
|
|
339
303
|
return collectCreateExportBindingsFallback(code, fnNames);
|
|
340
304
|
}
|
|
@@ -349,8 +313,6 @@ export function buildUnsupportedShapeWarning(
|
|
|
349
313
|
): string {
|
|
350
314
|
const lines = [`[rango] Unsupported ${fnName} shape in "${filePath}".`];
|
|
351
315
|
|
|
352
|
-
// Point at the exact call(s) so the location is clickable in the terminal/IDE
|
|
353
|
-
// (file:line:column) instead of leaving the user to scan the whole file.
|
|
354
316
|
if (sites.length === 1) {
|
|
355
317
|
const s = sites[0];
|
|
356
318
|
lines.push(
|
|
@@ -69,10 +69,6 @@ export function transformLocationState(
|
|
|
69
69
|
return hasChanges;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
/**
|
|
73
|
-
* Replace the entire file with lightweight stubs when ALL non-type exports are
|
|
74
|
-
* handler calls of the given type. Returns null for files with mixed exports.
|
|
75
|
-
*/
|
|
76
72
|
export function generateWholeFileStubs(
|
|
77
73
|
cfg: HandlerTransformConfig,
|
|
78
74
|
bindings: CreateExportBinding[],
|
|
@@ -91,10 +87,6 @@ export function generateWholeFileStubs(
|
|
|
91
87
|
return { code: stubs.join("\n") + "\n", map: null };
|
|
92
88
|
}
|
|
93
89
|
|
|
94
|
-
/**
|
|
95
|
-
* Replace handler call expressions with lightweight stub objects on the shared
|
|
96
|
-
* unified-pipeline MagicString so all transforms share one sourcemap.
|
|
97
|
-
*/
|
|
98
90
|
export function stubHandlerExprs(
|
|
99
91
|
cfg: HandlerTransformConfig,
|
|
100
92
|
bindings: CreateExportBinding[],
|
|
@@ -117,9 +109,6 @@ export function stubHandlerExprs(
|
|
|
117
109
|
return hasChanges;
|
|
118
110
|
}
|
|
119
111
|
|
|
120
|
-
/**
|
|
121
|
-
* Inject $$id into export const handler calls in RSC environments.
|
|
122
|
-
*/
|
|
123
112
|
export function transformHandlerIds(
|
|
124
113
|
cfg: HandlerTransformConfig,
|
|
125
114
|
bindings: CreateExportBinding[],
|
|
@@ -133,10 +122,6 @@ export function transformHandlerIds(
|
|
|
133
122
|
|
|
134
123
|
const handlerId = makeStubId(filePath, exportName, isBuild);
|
|
135
124
|
|
|
136
|
-
// Injection strategy matches the runtime overload signatures:
|
|
137
|
-
// 0 args -> inject undefined, "id"
|
|
138
|
-
// 1 arg (handler) -> inject , undefined, "id"
|
|
139
|
-
// 2+ args -> inject , "id"
|
|
140
125
|
let paramInjection: string;
|
|
141
126
|
if (binding.argCount === 0) {
|
|
142
127
|
paramInjection = `undefined, "${handlerId}"`;
|
|
@@ -9,18 +9,6 @@ export function hasCreateLoaderImport(code: string): boolean {
|
|
|
9
9
|
);
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
/**
|
|
13
|
-
* Generate lightweight client stubs for loader files.
|
|
14
|
-
*
|
|
15
|
-
* When a loader file is imported from a client component (e.g., for useLoader()),
|
|
16
|
-
* the client only needs { __brand: "loader", $$id: "..." } objects.
|
|
17
|
-
* This function replaces the entire file contents with just those stub exports,
|
|
18
|
-
* preventing server-only data (constants, DB queries, etc.) from leaking into
|
|
19
|
-
* the client bundle.
|
|
20
|
-
*
|
|
21
|
-
* Only applies when ALL named exports are createLoader() calls (plus type exports
|
|
22
|
-
* which are erased at compile time). Files with mixed exports are left untouched.
|
|
23
|
-
*/
|
|
24
12
|
export function generateClientLoaderStubs(
|
|
25
13
|
bindings: CreateExportBinding[],
|
|
26
14
|
code: string,
|
|
@@ -56,9 +44,6 @@ export function transformLoaders(
|
|
|
56
44
|
|
|
57
45
|
const loaderId = makeStubId(filePath, exportName, isBuild);
|
|
58
46
|
|
|
59
|
-
// Inject $$id as hidden third parameter.
|
|
60
|
-
// createLoader(fn) -> createLoader(fn, undefined, "id")
|
|
61
|
-
// createLoader(fn, true) -> createLoader(fn, true, "id")
|
|
62
47
|
const paramInjection =
|
|
63
48
|
binding.argCount === 1 ? `, undefined, "${loaderId}"` : `, "${loaderId}"`;
|
|
64
49
|
s.appendLeft(binding.callCloseParenPos, paramInjection);
|
|
@@ -32,15 +32,11 @@ export function transformRouter(
|
|
|
32
32
|
const callStart = match.index;
|
|
33
33
|
const parenPos = match.index + match[0].length - 1;
|
|
34
34
|
|
|
35
|
-
// Scope the $$id check to within this call's arguments only,
|
|
36
|
-
// not the entire remaining file.
|
|
37
35
|
const closeParen = findMatchingParen(code, parenPos + 1);
|
|
38
36
|
const callArgs = code.slice(parenPos + 1, closeParen);
|
|
39
37
|
|
|
40
|
-
// Skip if $$id is already present in this call
|
|
41
38
|
if (callArgs.includes("$$id")) continue;
|
|
42
39
|
|
|
43
|
-
// Compute line number for this call
|
|
44
40
|
const lineNumber = code.slice(0, callStart).split("\n").length;
|
|
45
41
|
const hash = createHash("sha256")
|
|
46
42
|
.update(`${filePath}:${lineNumber}`)
|
|
@@ -48,9 +44,6 @@ export function transformRouter(
|
|
|
48
44
|
.slice(0, 8);
|
|
49
45
|
|
|
50
46
|
changed = true;
|
|
51
|
-
// $$sourceFile uses the absolute path so that downstream consumers
|
|
52
|
-
// (virtual-module-codegen, runtime-discovery) can resolve gen file
|
|
53
|
-
// imports correctly via path.dirname / path.join.
|
|
54
47
|
const sourceFilePath = absolutePath ?? filePath;
|
|
55
48
|
const injected = ` $$id: "${hash}", $$sourceFile: "${sourceFilePath}", $$routeNames: ${routeNamesVar},`;
|
|
56
49
|
|
|
@@ -65,8 +58,6 @@ export function transformRouter(
|
|
|
65
58
|
|
|
66
59
|
if (!changed) return null;
|
|
67
60
|
|
|
68
|
-
// Prepend the static import as the first line. MagicString tracks the
|
|
69
|
-
// offset so all downstream source maps remain correct.
|
|
70
61
|
s.prepend(
|
|
71
62
|
`import { NamedRoutes as ${routeNamesVar} } from "${routeNamesImport}";\n`,
|
|
72
63
|
);
|
|
@@ -96,10 +87,6 @@ export function exposeRouterId(): Plugin {
|
|
|
96
87
|
},
|
|
97
88
|
transform(code, id) {
|
|
98
89
|
if (!code.includes("createRouter")) return null;
|
|
99
|
-
// Accepts both @rangojs/router and @rangojs/router/server subpath.
|
|
100
|
-
// NOTE: detectImports in expose-id-utils has a stricter check that
|
|
101
|
-
// excludes /server for its router flag -- that's intentional since
|
|
102
|
-
// detectImports is only used in exposeInternalIds, not here.
|
|
103
90
|
if (
|
|
104
91
|
!/import\s*\{[^}]*\bcreateRouter\b[^}]*\}\s*from\s*["']@rangojs\/router(?:\/server)?["']/.test(
|
|
105
92
|
code,
|
|
@@ -184,6 +184,16 @@ ${lazyImports.join(",\n")}
|
|
|
184
184
|
async buildStart() {
|
|
185
185
|
if (!isBuild) return;
|
|
186
186
|
|
|
187
|
+
// The loader pre-scan walks and reads the entire project, but the
|
|
188
|
+
// loaderRegistry it populates is consumed only by the RSC loader-manifest
|
|
189
|
+
// virtual module (and the transform hook already gates its registry writes
|
|
190
|
+
// with isRscEnv). plugin-rsc runs ~5 build passes (rsc-scan, ssr-scan, rsc,
|
|
191
|
+
// client, ssr) over this single shared plugin instance; without this gate
|
|
192
|
+
// the full-tree I/O ran on every pass with no consumer on the non-RSC
|
|
193
|
+
// ones. Skip only when the environment is known and not RSC, so an
|
|
194
|
+
// unavailable environment still falls through (no empty registry).
|
|
195
|
+
if (this.environment && this.environment.name !== "rsc") return;
|
|
196
|
+
|
|
187
197
|
const fs = await import("node:fs/promises");
|
|
188
198
|
|
|
189
199
|
const SKIP_DIRS = new Set(["node_modules", "dist", "build", "coverage"]);
|
|
@@ -54,9 +54,6 @@ export function performanceTracksOptimizeDepsPlugin(): {
|
|
|
54
54
|
/react-server-dom-webpack-client\.browser\.(development|production)\.js$/;
|
|
55
55
|
return {
|
|
56
56
|
name: "@rangojs/router:performance-tracks-optimize-deps",
|
|
57
|
-
// Vite 8 optimizes deps with Rolldown (Rollup-style plugin pipeline), so the
|
|
58
|
-
// pre-bundled RSDW client is patched via load() rather than esbuild's onLoad.
|
|
59
|
-
// Returning code overrides Rolldown's default filesystem read for the module.
|
|
60
57
|
async load(id: string): Promise<{ code: string } | null> {
|
|
61
58
|
const cleanId = id.split("?")[0] ?? id;
|
|
62
59
|
if (!RSDW_CLIENT_RE.test(cleanId)) return null;
|
|
@@ -30,11 +30,6 @@ const CACHE_RUNTIME_IMPORT = "@rangojs/router/cache-runtime";
|
|
|
30
30
|
// and should not be wrapped (children can't be cache-keyed).
|
|
31
31
|
const LAYOUT_TEMPLATE_PATTERN = /\/(layout|template)\.(tsx?|jsx?)$/;
|
|
32
32
|
|
|
33
|
-
/**
|
|
34
|
-
* Grammar for a valid function-level directive: `use cache` optionally followed
|
|
35
|
-
* by `: <profile-name>`. The single source of truth for both the transform and
|
|
36
|
-
* the near-miss validator below.
|
|
37
|
-
*/
|
|
38
33
|
export const USE_CACHE_DIRECTIVE_RE: RegExp = /^use cache(:\s*[\w-]+)?$/;
|
|
39
34
|
|
|
40
35
|
export function useCacheTransform(): Plugin {
|
|
@@ -72,7 +67,6 @@ export function useCacheTransform(): Plugin {
|
|
|
72
67
|
|
|
73
68
|
const start = counter ? performance.now() : 0;
|
|
74
69
|
try {
|
|
75
|
-
// Lazy-load transform helpers
|
|
76
70
|
if (!rscTransforms) {
|
|
77
71
|
try {
|
|
78
72
|
rscTransforms = await import("@vitejs/plugin-rsc/transforms");
|
|
@@ -87,7 +81,6 @@ export function useCacheTransform(): Plugin {
|
|
|
87
81
|
transformHoistInlineDirective,
|
|
88
82
|
} = rscTransforms;
|
|
89
83
|
|
|
90
|
-
// Parse AST
|
|
91
84
|
let ast: any;
|
|
92
85
|
try {
|
|
93
86
|
const { parseAst } = await import("vite");
|
|
@@ -99,7 +92,6 @@ export function useCacheTransform(): Plugin {
|
|
|
99
92
|
const filePath = normalizePath(path.relative(projectRoot, id));
|
|
100
93
|
const isLayoutOrTemplate = LAYOUT_TEMPLATE_PATTERN.test(id);
|
|
101
94
|
|
|
102
|
-
// Check for file-level "use cache"
|
|
103
95
|
if (hasDirective(ast.body, "use cache")) {
|
|
104
96
|
return transformFileLevelUseCache(
|
|
105
97
|
code,
|
|
@@ -112,8 +104,6 @@ export function useCacheTransform(): Plugin {
|
|
|
112
104
|
);
|
|
113
105
|
}
|
|
114
106
|
|
|
115
|
-
// Check for function-level "use cache" / "use cache: profileName"
|
|
116
|
-
// (only if there's no file-level directive but code still contains the string)
|
|
117
107
|
const functionResult = transformFunctionLevelUseCache(
|
|
118
108
|
code,
|
|
119
109
|
ast,
|
|
@@ -123,9 +113,6 @@ export function useCacheTransform(): Plugin {
|
|
|
123
113
|
transformHoistInlineDirective,
|
|
124
114
|
);
|
|
125
115
|
|
|
126
|
-
// Check for near-miss directives on the function-level path. The
|
|
127
|
-
// file-level branch above returns earlier (it wraps every export
|
|
128
|
-
// regardless), so this runs only when there is no file-level directive.
|
|
129
116
|
warnOnNearMissDirectives(ast, id, this.warn.bind(this));
|
|
130
117
|
|
|
131
118
|
if (functionResult) return functionResult;
|
|
@@ -145,7 +132,6 @@ function transformFileLevelUseCache(
|
|
|
145
132
|
isLayoutOrTemplate: boolean,
|
|
146
133
|
transformWrapExport: (typeof import("@vitejs/plugin-rsc/transforms"))["transformWrapExport"],
|
|
147
134
|
) {
|
|
148
|
-
// Collect non-function exports to report after wrapping
|
|
149
135
|
const nonFunctionExports: string[] = [];
|
|
150
136
|
|
|
151
137
|
const { exportNames, output } = transformWrapExport(code, ast, {
|
|
@@ -155,9 +141,7 @@ function transformFileLevelUseCache(
|
|
|
155
141
|
},
|
|
156
142
|
rejectNonAsyncFunction: false,
|
|
157
143
|
filter: (name: string, meta: { isFunction?: boolean }) => {
|
|
158
|
-
// Skip default export of layout/template files (they receive children)
|
|
159
144
|
if (name === "default" && isLayoutOrTemplate) return false;
|
|
160
|
-
// Non-function exports cannot be wrapped with registerCachedFunction
|
|
161
145
|
if (meta.isFunction === false) {
|
|
162
146
|
nonFunctionExports.push(name);
|
|
163
147
|
return false;
|
|
@@ -178,7 +162,6 @@ function transformFileLevelUseCache(
|
|
|
178
162
|
}
|
|
179
163
|
|
|
180
164
|
if (exportNames.length === 0) {
|
|
181
|
-
// Even if no exports were wrapped, strip the directive
|
|
182
165
|
const s = new MagicString(code);
|
|
183
166
|
const directive = findFileLevelDirective(ast);
|
|
184
167
|
if (directive) {
|
|
@@ -195,12 +178,10 @@ function transformFileLevelUseCache(
|
|
|
195
178
|
return;
|
|
196
179
|
}
|
|
197
180
|
|
|
198
|
-
// Prepend the import
|
|
199
181
|
output.prepend(
|
|
200
182
|
`import { registerCachedFunction as __rango_registerCachedFunction } from ${JSON.stringify(CACHE_RUNTIME_IMPORT)};\n`,
|
|
201
183
|
);
|
|
202
184
|
|
|
203
|
-
// Replace the directive with a comment
|
|
204
185
|
const directive = findFileLevelDirective(ast);
|
|
205
186
|
if (directive) {
|
|
206
187
|
output.overwrite(
|
|
@@ -244,9 +225,6 @@ function transformFunctionLevelUseCache(
|
|
|
244
225
|
|
|
245
226
|
if (names.length === 0) return;
|
|
246
227
|
|
|
247
|
-
// Use a top-level import instead of await import() — the hoisted wrapper
|
|
248
|
-
// may be placed in a non-async context (e.g., inside a synchronous
|
|
249
|
-
// urls() callback) where await is not allowed.
|
|
250
228
|
output.prepend(
|
|
251
229
|
`import { registerCachedFunction as __rango_registerCachedFunction } from ${JSON.stringify(CACHE_RUNTIME_IMPORT)};\n`,
|
|
252
230
|
);
|
|
@@ -261,9 +239,6 @@ function transformFunctionLevelUseCache(
|
|
|
261
239
|
}
|
|
262
240
|
}
|
|
263
241
|
|
|
264
|
-
/**
|
|
265
|
-
* Find the file-level "use cache" directive AST node for removal.
|
|
266
|
-
*/
|
|
267
242
|
function findFileLevelDirective(
|
|
268
243
|
ast: any,
|
|
269
244
|
): { start: number; end: number } | null {
|
|
@@ -280,18 +255,8 @@ function findFileLevelDirective(
|
|
|
280
255
|
return null;
|
|
281
256
|
}
|
|
282
257
|
|
|
283
|
-
/**
|
|
284
|
-
* Regex for near-miss: starts with "use cache:" but has invalid tokens.
|
|
285
|
-
*/
|
|
286
258
|
const NEAR_MISS_RE = /^use cache:\s*.+$/;
|
|
287
259
|
|
|
288
|
-
/**
|
|
289
|
-
* Walk the AST looking for string literals that look like malformed
|
|
290
|
-
* "use cache" directives and emit a Vite warning for each.
|
|
291
|
-
*
|
|
292
|
-
* This catches cases like `"use cache: bad.name"` or `"use cache: "`
|
|
293
|
-
* that the transform regex silently ignores.
|
|
294
|
-
*/
|
|
295
260
|
function warnOnNearMissDirectives(
|
|
296
261
|
ast: any,
|
|
297
262
|
fileId: string,
|
|
@@ -319,7 +284,6 @@ function warnOnNearMissDirectives(
|
|
|
319
284
|
}
|
|
320
285
|
}
|
|
321
286
|
|
|
322
|
-
// Walk into function bodies where directives appear
|
|
323
287
|
for (const key of Object.keys(node)) {
|
|
324
288
|
const child = node[key];
|
|
325
289
|
if (Array.isArray(child)) {
|
|
@@ -29,7 +29,6 @@ export function createVersionInjectorPlugin(
|
|
|
29
29
|
|
|
30
30
|
transform(code, id) {
|
|
31
31
|
if (!resolvedEntryPath) return null;
|
|
32
|
-
// Only transform the RSC entry file
|
|
33
32
|
const normalizedId = Vite.normalizePath(id);
|
|
34
33
|
const normalizedEntry = Vite.normalizePath(resolvedEntryPath);
|
|
35
34
|
|
|
@@ -37,25 +36,10 @@ export function createVersionInjectorPlugin(
|
|
|
37
36
|
return null;
|
|
38
37
|
}
|
|
39
38
|
|
|
40
|
-
// Always prepend `import "virtual:rsc-router/routes-manifest"` as the
|
|
41
|
-
// first side-effect import. The manifest virtual module's `load()` hook
|
|
42
|
-
// awaits `s.discoveryDone` so that, by the time the rest of the entry
|
|
43
|
-
// including any module-level `router.reverse()` calls under `./router.js`
|
|
44
|
-
// evaluates, runtime discovery has rewritten `router.named-routes.gen.ts`
|
|
45
|
-
// with the full route table.
|
|
46
|
-
//
|
|
47
|
-
// ES module evaluation order matters here: while imports are *parsed*
|
|
48
|
-
// hoisted, side-effect imports are evaluated in source order in the
|
|
49
|
-
// dependency graph. A user-authored `import "virtual:rsc-router/..."`
|
|
50
|
-
// placed after `import "./router.js"` runs too late: the manifest
|
|
51
|
-
// gate fires after router.tsx has already crashed on a stale gen file.
|
|
52
|
-
// We always prepend; ESM dedups any user-written duplicate, so module
|
|
53
|
-
// initialization still runs once.
|
|
54
39
|
const prepend: string[] = [
|
|
55
40
|
`import "virtual:rsc-router/routes-manifest";`,
|
|
56
41
|
];
|
|
57
42
|
|
|
58
|
-
// Auto-inject VERSION if file uses createRSCHandler without version
|
|
59
43
|
let newCode = code;
|
|
60
44
|
const needsVersion =
|
|
61
45
|
code.includes("createRSCHandler") &&
|
|
@@ -70,10 +54,6 @@ export function createVersionInjectorPlugin(
|
|
|
70
54
|
);
|
|
71
55
|
}
|
|
72
56
|
|
|
73
|
-
// Insert after any leading `/// <reference ... />` triple-slash
|
|
74
|
-
// directives (and surrounding blank lines). TypeScript requires those
|
|
75
|
-
// directives to precede all other code; putting our imports above
|
|
76
|
-
// them silently demotes the directives to plain comments.
|
|
77
57
|
const lines = newCode.split("\n");
|
|
78
58
|
let insertAt = 0;
|
|
79
59
|
while (insertAt < lines.length) {
|
|
@@ -138,8 +138,6 @@ export function createVersionPlugin(): Plugin {
|
|
|
138
138
|
|
|
139
139
|
let versionCounter = 0;
|
|
140
140
|
const bumpVersion = (reason: string) => {
|
|
141
|
-
// Use timestamp + counter to guarantee uniqueness even when multiple
|
|
142
|
-
// bumps happen within the same millisecond (e.g. cascading HMR events).
|
|
143
141
|
currentVersion = Date.now().toString(16) + String(++versionCounter);
|
|
144
142
|
console.log(`[rango] ${reason}, version updated: ${currentVersion}`);
|
|
145
143
|
|
|
@@ -158,9 +156,6 @@ export function createVersionPlugin(): Plugin {
|
|
|
158
156
|
|
|
159
157
|
configResolved(config) {
|
|
160
158
|
isDev = config.command === "serve";
|
|
161
|
-
// Capture the resolved cacheDir so we can ignore optimizer-output
|
|
162
|
-
// writes inside it. Vite resolves cacheDir against the project root,
|
|
163
|
-
// so this is a stable absolute path for the lifetime of the server.
|
|
164
159
|
resolvedCacheDir = config.cacheDir
|
|
165
160
|
? String(config.cacheDir).replace(/\\/g, "/")
|
|
166
161
|
: undefined;
|
|
@@ -210,27 +205,15 @@ export function createVersionPlugin(): Plugin {
|
|
|
210
205
|
return null;
|
|
211
206
|
},
|
|
212
207
|
|
|
213
|
-
// Track RSC module changes and update version
|
|
214
208
|
async hotUpdate(ctx) {
|
|
215
209
|
if (!isDev) return;
|
|
216
210
|
|
|
217
|
-
// Check if this is an RSC environment update (not client/ssr)
|
|
218
|
-
// RSC modules affect server-rendered content and cached payloads
|
|
219
|
-
// In Vite 6, environment is accessed via `this.environment`
|
|
220
211
|
const isRscModule = this.environment?.name === "rsc";
|
|
221
212
|
|
|
222
213
|
if (!isRscModule) return;
|
|
223
214
|
|
|
224
|
-
// Skip Vite's own pre-bundled dep cache writes. The optimizer rewrites
|
|
225
|
-
// files inside the configured `cacheDir` on every discovery cycle
|
|
226
|
-
// (and when other dev servers under the same cwd populate their own
|
|
227
|
-
// isolated cache dirs). These are not user-source changes, so bumping
|
|
228
|
-
// the app version on them produces spurious version mismatches that
|
|
229
|
-
// surface as forced reloads on in-flight actions.
|
|
230
215
|
if (isViteDepCachePath(ctx.file, resolvedCacheDir)) return;
|
|
231
216
|
|
|
232
|
-
// Skip re-bumping when the version virtual module itself is invalidated
|
|
233
|
-
// (our own bumpVersion() invalidates it, which re-triggers hotUpdate).
|
|
234
217
|
if (
|
|
235
218
|
ctx.modules.length === 1 &&
|
|
236
219
|
ctx.modules[0].id === "\0" + VIRTUAL_IDS.version
|
|
@@ -245,9 +228,6 @@ export function createVersionPlugin(): Plugin {
|
|
|
245
228
|
const source = await ctx.read();
|
|
246
229
|
const nextSignature = getClientModuleSignature(source);
|
|
247
230
|
if (nextSignature) {
|
|
248
|
-
// "use client" file — compare export signatures.
|
|
249
|
-
// client-component-hmr may have cleared ctx.modules, so we
|
|
250
|
-
// cannot rely on ctx.modules.length for these files.
|
|
251
231
|
clientModuleSignatures.set(filePath, nextSignature);
|
|
252
232
|
if (
|
|
253
233
|
previousSignature &&
|
|
@@ -258,20 +238,11 @@ export function createVersionPlugin(): Plugin {
|
|
|
258
238
|
} else {
|
|
259
239
|
clientModuleSignatures.delete(filePath);
|
|
260
240
|
if (!previousSignature) {
|
|
261
|
-
// Not and never was "use client" — use module graph check.
|
|
262
|
-
// ctx.modules is reliable for pure server files (only
|
|
263
|
-
// client-component-hmr clears it for "use client" modules).
|
|
264
241
|
if (ctx.modules.length === 0) return;
|
|
265
242
|
}
|
|
266
|
-
// Was "use client" but directive removed — boundary changed,
|
|
267
|
-
// bump below.
|
|
268
243
|
}
|
|
269
|
-
} catch {
|
|
270
|
-
// Fail open: if we can't read or parse the update, invalidate.
|
|
271
|
-
}
|
|
244
|
+
} catch {}
|
|
272
245
|
} else {
|
|
273
|
-
// Non-code file (json, css, etc.) — only bump if it's actually
|
|
274
|
-
// referenced by the RSC module graph.
|
|
275
246
|
if (ctx.modules.length === 0) return;
|
|
276
247
|
}
|
|
277
248
|
|
|
@@ -280,20 +251,6 @@ export function createVersionPlugin(): Plugin {
|
|
|
280
251
|
};
|
|
281
252
|
}
|
|
282
253
|
|
|
283
|
-
/**
|
|
284
|
-
* Match Vite's pre-bundled dep cache directories. These paths are rewritten
|
|
285
|
-
* by the dep optimizer (and by isolated test fixtures sharing the same cwd),
|
|
286
|
-
* not by user source changes, so they should not bump the app version (which
|
|
287
|
-
* would force a client reload mid-request).
|
|
288
|
-
*
|
|
289
|
-
* Two checks:
|
|
290
|
-
* 1. Anything inside the resolved `cacheDir` (precise — covers custom paths
|
|
291
|
-
* like the `RANGO_E2E_VITE_CACHE_DIR` overrides in the test fixtures).
|
|
292
|
-
* 2. Heuristic match for any `node_modules/.vite*` directory or a
|
|
293
|
-
* `.vite-isolated/` segment anywhere in the path. This catches the
|
|
294
|
-
* *other* dev servers in the same cwd whose cacheDir we cannot read
|
|
295
|
-
* (we only see config of the server we're attached to).
|
|
296
|
-
*/
|
|
297
254
|
export function isViteDepCachePath(
|
|
298
255
|
filePath: string | undefined,
|
|
299
256
|
cacheDir?: string,
|
|
@@ -311,11 +268,6 @@ export function isViteDepCachePath(
|
|
|
311
268
|
}
|
|
312
269
|
}
|
|
313
270
|
|
|
314
|
-
// Vite/optimizer convention: cache dirs always sit directly under
|
|
315
|
-
// `node_modules/` and start with `.vite` (e.g. `.vite`, `.vite-temp`,
|
|
316
|
-
// `.vite_rango_generate`, `.vite-e2e-test-app`). The `/.vite-isolated/`
|
|
317
|
-
// segment covers the test-fixture pattern that places the cache outside
|
|
318
|
-
// node_modules.
|
|
319
271
|
return (
|
|
320
272
|
/\/node_modules\/\.vite[^/]*\//.test(normalized) ||
|
|
321
273
|
normalized.includes("/.vite-isolated/")
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Default virtual entry file contents for rsc-router.
|
|
3
|
-
* These are used when users don't provide their own entry files.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
1
|
export const VIRTUAL_ENTRY_BROWSER: string = `
|
|
7
2
|
import {
|
|
8
3
|
createFromReadableStream,
|
|
@@ -51,9 +46,6 @@ export const renderHTML = createSSRHandler({
|
|
|
51
46
|
});
|
|
52
47
|
`.trim();
|
|
53
48
|
|
|
54
|
-
/**
|
|
55
|
-
* Generate the RSC entry content with the specified router path
|
|
56
|
-
*/
|
|
57
49
|
export function getVirtualEntryRSC(routerPath: string): string {
|
|
58
50
|
return `
|
|
59
51
|
import {
|
|
@@ -104,9 +96,6 @@ export default function handler(request, env) {
|
|
|
104
96
|
`.trim();
|
|
105
97
|
}
|
|
106
98
|
|
|
107
|
-
/**
|
|
108
|
-
* Virtual module IDs
|
|
109
|
-
*/
|
|
110
99
|
export const VIRTUAL_IDS = {
|
|
111
100
|
browser: "virtual:rsc-router/entry.browser.js",
|
|
112
101
|
ssr: "virtual:rsc-router/entry.ssr.js",
|
|
@@ -114,10 +103,6 @@ export const VIRTUAL_IDS = {
|
|
|
114
103
|
version: "@rangojs/router:version",
|
|
115
104
|
} as const;
|
|
116
105
|
|
|
117
|
-
/**
|
|
118
|
-
* Virtual module content for version.
|
|
119
|
-
* Exports VERSION - a timestamp that changes on server restart (dev) or at build time (production).
|
|
120
|
-
*/
|
|
121
106
|
export function getVirtualVersionContent(version: string): string {
|
|
122
107
|
return `export const VERSION = ${JSON.stringify(version)};`;
|
|
123
108
|
}
|