mirror of
https://github.com/morluto/rea
synced 2026-10-26 14:25:49 -04:00
fix(javascript): report path data records as route candidates (#6360)
* fix(javascript): report path data records as route candidates
A string `path` or `route` property was reported as an exact, observed
route endpoint whatever object held it, so data records such as
`{ type: "set", path: "updatedAt" }` became routes while a real
`app.get("/x")` stayed a low-confidence candidate.
Keep the property finding, but mark it as a candidate unless its object
has router fields (`component`, `element`, `Component`, `children`, or a
typed `_$` record), using the same shape check that already selects
automatic route roots. Route candidates get a route-specific limitation.
Fixes #6349
* fix(javascript): scan each route record's keys once
`#property` read the enclosing object's keys for every `path` or `route`
property, so an object repeating those keys was scanned quadratically.
Cache the record shape per object and share it with `#object`.
This commit is contained in:
@@ -2,21 +2,24 @@ import { expect, it } from "vitest";
|
||||
|
||||
import { analyzeJavaScriptStaticSource } from "./javascriptStaticAnalysis.js";
|
||||
|
||||
it("reads exact route-property templates and leaves interpolated values unknown", () => {
|
||||
const analysis = analyzeJavaScriptStaticSource(`
|
||||
const routes = [
|
||||
{ path: \`settings\` },
|
||||
{ route: \`users/\${userId}\` },
|
||||
{ path: "/login" },
|
||||
];
|
||||
`);
|
||||
|
||||
expect(
|
||||
analysis.endpoints.map(({ value, mechanism, interpretation }) => ({
|
||||
const routeEndpoints = (source: string) =>
|
||||
analyzeJavaScriptStaticSource(source)
|
||||
.endpoints.filter(({ kind }) => kind === "route")
|
||||
.map(({ value, mechanism, interpretation }) => ({
|
||||
value,
|
||||
mechanism,
|
||||
interpretation,
|
||||
})),
|
||||
}));
|
||||
|
||||
it("reads exact route-property templates and leaves interpolated values unknown", () => {
|
||||
expect(
|
||||
routeEndpoints(`
|
||||
const routes = [
|
||||
{ path: \`settings\`, component: Settings },
|
||||
{ route: \`users/\${userId}\`, component: User },
|
||||
{ path: "/login", element: login },
|
||||
];
|
||||
`),
|
||||
).toEqual([
|
||||
{
|
||||
value: "settings",
|
||||
@@ -30,3 +33,51 @@ it("reads exact route-property templates and leaves interpolated values unknown"
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("keeps route properties on router records exact", () => {
|
||||
expect(
|
||||
routeEndpoints(`
|
||||
const routes = [
|
||||
{ path: "admin", children: [] },
|
||||
{ _$: "route", path: "typed" },
|
||||
];
|
||||
`).map(({ value, interpretation }) => [value, interpretation]),
|
||||
).toEqual([
|
||||
["admin", undefined],
|
||||
["typed", undefined],
|
||||
]);
|
||||
});
|
||||
|
||||
it("reports path properties outside router records only as candidates", () => {
|
||||
expect(
|
||||
routeEndpoints(`
|
||||
const patch = [{ type: "set", path: "updatedAt", value: 1 }];
|
||||
const worktree = { path: "", head: "" };
|
||||
const routes = [{ path: "/bare" }];
|
||||
`),
|
||||
).toEqual([
|
||||
{
|
||||
value: "updatedAt",
|
||||
mechanism: "property:path",
|
||||
interpretation: "candidate",
|
||||
},
|
||||
{ value: "", mechanism: "property:path", interpretation: "candidate" },
|
||||
{
|
||||
value: "/bare",
|
||||
mechanism: "property:path",
|
||||
interpretation: "candidate",
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("scans an object's route fields once for repeated path keys", () => {
|
||||
const start = performance.now();
|
||||
const endpoints = routeEndpoints(
|
||||
`const record = { ${Array.from({ length: 20_000 }, (_, i) => `path: "p${String(i)}"`).join(", ")} };`,
|
||||
);
|
||||
expect(endpoints).toHaveLength(20_000);
|
||||
expect(
|
||||
endpoints.every(({ interpretation }) => interpretation === "candidate"),
|
||||
).toBe(true);
|
||||
expect(performance.now() - start).toBeLessThan(2000);
|
||||
}, 30000);
|
||||
|
||||
Reference in New Issue
Block a user