blume 2.0.1 → 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +82 -0
- package/dist/cli/{chunk-vtk4a6dg.js → chunk-00gs3wqs.js} +1 -1
- package/dist/cli/{chunk-fa25z98p.js → chunk-273ygyr4.js} +4 -3
- package/dist/cli/chunk-273ygyr4.js.map +11 -0
- package/dist/cli/{chunk-yw7dm696.js → chunk-3yce002v.js} +21 -8
- package/dist/cli/{chunk-yw7dm696.js.map → chunk-3yce002v.js.map} +3 -3
- package/dist/cli/{chunk-yt5n7ppj.js → chunk-4e9b9ra6.js} +17 -7
- package/dist/cli/chunk-4e9b9ra6.js.map +10 -0
- package/dist/cli/{chunk-fs23ddbb.js → chunk-5r8g91qn.js} +352 -676
- package/dist/cli/chunk-5r8g91qn.js.map +34 -0
- package/dist/cli/{chunk-6vm74dry.js → chunk-6dtt0zfn.js} +8 -8
- package/dist/cli/{chunk-qwsrynx5.js → chunk-6k4ftwze.js} +38 -19
- package/dist/cli/{chunk-qwsrynx5.js.map → chunk-6k4ftwze.js.map} +4 -4
- package/dist/cli/{chunk-qs4q5p4e.js → chunk-7mbqtmgb.js} +16 -7
- package/dist/cli/chunk-7mbqtmgb.js.map +10 -0
- package/dist/cli/{chunk-q5163e60.js → chunk-7vtckvaw.js} +21 -19
- package/dist/cli/chunk-7vtckvaw.js.map +11 -0
- package/dist/cli/{chunk-kdp5q7ke.js → chunk-8g8ytmgx.js} +17 -18
- package/dist/cli/{chunk-kdp5q7ke.js.map → chunk-8g8ytmgx.js.map} +2 -2
- package/dist/cli/{chunk-epjnccmv.js → chunk-91ws1n6j.js} +18 -15
- package/dist/cli/chunk-91ws1n6j.js.map +10 -0
- package/dist/cli/{chunk-fxypxtvm.js → chunk-bbnwccaz.js} +2 -2
- package/dist/cli/{chunk-m3vmjgmq.js → chunk-bfwp9vp6.js} +16 -8
- package/dist/cli/chunk-bfwp9vp6.js.map +10 -0
- package/dist/cli/{chunk-f2z5v128.js → chunk-d1v5rhy0.js} +14 -15
- package/dist/cli/{chunk-f2z5v128.js.map → chunk-d1v5rhy0.js.map} +2 -2
- package/dist/cli/{chunk-6crbhc3x.js → chunk-ddndchfr.js} +21 -6
- package/dist/cli/chunk-ddndchfr.js.map +14 -0
- package/dist/cli/{chunk-ce574jw2.js → chunk-esh98wmb.js} +1 -1
- package/dist/cli/{chunk-zxcczpyx.js → chunk-fsmrqk8a.js} +1 -1
- package/dist/cli/{chunk-hdpx1tax.js → chunk-g698a744.js} +5 -5
- package/dist/cli/{chunk-jts8mvcz.js → chunk-gs7r695n.js} +9 -3
- package/dist/cli/{chunk-jts8mvcz.js.map → chunk-gs7r695n.js.map} +3 -3
- package/dist/cli/{chunk-zxh4d9vy.js → chunk-h2ez8dzb.js} +4 -4
- package/dist/cli/{chunk-5shv93fd.js → chunk-h7k3nq3v.js} +2 -2
- package/dist/cli/{chunk-79jhk4py.js → chunk-hqp2ajnh.js} +251 -122
- package/dist/cli/chunk-hqp2ajnh.js.map +35 -0
- package/dist/cli/{chunk-2hn4b8z7.js → chunk-hr8ne106.js} +109 -40
- package/dist/cli/chunk-hr8ne106.js.map +13 -0
- package/dist/cli/{chunk-6hsn950k.js → chunk-j85scx15.js} +62 -18
- package/dist/cli/chunk-j85scx15.js.map +10 -0
- package/dist/cli/{chunk-s1p84fyh.js → chunk-k7pj68a8.js} +63 -22
- package/dist/cli/chunk-k7pj68a8.js.map +11 -0
- package/dist/cli/{chunk-ah61y8py.js → chunk-mqc662a6.js} +2 -2
- package/dist/cli/{chunk-ch6g3ar0.js → chunk-n1yg3tj3.js} +4 -4
- package/dist/cli/{chunk-mb2919y2.js → chunk-nfcyttvj.js} +17 -6
- package/dist/cli/chunk-nfcyttvj.js.map +10 -0
- package/dist/cli/{chunk-kpf8rrjc.js → chunk-pbg5a4s3.js} +56 -24
- package/dist/cli/chunk-pbg5a4s3.js.map +19 -0
- package/dist/cli/{chunk-jwyddg7y.js → chunk-ppzjqwx2.js} +21 -14
- package/dist/cli/{chunk-jwyddg7y.js.map → chunk-ppzjqwx2.js.map} +4 -4
- package/dist/cli/{chunk-dh8cwk36.js → chunk-qkb5a8sa.js} +24 -9
- package/dist/cli/chunk-qkb5a8sa.js.map +10 -0
- package/dist/cli/{chunk-wm7js3j9.js → chunk-sqw4ekg1.js} +2 -2
- package/dist/cli/{chunk-qkqwkpte.js → chunk-v6ya5kcb.js} +3083 -953
- package/dist/cli/chunk-v6ya5kcb.js.map +189 -0
- package/dist/cli/{chunk-fz5wtpmh.js → chunk-w4bxdvsa.js} +18 -15
- package/dist/cli/chunk-w4bxdvsa.js.map +10 -0
- package/dist/cli/{chunk-27g6wdth.js → chunk-wdrt2k2v.js} +2 -2
- package/dist/cli/{chunk-wgm7m9qk.js → chunk-xh43dwgw.js} +158 -43
- package/dist/cli/chunk-xh43dwgw.js.map +36 -0
- package/dist/cli/{chunk-s6jhgk0q.js → chunk-zp79m0ts.js} +2 -2
- package/dist/cli/index.js +160 -35
- package/dist/cli/index.js.map +4 -4
- package/dist/types/ai/agent-surface.d.ts +32 -0
- package/dist/types/ai/api-catalog.d.ts +7 -1
- package/dist/types/ai/ask-context.d.ts +7 -0
- package/dist/types/ai/component-markdown.d.ts +4 -4
- package/dist/types/ai/relative-links.d.ts +9 -4
- package/dist/types/ai/static-expression.d.ts +28 -0
- package/dist/types/analytics/databuddy.d.ts +43 -0
- package/dist/types/analytics/index.d.ts +2 -0
- package/dist/types/analytics/schema.d.ts +14 -0
- package/dist/types/cli/env.d.ts +5 -0
- package/dist/types/cli/init/scaffold.d.ts +19 -3
- package/dist/types/cli/init/starter-spec.d.ts +11 -0
- package/dist/types/core/base-path.d.ts +9 -0
- package/dist/types/core/config-input.d.ts +4 -4
- package/dist/types/core/config.d.ts +2 -2
- package/dist/types/core/graph.d.ts +2 -0
- package/dist/types/core/i18n-ui.d.ts +31 -0
- package/dist/types/core/i18n.d.ts +7 -1
- package/dist/types/core/links.d.ts +3 -1
- package/dist/types/core/load-module.d.ts +10 -0
- package/dist/types/core/locale-links.d.ts +12 -2
- package/dist/types/core/meta.d.ts +5 -1
- package/dist/types/core/nav-diagnostics.d.ts +10 -0
- package/dist/types/core/navigation.d.ts +38 -0
- package/dist/types/core/ordering-prefix.d.ts +4 -0
- package/dist/types/core/safe-links.d.ts +3 -1
- package/dist/types/core/schema.d.ts +52 -13
- package/dist/types/core/sources/github-releases.d.ts +5 -0
- package/dist/types/core/sources/lower.d.ts +14 -2
- package/dist/types/core/sources/normalize.d.ts +10 -1
- package/dist/types/core/sources/remote.d.ts +11 -1
- package/dist/types/core/sources/resolve.d.ts +12 -0
- package/dist/types/core/sources/types.d.ts +31 -0
- package/dist/types/core/types.d.ts +9 -0
- package/dist/types/deploy/adapters/node.d.ts +5 -2
- package/dist/types/deploy/adapters/types.d.ts +7 -0
- package/dist/types/deploy/cloudflare-negotiation.d.ts +3 -2
- package/dist/types/deploy/headers.d.ts +31 -7
- package/dist/types/deploy/node-headers.d.ts +43 -8
- package/dist/types/deploy/platforms/netlify.d.ts +27 -2
- package/dist/types/deploy/platforms/node.d.ts +6 -5
- package/dist/types/deploy/platforms/types.d.ts +7 -0
- package/dist/types/deploy/platforms/vercel.d.ts +3 -2
- package/dist/types/deploy/redirects.d.ts +7 -2
- package/dist/types/deploy/vercel-negotiation.d.ts +3 -2
- package/dist/types/openapi/model.d.ts +20 -6
- package/dist/types/search/sync/algolia.d.ts +3 -1
- package/docs/01-quickstart.mdx +3 -2
- package/docs/02-deployment.mdx +17 -8
- package/docs/08-faq.mdx +9 -2
- package/docs/advanced/changelog.mdx +1 -1
- package/docs/advanced/custom-pages.mdx +4 -2
- package/docs/cli/audit.mdx +2 -2
- package/docs/cli/doctor.mdx +2 -2
- package/docs/cli/evals.mdx +2 -2
- package/docs/cli/index.mdx +4 -1
- package/docs/cli/translate.mdx +1 -1
- package/docs/configuration/analytics.mdx +23 -2
- package/docs/configuration/assistant.mdx +1 -1
- package/docs/configuration/customization.mdx +4 -3
- package/docs/configuration/index.mdx +5 -3
- package/docs/configuration/search.mdx +1 -1
- package/docs/content/components.mdx +1 -1
- package/docs/content/frontmatter.mdx +5 -1
- package/docs/content/i18n.mdx +1 -1
- package/docs/content/index.mdx +4 -2
- package/docs/content/meta.mdx +5 -3
- package/docs/content/navigation.mdx +29 -4
- package/docs/content/sources.mdx +18 -12
- package/docs/content/versioning.mdx +1 -0
- package/docs/discoverability/agent-discovery.mdx +21 -7
- package/docs/discoverability/index.mdx +2 -2
- package/docs/discoverability/llms-txt.mdx +2 -5
- package/docs/discoverability/markdown.mdx +5 -3
- package/docs/discoverability/mcp.mdx +4 -0
- package/docs/discoverability/metadata.mdx +3 -2
- package/docs/discoverability/open-graph.mdx +6 -4
- package/docs/discoverability/rss.mdx +3 -1
- package/docs/references/asyncapi.mdx +1 -1
- package/docs/references/graphql.mdx +2 -2
- package/docs/references/openapi.mdx +3 -3
- package/package.json +1 -1
- package/skills/blume-migrate/SKILL.md +6 -6
- package/skills/blume-migrate/references/docusaurus.md +6 -6
- package/skills/blume-migrate/references/mintlify.md +3 -3
- package/skills/blume-migrate/references/monorepo.md +1 -1
- package/skills/blume-migrate/references/nextra.md +1 -1
- package/skills/blume-migrate/references/starlight.md +5 -5
- package/skills/blume-migrate/scripts/mintlify-codemod.mjs +2 -2
- package/skills/blume-update-docs/references/audit-checklist.md +1 -1
- package/src/ai/agent-surface.ts +56 -0
- package/src/ai/api/spec.ts +15 -2
- package/src/ai/api-catalog.ts +7 -1
- package/src/ai/ask-context.ts +14 -2
- package/src/ai/ask-data.ts +26 -12
- package/src/ai/component-markdown.ts +43 -37
- package/src/ai/link-headers.ts +8 -3
- package/src/ai/llms.ts +4 -3
- package/src/ai/markdown.ts +23 -11
- package/src/ai/mcp/server.ts +78 -7
- package/src/ai/relative-links.ts +78 -10
- package/src/ai/static-expression.ts +416 -0
- package/src/ai/visibility.ts +45 -14
- package/src/analytics/databuddy.ts +67 -0
- package/src/analytics/head.ts +4 -0
- package/src/analytics/index.ts +2 -0
- package/src/analytics/posthog.ts +21 -3
- package/src/analytics/schema.ts +2 -0
- package/src/astro/generate.ts +71 -27
- package/src/astro/templates.ts +61 -23
- package/src/audit/catalog.ts +2 -2
- package/src/audit/checks/assets.ts +25 -5
- package/src/audit/checks/content.ts +20 -2
- package/src/audit/checks/i18n.ts +46 -8
- package/src/audit/checks/indexability.ts +39 -19
- package/src/audit/checks/links.ts +14 -0
- package/src/audit/checks/llms.ts +6 -3
- package/src/audit/checks/network.ts +3 -1
- package/src/audit/checks/og-image.ts +10 -0
- package/src/audit/checks/robots.ts +6 -1
- package/src/audit/checks/sitemap.ts +56 -31
- package/src/audit/checks/social.ts +21 -2
- package/src/audit/crawl.ts +88 -15
- package/src/audit/report.ts +54 -17
- package/src/audit/run.ts +1 -0
- package/src/audit/types.ts +18 -0
- package/src/audit/url.ts +37 -6
- package/src/cli/build-failure.ts +50 -0
- package/src/cli/commands/audit.ts +7 -0
- package/src/cli/commands/build.ts +19 -5
- package/src/cli/commands/check.ts +2 -0
- package/src/cli/commands/eval.ts +1 -7
- package/src/cli/commands/init.ts +15 -5
- package/src/cli/commands/preview.ts +15 -0
- package/src/cli/commands/sync.ts +2 -0
- package/src/cli/commands/translate.ts +9 -6
- package/src/cli/commands/upgrade.ts +11 -0
- package/src/cli/eject-scripts.ts +32 -7
- package/src/cli/env.ts +12 -1
- package/src/cli/init/scaffold.ts +117 -11
- package/src/cli/init/starter-spec.ts +235 -0
- package/src/components/content/AccordionItem.astro +26 -22
- package/src/components/content/Badge.astro +2 -9
- package/src/components/content/Card.astro +2 -2
- package/src/components/content/Component.astro +9 -3
- package/src/components/content/Expandable.astro +5 -1
- package/src/components/content/Frame.astro +2 -7
- package/src/components/content/GithubInfo.astro +12 -2
- package/src/components/content/Prompt.astro +2 -7
- package/src/components/content/Tabs.astro +54 -5
- package/src/components/content/Tile.astro +1 -1
- package/src/components/content/Tooltip.astro +69 -7
- package/src/components/content/Tree.astro +7 -2
- package/src/components/content/TypeTable.astro +10 -5
- package/src/components/content/Update.astro +8 -2
- package/src/components/content/auto-type-table.ts +4 -1
- package/src/components/content/badge-color.ts +17 -0
- package/src/components/content/base-href.ts +18 -3
- package/src/components/content/inline-markdown.ts +27 -7
- package/src/components/copy-feedback.ts +35 -8
- package/src/components/islands/assistant.tsx +16 -2
- package/src/components/islands/hooks.ts +7 -2
- package/src/components/islands/webmcp.ts +12 -8
- package/src/components/layout/Banner.astro +23 -4
- package/src/components/layout/Header.astro +22 -9
- package/src/components/layout/Logo.astro +5 -0
- package/src/components/layout/NavSelector.astro +6 -1
- package/src/components/layout/NavTabMenu.astro +133 -0
- package/src/components/layout/NavTree.astro +15 -6
- package/src/components/layout/NavTreeCache.astro +5 -2
- package/src/components/layout/NavTreeScript.astro +45 -5
- package/src/components/layout/PageLayout.astro +22 -8
- package/src/components/layout/ReferenceLayout.astro +4 -1
- package/src/components/layout/RootLayout.astro +21 -8
- package/src/components/layout/Search.astro +5 -1
- package/src/components/layout/analytics-client.ts +2 -0
- package/src/components/openapi/GraphqlType.astro +11 -3
- package/src/components/openapi/MessageComposer.astro +1 -1
- package/src/components/openapi/Operation.astro +21 -5
- package/src/components/openapi/PanelTabs.astro +4 -1
- package/src/components/openapi/Playground.astro +8 -2
- package/src/components/openapi/RequestPanel.astro +9 -5
- package/src/components/openapi/SchemaProperty.astro +11 -33
- package/src/components/openapi/SchemaTable.astro +27 -52
- package/src/components/openapi/helpers.ts +90 -12
- package/src/components/openapi/message-composer.ts +8 -0
- package/src/components/openapi/message-model.ts +12 -2
- package/src/components/openapi/message.ts +4 -1
- package/src/components/openapi/operation-model.ts +40 -8
- package/src/components/openapi/panel.ts +29 -5
- package/src/components/openapi/playground-client.ts +37 -7
- package/src/components/openapi/playground-schema.ts +25 -6
- package/src/components/openapi/request.ts +2 -0
- package/src/components/openapi/schema-tree.ts +203 -0
- package/src/components/openapi/snippets.ts +30 -15
- package/src/components/openapi/validate-json.ts +1 -1
- package/src/core/base-path.ts +15 -0
- package/src/core/config-input.ts +4 -4
- package/src/core/config.ts +18 -4
- package/src/core/diagnostics.ts +217 -30
- package/src/core/graph.ts +120 -11
- package/src/core/i18n-ui.ts +39 -2
- package/src/core/i18n.ts +13 -2
- package/src/core/links.ts +7 -4
- package/src/core/load-module.ts +20 -0
- package/src/core/locale-links.ts +17 -17
- package/src/core/manifest.ts +3 -2
- package/src/core/meta.ts +69 -6
- package/src/core/nav-diagnostics.ts +56 -1
- package/src/core/navigation.ts +245 -76
- package/src/core/ordering-prefix.ts +27 -0
- package/src/core/project-graph.ts +23 -1
- package/src/core/safe-href.ts +53 -1
- package/src/core/safe-links.ts +11 -2
- package/src/core/schema.ts +70 -14
- package/src/core/sources/assets.ts +83 -41
- package/src/core/sources/contentful-rich-text.ts +3 -2
- package/src/core/sources/contentful.ts +25 -12
- package/src/core/sources/filesystem.ts +5 -1
- package/src/core/sources/github-releases.ts +106 -5
- package/src/core/sources/lexical.ts +9 -4
- package/src/core/sources/lower.ts +79 -26
- package/src/core/sources/mdx-remote.ts +1 -0
- package/src/core/sources/normalize.ts +132 -30
- package/src/core/sources/notion.ts +26 -10
- package/src/core/sources/obsidian.ts +17 -3
- package/src/core/sources/payload.ts +1 -0
- package/src/core/sources/portable-text.ts +63 -44
- package/src/core/sources/remote.ts +18 -2
- package/src/core/sources/resolve.ts +52 -33
- package/src/core/sources/sanity.ts +1 -0
- package/src/core/sources/strapi-blocks.ts +9 -4
- package/src/core/sources/strapi.ts +1 -0
- package/src/core/sources/types.ts +31 -0
- package/src/core/types.ts +9 -0
- package/src/core/ui-packs/ar.ts +13 -0
- package/src/core/ui-packs/bg.ts +13 -0
- package/src/core/ui-packs/bn.ts +13 -0
- package/src/core/ui-packs/ca.ts +13 -0
- package/src/core/ui-packs/cs.ts +13 -0
- package/src/core/ui-packs/da.ts +13 -0
- package/src/core/ui-packs/de.ts +13 -0
- package/src/core/ui-packs/el.ts +13 -0
- package/src/core/ui-packs/es.ts +13 -0
- package/src/core/ui-packs/fa.ts +13 -0
- package/src/core/ui-packs/fi.ts +13 -0
- package/src/core/ui-packs/fr.ts +13 -0
- package/src/core/ui-packs/he.ts +13 -0
- package/src/core/ui-packs/hi.ts +13 -0
- package/src/core/ui-packs/hr.ts +13 -0
- package/src/core/ui-packs/hu.ts +13 -0
- package/src/core/ui-packs/id.ts +13 -0
- package/src/core/ui-packs/it.ts +13 -0
- package/src/core/ui-packs/ja.ts +13 -0
- package/src/core/ui-packs/ko.ts +13 -0
- package/src/core/ui-packs/nl.ts +13 -0
- package/src/core/ui-packs/no.ts +13 -0
- package/src/core/ui-packs/pl.ts +13 -0
- package/src/core/ui-packs/pt-br.ts +13 -0
- package/src/core/ui-packs/pt.ts +13 -0
- package/src/core/ui-packs/ro.ts +13 -0
- package/src/core/ui-packs/ru.ts +13 -0
- package/src/core/ui-packs/sk.ts +13 -0
- package/src/core/ui-packs/sr.ts +13 -0
- package/src/core/ui-packs/sv.ts +13 -0
- package/src/core/ui-packs/th.ts +13 -0
- package/src/core/ui-packs/tr.ts +13 -0
- package/src/core/ui-packs/uk.ts +13 -0
- package/src/core/ui-packs/vi.ts +13 -0
- package/src/core/ui-packs/zh-tw.ts +13 -0
- package/src/core/ui-packs/zh.ts +13 -0
- package/src/core/version-cut.ts +17 -2
- package/src/core/versions.ts +4 -1
- package/src/deploy/adapters/node.ts +5 -2
- package/src/deploy/adapters/registry.ts +2 -1
- package/src/deploy/adapters/types.ts +13 -1
- package/src/deploy/artifacts.ts +25 -5
- package/src/deploy/cloudflare-negotiation.ts +23 -4
- package/src/deploy/headers.ts +67 -51
- package/src/deploy/node-headers.ts +148 -27
- package/src/deploy/platforms/cloudflare.ts +1 -0
- package/src/deploy/platforms/netlify.ts +82 -5
- package/src/deploy/platforms/node.ts +7 -5
- package/src/deploy/platforms/static.ts +1 -0
- package/src/deploy/platforms/types.ts +7 -0
- package/src/deploy/platforms/vercel.ts +9 -3
- package/src/deploy/redirects.ts +14 -3
- package/src/deploy/vercel-negotiation.ts +35 -3
- package/src/eval/agents.ts +10 -2
- package/src/eval/run.ts +25 -0
- package/src/markdown/base-links.ts +55 -8
- package/src/markdown/index.ts +6 -3
- package/src/markdown/relative-links.ts +3 -23
- package/src/markdown/route-snapshot.ts +37 -0
- package/src/og/card.ts +20 -4
- package/src/og/derive.ts +145 -4
- package/src/openapi/graphql-build.ts +28 -2
- package/src/openapi/model.ts +77 -13
- package/src/openapi/render-mdx.ts +10 -1
- package/src/registry/eject.ts +119 -22
- package/src/search/documents.ts +22 -1
- package/src/search/sync/algolia.ts +36 -2
- package/src/sources/registry.ts +5 -0
- package/src/theme/entry.ts +11 -4
- package/src/translate/agents.ts +6 -1
- package/src/translate/ledger.ts +26 -3
- package/src/translate/meta.ts +11 -3
- package/src/translate/run.ts +11 -5
- package/src/translate/validate.ts +10 -1
- package/src/translate/work-list.ts +36 -3
- package/src/upgrade/upgrade.ts +36 -4
- package/dist/cli/chunk-2hn4b8z7.js.map +0 -12
- package/dist/cli/chunk-6crbhc3x.js.map +0 -14
- package/dist/cli/chunk-6hsn950k.js.map +0 -10
- package/dist/cli/chunk-79jhk4py.js.map +0 -35
- package/dist/cli/chunk-82bbrxdn.js +0 -51
- package/dist/cli/chunk-82bbrxdn.js.map +0 -10
- package/dist/cli/chunk-abh8yjkn.js +0 -31
- package/dist/cli/chunk-abh8yjkn.js.map +0 -10
- package/dist/cli/chunk-dh8cwk36.js.map +0 -10
- package/dist/cli/chunk-epjnccmv.js.map +0 -10
- package/dist/cli/chunk-fa25z98p.js.map +0 -11
- package/dist/cli/chunk-fs23ddbb.js.map +0 -35
- package/dist/cli/chunk-fz5wtpmh.js.map +0 -10
- package/dist/cli/chunk-kpf8rrjc.js.map +0 -19
- package/dist/cli/chunk-m3vmjgmq.js.map +0 -10
- package/dist/cli/chunk-mb2919y2.js.map +0 -10
- package/dist/cli/chunk-q5163e60.js.map +0 -11
- package/dist/cli/chunk-qkqwkpte.js.map +0 -182
- package/dist/cli/chunk-qs4q5p4e.js.map +0 -10
- package/dist/cli/chunk-s1p84fyh.js.map +0 -10
- package/dist/cli/chunk-wgm7m9qk.js.map +0 -36
- package/dist/cli/chunk-yt5n7ppj.js.map +0 -10
- /package/dist/cli/{chunk-vtk4a6dg.js.map → chunk-00gs3wqs.js.map} +0 -0
- /package/dist/cli/{chunk-6vm74dry.js.map → chunk-6dtt0zfn.js.map} +0 -0
- /package/dist/cli/{chunk-fxypxtvm.js.map → chunk-bbnwccaz.js.map} +0 -0
- /package/dist/cli/{chunk-ce574jw2.js.map → chunk-esh98wmb.js.map} +0 -0
- /package/dist/cli/{chunk-zxcczpyx.js.map → chunk-fsmrqk8a.js.map} +0 -0
- /package/dist/cli/{chunk-hdpx1tax.js.map → chunk-g698a744.js.map} +0 -0
- /package/dist/cli/{chunk-zxh4d9vy.js.map → chunk-h2ez8dzb.js.map} +0 -0
- /package/dist/cli/{chunk-5shv93fd.js.map → chunk-h7k3nq3v.js.map} +0 -0
- /package/dist/cli/{chunk-ah61y8py.js.map → chunk-mqc662a6.js.map} +0 -0
- /package/dist/cli/{chunk-ch6g3ar0.js.map → chunk-n1yg3tj3.js.map} +0 -0
- /package/dist/cli/{chunk-wm7js3j9.js.map → chunk-sqw4ekg1.js.map} +0 -0
- /package/dist/cli/{chunk-27g6wdth.js.map → chunk-wdrt2k2v.js.map} +0 -0
- /package/dist/cli/{chunk-s6jhgk0q.js.map → chunk-zp79m0ts.js.map} +0 -0
package/src/core/navigation.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { extname } from "pathe";
|
|
2
2
|
|
|
3
3
|
import { stripBasePath, withBasePath } from "./base-path.ts";
|
|
4
|
+
import { orderingPrefix, stripOrderingPrefix } from "./ordering-prefix.ts";
|
|
4
5
|
import type {
|
|
5
6
|
FolderMeta,
|
|
6
7
|
SidebarDisplay,
|
|
@@ -17,7 +18,6 @@ import type {
|
|
|
17
18
|
PageRecord,
|
|
18
19
|
} from "./types.ts";
|
|
19
20
|
|
|
20
|
-
const NUMERIC_PREFIX = /^(?<order>\d+)[-_.]/u;
|
|
21
21
|
const GROUP_FOLDER = /^\((?<label>.+)\)$/u;
|
|
22
22
|
const WORD_SPLIT = /[-_]/u;
|
|
23
23
|
|
|
@@ -94,26 +94,31 @@ const WORD_FORMS = new Map([
|
|
|
94
94
|
["yaml", "YAML"],
|
|
95
95
|
]);
|
|
96
96
|
|
|
97
|
+
/**
|
|
98
|
+
* Capitalize one word of a slug-like name for display, spelling the acronyms
|
|
99
|
+
* and brand casings in {@link WORD_FORMS} their own way (`api` → "API").
|
|
100
|
+
* Shared with page titles derived from file names, so a page and a folder
|
|
101
|
+
* named alike read alike.
|
|
102
|
+
*/
|
|
103
|
+
export const titleWord = (word: string): string =>
|
|
104
|
+
WORD_FORMS.get(word) ?? word.charAt(0).toUpperCase() + word.slice(1);
|
|
105
|
+
|
|
97
106
|
const humanize = (segment: string): string =>
|
|
98
|
-
segment
|
|
99
|
-
.replace(NUMERIC_PREFIX, "")
|
|
107
|
+
stripOrderingPrefix(segment)
|
|
100
108
|
.split(WORD_SPLIT)
|
|
101
109
|
.filter(Boolean)
|
|
102
|
-
.map(
|
|
103
|
-
(word) =>
|
|
104
|
-
WORD_FORMS.get(word) ?? word.charAt(0).toUpperCase() + word.slice(1)
|
|
105
|
-
)
|
|
110
|
+
.map(titleWord)
|
|
106
111
|
.join(" ");
|
|
107
112
|
|
|
108
113
|
const numericOrder = (segment: string): number => {
|
|
109
|
-
const value = segment
|
|
114
|
+
const value = orderingPrefix(segment);
|
|
110
115
|
return value ? Math.trunc(Number(value)) : Number.POSITIVE_INFINITY;
|
|
111
116
|
};
|
|
112
117
|
|
|
113
118
|
/** The nav key of a raw path segment: group label or numeric-stripped name. */
|
|
114
119
|
const segmentKey = (raw: string): string => {
|
|
115
120
|
const group = raw.match(GROUP_FOLDER)?.groups?.label;
|
|
116
|
-
return (group ?? raw)
|
|
121
|
+
return stripOrderingPrefix(group ?? raw);
|
|
117
122
|
};
|
|
118
123
|
|
|
119
124
|
/**
|
|
@@ -122,7 +127,7 @@ const segmentKey = (raw: string): string => {
|
|
|
122
127
|
* exactly like `index` and must be treated as one here too.
|
|
123
128
|
*/
|
|
124
129
|
const isIndexStem = (stem: string): boolean =>
|
|
125
|
-
stem
|
|
130
|
+
stripOrderingPrefix(stem) === "index";
|
|
126
131
|
|
|
127
132
|
/** A filename's stem: the name with its extension stripped. */
|
|
128
133
|
const stemOf = (filename: string): string =>
|
|
@@ -158,6 +163,8 @@ interface MutablePage {
|
|
|
158
163
|
* so they're excluded from the duplicate-order diagnostic.
|
|
159
164
|
*/
|
|
160
165
|
orderIsAuthored: boolean;
|
|
166
|
+
/** Whether `order` is the node's position in its folder meta `pages` list. */
|
|
167
|
+
ranked?: boolean;
|
|
161
168
|
}
|
|
162
169
|
|
|
163
170
|
interface MutableGroup {
|
|
@@ -166,6 +173,12 @@ interface MutableGroup {
|
|
|
166
173
|
path: string;
|
|
167
174
|
/** The group's URL path (folder route prefix); set as pages are inserted. */
|
|
168
175
|
routePath?: string;
|
|
176
|
+
/**
|
|
177
|
+
* The group's URL path read off its folder names, from a page whose
|
|
178
|
+
* frontmatter `slug` moved it off the folder's path. Fills `routePath` only
|
|
179
|
+
* when no other page under the folder supplies one.
|
|
180
|
+
*/
|
|
181
|
+
folderPath?: string;
|
|
169
182
|
/** The folder's index page route, when it has one; the group row's link. */
|
|
170
183
|
route?: string;
|
|
171
184
|
label: string;
|
|
@@ -173,6 +186,8 @@ interface MutableGroup {
|
|
|
173
186
|
collapsed?: boolean;
|
|
174
187
|
display?: SidebarDisplay;
|
|
175
188
|
order: number;
|
|
189
|
+
/** Whether `order` is the node's position in its folder meta `pages` list. */
|
|
190
|
+
ranked?: boolean;
|
|
176
191
|
children: MutableNode[];
|
|
177
192
|
index: Map<string, MutableGroup>;
|
|
178
193
|
}
|
|
@@ -240,7 +255,8 @@ const pageOrder = (page: PageRecord, filename: string): PageOrder => {
|
|
|
240
255
|
}
|
|
241
256
|
}
|
|
242
257
|
// An undated changelog entry's numeric filename prefix is usually a date
|
|
243
|
-
// (`
|
|
258
|
+
// (`20240105-release.md`) rather than a rank, so it is derived too. An ISO
|
|
259
|
+
// date (`2024-01-05-release.md`) is no ordering prefix at all.
|
|
244
260
|
const order = numericOrder(filename);
|
|
245
261
|
return {
|
|
246
262
|
order,
|
|
@@ -260,6 +276,17 @@ const metaKey = (path: string, metaPrefix: string): string => {
|
|
|
260
276
|
return path ? `${metaPrefix}/${path}` : metaPrefix;
|
|
261
277
|
};
|
|
262
278
|
|
|
279
|
+
/**
|
|
280
|
+
* What an i18n locale's tree mirrors from the fallback locale for a folder it
|
|
281
|
+
* has no meta of its own for: the fallback locale's folder meta, and the
|
|
282
|
+
* `sidebar.display` of a fallback-filled index page. Empty for the fallback
|
|
283
|
+
* locale itself and for single-locale sites.
|
|
284
|
+
*/
|
|
285
|
+
interface FallbackFolderMeta {
|
|
286
|
+
indexDisplay: Map<string, SidebarDisplay>;
|
|
287
|
+
meta: (path: string) => FolderMeta | undefined;
|
|
288
|
+
}
|
|
289
|
+
|
|
263
290
|
/**
|
|
264
291
|
* Apply folder meta (title/order/icon/collapsed/display and explicit page
|
|
265
292
|
* order), plus the index-frontmatter display sugar collected per folder path.
|
|
@@ -271,41 +298,40 @@ const applyFolderMeta = (
|
|
|
271
298
|
metaPrefix: string,
|
|
272
299
|
sharedMetaPrefix: string,
|
|
273
300
|
indexDisplay: Map<string, SidebarDisplay>,
|
|
274
|
-
indexRoute: Map<string, string
|
|
301
|
+
indexRoute: Map<string, string>,
|
|
302
|
+
fallback: FallbackFolderMeta
|
|
275
303
|
): void => {
|
|
276
304
|
// A folder with an index page links its group row to it — the same shape
|
|
277
305
|
// as an explicit-config group's `root`, and the only sidebar link to the
|
|
278
306
|
// section's own page once the index row is hidden. Index-less folders keep
|
|
279
307
|
// no link: their row would 404.
|
|
280
308
|
group.route = indexRoute.get(group.path);
|
|
309
|
+
group.routePath ??= group.folderPath;
|
|
281
310
|
// Locale-specific meta wins; a shared `meta.$.*` (keyed by the locale-stripped
|
|
282
311
|
// group path — version-prefixed inside a snapshot) applies to every locale
|
|
283
312
|
// otherwise.
|
|
284
|
-
const
|
|
313
|
+
const localeMeta =
|
|
285
314
|
folderMeta.get(metaKey(group.path, metaPrefix)) ??
|
|
286
315
|
sharedMeta.get(metaKey(group.path, sharedMetaPrefix));
|
|
316
|
+
// A locale with neither mirrors the fallback locale's group, the way its
|
|
317
|
+
// tree mirrors that locale's untranslated pages: the fallback's folder
|
|
318
|
+
// meta, and — ahead of it, as in the fallback's own tree — the display its
|
|
319
|
+
// fallback-filled index page sets. A locale's own meta owns its group, so
|
|
320
|
+
// the fallback's index frontmatter never overrides it.
|
|
321
|
+
const mirrored = localeMeta === undefined;
|
|
322
|
+
const meta = localeMeta ?? fallback.meta(group.path);
|
|
287
323
|
// The group's own render mode, resolved index frontmatter first, then folder
|
|
288
324
|
// meta; `toNavNode` falls back to the global mode. Applies to this group
|
|
289
325
|
// only — nested subgroups resolve their own value through the same chain.
|
|
290
|
-
group.display =
|
|
326
|
+
group.display =
|
|
327
|
+
indexDisplay.get(group.path) ??
|
|
328
|
+
(mirrored ? fallback.indexDisplay.get(group.path) : undefined) ??
|
|
329
|
+
meta?.display;
|
|
291
330
|
if (meta) {
|
|
292
331
|
group.label = meta.title ?? group.label;
|
|
293
332
|
group.icon = meta.icon ?? group.icon;
|
|
294
333
|
group.order = meta.order ?? group.order;
|
|
295
334
|
group.collapsed = meta.collapsed ?? group.collapsed;
|
|
296
|
-
|
|
297
|
-
if (meta.pages) {
|
|
298
|
-
const rank = new Map(meta.pages.map((key, i) => [key, i]));
|
|
299
|
-
for (const child of group.children) {
|
|
300
|
-
const position = rank.get(child.key);
|
|
301
|
-
if (position !== undefined) {
|
|
302
|
-
child.order = position;
|
|
303
|
-
if (child.kind === "page") {
|
|
304
|
-
child.orderIsAuthored = true;
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
335
|
}
|
|
310
336
|
|
|
311
337
|
for (const child of group.children) {
|
|
@@ -317,10 +343,29 @@ const applyFolderMeta = (
|
|
|
317
343
|
metaPrefix,
|
|
318
344
|
sharedMetaPrefix,
|
|
319
345
|
indexDisplay,
|
|
320
|
-
indexRoute
|
|
346
|
+
indexRoute,
|
|
347
|
+
fallback
|
|
321
348
|
);
|
|
322
349
|
}
|
|
323
350
|
}
|
|
351
|
+
|
|
352
|
+
// Ranked after the children resolved their own meta: the `pages` list is
|
|
353
|
+
// this folder's word on its children's order, so it wins over a listed
|
|
354
|
+
// subfolder's own `order` just as it wins over a listed page's
|
|
355
|
+
// `sidebar.order`.
|
|
356
|
+
if (meta?.pages) {
|
|
357
|
+
const rank = new Map(meta.pages.map((key, i) => [key, i]));
|
|
358
|
+
for (const child of group.children) {
|
|
359
|
+
const position = rank.get(child.key);
|
|
360
|
+
if (position !== undefined) {
|
|
361
|
+
child.order = position;
|
|
362
|
+
child.ranked = true;
|
|
363
|
+
if (child.kind === "page") {
|
|
364
|
+
child.orderIsAuthored = true;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
324
369
|
};
|
|
325
370
|
|
|
326
371
|
/**
|
|
@@ -457,22 +502,27 @@ const isAuthoredOrder = (node: MutableNode): boolean =>
|
|
|
457
502
|
* excluded: that's the common, intentional case of "just sort alphabetically."
|
|
458
503
|
* So is a derived, non-authored order (e.g. two changelog entries published
|
|
459
504
|
* on the same day) — not an authoring mistake.
|
|
505
|
+
*
|
|
506
|
+
* A folder meta `pages` rank is a list position, not a number the author
|
|
507
|
+
* picked, so it only ties with another rank: a listed page at position 1
|
|
508
|
+
* beside an unlisted `01-setup.md` is not a conflict anyone wrote.
|
|
460
509
|
*/
|
|
461
510
|
const duplicateOrderDiagnostics = (nodes: MutableNode[]): Diagnostic[] => {
|
|
462
|
-
const byOrder = new Map<
|
|
511
|
+
const byOrder = new Map<string, { order: number; tied: MutableNode[] }>();
|
|
463
512
|
for (const node of nodes) {
|
|
464
513
|
if (!Number.isFinite(node.order) || !isAuthoredOrder(node)) {
|
|
465
514
|
continue;
|
|
466
515
|
}
|
|
467
|
-
const
|
|
468
|
-
|
|
469
|
-
|
|
516
|
+
const key = `${node.ranked === true}:${node.order}`;
|
|
517
|
+
const entry = byOrder.get(key);
|
|
518
|
+
if (entry) {
|
|
519
|
+
entry.tied.push(node);
|
|
470
520
|
} else {
|
|
471
|
-
byOrder.set(node.order, [node]);
|
|
521
|
+
byOrder.set(key, { order: node.order, tied: [node] });
|
|
472
522
|
}
|
|
473
523
|
}
|
|
474
524
|
const diagnostics: Diagnostic[] = [];
|
|
475
|
-
for (const
|
|
525
|
+
for (const { order, tied } of byOrder.values()) {
|
|
476
526
|
if (tied.length > 1) {
|
|
477
527
|
const names = tied.map((node) => `"${node.label}"`);
|
|
478
528
|
const list =
|
|
@@ -596,13 +646,23 @@ const buildFileSystemSidebar = (
|
|
|
596
646
|
sharedMetaPrefix: string,
|
|
597
647
|
display: SidebarDisplay,
|
|
598
648
|
tabPaths: Set<string>,
|
|
599
|
-
diagnostics: Diagnostic[] = []
|
|
649
|
+
diagnostics: Diagnostic[] = [],
|
|
650
|
+
fallbackMetaPrefix?: string
|
|
600
651
|
): NavNode[] => {
|
|
601
652
|
const root = createGroup("", "", "", 0);
|
|
602
653
|
// Folder path -> `sidebar.display` from that folder's index page frontmatter.
|
|
603
654
|
// Collected before the hidden filter (like the title check): hiding the index
|
|
604
655
|
// row from the panel shouldn't stop it configuring its group.
|
|
605
656
|
const indexDisplay = new Map<string, SidebarDisplay>();
|
|
657
|
+
// What a group with no meta of this locale's mirrors from the fallback
|
|
658
|
+
// locale (see `applyFolderMeta`).
|
|
659
|
+
const fallback: FallbackFolderMeta = {
|
|
660
|
+
indexDisplay: new Map(),
|
|
661
|
+
meta: (path) =>
|
|
662
|
+
fallbackMetaPrefix === undefined
|
|
663
|
+
? undefined
|
|
664
|
+
: folderMeta.get(metaKey(path, fallbackMetaPrefix)),
|
|
665
|
+
};
|
|
606
666
|
// Folder path -> that folder's index page route, for the group row's link.
|
|
607
667
|
// Also collected before the hidden filter: hiding the index row is how a
|
|
608
668
|
// site drops the duplicate label under a linked header, so the link must
|
|
@@ -631,12 +691,16 @@ const buildFileSystemSidebar = (
|
|
|
631
691
|
if (diagnostic) {
|
|
632
692
|
diagnostics.push(diagnostic);
|
|
633
693
|
}
|
|
634
|
-
//
|
|
635
|
-
// frontmatter belongs to the fallback locale,
|
|
636
|
-
//
|
|
637
|
-
//
|
|
638
|
-
|
|
639
|
-
|
|
694
|
+
// A fallback-filled index's display is kept apart (like the title check
|
|
695
|
+
// above exempts it): its frontmatter belongs to the fallback locale, so
|
|
696
|
+
// it only speaks for a group this locale has no meta for — letting it
|
|
697
|
+
// override this locale's own authored `meta.ts` display would silently
|
|
698
|
+
// flip again the day the index gets translated.
|
|
699
|
+
if (page.meta.sidebar.display) {
|
|
700
|
+
(page.fallback ? fallback.indexDisplay : indexDisplay).set(
|
|
701
|
+
dirs.join("/"),
|
|
702
|
+
page.meta.sidebar.display
|
|
703
|
+
);
|
|
640
704
|
}
|
|
641
705
|
if (dirs.length > 0) {
|
|
642
706
|
indexRoute.set(dirs.join("/"), page.route);
|
|
@@ -660,15 +724,35 @@ const buildFileSystemSidebar = (
|
|
|
660
724
|
: routeSegments.slice(0, -1);
|
|
661
725
|
const routeDirCount = dirs.filter((dir) => !GROUP_FOLDER.test(dir)).length;
|
|
662
726
|
const offset = Math.max(0, folderParts.length - routeDirCount);
|
|
727
|
+
// A frontmatter `slug` publishes the page away from its folder, so its
|
|
728
|
+
// route says nothing about where the folder lives (`guides/a.md` with
|
|
729
|
+
// `slug: install` is `/install`). Its groups read their path off the folder
|
|
730
|
+
// names instead, behind the route's leading base/locale/version segments
|
|
731
|
+
// (whatever precedes the slug's own `versionKey`), and only where no
|
|
732
|
+
// other page under the folder supplies one.
|
|
733
|
+
const slugged = page.meta.slug !== undefined;
|
|
734
|
+
const leading = routeSegments.slice(
|
|
735
|
+
0,
|
|
736
|
+
Math.max(
|
|
737
|
+
0,
|
|
738
|
+
routeSegments.length - page.versionKey.split("/").filter(Boolean).length
|
|
739
|
+
)
|
|
740
|
+
);
|
|
663
741
|
|
|
664
742
|
let parent = root;
|
|
665
743
|
let consumed = offset;
|
|
744
|
+
const folders: string[] = [];
|
|
666
745
|
for (const dir of dirs) {
|
|
667
746
|
parent = ensureGroup(parent, dir);
|
|
668
747
|
if (!GROUP_FOLDER.test(dir)) {
|
|
669
748
|
consumed += 1;
|
|
749
|
+
folders.push(stripOrderingPrefix(dir));
|
|
750
|
+
}
|
|
751
|
+
if (slugged) {
|
|
752
|
+
parent.folderPath ??= `/${[...leading, ...folders].join("/")}`;
|
|
753
|
+
} else {
|
|
754
|
+
parent.routePath ??= `/${folderParts.slice(0, consumed).join("/")}`;
|
|
670
755
|
}
|
|
671
|
-
parent.routePath ??= `/${folderParts.slice(0, consumed).join("/")}`;
|
|
672
756
|
}
|
|
673
757
|
|
|
674
758
|
const { order, orderIsAuthored } = pageOrder(page, filename);
|
|
@@ -695,7 +779,8 @@ const buildFileSystemSidebar = (
|
|
|
695
779
|
metaPrefix,
|
|
696
780
|
sharedMetaPrefix,
|
|
697
781
|
indexDisplay,
|
|
698
|
-
indexRoute
|
|
782
|
+
indexRoute,
|
|
783
|
+
fallback
|
|
699
784
|
);
|
|
700
785
|
sortNodes(root.children, diagnostics);
|
|
701
786
|
hoistPages(root.children, display, true);
|
|
@@ -703,7 +788,11 @@ const buildFileSystemSidebar = (
|
|
|
703
788
|
return root.children.map((child) => toNavNode(child, display));
|
|
704
789
|
};
|
|
705
790
|
|
|
706
|
-
|
|
791
|
+
/**
|
|
792
|
+
* An explicit-sidebar ref (`"guides/"`, `"/guides/index"`, `"index"`) in the
|
|
793
|
+
* slashless route form pages are keyed by (`/guides`, `/`).
|
|
794
|
+
*/
|
|
795
|
+
export const normalizeRef = (ref: string): string => {
|
|
707
796
|
if (ref === "index") {
|
|
708
797
|
return "/";
|
|
709
798
|
}
|
|
@@ -734,9 +823,36 @@ const routeForRef = (
|
|
|
734
823
|
};
|
|
735
824
|
|
|
736
825
|
/** An explicit-config sidebar item written as a bare page-ref string. */
|
|
737
|
-
const isPageRef = (item: SidebarItemConfig): item is string =>
|
|
826
|
+
export const isPageRef = (item: SidebarItemConfig): item is string =>
|
|
738
827
|
typeof item === "string";
|
|
739
828
|
|
|
829
|
+
/**
|
|
830
|
+
* Pages keyed the way explicit-sidebar refs look them up: by the
|
|
831
|
+
* locale-agnostic `translationKey` under i18n (`byLogical`), so one authored
|
|
832
|
+
* sidebar maps onto every locale's pages, else by route. Refs are authored as
|
|
833
|
+
* if mounted at root while `page.route` carries the base, so each page is also
|
|
834
|
+
* aliased under its base-less route (the `translationKey` is base-less
|
|
835
|
+
* already).
|
|
836
|
+
*/
|
|
837
|
+
export const pagesByRef = (
|
|
838
|
+
pages: PageRecord[],
|
|
839
|
+
basePath: string,
|
|
840
|
+
byLogical: boolean
|
|
841
|
+
): Map<string, PageRecord> => {
|
|
842
|
+
const byRoute = new Map(
|
|
843
|
+
pages.map((page) => [byLogical ? page.translationKey : page.route, page])
|
|
844
|
+
);
|
|
845
|
+
if (basePath && !byLogical) {
|
|
846
|
+
for (const page of pages) {
|
|
847
|
+
const bare = stripBasePath(basePath, page.route);
|
|
848
|
+
if (!byRoute.has(bare)) {
|
|
849
|
+
byRoute.set(bare, page);
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
return byRoute;
|
|
854
|
+
};
|
|
855
|
+
|
|
740
856
|
/**
|
|
741
857
|
* Convert one non-group explicit-config sidebar item (string ref, `root`, or
|
|
742
858
|
* `href`) to a nav node, or null to skip. Group items (`item.items`) are handled
|
|
@@ -822,6 +938,35 @@ const buildConfigSidebar = (
|
|
|
822
938
|
return nodes;
|
|
823
939
|
};
|
|
824
940
|
|
|
941
|
+
/**
|
|
942
|
+
* The two roots a tab's target resolves between, both based: `tabs`, the root
|
|
943
|
+
* of the space tab paths are written in (the current docs' localized root,
|
|
944
|
+
* `/fr`), and `tree`, the root this tree's pages live under — the same route,
|
|
945
|
+
* except in an archived version tree (`/fr/v1.0`).
|
|
946
|
+
*/
|
|
947
|
+
interface TabRoots {
|
|
948
|
+
tabs: string;
|
|
949
|
+
tree: string;
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
/**
|
|
953
|
+
* `path` relative to `root` (`/fr/guides` under `/fr` is `/guides`, the root
|
|
954
|
+
* itself is `/`), or undefined when it isn't under it.
|
|
955
|
+
*/
|
|
956
|
+
const pathUnder = (path: string, root: string): string | undefined => {
|
|
957
|
+
if (root === "/") {
|
|
958
|
+
return path;
|
|
959
|
+
}
|
|
960
|
+
if (path === root) {
|
|
961
|
+
return "/";
|
|
962
|
+
}
|
|
963
|
+
return path.startsWith(`${root}/`) ? path.slice(root.length) : undefined;
|
|
964
|
+
};
|
|
965
|
+
|
|
966
|
+
/** Mount a root-relative remainder (`/guides`, never `/`) under `root`. */
|
|
967
|
+
const joinRoute = (root: string, rest: string): string =>
|
|
968
|
+
root === "/" ? rest : `${root}${rest}`;
|
|
969
|
+
|
|
825
970
|
/**
|
|
826
971
|
* Resolve a tab's clickable target. A tab's `path` scopes its sidebar section
|
|
827
972
|
* but need not be a real route — a section with no index page would 404 if the
|
|
@@ -832,30 +977,40 @@ const buildConfigSidebar = (
|
|
|
832
977
|
*
|
|
833
978
|
* Routes outside the content tree are served at their own path, not under
|
|
834
979
|
* `basePath` (`pages/changelog.astro` answers `/changelog` on a `/docs`-based
|
|
835
|
-
* site), while tab paths arrive based
|
|
836
|
-
* path is neither an outside route nor
|
|
837
|
-
*
|
|
980
|
+
* site) or a locale prefix, while tab paths arrive based and localized
|
|
981
|
+
* (`/docs/fr/changelog`). So when the tab path is neither an outside route nor
|
|
982
|
+
* a tree node, its base-less and then its root-relative form (`/changelog`)
|
|
983
|
+
* are checked against the outside routes before the section's first page
|
|
984
|
+
* wins.
|
|
985
|
+
*
|
|
986
|
+
* In an archived version tree the section is looked up under the versioned
|
|
987
|
+
* root (`/v1.0/guides`), where that tree's pages live — tab paths stay in
|
|
988
|
+
* current-docs space, where nothing in the tree could match.
|
|
838
989
|
*/
|
|
839
990
|
const resolveTabHref = (
|
|
840
991
|
sidebar: NavNode[],
|
|
841
992
|
path: string,
|
|
842
993
|
extraRoutes: ReadonlySet<string>,
|
|
843
|
-
basePath: string
|
|
994
|
+
basePath: string,
|
|
995
|
+
roots: TabRoots
|
|
844
996
|
): string => {
|
|
845
997
|
if (extraRoutes.has(path)) {
|
|
846
998
|
return path;
|
|
847
999
|
}
|
|
1000
|
+
const rest = pathUnder(path, roots.tabs);
|
|
1001
|
+
const section =
|
|
1002
|
+
rest === undefined || rest === "/" ? path : joinRoute(roots.tree, rest);
|
|
848
1003
|
let first: string | undefined;
|
|
849
1004
|
const walk = (nodes: NavNode[]): boolean => {
|
|
850
1005
|
for (const node of nodes) {
|
|
851
1006
|
const { route } = node;
|
|
852
|
-
if (route ===
|
|
1007
|
+
if (route === section) {
|
|
853
1008
|
return true;
|
|
854
1009
|
}
|
|
855
1010
|
if (
|
|
856
1011
|
first === undefined &&
|
|
857
1012
|
route !== undefined &&
|
|
858
|
-
route.startsWith(`${
|
|
1013
|
+
route.startsWith(`${section}/`)
|
|
859
1014
|
) {
|
|
860
1015
|
first = route;
|
|
861
1016
|
}
|
|
@@ -866,10 +1021,16 @@ const resolveTabHref = (
|
|
|
866
1021
|
return false;
|
|
867
1022
|
};
|
|
868
1023
|
if (walk(sidebar)) {
|
|
869
|
-
return
|
|
1024
|
+
return section;
|
|
870
1025
|
}
|
|
871
1026
|
const bare = stripBasePath(basePath, path);
|
|
872
|
-
|
|
1027
|
+
if (extraRoutes.has(bare)) {
|
|
1028
|
+
return bare;
|
|
1029
|
+
}
|
|
1030
|
+
if (rest !== undefined && extraRoutes.has(rest)) {
|
|
1031
|
+
return rest;
|
|
1032
|
+
}
|
|
1033
|
+
return first ?? path;
|
|
873
1034
|
};
|
|
874
1035
|
|
|
875
1036
|
/**
|
|
@@ -884,11 +1045,13 @@ const withTabHrefs = (
|
|
|
884
1045
|
tabs: NavTab[],
|
|
885
1046
|
sidebar: NavNode[],
|
|
886
1047
|
extraRoutes: ReadonlySet<string>,
|
|
887
|
-
basePath: string
|
|
1048
|
+
basePath: string,
|
|
1049
|
+
roots: TabRoots
|
|
888
1050
|
): NavTab[] =>
|
|
889
1051
|
tabs.map((tab) => {
|
|
890
1052
|
const href =
|
|
891
|
-
tab.href ??
|
|
1053
|
+
tab.href ??
|
|
1054
|
+
resolveTabHref(sidebar, tab.path, extraRoutes, basePath, roots);
|
|
892
1055
|
return href === tab.path ? tab : { ...tab, href };
|
|
893
1056
|
});
|
|
894
1057
|
|
|
@@ -978,6 +1141,13 @@ export const buildNavigation = (
|
|
|
978
1141
|
* group path, e.g. `fr`, `v1.0`, or `v1.0/fr`.
|
|
979
1142
|
*/
|
|
980
1143
|
metaPrefix?: string;
|
|
1144
|
+
/**
|
|
1145
|
+
* Folder-meta lookup prefix of the i18n fallback locale, whose untranslated
|
|
1146
|
+
* pages pad this tree (`""` for the default locale of the current
|
|
1147
|
+
* version). A group with no meta of this locale's mirrors the fallback
|
|
1148
|
+
* locale's. Unset for the fallback locale itself, and without i18n.
|
|
1149
|
+
*/
|
|
1150
|
+
fallbackMetaPrefix?: string;
|
|
981
1151
|
/**
|
|
982
1152
|
* Prefix for shared `meta.$.*` lookups — the version dir inside a
|
|
983
1153
|
* snapshot (`v1.0`), since shared meta is locale-agnostic but still
|
|
@@ -999,6 +1169,14 @@ export const buildNavigation = (
|
|
|
999
1169
|
* scoping.
|
|
1000
1170
|
*/
|
|
1001
1171
|
localizedRoot?: string;
|
|
1172
|
+
/**
|
|
1173
|
+
* The root of the space tab paths are written in, before `basePath`: the
|
|
1174
|
+
* current docs' localized root (`"/"`, `/fr`). Defaults to
|
|
1175
|
+
* `localizedRoot`; an archived version tree passes it because its own
|
|
1176
|
+
* root is versionized (`/fr/v1.0`) while tab paths are not, so a tab's
|
|
1177
|
+
* section can be found under the version.
|
|
1178
|
+
*/
|
|
1179
|
+
tabRoot?: string;
|
|
1002
1180
|
/**
|
|
1003
1181
|
* Sink for diagnostics produced while building the tree (duplicate sidebar
|
|
1004
1182
|
* `order` values, index-page title/folder-meta-title mismatches). Pushed
|
|
@@ -1027,24 +1205,7 @@ export const buildNavigation = (
|
|
|
1027
1205
|
basePath,
|
|
1028
1206
|
options
|
|
1029
1207
|
);
|
|
1030
|
-
const byRoute =
|
|
1031
|
-
pages.map((page) => [
|
|
1032
|
-
options.refByLogical ? page.translationKey : page.route,
|
|
1033
|
-
page,
|
|
1034
|
-
])
|
|
1035
|
-
);
|
|
1036
|
-
// Explicit-sidebar refs (`"foo/index"`) are authored as if mounted at root,
|
|
1037
|
-
// but `page.route` carries the base — alias each page under its base-less
|
|
1038
|
-
// route so a bare ref still resolves. (The i18n `refByLogical` map is already
|
|
1039
|
-
// keyed by the base-less `translationKey`, so it needs no alias.)
|
|
1040
|
-
if (basePath && !options.refByLogical) {
|
|
1041
|
-
for (const page of pages) {
|
|
1042
|
-
const bare = stripBasePath(basePath, page.route);
|
|
1043
|
-
if (!byRoute.has(bare)) {
|
|
1044
|
-
byRoute.set(bare, page);
|
|
1045
|
-
}
|
|
1046
|
-
}
|
|
1047
|
-
}
|
|
1208
|
+
const byRoute = pagesByRef(pages, basePath, Boolean(options.refByLogical));
|
|
1048
1209
|
|
|
1049
1210
|
// A tab pointing at the tree root spans the whole sidebar rather than one
|
|
1050
1211
|
// section, so it must not feed tab-section hoisting. `tabs` carries final
|
|
@@ -1057,6 +1218,13 @@ export const buildNavigation = (
|
|
|
1057
1218
|
// paths stay in current-docs space, so root-tab checks use `isRootTab`
|
|
1058
1219
|
// containment, not equality.
|
|
1059
1220
|
const rootTabPath = withBasePath(basePath, options.localizedRoot ?? "/");
|
|
1221
|
+
const tabRoots: TabRoots = {
|
|
1222
|
+
tabs: withBasePath(
|
|
1223
|
+
basePath,
|
|
1224
|
+
options.tabRoot ?? options.localizedRoot ?? "/"
|
|
1225
|
+
),
|
|
1226
|
+
tree: rootTabPath,
|
|
1227
|
+
};
|
|
1060
1228
|
|
|
1061
1229
|
// Emitted here, before the sidebar-mode branch: an explicit config sidebar
|
|
1062
1230
|
// ignores a stray `sidebar.display` just like the filesystem sidebar does
|
|
@@ -1080,7 +1248,7 @@ export const buildNavigation = (
|
|
|
1080
1248
|
root: rootTabPath,
|
|
1081
1249
|
selectors,
|
|
1082
1250
|
sidebar,
|
|
1083
|
-
tabs: withTabHrefs(tabs, sidebar, extraRoutes, basePath),
|
|
1251
|
+
tabs: withTabHrefs(tabs, sidebar, extraRoutes, basePath, tabRoots),
|
|
1084
1252
|
};
|
|
1085
1253
|
}
|
|
1086
1254
|
|
|
@@ -1094,7 +1262,8 @@ export const buildNavigation = (
|
|
|
1094
1262
|
new Set(
|
|
1095
1263
|
tabs.flatMap((tab) => (isRootTab(tab, rootTabPath) ? [] : [tab.path]))
|
|
1096
1264
|
),
|
|
1097
|
-
diagnostics
|
|
1265
|
+
diagnostics,
|
|
1266
|
+
options.fallbackMetaPrefix
|
|
1098
1267
|
);
|
|
1099
1268
|
return {
|
|
1100
1269
|
actions,
|
|
@@ -1103,6 +1272,6 @@ export const buildNavigation = (
|
|
|
1103
1272
|
root: rootTabPath,
|
|
1104
1273
|
selectors,
|
|
1105
1274
|
sidebar,
|
|
1106
|
-
tabs: withTabHrefs(tabs, sidebar, extraRoutes, basePath),
|
|
1275
|
+
tabs: withTabHrefs(tabs, sidebar, extraRoutes, basePath, tabRoots),
|
|
1107
1276
|
};
|
|
1108
1277
|
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A file or folder name's ordering prefix: digits, then a `-`, `_`, or `.`
|
|
3
|
+
* separator (`01-intro`, `2_setup`). It sorts the generated sidebar and is
|
|
4
|
+
* dropped from the route.
|
|
5
|
+
*/
|
|
6
|
+
const ORDERING_PREFIX = /^(?<order>\d+)[-_.]/u;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Digit-led names that are names, not orders, and stay whole: a version
|
|
10
|
+
* (`1.2.0`, `2.0`), which a `1.` prefix would split into `2.0`, and an ISO date
|
|
11
|
+
* (`2024-01-05`, `2024-01-05-first-post`), whose year would sort the sidebar
|
|
12
|
+
* and leave only `01-05…` of the route.
|
|
13
|
+
*/
|
|
14
|
+
const NOT_AN_ORDER = /^(?:\d+\.\d|\d{4}-\d{2}-\d{2}(?:[-_.]|$))/u;
|
|
15
|
+
|
|
16
|
+
/** The digits of `name`'s ordering prefix (`01` of `01-intro`), if it has one. */
|
|
17
|
+
export const orderingPrefix = (name: string): string | undefined =>
|
|
18
|
+
NOT_AN_ORDER.test(name)
|
|
19
|
+
? undefined
|
|
20
|
+
: name.match(ORDERING_PREFIX)?.groups?.order;
|
|
21
|
+
|
|
22
|
+
/** `name` without its ordering prefix (`01-intro` -> `intro`). */
|
|
23
|
+
export const stripOrderingPrefix = (name: string): string => {
|
|
24
|
+
const order = orderingPrefix(name);
|
|
25
|
+
// The prefix is the digits plus their one-character separator.
|
|
26
|
+
return order === undefined ? name : name.slice(order.length + 1);
|
|
27
|
+
};
|
|
@@ -184,6 +184,7 @@ const normalizeLoadedEntries = (
|
|
|
184
184
|
source: {
|
|
185
185
|
monolingual: source.monolingual,
|
|
186
186
|
name: source.name,
|
|
187
|
+
orderedNames: source.orderedNames,
|
|
187
188
|
prefix: source.prefix,
|
|
188
189
|
staged: source.staged,
|
|
189
190
|
},
|
|
@@ -215,6 +216,19 @@ const isLogoShorthand = (
|
|
|
215
216
|
const logoHref = (logo: ResolvedConfig["logo"]): string =>
|
|
216
217
|
logo === undefined || isLogoShorthand(logo) ? "/" : (logo.href ?? "/");
|
|
217
218
|
|
|
219
|
+
/** Narrows the banner config's text shorthand from its object form. */
|
|
220
|
+
const isBannerShorthand = (
|
|
221
|
+
banner: NonNullable<ResolvedConfig["banner"]>
|
|
222
|
+
): banner is string => typeof banner === "string";
|
|
223
|
+
|
|
224
|
+
/** The configured banner link target, when the banner has a link. */
|
|
225
|
+
const bannerLinkHref = (
|
|
226
|
+
banner: ResolvedConfig["banner"]
|
|
227
|
+
): string | undefined =>
|
|
228
|
+
banner === undefined || isBannerShorthand(banner)
|
|
229
|
+
? undefined
|
|
230
|
+
: banner.link?.href;
|
|
231
|
+
|
|
218
232
|
export const scanProject = async (
|
|
219
233
|
root: string,
|
|
220
234
|
options: {
|
|
@@ -255,7 +269,14 @@ export const scanProject = async (
|
|
|
255
269
|
const metaSources: FolderMetaSource[] = sources.flatMap((source) =>
|
|
256
270
|
source.staged || !source.contentRoot
|
|
257
271
|
? []
|
|
258
|
-
: [
|
|
272
|
+
: [
|
|
273
|
+
{
|
|
274
|
+
exclude: source.exclude,
|
|
275
|
+
include: source.include,
|
|
276
|
+
prefix: source.prefix,
|
|
277
|
+
root: source.contentRoot,
|
|
278
|
+
},
|
|
279
|
+
]
|
|
259
280
|
);
|
|
260
281
|
|
|
261
282
|
// Run every source's `load()` in parallel, then funnel each entry through the
|
|
@@ -382,6 +403,7 @@ export const scanProject = async (
|
|
|
382
403
|
? await discoverPages(context.pagesRoot)
|
|
383
404
|
: [];
|
|
384
405
|
const graph = buildContentGraph(pages, {
|
|
406
|
+
bannerHref: bannerLinkHref(config.banner),
|
|
385
407
|
basePath: config.basePath,
|
|
386
408
|
brandHref: logoHref(config.logo),
|
|
387
409
|
extraRoutes: new Set([
|