77 lines
3.4 KiB
TypeScript
77 lines
3.4 KiB
TypeScript
// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
|
|
|
|
// The first path segment is an organization's slug (`src/routes/$org*`), so every other top-level route
|
|
// the web shell mounts must be a slug no organization can take: kit-tenancy refuses the slugs in
|
|
// RESERVED_SLUGS when an organization is created. A new top-level route that is not reserved would
|
|
// let an organization of that name shadow it, or the route shadow the organization (R8: `/members`).
|
|
// `server.mjs` serves the build's static files before any route, so a folder of those counts as well.
|
|
// The same holds one level down: the third segment is a project's slug, so a static route beside
|
|
// `$org.$workspace.$project` (`modules`, F10's pages above a project) is a slug no project can take.
|
|
|
|
import { existsSync, readdirSync } from "node:fs"
|
|
import { RESERVED_PROJECT_SLUGS, RESERVED_SLUGS } from "@conusai-kit/tenancy"
|
|
import { describe, expect, it } from "vitest"
|
|
|
|
const routesDir = new URL("../apps/web/src/routes/", import.meta.url)
|
|
const publicDir = new URL("../apps/web/public/", import.meta.url)
|
|
|
|
/** A route file's first URL segment: `_layout.` and `(group).` add none, so what follows them is the route. */
|
|
const firstSegment = (file: string): string | undefined =>
|
|
file
|
|
.replace(/\.tsx?$/, "")
|
|
.split(".")
|
|
.find((part) => !/^[_(]/.test(part))
|
|
|
|
/** The routes' top-level segments: not `$param`, `__root` or `index`. */
|
|
const startSegments = readdirSync(routesDir)
|
|
.map(firstSegment)
|
|
.filter((segment): segment is string => segment !== undefined && !segment.startsWith("$") && segment !== "index")
|
|
|
|
/** Folders `dist/client` holds: Vite's own (its default `assetsDir`) and whatever `public/` brings. */
|
|
const staticSegments = [
|
|
"assets",
|
|
...(existsSync(publicDir) ? readdirSync(publicDir, { withFileTypes: true }) : [])
|
|
.filter((entry) => entry.isDirectory())
|
|
.map((entry) => entry.name),
|
|
]
|
|
|
|
const routeSegments = [...new Set([...startSegments, ...staticSegments])]
|
|
|
|
/** The static segments beside a project: `$org.$workspace.<segment>...`, but not the workspace's own `index`. */
|
|
const workspaceSegments = [
|
|
...new Set(
|
|
readdirSync(routesDir)
|
|
.map((file) => /^\$org\.\$workspace\.([^$.][^.]*)\./.exec(file)?.[1])
|
|
.filter((segment): segment is string => segment !== undefined && segment !== "index"),
|
|
),
|
|
]
|
|
|
|
describe("top-level routes", () => {
|
|
it("finds the routes the shell mounts, or this suite proves nothing", () => {
|
|
// Each is a route R8's rule applies to; the list is what apps/web/ROUTES.md names at the top level.
|
|
expect(startSegments).toEqual(
|
|
expect.arrayContaining(["api", "auth", "invitations", "members", "onboarding", "settings"]),
|
|
)
|
|
})
|
|
|
|
it("reads a route behind a pathless layout as the route it is", () => {
|
|
expect(firstSegment("_public.pricing.tsx")).toBe("pricing")
|
|
expect(firstSegment("(marketing).pricing.tsx")).toBe("pricing")
|
|
expect(firstSegment("__root.tsx")).toBeUndefined()
|
|
})
|
|
|
|
it.each(routeSegments)("reserves /%s from organization slugs", (segment) => {
|
|
expect(RESERVED_SLUGS).toContain(segment)
|
|
})
|
|
})
|
|
|
|
describe("routes beside a project", () => {
|
|
it("finds the route of a module's pages above a project, or this suite proves nothing", () => {
|
|
expect(workspaceSegments).toContain("modules")
|
|
})
|
|
|
|
it.each(workspaceSegments)("reserves /<org>/<workspace>/%s from project slugs", (segment) => {
|
|
expect(RESERVED_PROJECT_SLUGS).toContain(segment)
|
|
})
|
|
})
|