mirror of
https://github.com/cloudflare/vinext.git
synced 2026-09-14 19:04:59 +08:00
49b45b9891
* feat(ppr): add fallback shell payload identity * refactor(ppr): address review feedback on payload identity helpers - normalizePregeneratedPathname: document the deliberate non-strict vs strict normalization choice so it isn't "fixed" into a build-time throw - addPregeneratedConcretePath: normalize the pathname internally as the single source of truth, removing the caller pre-normalize footgun - export PrerenderRouteParamsRouteMatch for #1716 consumers - cover direct un-normalized recording with a test * fix(ppr): mark PrerenderRouteParamsRouteMatch @public for knip The type is exported for #1716's serving consumers but has no in-repo reference yet, so knip's no-unused-exports gate flags it. Knip excludes exports tagged @public from that report; this keeps the reviewer-requested export without failing CI. * docs(ppr): document live-Set and dedup contracts on concrete-path registry Address bonk review notes: - getRenderedConcreteUrlPathsForRoute returns the live backing Set for allocation-free hot-path lookups; document that callers must not retain the reference across a re-seed (clear empties the map, stranding it). - parsePregeneratedConcretePaths intentionally does not dedupe repeated route patterns; document that they merge additively and value-dedup in addPregeneratedConcretePath makes the result equivalent. --------- Co-authored-by: James <james@eli.cx>
90 lines
3.4 KiB
TypeScript
90 lines
3.4 KiB
TypeScript
import { describe, it, expect, afterEach } from "vite-plus/test";
|
|
import {
|
|
clearPregeneratedConcretePaths,
|
|
addPregeneratedConcretePath,
|
|
getRenderedConcreteUrlPathsForRoute,
|
|
initPregeneratedPathsFromGlobals,
|
|
normalizePregeneratedPathname,
|
|
} from "../packages/vinext/src/server/pregenerated-concrete-paths.js";
|
|
|
|
describe("pregenerated concrete paths", () => {
|
|
afterEach(() => {
|
|
clearPregeneratedConcretePaths();
|
|
});
|
|
|
|
it("returns undefined for an unknown route pattern", () => {
|
|
expect(getRenderedConcreteUrlPathsForRoute("/en/blog/[slug]")).toBeUndefined();
|
|
});
|
|
|
|
it("stores and retrieves pathnames for a route pattern", () => {
|
|
addPregeneratedConcretePath("/:locale/blog/:slug", "/en/blog/hello");
|
|
addPregeneratedConcretePath("/:locale/blog/:slug", "/fr/blog/bonjour");
|
|
|
|
const paths = getRenderedConcreteUrlPathsForRoute("/:locale/blog/:slug");
|
|
expect(paths).toBeDefined();
|
|
expect([...paths!]).toEqual(["/en/blog/hello", "/fr/blog/bonjour"]);
|
|
});
|
|
|
|
it("supports independent route patterns", () => {
|
|
addPregeneratedConcretePath("/:locale/blog/:slug", "/en/blog/hello");
|
|
addPregeneratedConcretePath("/products/:id", "/products/42");
|
|
|
|
expect([...getRenderedConcreteUrlPathsForRoute("/:locale/blog/:slug")!]).toEqual([
|
|
"/en/blog/hello",
|
|
]);
|
|
expect([...getRenderedConcreteUrlPathsForRoute("/products/:id")!]).toEqual(["/products/42"]);
|
|
});
|
|
|
|
it("returns an empty state after clear", () => {
|
|
addPregeneratedConcretePath("/en/blog/[slug]", "/en/blog/persistent");
|
|
expect(getRenderedConcreteUrlPathsForRoute("/en/blog/[slug]")).toBeDefined();
|
|
|
|
clearPregeneratedConcretePaths();
|
|
|
|
expect(getRenderedConcreteUrlPathsForRoute("/en/blog/[slug]")).toBeUndefined();
|
|
});
|
|
|
|
it("clears stale paths from a previous build on re-population (issue 3)", () => {
|
|
// Build A
|
|
addPregeneratedConcretePath("/en/blog/[slug]", "/en/blog/old");
|
|
addPregeneratedConcretePath("/en/blog/[slug]", "/en/blog/also-old");
|
|
expect(getRenderedConcreteUrlPathsForRoute("/en/blog/[slug]")!.size).toBe(2);
|
|
|
|
// Build B — clear and re-seed without the old paths
|
|
clearPregeneratedConcretePaths();
|
|
addPregeneratedConcretePath("/en/blog/[slug]", "/en/blog/new");
|
|
|
|
const paths = getRenderedConcreteUrlPathsForRoute("/en/blog/[slug]")!;
|
|
expect(paths.has("/en/blog/old")).toBe(false);
|
|
expect(paths.has("/en/blog/also-old")).toBe(false);
|
|
expect(paths.has("/en/blog/new")).toBe(true);
|
|
expect(paths.size).toBe(1);
|
|
});
|
|
|
|
it("normalizes percent-encoded pathnames", () => {
|
|
expect(normalizePregeneratedPathname("/en/blog/hello%20world")).toBe("/en/blog/hello world");
|
|
});
|
|
|
|
it("normalizes un-normalized pathnames recorded directly", () => {
|
|
addPregeneratedConcretePath("/:locale/blog/:slug", "/en/blog/hello%20world");
|
|
addPregeneratedConcretePath("/:locale/blog/:slug", "//en/./blog/world");
|
|
|
|
expect([...getRenderedConcreteUrlPathsForRoute("/:locale/blog/:slug")!]).toEqual([
|
|
"/en/blog/hello world",
|
|
"/en/blog/world",
|
|
]);
|
|
});
|
|
|
|
it("initializes from the Worker global concrete-path table", () => {
|
|
globalThis.__VINEXT_PREGENERATED_CONCRETE_PATHS = [
|
|
["/:locale/blog/:slug", ["/en/blog/hello%20world"]],
|
|
];
|
|
initPregeneratedPathsFromGlobals();
|
|
delete globalThis.__VINEXT_PREGENERATED_CONCRETE_PATHS;
|
|
|
|
expect([...getRenderedConcreteUrlPathsForRoute("/:locale/blog/:slug")!]).toEqual([
|
|
"/en/blog/hello world",
|
|
]);
|
|
});
|
|
});
|