diff --git a/apps/control-panel/src/components/experiments/experiment-results-comparison.test.tsx b/apps/control-panel/src/components/experiments/experiment-results-comparison.test.tsx index 8009acbd..d14b7bac 100644 --- a/apps/control-panel/src/components/experiments/experiment-results-comparison.test.tsx +++ b/apps/control-panel/src/components/experiments/experiment-results-comparison.test.tsx @@ -227,13 +227,13 @@ describe("ExperimentResultsComparison ordering and states", () => { expect(text).toContain("Confidence interval unavailable"); }); - it("does not format estimates or differences without the Metric type", () => { + it("does not format estimates or differences when the Metric is missing from the catalog", () => { const text = visibleText( renderToStaticMarkup( , diff --git a/apps/control-panel/src/components/experiments/experiment-results-panel.tsx b/apps/control-panel/src/components/experiments/experiment-results-panel.tsx index c78ceb10..4cb28591 100644 --- a/apps/control-panel/src/components/experiments/experiment-results-panel.tsx +++ b/apps/control-panel/src/components/experiments/experiment-results-panel.tsx @@ -7,7 +7,6 @@ const ExperimentConclusionDialog = lazy(() => })), ); -import type { Metric } from "@splitch/contracts"; import type { PanelExperimentRun } from "@splitch/control-plane-sdk/panel-experiments"; import { useSuspenseQuery } from "@tanstack/react-query"; import { @@ -16,6 +15,7 @@ import { ExperimentResultsWaiting, } from "#components/experiments/experiment-results"; import { experimentResultsQuery } from "#lib/experiments/experiments-query"; +import type { ComparisonMetric } from "#lib/experiments/metric-comparison-rows"; /** * Route-facing wrapper: resolves the Run to read, then renders it. @@ -43,7 +43,7 @@ export function ExperimentResultsPanel({ flagId: string; environmentId: string; experimentId: string; - metrics: readonly Pick[]; + metrics: readonly ComparisonMetric[]; run: PanelExperimentRun | undefined; }) { if (!run) return ; @@ -77,7 +77,7 @@ function ExperimentResultsForRun({ flagId: string; environmentId: string; experimentId: string; - metrics: readonly Pick[]; + metrics: readonly ComparisonMetric[]; run: PanelExperimentRun; }) { const [concluding, setConcluding] = useState(false); diff --git a/apps/control-panel/src/components/experiments/experiment-results-test-fixtures.ts b/apps/control-panel/src/components/experiments/experiment-results-test-fixtures.ts index a503b851..4aa8ea57 100644 --- a/apps/control-panel/src/components/experiments/experiment-results-test-fixtures.ts +++ b/apps/control-panel/src/components/experiments/experiment-results-test-fixtures.ts @@ -9,6 +9,7 @@ import type { PanelExperimentResultsReady, PanelExperimentRun, } from "@splitch/control-plane-sdk/panel-experiments"; +import type { ComparisonMetric } from "#lib/experiments/metric-comparison-rows"; /** * Panel fixtures built the way the Worker builds the payload: the gate is @@ -251,10 +252,10 @@ export function resultsNoDataFixture( } /** Catalog rows for the Metric ids the stats fixtures use, as the detail read returns them. */ -export function metricsFixture(): { id: string; name: string }[] { +export function metricsFixture(): ComparisonMetric[] { return [ - { id: "checkout_conversion", name: "Checkout conversion" }, - { id: "checkout_latency_p95", name: "Checkout latency p95" }, + { id: "checkout_conversion", name: "Checkout conversion", kind: "binomial", direction: null }, + { id: "checkout_latency_p95", name: "Checkout latency p95", kind: "count", direction: null }, ]; } diff --git a/apps/control-panel/src/lib/experiments/metric-comparison-rows.ts b/apps/control-panel/src/lib/experiments/metric-comparison-rows.ts index 3d233321..8b8722b0 100644 --- a/apps/control-panel/src/lib/experiments/metric-comparison-rows.ts +++ b/apps/control-panel/src/lib/experiments/metric-comparison-rows.ts @@ -20,8 +20,7 @@ import { type MetricNames, metricDisplayName, metricNamesById } from "./metric-n * arrive computed. */ -export type ComparisonMetric = Pick & - Partial>; +export type ComparisonMetric = Pick; export type MetricComparisonRole = "decision" | "guardrail" | "exploratory"; diff --git a/apps/marketing/src/docs/markdown-route.test.ts b/apps/marketing/src/docs/markdown-route.test.ts index 72b7ec6d..31d7be80 100644 --- a/apps/marketing/src/docs/markdown-route.test.ts +++ b/apps/marketing/src/docs/markdown-route.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from "vitest"; import { quickstartMarkdown } from "./markdown"; -import { markdownForPath, staticMarkdownPaths } from "./markdown-route"; -import { staticPagePaths } from "./sitemap"; +import { markdownForPath } from "./markdown-route"; +import { canonicalPageUrls, staticPagePaths } from "./sitemap"; function markdownUrlForPage(path: string): string { if (path === "/") return "/.md"; @@ -32,16 +32,14 @@ describe("markdownForPath", () => { expect(markdown).toMatch(/^# Zero to a resolving Flag/m); }); - it("serves the .md suffix for every static HTML page, including top-level ones", () => { - const topLevelHtmlPages = staticPagePaths.filter((path) => !path.startsWith("/docs")); - expect(topLevelHtmlPages).toEqual(["/", "/quickstart"]); - expect(staticMarkdownPaths).toContain("/quickstart"); - expect(staticMarkdownPaths.filter((path) => !path.startsWith("/docs"))).toEqual([ - "/", - "/quickstart", - ]); + it("serves the .md suffix for every canonical HTML page, including top-level ones", () => { + // The Worker answers `.md` before the router runs, so this is the + // only guard that a new page ships with its markdown twin. + const pages = canonicalPageUrls.map((href) => new URL(href).pathname); + expect(pages.filter((path) => !path.startsWith("/docs"))).toEqual(["/", "/quickstart"]); + expect(pages.length).toBeGreaterThan(staticPagePaths.length); - for (const path of staticMarkdownPaths) { + for (const path of pages) { const markdown = markdownForPath(path); expect(markdown, path).not.toBeNull(); expect(markdownForPath(markdownUrlForPage(path)), markdownUrlForPage(path)).toBe(markdown); diff --git a/apps/marketing/src/docs/markdown-route.ts b/apps/marketing/src/docs/markdown-route.ts index e5d70c0d..9e42920c 100644 --- a/apps/marketing/src/docs/markdown-route.ts +++ b/apps/marketing/src/docs/markdown-route.ts @@ -23,8 +23,6 @@ const staticMarkdown = new Map string>([ ["/docs/errors", errorIndexMarkdown], ]); -export const staticMarkdownPaths = [...staticMarkdown.keys()] as const; - export function markdownForPath(pathname: string): string | null { const documentPath = htmlPathForMarkdownUrl(pathname); const staticDocument = staticMarkdown.get(documentPath); diff --git a/apps/marketing/src/routeTree.gen.ts b/apps/marketing/src/routeTree.gen.ts index 82cc80b2..1a323689 100644 --- a/apps/marketing/src/routeTree.gen.ts +++ b/apps/marketing/src/routeTree.gen.ts @@ -13,19 +13,14 @@ import { Route as IndexRouteImport } from './routes/index' import { Route as HealthRouteImport } from './routes/health' import { Route as LlmsDottxtRouteImport } from './routes/llms[.]txt' import { Route as QuickstartRouteImport } from './routes/quickstart' -import { Route as QuickstartDotmdRouteImport } from './routes/quickstart[.]md' import { Route as RobotsDottxtRouteImport } from './routes/robots[.]txt' import { Route as SitemapDotxmlRouteImport } from './routes/sitemap[.]xml' import { Route as DotwellKnownApiCatalogRouteImport } from './routes/[.]well-known.api-catalog' import { Route as DocsIndexRouteImport } from './routes/docs.index' import { Route as DocsCliRouteImport } from './routes/docs.cli' -import { Route as DocsCliDotmdRouteImport } from './routes/docs.cli[.]md' import { Route as DocsCodeAgentsRouteImport } from './routes/docs.code-agents' -import { Route as DocsCodeAgentsDotmdRouteImport } from './routes/docs.code-agents[.]md' import { Route as DocsErrorsRouteImport } from './routes/docs.errors' -import { Route as DocsErrorsDotmdRouteImport } from './routes/docs.errors[.]md' import { Route as DocsFlagsRouteImport } from './routes/docs.flags' -import { Route as DocsFlagsDotmdRouteImport } from './routes/docs.flags[.]md' import { Route as DocsErrorCodeRouteImport } from './routes/docs.error.$code' import { Route as DocsSdkTopicRouteImport } from './routes/docs.sdk.$topic' @@ -49,11 +44,6 @@ const QuickstartRoute = QuickstartRouteImport.update({ path: '/quickstart', getParentRoute: () => rootRouteImport, } as any) -const QuickstartDotmdRoute = QuickstartDotmdRouteImport.update({ - id: '/quickstart.md', - path: '/quickstart.md', - getParentRoute: () => rootRouteImport, -} as any) const RobotsDottxtRoute = RobotsDottxtRouteImport.update({ id: '/robots.txt', path: '/robots.txt', @@ -79,41 +69,21 @@ const DocsCliRoute = DocsCliRouteImport.update({ path: '/docs/cli', getParentRoute: () => rootRouteImport, } as any) -const DocsCliDotmdRoute = DocsCliDotmdRouteImport.update({ - id: '/docs/cli.md', - path: '/docs/cli.md', - getParentRoute: () => rootRouteImport, -} as any) const DocsCodeAgentsRoute = DocsCodeAgentsRouteImport.update({ id: '/docs/code-agents', path: '/docs/code-agents', getParentRoute: () => rootRouteImport, } as any) -const DocsCodeAgentsDotmdRoute = DocsCodeAgentsDotmdRouteImport.update({ - id: '/docs/code-agents.md', - path: '/docs/code-agents.md', - getParentRoute: () => rootRouteImport, -} as any) const DocsErrorsRoute = DocsErrorsRouteImport.update({ id: '/docs/errors', path: '/docs/errors', getParentRoute: () => rootRouteImport, } as any) -const DocsErrorsDotmdRoute = DocsErrorsDotmdRouteImport.update({ - id: '/docs/errors.md', - path: '/docs/errors.md', - getParentRoute: () => rootRouteImport, -} as any) const DocsFlagsRoute = DocsFlagsRouteImport.update({ id: '/docs/flags', path: '/docs/flags', getParentRoute: () => rootRouteImport, } as any) -const DocsFlagsDotmdRoute = DocsFlagsDotmdRouteImport.update({ - id: '/docs/flags.md', - path: '/docs/flags.md', - getParentRoute: () => rootRouteImport, -} as any) const DocsErrorCodeRoute = DocsErrorCodeRouteImport.update({ id: '/docs/error/$code', path: '/docs/error/$code', @@ -130,18 +100,13 @@ export interface FileRoutesByFullPath { '/health': typeof HealthRoute '/llms.txt': typeof LlmsDottxtRoute '/quickstart': typeof QuickstartRoute - '/quickstart.md': typeof QuickstartDotmdRoute '/robots.txt': typeof RobotsDottxtRoute '/sitemap.xml': typeof SitemapDotxmlRoute '/.well-known/api-catalog': typeof DotwellKnownApiCatalogRoute '/docs/cli': typeof DocsCliRoute - '/docs/cli.md': typeof DocsCliDotmdRoute '/docs/code-agents': typeof DocsCodeAgentsRoute - '/docs/code-agents.md': typeof DocsCodeAgentsDotmdRoute '/docs/errors': typeof DocsErrorsRoute - '/docs/errors.md': typeof DocsErrorsDotmdRoute '/docs/flags': typeof DocsFlagsRoute - '/docs/flags.md': typeof DocsFlagsDotmdRoute '/docs/': typeof DocsIndexRoute '/docs/error/$code': typeof DocsErrorCodeRoute '/docs/sdk/$topic': typeof DocsSdkTopicRoute @@ -151,18 +116,13 @@ export interface FileRoutesByTo { '/health': typeof HealthRoute '/llms.txt': typeof LlmsDottxtRoute '/quickstart': typeof QuickstartRoute - '/quickstart.md': typeof QuickstartDotmdRoute '/robots.txt': typeof RobotsDottxtRoute '/sitemap.xml': typeof SitemapDotxmlRoute '/.well-known/api-catalog': typeof DotwellKnownApiCatalogRoute '/docs/cli': typeof DocsCliRoute - '/docs/cli.md': typeof DocsCliDotmdRoute '/docs/code-agents': typeof DocsCodeAgentsRoute - '/docs/code-agents.md': typeof DocsCodeAgentsDotmdRoute '/docs/errors': typeof DocsErrorsRoute - '/docs/errors.md': typeof DocsErrorsDotmdRoute '/docs/flags': typeof DocsFlagsRoute - '/docs/flags.md': typeof DocsFlagsDotmdRoute '/docs': typeof DocsIndexRoute '/docs/error/$code': typeof DocsErrorCodeRoute '/docs/sdk/$topic': typeof DocsSdkTopicRoute @@ -173,18 +133,13 @@ export interface FileRoutesById { '/health': typeof HealthRoute '/llms.txt': typeof LlmsDottxtRoute '/quickstart': typeof QuickstartRoute - '/quickstart.md': typeof QuickstartDotmdRoute '/robots.txt': typeof RobotsDottxtRoute '/sitemap.xml': typeof SitemapDotxmlRoute '/.well-known/api-catalog': typeof DotwellKnownApiCatalogRoute '/docs/cli': typeof DocsCliRoute - '/docs/cli.md': typeof DocsCliDotmdRoute '/docs/code-agents': typeof DocsCodeAgentsRoute - '/docs/code-agents.md': typeof DocsCodeAgentsDotmdRoute '/docs/errors': typeof DocsErrorsRoute - '/docs/errors.md': typeof DocsErrorsDotmdRoute '/docs/flags': typeof DocsFlagsRoute - '/docs/flags.md': typeof DocsFlagsDotmdRoute '/docs/': typeof DocsIndexRoute '/docs/error/$code': typeof DocsErrorCodeRoute '/docs/sdk/$topic': typeof DocsSdkTopicRoute @@ -196,18 +151,13 @@ export interface FileRouteTypes { | '/health' | '/llms.txt' | '/quickstart' - | '/quickstart.md' | '/robots.txt' | '/sitemap.xml' | '/.well-known/api-catalog' | '/docs/cli' - | '/docs/cli.md' | '/docs/code-agents' - | '/docs/code-agents.md' | '/docs/errors' - | '/docs/errors.md' | '/docs/flags' - | '/docs/flags.md' | '/docs/' | '/docs/error/$code' | '/docs/sdk/$topic' @@ -217,18 +167,13 @@ export interface FileRouteTypes { | '/health' | '/llms.txt' | '/quickstart' - | '/quickstart.md' | '/robots.txt' | '/sitemap.xml' | '/.well-known/api-catalog' | '/docs/cli' - | '/docs/cli.md' | '/docs/code-agents' - | '/docs/code-agents.md' | '/docs/errors' - | '/docs/errors.md' | '/docs/flags' - | '/docs/flags.md' | '/docs' | '/docs/error/$code' | '/docs/sdk/$topic' @@ -238,18 +183,13 @@ export interface FileRouteTypes { | '/health' | '/llms.txt' | '/quickstart' - | '/quickstart.md' | '/robots.txt' | '/sitemap.xml' | '/.well-known/api-catalog' | '/docs/cli' - | '/docs/cli.md' | '/docs/code-agents' - | '/docs/code-agents.md' | '/docs/errors' - | '/docs/errors.md' | '/docs/flags' - | '/docs/flags.md' | '/docs/' | '/docs/error/$code' | '/docs/sdk/$topic' @@ -260,18 +200,13 @@ export interface RootRouteChildren { HealthRoute: typeof HealthRoute LlmsDottxtRoute: typeof LlmsDottxtRoute QuickstartRoute: typeof QuickstartRoute - QuickstartDotmdRoute: typeof QuickstartDotmdRoute RobotsDottxtRoute: typeof RobotsDottxtRoute SitemapDotxmlRoute: typeof SitemapDotxmlRoute DotwellKnownApiCatalogRoute: typeof DotwellKnownApiCatalogRoute DocsCliRoute: typeof DocsCliRoute - DocsCliDotmdRoute: typeof DocsCliDotmdRoute DocsCodeAgentsRoute: typeof DocsCodeAgentsRoute - DocsCodeAgentsDotmdRoute: typeof DocsCodeAgentsDotmdRoute DocsErrorsRoute: typeof DocsErrorsRoute - DocsErrorsDotmdRoute: typeof DocsErrorsDotmdRoute DocsFlagsRoute: typeof DocsFlagsRoute - DocsFlagsDotmdRoute: typeof DocsFlagsDotmdRoute DocsIndexRoute: typeof DocsIndexRoute DocsErrorCodeRoute: typeof DocsErrorCodeRoute DocsSdkTopicRoute: typeof DocsSdkTopicRoute @@ -307,13 +242,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof QuickstartRouteImport parentRoute: typeof rootRouteImport } - '/quickstart.md': { - id: '/quickstart.md' - path: '/quickstart.md' - fullPath: '/quickstart.md' - preLoaderRoute: typeof QuickstartDotmdRouteImport - parentRoute: typeof rootRouteImport - } '/robots.txt': { id: '/robots.txt' path: '/robots.txt' @@ -349,13 +277,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof DocsCliRouteImport parentRoute: typeof rootRouteImport } - '/docs/cli.md': { - id: '/docs/cli.md' - path: '/docs/cli.md' - fullPath: '/docs/cli.md' - preLoaderRoute: typeof DocsCliDotmdRouteImport - parentRoute: typeof rootRouteImport - } '/docs/code-agents': { id: '/docs/code-agents' path: '/docs/code-agents' @@ -363,13 +284,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof DocsCodeAgentsRouteImport parentRoute: typeof rootRouteImport } - '/docs/code-agents.md': { - id: '/docs/code-agents.md' - path: '/docs/code-agents.md' - fullPath: '/docs/code-agents.md' - preLoaderRoute: typeof DocsCodeAgentsDotmdRouteImport - parentRoute: typeof rootRouteImport - } '/docs/errors': { id: '/docs/errors' path: '/docs/errors' @@ -377,13 +291,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof DocsErrorsRouteImport parentRoute: typeof rootRouteImport } - '/docs/errors.md': { - id: '/docs/errors.md' - path: '/docs/errors.md' - fullPath: '/docs/errors.md' - preLoaderRoute: typeof DocsErrorsDotmdRouteImport - parentRoute: typeof rootRouteImport - } '/docs/flags': { id: '/docs/flags' path: '/docs/flags' @@ -391,13 +298,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof DocsFlagsRouteImport parentRoute: typeof rootRouteImport } - '/docs/flags.md': { - id: '/docs/flags.md' - path: '/docs/flags.md' - fullPath: '/docs/flags.md' - preLoaderRoute: typeof DocsFlagsDotmdRouteImport - parentRoute: typeof rootRouteImport - } '/docs/error/$code': { id: '/docs/error/$code' path: '/docs/error/$code' @@ -420,18 +320,13 @@ const rootRouteChildren: RootRouteChildren = { HealthRoute: HealthRoute, LlmsDottxtRoute: LlmsDottxtRoute, QuickstartRoute: QuickstartRoute, - QuickstartDotmdRoute: QuickstartDotmdRoute, RobotsDottxtRoute: RobotsDottxtRoute, SitemapDotxmlRoute: SitemapDotxmlRoute, DotwellKnownApiCatalogRoute: DotwellKnownApiCatalogRoute, DocsCliRoute: DocsCliRoute, - DocsCliDotmdRoute: DocsCliDotmdRoute, DocsCodeAgentsRoute: DocsCodeAgentsRoute, - DocsCodeAgentsDotmdRoute: DocsCodeAgentsDotmdRoute, DocsErrorsRoute: DocsErrorsRoute, - DocsErrorsDotmdRoute: DocsErrorsDotmdRoute, DocsFlagsRoute: DocsFlagsRoute, - DocsFlagsDotmdRoute: DocsFlagsDotmdRoute, DocsIndexRoute: DocsIndexRoute, DocsErrorCodeRoute: DocsErrorCodeRoute, DocsSdkTopicRoute: DocsSdkTopicRoute, diff --git a/apps/marketing/src/routes/docs.cli[.]md.ts b/apps/marketing/src/routes/docs.cli[.]md.ts deleted file mode 100644 index 6853c44f..00000000 --- a/apps/marketing/src/routes/docs.cli[.]md.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { createFileRoute } from "@tanstack/react-router"; -import { cliDocMarkdown } from "../docs/markdown"; -import { markdownResponse } from "../docs/serve-markdown"; - -export const Route = createFileRoute("/docs/cli.md")({ - server: { - handlers: { - GET: async () => markdownResponse(cliDocMarkdown()), - }, - }, -}); diff --git a/apps/marketing/src/routes/docs.code-agents[.]md.ts b/apps/marketing/src/routes/docs.code-agents[.]md.ts deleted file mode 100644 index b82e432a..00000000 --- a/apps/marketing/src/routes/docs.code-agents[.]md.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { createFileRoute } from "@tanstack/react-router"; -import { codeAgentsDocMarkdown } from "../docs/markdown"; -import { markdownResponse } from "../docs/serve-markdown"; - -export const Route = createFileRoute("/docs/code-agents.md")({ - server: { - handlers: { - GET: async () => markdownResponse(codeAgentsDocMarkdown()), - }, - }, -}); diff --git a/apps/marketing/src/routes/docs.errors[.]md.ts b/apps/marketing/src/routes/docs.errors[.]md.ts deleted file mode 100644 index 8c1054f1..00000000 --- a/apps/marketing/src/routes/docs.errors[.]md.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { createFileRoute } from "@tanstack/react-router"; -import { errorIndexMarkdown } from "../docs/markdown"; -import { markdownResponse } from "../docs/serve-markdown"; - -export const Route = createFileRoute("/docs/errors.md")({ - server: { - handlers: { - GET: async () => markdownResponse(errorIndexMarkdown()), - }, - }, -}); diff --git a/apps/marketing/src/routes/docs.flags[.]md.ts b/apps/marketing/src/routes/docs.flags[.]md.ts deleted file mode 100644 index 72880e2f..00000000 --- a/apps/marketing/src/routes/docs.flags[.]md.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { createFileRoute } from "@tanstack/react-router"; -import { flagsDocMarkdown } from "../docs/markdown"; -import { markdownResponse } from "../docs/serve-markdown"; - -export const Route = createFileRoute("/docs/flags.md")({ - server: { - handlers: { - GET: async () => markdownResponse(flagsDocMarkdown()), - }, - }, -}); diff --git a/apps/marketing/src/routes/quickstart[.]md.ts b/apps/marketing/src/routes/quickstart[.]md.ts deleted file mode 100644 index c8753347..00000000 --- a/apps/marketing/src/routes/quickstart[.]md.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { createFileRoute } from "@tanstack/react-router"; -import { quickstartMarkdown } from "../docs/markdown"; -import { markdownResponse } from "../docs/serve-markdown"; - -export const Route = createFileRoute("/quickstart.md")({ - server: { - handlers: { - GET: async () => markdownResponse(quickstartMarkdown()), - }, - }, -}); diff --git a/scripts/docs-link-lint.mjs b/scripts/docs-link-lint.mjs index a4d88408..13190510 100644 --- a/scripts/docs-link-lint.mjs +++ b/scripts/docs-link-lint.mjs @@ -135,19 +135,19 @@ export function lintPublishedDocsText(filePath, text, inventory) { function violationForPublishedUrl(url, inventory) { const pathname = normalizePathname(decodeURIComponent(url.pathname)); - const route = routeForPath(inventory, pathname); + // The Worker serves `.md` for every page, so a markdown URL is valid + // exactly when its HTML page is a route. There are no markdown file routes. + const pagePath = pathname.endsWith(".md") ? pathname.slice(0, -3) : pathname; + const route = routeForPath(inventory, pagePath); if (!route) return `${url.href} names no marketing route.`; - if (pathname.endsWith(".md") && !routeForPath(inventory, pathname.slice(0, -3))) { - return `${url.href} has no HTML twin.`; - } const anchor = decodeURIComponent(url.hash.slice(1)); if (!anchor) return undefined; - const routedAlternative = `${pathname === "/" ? "" : pathname}/${anchor}`; + const routedAlternative = `${pagePath === "/" ? "" : pagePath}/${anchor}`; if (hasStaticRoute(inventory, routedAlternative)) { return `${url.href} is stale; ${origin}${routedAlternative} is a route, not a section link.`; } - if (!route.anchors.has(anchor)) return `${url.href} names no section anchor on ${pathname}.`; + if (!route.anchors.has(anchor)) return `${url.href} names no section anchor on ${pagePath}.`; return undefined; } diff --git a/scripts/docs-link-lint.test.mjs b/scripts/docs-link-lint.test.mjs index 4869e52d..89c9f3ea 100644 --- a/scripts/docs-link-lint.test.mjs +++ b/scripts/docs-link-lint.test.mjs @@ -20,7 +20,6 @@ async function fixtureRoutes(t) { 'createFileRoute("/docs/")({});\n
', ), writeFile(path.join(routes, "docs.errors.tsx"), 'createFileRoute("/docs/errors")({});'), - writeFile(path.join(routes, "docs.errors[.]md.ts"), 'createFileRoute("/docs/errors.md")({});'), writeFile( path.join(routes, "docs.error.$code.tsx"), 'createFileRoute("/docs/error/$code")({});', @@ -63,14 +62,14 @@ test("rejects a route-side mutation that removes a published target", async (t) ); }); -test("rejects markdown routes without an HTML twin", async (t) => { +test("rejects a markdown URL whose HTML page is not a route", async (t) => { const routes = await fixtureRoutes(t); await rm(path.join(routes, "docs.errors.tsx")); const inventory = await buildRouteInventory(routes); assert.match( lintPublishedDocsText("README.md", `${origin}/docs/errors.md`, inventory)[0].message, - /has no HTML twin/, + /names no marketing route/, ); }); @@ -84,4 +83,8 @@ test("rejects missing anchors and stale fragment collisions", async (t) => { /names no section anchor/, ); assert.match(lintPublishedDocsText("README.md", movedCatalog, inventory)[0].message, /is stale/); + assert.match( + lintPublishedDocsText("README.md", `${origin}/docs.md#${"errors"}`, inventory)[0].message, + /is stale/, + ); });