Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 24 additions & 4 deletions packages/core/src/loop/boringstack/reachability.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,7 @@ export function checkFeatureReachable(
}

// 2. API registration: the resource must be mounted in the API route table.
if (
inputs.apiRoutes !== null &&
!inputs.apiRoutes.includes(`${camel}Routes`)
) {
if (inputs.apiRoutes !== null && !registersRoutes(inputs.apiRoutes, camel)) {
problems.push(
`API route missing: ${camel}Routes is not registered in ` +
`config/routes/routes.ts — the endpoints would be unreachable.`
Expand All @@ -80,6 +77,29 @@ export function checkFeatureReachable(
return { ok: problems.length === 0, problems };
}

/**
* True when `apiRoutes` mounts `<camel>Routes` as a WHOLE identifier, not merely as a
* substring of a longer name. A bare `.includes("countRoutes")` matches an unrelated
* slice's `accountRoutes` (`account` ⊃ `count`), so a `count` feature whose own
* `countRoutes` is genuinely absent would be reported reachable — a false green in the
* one check meant to catch unregistered (hollow) features. The `\p{ID_Continue}`
* lookarounds (the canonical Unicode "identifier continue" set — same idiom as
* `fieldIsMentioned`) reject a match glued to an adjacent identifier char on either
* side, while still matching every real mount idiom (`count: countRoutes`,
* `.use(countRoutes)`, `[countRoutes]`), whose delimiter before the name is not an
* identifier char. `$` is added to the class because it is a JS IdentifierPart that
* `\p{ID_Continue}` omits — so `$countRoutes` is a distinct identifier, not a match.
* (`_`, ZWJ, and ZWNJ are already in `\p{ID_Continue}`, verified at runtime.)
*/
function registersRoutes(apiRoutes: string, camel: string): boolean {
const boundary = "[\\p{ID_Continue}$]";
const ident = `${camel}Routes`.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");

return new RegExp(`(?<!${boundary})${ident}(?!${boundary})`, "u").test(
apiRoutes
);
}

/** True when a locale JSON string has non-empty `features.<lower>.title` + `.empty`. */
function localeHasFeatureKeys(jsonSrc: string, lower: string): boolean {
let data: unknown;
Expand Down
55 changes: 55 additions & 0 deletions packages/core/tests/boringstack-reachability.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,61 @@ describe("checkFeatureReachable", () => {
expect(r.problems.join("\n")).toMatch(/API route missing/u);
});

test("a substring-only API match is NOT accepted (no false green off a sibling slice)", () => {
// `count`'s own `countRoutes` is absent, but a prior slice registered
// `accountRoutes` — which CONTAINS "countRoutes". A bare `.includes` would wrongly
// pass `count` as reachable; the whole-identifier check must still flag it.
const r = checkFeatureReachable("Count", {
uiRoutes:
'const CountPage = lazy(() => import("@/features/count/components/CountPage/CountPage"));',
apiRoutes: "export const routes = {\n account: accountRoutes,\n};",
localeJsons: [
JSON.stringify({
features: { count: { title: "Counts", empty: "None." } },
}),
],
});

expect(r.ok).toBe(false);
expect(r.problems.join("\n")).toMatch(/API route missing/u);
});

test("a `$`-prefixed sibling identifier is NOT accepted (JS `$` is an identifier char)", () => {
// `$` is a valid JS IdentifierPart that `\p{ID_Continue}` omits, so `$countRoutes`
// is a DISTINCT identifier — it must not satisfy `count`'s reachability.
const r = checkFeatureReachable("Count", {
uiRoutes:
'const CountPage = lazy(() => import("@/features/count/components/CountPage/CountPage"));',
apiRoutes: "export const routes = {\n meta: $countRoutes,\n};",
localeJsons: [
JSON.stringify({
features: { count: { title: "Counts", empty: "None." } },
}),
],
});

expect(r.ok).toBe(false);
expect(r.problems.join("\n")).toMatch(/API route missing/u);
});

test("a whole-identifier API match is accepted even next to a superstring sibling", () => {
// Both `count` and `account` are registered: `count` must be seen as reachable
// (its own whole-word `countRoutes` is present), not masked by `accountRoutes`.
const r = checkFeatureReachable("Count", {
uiRoutes:
'const CountPage = lazy(() => import("@/features/count/components/CountPage/CountPage"));',
apiRoutes:
"export const routes = {\n account: accountRoutes,\n count: countRoutes,\n};",
localeJsons: [
JSON.stringify({
features: { count: { title: "Counts", empty: "None." } },
}),
],
});

expect(r.ok).toBe(true);
});

test("flags missing i18n keys (page would show raw keys)", () => {
const r = checkFeatureReachable("Bookmark", {
...wiredInputs(),
Expand Down