vinext 1.2.0 → 1.3.0
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/README.md +13 -10
- package/dist/build/client-build-config.d.ts +64 -3
- package/dist/build/client-build-config.js +131 -4
- package/dist/build/client-reference-groups.d.ts +163 -0
- package/dist/build/client-reference-groups.js +534 -0
- package/dist/build/module-dependency-cache.d.ts +31 -0
- package/dist/build/module-dependency-cache.js +51 -1
- package/dist/build/next-client-runtime-manifests.d.ts +10 -1
- package/dist/build/next-client-runtime-manifests.js +19 -4
- package/dist/build/nitro-public-files.d.ts +24 -0
- package/dist/build/nitro-public-files.js +60 -0
- package/dist/build/output-root-externals.d.ts +21 -0
- package/dist/build/output-root-externals.js +40 -0
- package/dist/build/pages-client-assets-module.js +1 -1
- package/dist/build/prerender-paths.js +6 -3
- package/dist/build/prerender.js +1 -1
- package/dist/build/server-actions-encryption-key.d.ts +23 -0
- package/dist/build/server-actions-encryption-key.js +101 -0
- package/dist/build/ssr-manifest.d.ts +3 -0
- package/dist/build/ssr-manifest.js +10 -1
- package/dist/check.js +5 -6
- package/dist/cli-dev-config.js +3 -3
- package/dist/cli.js +1 -1
- package/dist/client/client-rewrite-params.d.ts +9 -0
- package/dist/client/client-rewrite-params.js +20 -0
- package/dist/client/dev-error-overlay.js +2 -1
- package/dist/client/navigation-runtime.d.ts +25 -2
- package/dist/client/navigation-runtime.js +42 -11
- package/dist/client/vinext-next-data.d.ts +0 -1
- package/dist/config/config-matchers.d.ts +15 -40
- package/dist/config/config-matchers.js +195 -262
- package/dist/config/config-source.d.ts +34 -0
- package/dist/config/config-source.js +141 -0
- package/dist/config/header-params.d.ts +29 -0
- package/dist/config/header-params.js +106 -0
- package/dist/config/next-config.d.ts +7 -5
- package/dist/config/next-config.js +120 -7
- package/dist/entries/app-browser-entry.d.ts +14 -1
- package/dist/entries/app-browser-entry.js +26 -36
- package/dist/entries/app-rsc-entry.d.ts +2 -0
- package/dist/entries/app-rsc-entry.js +92 -57
- package/dist/entries/app-rsc-manifest.d.ts +10 -0
- package/dist/entries/app-rsc-manifest.js +20 -1
- package/dist/entries/app-ssr-entry.d.ts +4 -1
- package/dist/entries/app-ssr-entry.js +9 -2
- package/dist/entries/pages-client-entry.d.ts +1 -0
- package/dist/entries/pages-client-entry.js +6 -6
- package/dist/entries/pages-server-entry.js +17 -14
- package/dist/index.d.ts +11 -0
- package/dist/index.js +350 -175
- package/dist/init.d.ts +1 -1
- package/dist/init.js +103 -6
- package/dist/plugins/ast-utils.d.ts +53 -2
- package/dist/plugins/ast-utils.js +90 -2
- package/dist/plugins/cloudflare-dev-module-urls.d.ts +41 -0
- package/dist/plugins/cloudflare-dev-module-urls.js +271 -0
- package/dist/plugins/commonjs-esm-facade.js +2 -2
- package/dist/plugins/css-module-imports.js +13 -20
- package/dist/plugins/dynamic-preload-metadata.js +2 -3
- package/dist/plugins/extensionless-dynamic-import.js +2 -3
- package/dist/plugins/fonts.d.ts +1 -1
- package/dist/plugins/fonts.js +3 -3
- package/dist/plugins/ignore-dynamic-requests.js +2 -3
- package/dist/plugins/import-meta-url.js +3 -4
- package/dist/plugins/instrumentation-client.js +3 -2
- package/dist/plugins/middleware-export-validation.js +2 -3
- package/dist/plugins/og-harfbuzz.js +2 -3
- package/dist/plugins/optimize-imports.d.ts +25 -2
- package/dist/plugins/optimize-imports.js +109 -24
- package/dist/plugins/pages-node-externals.js +2 -3
- package/dist/plugins/remove-console.js +2 -18
- package/dist/plugins/require-condition-resolution.js +3 -3
- package/dist/plugins/require-context.js +2 -3
- package/dist/plugins/rsc-client-reference-loaders.d.ts +45 -1
- package/dist/plugins/rsc-client-reference-loaders.js +336 -18
- package/dist/plugins/strip-server-exports.js +3 -4
- package/dist/plugins/styled-jsx.js +2 -3
- package/dist/plugins/use-cache-callable.d.ts +26 -2
- package/dist/plugins/use-cache-callable.js +101 -36
- package/dist/plugins/worker-image-imports.d.ts +1 -0
- package/dist/plugins/worker-image-imports.js +31 -34
- package/dist/plugins/worker-use-server.js +38 -14
- package/dist/routing/app-route-graph.d.ts +1 -1
- package/dist/routing/browser-route-manifest.d.ts +43 -0
- package/dist/routing/browser-route-manifest.js +195 -0
- package/dist/routing/hashed-route-patterns.d.ts +4 -0
- package/dist/routing/hashed-route-patterns.js +22 -0
- package/dist/routing/optimistic-route-trie.d.ts +27 -0
- package/dist/routing/optimistic-route-trie.js +102 -0
- package/dist/routing/route-validation.d.ts +6 -1
- package/dist/routing/route-validation.js +26 -10
- package/dist/server/api-handler.js +1 -1
- package/dist/server/app-action-forwarding.d.ts +8 -0
- package/dist/server/app-action-forwarding.js +17 -3
- package/dist/server/app-browser-entry.js +149 -62
- package/dist/server/app-browser-error.d.ts +3 -1
- package/dist/server/app-browser-error.js +7 -2
- package/dist/server/app-browser-interception-context.d.ts +2 -2
- package/dist/server/app-browser-interception-context.js +1 -1
- package/dist/server/app-browser-mpa-navigation.d.ts +1 -0
- package/dist/server/app-browser-mpa-navigation.js +6 -5
- package/dist/server/app-browser-navigation-controller.d.ts +17 -6
- package/dist/server/app-browser-navigation-controller.js +88 -26
- package/dist/server/app-browser-prefetch-response.js +2 -2
- package/dist/server/app-browser-state.d.ts +2 -2
- package/dist/server/app-browser-state.js +1 -1
- package/dist/server/app-browser-supplemental-refresh.js +1 -1
- package/dist/server/app-browser-visible-commit.d.ts +4 -4
- package/dist/server/app-client-reference-preloader.d.ts +23 -0
- package/dist/server/app-client-reference-preloader.js +30 -7
- package/dist/server/app-fallback-renderer.js +2 -1
- package/dist/server/app-middleware.d.ts +6 -0
- package/dist/server/app-middleware.js +8 -10
- package/dist/server/app-optimistic-routing.d.ts +8 -10
- package/dist/server/app-optimistic-routing.js +4 -95
- package/dist/server/app-page-boundary-render.js +8 -1
- package/dist/server/app-page-cache-render.js +1 -0
- package/dist/server/app-page-dispatch.js +3 -1
- package/dist/server/app-page-render.js +4 -2
- package/dist/server/app-page-rsc-render-status.d.ts +1 -0
- package/dist/server/app-page-rsc-render-status.js +1 -0
- package/dist/server/app-page-search-params-observation.d.ts +1 -4
- package/dist/server/app-page-search-params-observation.js +8 -10
- package/dist/server/app-page-stream.d.ts +7 -0
- package/dist/server/app-page-stream.js +1 -0
- package/dist/server/app-pages-bridge.d.ts +2 -1
- package/dist/server/app-pages-bridge.js +12 -8
- package/dist/server/app-prerender-endpoints.js +1 -1
- package/dist/server/app-prerender-static-params.js +3 -2
- package/dist/server/app-render-abort-error.d.ts +19 -0
- package/dist/server/app-render-abort-error.js +31 -1
- package/dist/server/app-render-dependency.js +42 -2
- package/dist/server/app-request-stage-independent-entry.js +7 -7
- package/dist/server/app-response-stage-entry.js +5 -5
- package/dist/server/app-route-handler-dispatch.js +3 -1
- package/dist/server/app-route-handler-execution.d.ts +2 -0
- package/dist/server/app-route-handler-execution.js +9 -2
- package/dist/server/app-route-handler-middleware-context.d.ts +2 -1
- package/dist/server/app-route-handler-middleware-context.js +3 -3
- package/dist/server/app-route-handler-response.d.ts +2 -1
- package/dist/server/app-route-handler-response.js +2 -2
- package/dist/server/app-route-handler-runtime.d.ts +2 -0
- package/dist/server/app-route-handler-runtime.js +21 -4
- package/dist/server/app-route-module-loader.js +7 -6
- package/dist/server/app-route-tree.d.ts +20 -0
- package/dist/server/app-route-tree.js +18 -0
- package/dist/server/app-router-entry.js +7 -7
- package/dist/server/app-rsc-cache-busting.d.ts +16 -0
- package/dist/server/app-rsc-cache-busting.js +29 -12
- package/dist/server/app-rsc-combined-handler.js +8 -1
- package/dist/server/app-rsc-errors.d.ts +7 -0
- package/dist/server/app-rsc-errors.js +9 -2
- package/dist/server/app-rsc-handler.d.ts +4 -1
- package/dist/server/app-rsc-handler.js +151 -135
- package/dist/server/app-rsc-request-normalization.js +3 -3
- package/dist/server/app-rsc-response-finalizer.d.ts +5 -0
- package/dist/server/app-rsc-response-finalizer.js +4 -1
- package/dist/server/app-rsc-response-stage.js +5 -3
- package/dist/server/app-rsc-route-matching.d.ts +1 -0
- package/dist/server/app-rsc-route-matching.js +1 -1
- package/dist/server/app-server-action-execution.d.ts +23 -6
- package/dist/server/app-server-action-execution.js +51 -57
- package/dist/server/app-ssr-entry.d.ts +6 -0
- package/dist/server/app-ssr-entry.js +21 -11
- package/dist/server/app-ssr-stream.d.ts +7 -0
- package/dist/server/app-ssr-stream.js +13 -1
- package/dist/server/app-visited-response-cache.js +2 -2
- package/dist/server/config-headers.js +13 -4
- package/dist/server/defer-until-stream-consumed.d.ts +2 -1
- package/dist/server/defer-until-stream-consumed.js +3 -1
- package/dist/server/dev-server.js +1 -1
- package/dist/server/image-optimization.js +1 -0
- package/dist/server/isolate-shared-promise.d.ts +10 -0
- package/dist/server/isolate-shared-promise.js +33 -0
- package/dist/server/isr-cache.js +3 -2
- package/dist/server/metadata-route-response.js +2 -2
- package/dist/server/middleware-matcher-pattern.d.ts +0 -1
- package/dist/server/middleware-matcher-pattern.js +11 -140
- package/dist/server/middleware-matcher.js +5 -6
- package/dist/server/middleware-response-headers.d.ts +31 -0
- package/dist/server/middleware-response-headers.js +57 -1
- package/dist/server/middleware-runtime.js +30 -17
- package/dist/server/navigation-planner.d.ts +2 -2
- package/dist/server/navigation-planner.js +1 -1
- package/dist/server/nitro-public-files-plugin.d.ts +38 -0
- package/dist/server/nitro-public-files-plugin.js +106 -0
- package/dist/server/nitro-public-files.d.ts +21 -0
- package/dist/server/nitro-public-files.js +39 -0
- package/dist/server/open-redirect.d.ts +11 -4
- package/dist/server/open-redirect.js +11 -4
- package/dist/server/pages-api-route.js +2 -2
- package/dist/server/pages-asset-tags.d.ts +5 -0
- package/dist/server/pages-asset-tags.js +9 -2
- package/dist/server/pages-client-assets.d.ts +4 -1
- package/dist/server/pages-data-route.js +2 -1
- package/dist/server/pages-i18n.d.ts +18 -0
- package/dist/server/pages-i18n.js +18 -2
- package/dist/server/pages-node-compat.d.ts +5 -3
- package/dist/server/pages-node-compat.js +35 -16
- package/dist/server/pages-page-data.js +1 -1
- package/dist/server/pages-page-handler.js +1 -1
- package/dist/server/pages-page-response.js +1 -1
- package/dist/server/pages-request-pipeline.d.ts +6 -1
- package/dist/server/pages-request-pipeline.js +35 -39
- package/dist/server/pages-request-stage-entry.js +20 -10
- package/dist/server/pages-response-stage-entry.js +1 -1
- package/dist/server/pages-revalidate.js +3 -2
- package/dist/server/pages-router-entry.js +2 -2
- package/dist/server/prod-server.d.ts +2 -2
- package/dist/server/prod-server.js +74 -36
- package/dist/server/proxy-trust.d.ts +6 -0
- package/dist/server/proxy-trust.js +12 -6
- package/dist/server/request-pipeline.d.ts +5 -0
- package/dist/server/request-pipeline.js +12 -1
- package/dist/server/rsc-stream-hints.d.ts +1 -1
- package/dist/server/rsc-stream-hints.js +21 -3
- package/dist/server/server-reference-loader.js +10 -1
- package/dist/server/static-file-cache.js +15 -3
- package/dist/server/worker-image-optimization.js +19 -6
- package/dist/server/worker-utils.js +1 -1
- package/dist/shims/cache-handler.js +3 -2
- package/dist/shims/cache-request-state.d.ts +40 -3
- package/dist/shims/cache-request-state.js +70 -5
- package/dist/shims/cache-runtime.js +47 -19
- package/dist/shims/cache.d.ts +2 -2
- package/dist/shims/cache.js +17 -8
- package/dist/shims/cdn-purge-routes.d.ts +24 -0
- package/dist/shims/cdn-purge-routes.js +77 -0
- package/dist/shims/dynamic.js +17 -4
- package/dist/shims/form.js +8 -3
- package/dist/shims/head.js +1 -1
- package/dist/shims/headers.js +2 -2
- package/dist/shims/image-props.d.ts +93 -0
- package/dist/shims/image-props.js +323 -0
- package/dist/shims/image.d.ts +4 -18
- package/dist/shims/image.js +4 -319
- package/dist/shims/internal/headers-state.d.ts +7 -0
- package/dist/shims/internal/headers-state.js +14 -2
- package/dist/shims/internal/hybrid-client-route-owner-direct.d.ts +4 -1
- package/dist/shims/internal/hybrid-client-route-owner-direct.js +32 -15
- package/dist/shims/internal/hybrid-client-route-owner.js +9 -8
- package/dist/shims/link-prefetch.js +5 -4
- package/dist/shims/link.js +7 -5
- package/dist/shims/navigation.d.ts +1 -1
- package/dist/shims/navigation.js +21 -10
- package/dist/shims/og.js +35 -10
- package/dist/shims/request-context.d.ts +13 -0
- package/dist/shims/router.js +28 -52
- package/dist/shims/server.d.ts +42 -8
- package/dist/shims/server.js +99 -15
- package/dist/shims/thenable-params.d.ts +6 -1
- package/dist/shims/thenable-params.js +11 -2
- package/dist/shims/unified-request-context.d.ts +5 -0
- package/dist/shims/unified-request-context.js +2 -0
- package/dist/shims/url-utils.d.ts +7 -0
- package/dist/shims/url-utils.js +12 -3
- package/dist/utils/asset-prefix.d.ts +7 -0
- package/dist/utils/asset-prefix.js +10 -1
- package/dist/utils/client-build-manifest.js +12 -5
- package/dist/utils/client-runtime-metadata.d.ts +1 -0
- package/dist/utils/client-runtime-metadata.js +15 -2
- package/dist/utils/domain-locale.js +1 -1
- package/dist/utils/lazy-chunks.d.ts +4 -0
- package/dist/utils/manifest-paths.d.ts +14 -0
- package/dist/utils/manifest-paths.js +22 -1
- package/dist/utils/path-to-regexp.d.ts +21 -0
- package/dist/{server/middleware-path-to-regexp.js → utils/path-to-regexp.js} +14 -11
- package/dist/utils/plugin-react-version.d.ts +15 -0
- package/dist/utils/plugin-react-version.js +64 -0
- package/dist/utils/plugin-rsc-version.d.ts +2 -0
- package/dist/utils/plugin-rsc-version.js +28 -2
- package/dist/utils/public-routes.d.ts +2 -0
- package/dist/utils/public-routes.js +18 -7
- package/dist/utils/react-version.d.ts +7 -0
- package/dist/utils/react-version.js +39 -16
- package/package.json +6 -6
- package/dist/build/nitro-route-rules.d.ts +0 -47
- package/dist/build/nitro-route-rules.js +0 -79
- package/dist/plugins/cache-flight-codec.d.ts +0 -19
- package/dist/plugins/cache-flight-codec.js +0 -68
- package/dist/server/middleware-path-to-regexp.d.ts +0 -13
- package/dist/utils/regex-safety.d.ts +0 -17
- package/dist/utils/regex-safety.js +0 -789
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import path, { toSlash } from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
|
|
2
|
-
import {
|
|
2
|
+
import { parseModuleRecord, quotedStringAt } from "./ast-utils.js";
|
|
3
3
|
import { magicStringTransformResult } from "./transform-result.js";
|
|
4
4
|
import { appendDeploymentIdQuery } from "../utils/deployment-id.js";
|
|
5
5
|
import { NODE_MODULES_PATH_RE, stripViteModuleQuery } from "../utils/path.js";
|
|
6
6
|
import fs from "node:fs";
|
|
7
|
-
import { parseAst } from "vite";
|
|
8
7
|
import MagicString from "magic-string";
|
|
9
8
|
//#region src/plugins/worker-image-imports.ts
|
|
10
9
|
const WORKER_IMAGE_METADATA_PREFIX = "\0vinext-worker-image-meta:";
|
|
@@ -70,24 +69,19 @@ function createWorkerImageImportsPlugin(options = {}) {
|
|
|
70
69
|
},
|
|
71
70
|
async handler(code, id) {
|
|
72
71
|
const sourceId = toSlash(stripViteModuleQuery(id));
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
ast = parseAst(code, { lang: /\.(?:[cm]?ts)$/.test(sourceId) ? "ts" : "tsx" });
|
|
76
|
-
} catch {
|
|
77
|
-
return null;
|
|
78
|
-
}
|
|
72
|
+
const record = parseModuleRecord(code, /\.(?:[cm]?ts)$/.test(sourceId) ? "ts" : "tsx");
|
|
73
|
+
if (!record) return null;
|
|
79
74
|
const output = new MagicString(code);
|
|
80
75
|
const imageImports = [];
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
});
|
|
76
|
+
for (const dynamicImport of record.dynamicImports) {
|
|
77
|
+
const importPath = quotedStringAt(code, dynamicImport.moduleRequest);
|
|
78
|
+
if (importPath === null || !WORKER_IMAGE_EXTENSION_RE.test(importPath)) continue;
|
|
79
|
+
imageImports.push({
|
|
80
|
+
end: dynamicImport.end,
|
|
81
|
+
importPath,
|
|
82
|
+
start: dynamicImport.start
|
|
83
|
+
});
|
|
84
|
+
}
|
|
91
85
|
let changed = false;
|
|
92
86
|
for (const imageImport of imageImports) {
|
|
93
87
|
const resolved = await this.resolve(imageImport.importPath, sourceId, { skipSelf: true });
|
|
@@ -107,23 +101,26 @@ function createWorkerImageImportsPlugin(options = {}) {
|
|
|
107
101
|
if (!options.deploymentId) return null;
|
|
108
102
|
const chunkSpecifiers = new Set([...chunk.imports, ...chunk.dynamicImports].map((target) => workerChunkSpecifier(chunk.fileName, target)));
|
|
109
103
|
if (chunkSpecifiers.size === 0) return null;
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
104
|
+
const record = parseModuleRecord(code, "js");
|
|
105
|
+
if (!record) return null;
|
|
106
|
+
const sources = [
|
|
107
|
+
...record.staticImports.map((statement) => statement.moduleRequest),
|
|
108
|
+
...record.staticExports.flatMap((statement) => statement.entries[0]?.moduleRequest ?? []),
|
|
109
|
+
...record.dynamicImports.map((dynamicImport) => {
|
|
110
|
+
const value = quotedStringAt(code, dynamicImport.moduleRequest);
|
|
111
|
+
return value === null ? [] : {
|
|
112
|
+
...dynamicImport.moduleRequest,
|
|
113
|
+
value
|
|
114
|
+
};
|
|
115
|
+
})
|
|
116
|
+
].flat();
|
|
116
117
|
const output = new MagicString(code);
|
|
117
118
|
let changed = false;
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
changed = true;
|
|
124
|
-
return false;
|
|
125
|
-
}
|
|
126
|
-
});
|
|
119
|
+
for (const source of sources) {
|
|
120
|
+
if (!chunkSpecifiers.has(source.value)) continue;
|
|
121
|
+
output.overwrite(source.start, source.end, JSON.stringify(appendDeploymentIdQuery(source.value, options.deploymentId)));
|
|
122
|
+
changed = true;
|
|
123
|
+
}
|
|
127
124
|
if (!changed) return null;
|
|
128
125
|
return magicStringTransformResult(output);
|
|
129
126
|
}
|
|
@@ -131,4 +128,4 @@ function createWorkerImageImportsPlugin(options = {}) {
|
|
|
131
128
|
};
|
|
132
129
|
}
|
|
133
130
|
//#endregion
|
|
134
|
-
export { createWorkerImageImportsPlugin };
|
|
131
|
+
export { WORKER_IMAGE_METADATA_PREFIX, createWorkerImageImportsPlugin };
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import path, { toSlash } from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
|
|
2
2
|
import { escapeRegExp } from "../utils/regex.js";
|
|
3
|
+
import { parseProgramAsync } from "./ast-utils.js";
|
|
3
4
|
import { magicStringTransformResult } from "./transform-result.js";
|
|
5
|
+
import { USE_CACHE_PLUGIN_NAME as PLUGIN_NAME } from "./use-cache-callable.js";
|
|
4
6
|
import { createRequire } from "node:module";
|
|
5
7
|
import fs from "node:fs";
|
|
6
8
|
import { pathToFileURL } from "node:url";
|
|
7
|
-
import {
|
|
9
|
+
import { isCSSRequest, transformWithOxc } from "vite";
|
|
8
10
|
//#region src/plugins/worker-use-server.ts
|
|
9
11
|
const WORKER_REFERENCE_RUNTIME_ID = "\0vinext:worker-server-reference";
|
|
10
12
|
const WORKER_REFERENCE_RUNTIME_ID_RE = /^\0vinext:worker-server-reference$/;
|
|
@@ -60,12 +62,14 @@ async function createWorkerUseServerPlugins(options) {
|
|
|
60
62
|
const browserRuntime = pathToFileURL(rscRequire.resolve("@vitejs/plugin-rsc/react/browser")).href;
|
|
61
63
|
const [transforms, rscCore] = await Promise.all([importRscModule("@vitejs/plugin-rsc/transforms"), importRscModule("@vitejs/plugin-rsc/core/plugin")]);
|
|
62
64
|
let manager;
|
|
65
|
+
let useCacheApi;
|
|
63
66
|
const useServerPlugin = {
|
|
64
67
|
name: "vinext:worker-use-server",
|
|
65
68
|
async configResolved(config) {
|
|
66
69
|
// @vitejs/plugin-rsc's plugin API (and its reference manager) lives.
|
|
67
70
|
const mainConfig = config.mainConfig;
|
|
68
71
|
manager = (await options.rscPluginModule).getPluginApi(mainConfig ?? config)?.manager;
|
|
72
|
+
useCacheApi = (mainConfig ?? config).plugins.find((plugin) => plugin.name === PLUGIN_NAME)?.api;
|
|
69
73
|
},
|
|
70
74
|
resolveId: {
|
|
71
75
|
filter: { id: WORKER_REFERENCE_RUNTIME_ID_RE },
|
|
@@ -78,7 +82,7 @@ async function createWorkerUseServerPlugins(options) {
|
|
|
78
82
|
transform: {
|
|
79
83
|
filter: { code: "use server" },
|
|
80
84
|
async handler(code, id) {
|
|
81
|
-
let ast = await
|
|
85
|
+
let ast = await parseProgramAsync(code);
|
|
82
86
|
if (!transforms.hasDirective(ast.body, "use server")) return null;
|
|
83
87
|
if (!manager) throw new Error("vinext: cannot compile a \"use server\" module in a Web Worker without @vitejs/plugin-rsc.");
|
|
84
88
|
const expanded = await withPositionedError(this, () => transforms.transformExpandExportAll({
|
|
@@ -90,12 +94,12 @@ async function createWorkerUseServerPlugins(options) {
|
|
|
90
94
|
if (!isPlainFilePath(target)) throw new Error(`vinext: a "use server" module imported by a Web Worker cannot \`export *\` from ${JSON.stringify(target)}; re-export its names explicitly.`);
|
|
91
95
|
this.addWatchFile(target);
|
|
92
96
|
const source = await fs.promises.readFile(target, "utf-8");
|
|
93
|
-
return
|
|
97
|
+
return parseProgramAsync((await transformWithOxc(source, target, { sourcemap: false })).code);
|
|
94
98
|
}
|
|
95
99
|
}));
|
|
96
100
|
if (expanded) {
|
|
97
101
|
code = expanded.code;
|
|
98
|
-
ast = await
|
|
102
|
+
ast = await parseProgramAsync(code);
|
|
99
103
|
}
|
|
100
104
|
const { referenceKey } = manager.serverReferences.resolve(id, "rsc");
|
|
101
105
|
const runtime = uniqueBinding(code, "$$vinextWorkerReference");
|
|
@@ -111,19 +115,39 @@ async function createWorkerUseServerPlugins(options) {
|
|
|
111
115
|
}
|
|
112
116
|
}
|
|
113
117
|
};
|
|
118
|
+
const useCachePlugin = {
|
|
119
|
+
name: "vinext:worker-use-cache",
|
|
120
|
+
transform: {
|
|
121
|
+
filter: { code: "use cache" },
|
|
122
|
+
async handler(code, id) {
|
|
123
|
+
if (isCSSRequest(id)) return null;
|
|
124
|
+
const api = useCacheApi;
|
|
125
|
+
if (!api) throw new Error("vinext: cannot compile a \"use cache\" module in a Web Worker without vinext's \"use cache\" plugin.");
|
|
126
|
+
const runtime = uniqueBinding(code, "$$vinextWorkerReference");
|
|
127
|
+
const output = await withPositionedError(this, () => api.transformWorkerModule(code, id, (referenceId, name) => `${runtime}(${JSON.stringify(referenceId)}, ${JSON.stringify(name)})`));
|
|
128
|
+
if (!output) return null;
|
|
129
|
+
output.prepend(`import { createWorkerServerReference as ${runtime} } from ${JSON.stringify(WORKER_REFERENCE_RUNTIME_ID)};\n`);
|
|
130
|
+
return magicStringTransformResult(output);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
};
|
|
114
134
|
const patchHook = rscCore.default().find((plugin) => plugin.name === "rsc:patch-react-server-dom-webpack")?.transform;
|
|
115
135
|
if (!patchHook || typeof patchHook !== "object") throw new Error("vinext: @vitejs/plugin-rsc no longer exposes its Flight client patch plugin.");
|
|
116
136
|
const flightClientDir = path.dirname(rscRequire.resolve("@vitejs/plugin-rsc/vendor/react-server-dom/client.browser"));
|
|
117
|
-
return [
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
137
|
+
return [
|
|
138
|
+
{
|
|
139
|
+
name: "vinext:worker-patch-react-server-dom",
|
|
140
|
+
transform: {
|
|
141
|
+
filter: {
|
|
142
|
+
id: new RegExp(`^${escapeRegExp(`${toSlash(flightClientDir)}/`)}`),
|
|
143
|
+
code: "__webpack_require__"
|
|
144
|
+
},
|
|
145
|
+
handler: patchHook.handler
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
useCachePlugin,
|
|
149
|
+
useServerPlugin
|
|
150
|
+
];
|
|
127
151
|
}
|
|
128
152
|
//#endregion
|
|
129
153
|
export { createWorkerUseServerPlugins };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { GraphVersion, RouteManifest, RouteManifestInterception, RouteManifestLayout, RouteManifestRootBoundary, RouteManifestRoute, RouteManifestSlotBinding } from "./app-route-graph.js";
|
|
2
|
+
//#region src/routing/browser-route-manifest.d.ts
|
|
3
|
+
export type BrowserRouteManifestRoute = Pick<RouteManifestRoute, "id" | "patternParts" | "isDynamic" | "paramNames" | "rootBoundaryId" | "pageId" | "layoutIds" | "slotIds">;
|
|
4
|
+
type BrowserRouteManifestLayout = Pick<RouteManifestLayout, "id" | "patternParts" | "rootBoundaryId">;
|
|
5
|
+
type BrowserRouteManifestSlotBinding = Pick<RouteManifestSlotBinding, "id" | "routeId" | "slotId" | "ownerLayoutId" | "state" | "routeSegments" | "slotPatternParts" | "slotParamNames">;
|
|
6
|
+
export type BrowserRouteManifestInterception = Pick<RouteManifestInterception, "id" | "sourcePatternParts" | "targetPatternParts" | "slotId" | "ownerLayoutId" | "targetRouteId">;
|
|
7
|
+
type BrowserRouteManifestRootBoundary = Pick<RouteManifestRootBoundary, "id" | "treePath">;
|
|
8
|
+
/**
|
|
9
|
+
* The part of the RouteManifest the browser navigation runtime reads: the
|
|
10
|
+
* maps and fields the navigation planner, optimistic routing and interception
|
|
11
|
+
* context resolution use.
|
|
12
|
+
*/
|
|
13
|
+
export type BrowserRouteManifest = {
|
|
14
|
+
graphVersion: GraphVersion;
|
|
15
|
+
segmentGraph: {
|
|
16
|
+
routes: ReadonlyMap<string, BrowserRouteManifestRoute>;
|
|
17
|
+
layouts: ReadonlyMap<string, BrowserRouteManifestLayout>;
|
|
18
|
+
slotBindings: ReadonlyMap<string, BrowserRouteManifestSlotBinding>;
|
|
19
|
+
interceptions: ReadonlyMap<string, BrowserRouteManifestInterception>;
|
|
20
|
+
interceptionsBySlotId: ReadonlyMap<string, readonly BrowserRouteManifestInterception[]>;
|
|
21
|
+
rootBoundaries: ReadonlyMap<string, BrowserRouteManifestRootBoundary>;
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* JSON form of `BrowserRouteManifest`. Every map is keyed by its values' `id`,
|
|
26
|
+
* so maps travel as arrays of values in map order. A route omits `id` when it
|
|
27
|
+
* is `route:` plus its pattern, and `interceptionsBySlotId` lists interception
|
|
28
|
+
* ids instead of repeating the interceptions.
|
|
29
|
+
*/
|
|
30
|
+
export type BrowserRouteManifestWire = {
|
|
31
|
+
graphVersion: string;
|
|
32
|
+
routes: (Omit<BrowserRouteManifestRoute, "id"> & {
|
|
33
|
+
id?: string;
|
|
34
|
+
})[];
|
|
35
|
+
layouts: BrowserRouteManifestLayout[];
|
|
36
|
+
slotBindings: BrowserRouteManifestSlotBinding[];
|
|
37
|
+
interceptions: BrowserRouteManifestInterception[];
|
|
38
|
+
interceptionsBySlotId: [slotId: string, interceptionIds: string[]][];
|
|
39
|
+
rootBoundaries: BrowserRouteManifestRootBoundary[];
|
|
40
|
+
};
|
|
41
|
+
export declare function toBrowserRouteManifestWire(routeManifest: RouteManifest): BrowserRouteManifestWire;
|
|
42
|
+
export declare function decodeBrowserRouteManifest(wire: BrowserRouteManifestWire): BrowserRouteManifest;
|
|
43
|
+
//#endregion
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { buildOptimisticRouteTrie } from "./optimistic-route-trie.js";
|
|
2
|
+
//#region src/routing/browser-route-manifest.ts
|
|
3
|
+
function createRouteIdFromPatternParts(patternParts) {
|
|
4
|
+
return `route:/${patternParts.join("/")}`;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Route matching precedence of one pattern part, lower wins: the URL ending
|
|
8
|
+
* here, then a static, dynamic, catch-all and optional catch-all part.
|
|
9
|
+
*/
|
|
10
|
+
function patternPartRank(part) {
|
|
11
|
+
if (part === void 0) return 0;
|
|
12
|
+
if (!part.startsWith(":")) return 1;
|
|
13
|
+
if (part.endsWith("+")) return 3;
|
|
14
|
+
if (part.endsWith("*")) return 4;
|
|
15
|
+
return 2;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Whether the router sends a URL pathname that both patterns match to the
|
|
19
|
+
* route handler: it wins at the first part whose kind differs. Two patterns
|
|
20
|
+
* that never differ would be the same route shape, which cannot hold both a
|
|
21
|
+
* handler and a page.
|
|
22
|
+
*/
|
|
23
|
+
function routeHandlerOutranks(handler, rendered) {
|
|
24
|
+
for (let index = 0;; index++) {
|
|
25
|
+
const handlerRank = patternPartRank(handler[index]);
|
|
26
|
+
const renderedRank = patternPartRank(rendered[index]);
|
|
27
|
+
if (handlerRank !== renderedRank) return handlerRank < renderedRank;
|
|
28
|
+
if (handlerRank === 0 || handlerRank >= 3) return false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Whether the optimistic trie of the rendered routes predicts a rendered
|
|
33
|
+
* route that the route handler outranks for some URL pathname the handler
|
|
34
|
+
* pattern matches. The trie match descends one path and only backtracks to a
|
|
35
|
+
* catch-all on its way up, so this follows that path for every URL: a param
|
|
36
|
+
* segment either names one of the node's static children or none of them,
|
|
37
|
+
* and a catch-all takes any number of such segments.
|
|
38
|
+
*/
|
|
39
|
+
function triePredictsOutrankedRoute(trie, handler) {
|
|
40
|
+
const lastPart = handler.at(-1);
|
|
41
|
+
const isCatchAll = lastPart?.startsWith(":") === true && /[+*]$/.test(lastPart);
|
|
42
|
+
const fixedLength = isCatchAll ? handler.length - 1 : handler.length;
|
|
43
|
+
const minLength = lastPart?.endsWith("+") === true ? handler.length : fixedLength;
|
|
44
|
+
const predict = (route) => route === null ? {
|
|
45
|
+
outranked: false,
|
|
46
|
+
unmatched: true
|
|
47
|
+
} : {
|
|
48
|
+
outranked: routeHandlerOutranks(handler, route.patternParts),
|
|
49
|
+
unmatched: false
|
|
50
|
+
};
|
|
51
|
+
const afterOtherSegment = (node, depth) => {
|
|
52
|
+
const dynamic = node.dynamicChild ? predictFrom(node.dynamicChild.node, depth) : {
|
|
53
|
+
outranked: false,
|
|
54
|
+
unmatched: true
|
|
55
|
+
};
|
|
56
|
+
if (!dynamic.unmatched) return dynamic;
|
|
57
|
+
const fallback = predict(node.catchAllChild?.route ?? node.optionalCatchAllChild?.route ?? null);
|
|
58
|
+
return {
|
|
59
|
+
outranked: dynamic.outranked || fallback.outranked,
|
|
60
|
+
unmatched: fallback.unmatched
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
const predictFrom = (node, depth) => {
|
|
64
|
+
const predictions = {
|
|
65
|
+
outranked: false,
|
|
66
|
+
unmatched: false
|
|
67
|
+
};
|
|
68
|
+
const add = (more) => {
|
|
69
|
+
predictions.outranked ||= more.outranked;
|
|
70
|
+
predictions.unmatched ||= more.unmatched;
|
|
71
|
+
};
|
|
72
|
+
if (isCatchAll ? depth >= minLength : depth === fixedLength) add(predict(node.route ?? node.optionalCatchAllChild?.route ?? null));
|
|
73
|
+
if (depth < fixedLength && !handler[depth].startsWith(":")) {
|
|
74
|
+
const child = node.staticChildren.get(handler[depth]);
|
|
75
|
+
add(child ? predictFrom(child, depth + 1) : afterOtherSegment(node, depth + 1));
|
|
76
|
+
} else if (depth < fixedLength || isCatchAll) {
|
|
77
|
+
for (const child of node.staticChildren.values()) add(predictFrom(child, depth + 1));
|
|
78
|
+
add(afterOtherSegment(node, depth + 1));
|
|
79
|
+
}
|
|
80
|
+
return predictions;
|
|
81
|
+
};
|
|
82
|
+
return predictFrom(trie, 0).outranked;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* The browser only needs a route handler where the optimistic route trie of
|
|
86
|
+
* the rendered routes would predict a route the handler outranks: the matcher
|
|
87
|
+
* must find the handler there instead of rendering that route's shell. Any
|
|
88
|
+
* other handler URL matches no rendered route on the client, or one the
|
|
89
|
+
* router prefers, and its non-Flight response already turns the navigation
|
|
90
|
+
* into a document load, so shipping its path would only publish the app's
|
|
91
|
+
* API surface (issue #3624).
|
|
92
|
+
*/
|
|
93
|
+
function selectBrowserRoutes(routes) {
|
|
94
|
+
const allRoutes = [...routes];
|
|
95
|
+
const renderedTrie = buildOptimisticRouteTrie(allRoutes.filter((route) => route.routeHandlerId === null));
|
|
96
|
+
return allRoutes.filter((route) => route.routeHandlerId === null || triePredictsOutrankedRoute(renderedTrie, route.patternParts));
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* The graph records the browser receives: the shipped routes, the rendered
|
|
100
|
+
* routes' slot bindings, and the layouts and root boundaries those and the
|
|
101
|
+
* interceptions reach. A shipped route handler only stops an optimistic
|
|
102
|
+
* match, and its non-Flight response never renders a tree, so it ships as a
|
|
103
|
+
* bare pattern; its layouts, slots and root boundary would only name its
|
|
104
|
+
* directories.
|
|
105
|
+
*/
|
|
106
|
+
function selectBrowserGraph(graph) {
|
|
107
|
+
const routes = selectBrowserRoutes(graph.routes.values()).map((route) => route.routeHandlerId === null ? route : {
|
|
108
|
+
...route,
|
|
109
|
+
layoutIds: [],
|
|
110
|
+
rootBoundaryId: null,
|
|
111
|
+
slotIds: []
|
|
112
|
+
});
|
|
113
|
+
const renderedRouteIds = new Set(routes.filter((route) => route.routeHandlerId === null).map((route) => route.id));
|
|
114
|
+
const slotBindings = [...graph.slotBindings.values()].filter((binding) => renderedRouteIds.has(binding.routeId));
|
|
115
|
+
const layoutIds = /* @__PURE__ */ new Set([
|
|
116
|
+
...routes.flatMap((route) => route.layoutIds),
|
|
117
|
+
...slotBindings.flatMap((binding) => binding.ownerLayoutId ?? []),
|
|
118
|
+
...[...graph.interceptions.values()].flatMap((interception) => interception.ownerLayoutId ?? [])
|
|
119
|
+
]);
|
|
120
|
+
const layouts = [...graph.layouts.values()].filter((layout) => layoutIds.has(layout.id));
|
|
121
|
+
const rootBoundaryIds = new Set([...routes, ...layouts].flatMap((record) => record.rootBoundaryId ?? []));
|
|
122
|
+
return {
|
|
123
|
+
routes,
|
|
124
|
+
layouts,
|
|
125
|
+
slotBindings,
|
|
126
|
+
rootBoundaries: [...graph.rootBoundaries.values()].filter((rootBoundary) => rootBoundaryIds.has(rootBoundary.id))
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
function toBrowserRouteManifestWire(routeManifest) {
|
|
130
|
+
const graph = routeManifest.segmentGraph;
|
|
131
|
+
const browserGraph = selectBrowserGraph(graph);
|
|
132
|
+
return {
|
|
133
|
+
graphVersion: routeManifest.graphVersion,
|
|
134
|
+
routes: browserGraph.routes.map((route) => ({
|
|
135
|
+
...route.id === createRouteIdFromPatternParts(route.patternParts) ? {} : { id: route.id },
|
|
136
|
+
patternParts: route.patternParts,
|
|
137
|
+
isDynamic: route.isDynamic,
|
|
138
|
+
paramNames: route.paramNames,
|
|
139
|
+
rootBoundaryId: route.rootBoundaryId,
|
|
140
|
+
pageId: route.pageId,
|
|
141
|
+
layoutIds: route.layoutIds,
|
|
142
|
+
slotIds: route.slotIds
|
|
143
|
+
})),
|
|
144
|
+
layouts: browserGraph.layouts.map((layout) => ({
|
|
145
|
+
id: layout.id,
|
|
146
|
+
patternParts: layout.patternParts,
|
|
147
|
+
rootBoundaryId: layout.rootBoundaryId
|
|
148
|
+
})),
|
|
149
|
+
slotBindings: browserGraph.slotBindings.map((binding) => ({
|
|
150
|
+
id: binding.id,
|
|
151
|
+
routeId: binding.routeId,
|
|
152
|
+
slotId: binding.slotId,
|
|
153
|
+
ownerLayoutId: binding.ownerLayoutId,
|
|
154
|
+
state: binding.state,
|
|
155
|
+
routeSegments: binding.routeSegments,
|
|
156
|
+
slotPatternParts: binding.slotPatternParts,
|
|
157
|
+
slotParamNames: binding.slotParamNames
|
|
158
|
+
})),
|
|
159
|
+
interceptions: Array.from(graph.interceptions.values(), (interception) => ({
|
|
160
|
+
id: interception.id,
|
|
161
|
+
sourcePatternParts: interception.sourcePatternParts,
|
|
162
|
+
targetPatternParts: interception.targetPatternParts,
|
|
163
|
+
slotId: interception.slotId,
|
|
164
|
+
ownerLayoutId: interception.ownerLayoutId,
|
|
165
|
+
targetRouteId: interception.targetRouteId
|
|
166
|
+
})),
|
|
167
|
+
interceptionsBySlotId: Array.from(graph.interceptionsBySlotId, ([slotId, interceptions]) => [slotId, interceptions.map((interception) => interception.id)]),
|
|
168
|
+
rootBoundaries: browserGraph.rootBoundaries.map((rootBoundary) => ({
|
|
169
|
+
id: rootBoundary.id,
|
|
170
|
+
treePath: rootBoundary.treePath
|
|
171
|
+
}))
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
function mapById(values) {
|
|
175
|
+
return new Map(values.map((value) => [value.id, value]));
|
|
176
|
+
}
|
|
177
|
+
function decodeBrowserRouteManifest(wire) {
|
|
178
|
+
const interceptions = mapById(wire.interceptions);
|
|
179
|
+
return {
|
|
180
|
+
graphVersion: wire.graphVersion,
|
|
181
|
+
segmentGraph: {
|
|
182
|
+
routes: mapById(wire.routes.map((route) => ({
|
|
183
|
+
...route,
|
|
184
|
+
id: route.id ?? createRouteIdFromPatternParts(route.patternParts)
|
|
185
|
+
}))),
|
|
186
|
+
layouts: mapById(wire.layouts),
|
|
187
|
+
slotBindings: mapById(wire.slotBindings),
|
|
188
|
+
interceptions,
|
|
189
|
+
interceptionsBySlotId: new Map(wire.interceptionsBySlotId.map(([slotId, interceptionIds]) => [slotId, interceptionIds.flatMap((id) => interceptions.get(id) ?? [])])),
|
|
190
|
+
rootBoundaries: mapById(wire.rootBoundaries)
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
//#endregion
|
|
195
|
+
export { decodeBrowserRouteManifest, toBrowserRouteManifestWire };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { splitPathnameForRouteMatch } from "./utils.js";
|
|
2
|
+
import { fnv1a64 } from "../utils/hash.js";
|
|
3
|
+
//#region src/routing/hashed-route-patterns.ts
|
|
4
|
+
/**
|
|
5
|
+
* Hybrid App/Pages clients need App route handler patterns only to decide
|
|
6
|
+
* route ownership, so they receive them with every static segment hashed and
|
|
7
|
+
* never see the handlers' paths in plain text (issue #3624). Dynamic parts
|
|
8
|
+
* stay as they are, so a hashed pattern matches a hashed URL with the usual
|
|
9
|
+
* route trie. A collision can only add a false handler match, which sends the
|
|
10
|
+
* navigation to the document.
|
|
11
|
+
*/
|
|
12
|
+
function hashRouteSegment(segment) {
|
|
13
|
+
return fnv1a64(segment).slice(0, 8);
|
|
14
|
+
}
|
|
15
|
+
function hashRoutePatternParts(patternParts) {
|
|
16
|
+
return patternParts.map((part) => part.startsWith(":") ? part : hashRouteSegment(part));
|
|
17
|
+
}
|
|
18
|
+
function hashRoutePathname(pathname) {
|
|
19
|
+
return "/" + splitPathnameForRouteMatch(pathname).map(hashRouteSegment).join("/");
|
|
20
|
+
}
|
|
21
|
+
//#endregion
|
|
22
|
+
export { hashRoutePathname, hashRoutePatternParts };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
//#region src/routing/optimistic-route-trie.d.ts
|
|
2
|
+
type OptimisticTrieRoute = {
|
|
3
|
+
readonly patternParts: readonly string[];
|
|
4
|
+
};
|
|
5
|
+
export type OptimisticRouteTrieNode<Route extends OptimisticTrieRoute> = {
|
|
6
|
+
catchAllChild: {
|
|
7
|
+
paramName: string;
|
|
8
|
+
route: Route;
|
|
9
|
+
} | null;
|
|
10
|
+
dynamicChild: {
|
|
11
|
+
node: OptimisticRouteTrieNode<Route>;
|
|
12
|
+
paramName: string;
|
|
13
|
+
} | null;
|
|
14
|
+
optionalCatchAllChild: {
|
|
15
|
+
paramName: string;
|
|
16
|
+
route: Route;
|
|
17
|
+
} | null;
|
|
18
|
+
route: Route | null;
|
|
19
|
+
staticChildren: Map<string, OptimisticRouteTrieNode<Route>>;
|
|
20
|
+
};
|
|
21
|
+
export type OptimisticRouteTrieMatch<Route extends OptimisticTrieRoute> = {
|
|
22
|
+
params: Record<string, string | string[]>;
|
|
23
|
+
route: Route;
|
|
24
|
+
};
|
|
25
|
+
export declare function buildOptimisticRouteTrie<Route extends OptimisticTrieRoute>(routes: Iterable<Route>, onDynamicSegmentConflict?: (existingParamName: string, part: string) => void): OptimisticRouteTrieNode<Route>;
|
|
26
|
+
export declare function matchOptimisticRouteTrie<Route extends OptimisticTrieRoute>(trie: OptimisticRouteTrieNode<Route>, urlParts: readonly string[]): OptimisticRouteTrieMatch<Route> | null;
|
|
27
|
+
//#endregion
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { buildParams } from "./utils.js";
|
|
2
|
+
//#region src/routing/optimistic-route-trie.ts
|
|
3
|
+
function createNode() {
|
|
4
|
+
return {
|
|
5
|
+
catchAllChild: null,
|
|
6
|
+
dynamicChild: null,
|
|
7
|
+
optionalCatchAllChild: null,
|
|
8
|
+
route: null,
|
|
9
|
+
staticChildren: /* @__PURE__ */ new Map()
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
function buildOptimisticRouteTrie(routes, onDynamicSegmentConflict) {
|
|
13
|
+
const root = createNode();
|
|
14
|
+
for (const route of routes) {
|
|
15
|
+
let node = root;
|
|
16
|
+
const parts = route.patternParts;
|
|
17
|
+
if (parts.length === 0) {
|
|
18
|
+
node.route ??= route;
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
for (const [index, part] of parts.entries()) {
|
|
22
|
+
const isTerminal = index === parts.length - 1;
|
|
23
|
+
if (part.startsWith(":") && part.endsWith("+")) {
|
|
24
|
+
if (isTerminal && node.catchAllChild === null) node.catchAllChild = {
|
|
25
|
+
paramName: part.slice(1, -1),
|
|
26
|
+
route
|
|
27
|
+
};
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
if (part.startsWith(":") && part.endsWith("*")) {
|
|
31
|
+
if (isTerminal && node.optionalCatchAllChild === null) node.optionalCatchAllChild = {
|
|
32
|
+
paramName: part.slice(1, -1),
|
|
33
|
+
route
|
|
34
|
+
};
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
if (part.startsWith(":")) {
|
|
38
|
+
const paramName = part.slice(1);
|
|
39
|
+
if (node.dynamicChild === null) node.dynamicChild = {
|
|
40
|
+
node: createNode(),
|
|
41
|
+
paramName
|
|
42
|
+
};
|
|
43
|
+
else if (node.dynamicChild.paramName !== paramName) onDynamicSegmentConflict?.(node.dynamicChild.paramName, part);
|
|
44
|
+
node = node.dynamicChild.node;
|
|
45
|
+
if (isTerminal) node.route ??= route;
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
let staticChild = node.staticChildren.get(part);
|
|
49
|
+
if (staticChild === void 0) {
|
|
50
|
+
staticChild = createNode();
|
|
51
|
+
node.staticChildren.set(part, staticChild);
|
|
52
|
+
}
|
|
53
|
+
node = staticChild;
|
|
54
|
+
if (isTerminal) node.route ??= route;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return root;
|
|
58
|
+
}
|
|
59
|
+
function matchNode(node, urlParts, index, entries) {
|
|
60
|
+
if (index === urlParts.length) {
|
|
61
|
+
if (node.route !== null) return {
|
|
62
|
+
route: node.route,
|
|
63
|
+
params: buildParams(entries)
|
|
64
|
+
};
|
|
65
|
+
if (node.optionalCatchAllChild !== null) return {
|
|
66
|
+
route: node.optionalCatchAllChild.route,
|
|
67
|
+
params: buildParams(entries)
|
|
68
|
+
};
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
const segment = urlParts[index];
|
|
72
|
+
const staticChild = node.staticChildren.get(segment);
|
|
73
|
+
if (staticChild !== void 0) return matchNode(staticChild, urlParts, index + 1, entries);
|
|
74
|
+
if (node.dynamicChild !== null) {
|
|
75
|
+
entries.push([node.dynamicChild.paramName, segment]);
|
|
76
|
+
const match = matchNode(node.dynamicChild.node, urlParts, index + 1, entries);
|
|
77
|
+
if (match !== null) return match;
|
|
78
|
+
entries.pop();
|
|
79
|
+
}
|
|
80
|
+
if (node.catchAllChild !== null) {
|
|
81
|
+
const params = buildParams(entries);
|
|
82
|
+
params[node.catchAllChild.paramName] = urlParts.slice(index);
|
|
83
|
+
return {
|
|
84
|
+
route: node.catchAllChild.route,
|
|
85
|
+
params
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
if (node.optionalCatchAllChild !== null) {
|
|
89
|
+
const params = buildParams(entries);
|
|
90
|
+
params[node.optionalCatchAllChild.paramName] = urlParts.slice(index);
|
|
91
|
+
return {
|
|
92
|
+
route: node.optionalCatchAllChild.route,
|
|
93
|
+
params
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
function matchOptimisticRouteTrie(trie, urlParts) {
|
|
99
|
+
return matchNode(trie, urlParts, 0, []);
|
|
100
|
+
}
|
|
101
|
+
//#endregion
|
|
102
|
+
export { buildOptimisticRouteTrie, matchOptimisticRouteTrie };
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
//#region src/routing/route-validation.d.ts
|
|
2
2
|
/**
|
|
3
|
-
* Dynamic route validation adapted from Next.js' sorted-routes implementation.
|
|
3
|
+
* Dynamic route validation and sorting adapted from Next.js' sorted-routes implementation.
|
|
4
4
|
* Source:
|
|
5
5
|
* https://github.com/vercel/next.js/blob/canary/packages/next/src/shared/lib/router/utils/sorted-routes.ts
|
|
6
6
|
*/
|
|
7
|
+
/**
|
|
8
|
+
* Sort Next.js-format page paths (e.g. "/posts/[id]") into route-priority
|
|
9
|
+
* order, matching Next.js' `getSortedRoutes`.
|
|
10
|
+
*/
|
|
11
|
+
export declare function getSortedRoutes(pages: readonly string[]): string[];
|
|
7
12
|
export declare function patternToNextFormat(pattern: string): string;
|
|
8
13
|
export declare function validateRoutePatterns(patterns: readonly string[]): void;
|
|
9
14
|
//#endregion
|