blume 2.0.1 → 2.0.3
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 +147 -0
- package/dist/cli/{chunk-qwsrynx5.js → chunk-1d7ve1dm.js} +38 -19
- package/dist/cli/{chunk-qwsrynx5.js.map → chunk-1d7ve1dm.js.map} +4 -4
- package/dist/cli/{chunk-s6jhgk0q.js → chunk-2eytanqx.js} +2 -2
- package/dist/cli/{chunk-kdp5q7ke.js → chunk-2hsdwb9n.js} +18 -19
- package/dist/cli/{chunk-kdp5q7ke.js.map → chunk-2hsdwb9n.js.map} +2 -2
- package/dist/cli/{chunk-hdpx1tax.js → chunk-2z928egk.js} +5 -5
- package/dist/cli/{chunk-jwyddg7y.js → chunk-35d4wj9f.js} +30 -24
- package/dist/cli/chunk-35d4wj9f.js.map +15 -0
- package/dist/cli/{chunk-ah61y8py.js → chunk-364znk6q.js} +2 -2
- package/dist/cli/{chunk-jts8mvcz.js → chunk-3em5wd2y.js} +25 -6
- package/dist/cli/{chunk-jts8mvcz.js.map → chunk-3em5wd2y.js.map} +3 -3
- package/dist/cli/{chunk-s1p84fyh.js → chunk-5f86nr5m.js} +63 -22
- package/dist/cli/chunk-5f86nr5m.js.map +11 -0
- package/dist/cli/{chunk-2hn4b8z7.js → chunk-5m5nmvyq.js} +170 -76
- package/dist/cli/chunk-5m5nmvyq.js.map +13 -0
- package/dist/cli/{chunk-epjnccmv.js → chunk-5xvm6tfj.js} +18 -15
- package/dist/cli/chunk-5xvm6tfj.js.map +10 -0
- package/dist/cli/{chunk-fz5wtpmh.js → chunk-6dsbexzp.js} +18 -15
- package/dist/cli/chunk-6dsbexzp.js.map +10 -0
- package/dist/cli/{chunk-vtk4a6dg.js → chunk-8ktnccpt.js} +1 -1
- package/dist/cli/{chunk-6hsn950k.js → chunk-9t7a85s3.js} +178 -48
- package/dist/cli/chunk-9t7a85s3.js.map +10 -0
- package/dist/cli/{chunk-fxypxtvm.js → chunk-a58773jm.js} +2 -2
- package/dist/cli/{chunk-mb2919y2.js → chunk-acanzt5p.js} +17 -6
- package/dist/cli/chunk-acanzt5p.js.map +10 -0
- package/dist/cli/{chunk-27g6wdth.js → chunk-akbpwfxc.js} +90 -26
- package/dist/cli/chunk-akbpwfxc.js.map +10 -0
- package/dist/cli/{chunk-wm7js3j9.js → chunk-b07cmahc.js} +2 -2
- package/dist/cli/{chunk-wgm7m9qk.js → chunk-c8chx29p.js} +179 -51
- package/dist/cli/chunk-c8chx29p.js.map +36 -0
- package/dist/cli/{chunk-qs4q5p4e.js → chunk-crgn1q09.js} +32 -14
- package/dist/cli/chunk-crgn1q09.js.map +10 -0
- package/dist/cli/chunk-e04dxsz1.js +39 -0
- package/dist/cli/chunk-e04dxsz1.js.map +10 -0
- package/dist/cli/{chunk-yt5n7ppj.js → chunk-ey84smr6.js} +17 -7
- package/dist/cli/chunk-ey84smr6.js.map +10 -0
- package/dist/cli/{chunk-f2z5v128.js → chunk-g4hq16wv.js} +14 -15
- package/dist/cli/{chunk-f2z5v128.js.map → chunk-g4hq16wv.js.map} +2 -2
- package/dist/cli/{chunk-fa25z98p.js → chunk-ga0pf4aj.js} +9 -8
- package/dist/cli/chunk-ga0pf4aj.js.map +11 -0
- package/dist/cli/{chunk-kpf8rrjc.js → chunk-hm3vjy5s.js} +109 -58
- package/dist/cli/chunk-hm3vjy5s.js.map +19 -0
- package/dist/cli/{chunk-fs23ddbb.js → chunk-j85vccga.js} +625 -750
- package/dist/cli/chunk-j85vccga.js.map +36 -0
- package/dist/cli/{chunk-ch6g3ar0.js → chunk-p3v96n38.js} +6 -6
- package/dist/cli/{chunk-ch6g3ar0.js.map → chunk-p3v96n38.js.map} +3 -3
- package/dist/cli/{chunk-q5163e60.js → chunk-p73c0m7w.js} +21 -19
- package/dist/cli/chunk-p73c0m7w.js.map +11 -0
- package/dist/cli/{chunk-dh8cwk36.js → chunk-pehfxfta.js} +24 -9
- package/dist/cli/chunk-pehfxfta.js.map +10 -0
- package/dist/cli/{chunk-qkqwkpte.js → chunk-pv29h0wf.js} +3688 -1313
- package/dist/cli/chunk-pv29h0wf.js.map +190 -0
- package/dist/cli/{chunk-6vm74dry.js → chunk-q58y5e6a.js} +10 -10
- package/dist/cli/{chunk-6vm74dry.js.map → chunk-q58y5e6a.js.map} +3 -3
- package/dist/cli/{chunk-zxcczpyx.js → chunk-r20tn01b.js} +1 -1
- package/dist/cli/{chunk-m3vmjgmq.js → chunk-r9rcc4w7.js} +16 -8
- package/dist/cli/chunk-r9rcc4w7.js.map +10 -0
- package/dist/cli/{chunk-5shv93fd.js → chunk-tkacnehg.js} +2 -2
- package/dist/cli/{chunk-zxh4d9vy.js → chunk-tzmab476.js} +4 -4
- package/dist/cli/{chunk-yw7dm696.js → chunk-vg9r4eb9.js} +24 -11
- package/dist/cli/chunk-vg9r4eb9.js.map +14 -0
- package/dist/cli/{chunk-79jhk4py.js → chunk-wrr3j9w9.js} +260 -132
- package/dist/cli/chunk-wrr3j9w9.js.map +35 -0
- package/dist/cli/{chunk-6crbhc3x.js → chunk-yfyb25rh.js} +54 -27
- package/dist/cli/chunk-yfyb25rh.js.map +14 -0
- package/dist/cli/index.js +162 -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/link-headers.d.ts +8 -1
- package/dist/types/ai/openapi-components.d.ts +5 -2
- package/dist/types/ai/relative-links.d.ts +11 -4
- package/dist/types/ai/skills.d.ts +4 -1
- package/dist/types/ai/static-expression.d.ts +28 -0
- package/dist/types/ai/tar.d.ts +1 -3
- package/dist/types/analytics/databuddy.d.ts +43 -0
- package/dist/types/analytics/index.d.ts +4 -0
- package/dist/types/analytics/one-dollar-stats.d.ts +48 -0
- package/dist/types/analytics/schema.d.ts +28 -0
- package/dist/types/astro/integration.d.ts +3 -2
- 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 +24 -1
- package/dist/types/core/config-input.d.ts +13 -11
- package/dist/types/core/config.d.ts +2 -2
- package/dist/types/core/directive-diagnostics.d.ts +12 -0
- package/dist/types/core/graph.d.ts +2 -0
- package/dist/types/core/heading-markers.d.ts +5 -7
- package/dist/types/core/i18n-ui.d.ts +31 -0
- package/dist/types/core/i18n.d.ts +9 -1
- package/dist/types/core/last-modified.d.ts +10 -0
- 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 +13 -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 +59 -13
- package/dist/types/core/sources/github-releases.d.ts +5 -0
- package/dist/types/core/sources/lower.d.ts +38 -13
- package/dist/types/core/sources/normalize.d.ts +23 -2
- 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/sources/watch.d.ts +12 -5
- package/dist/types/core/standard-schema.d.ts +5 -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/artifacts.d.ts +6 -4
- package/dist/types/deploy/cloudflare-negotiation.d.ts +3 -2
- package/dist/types/deploy/headers.d.ts +36 -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 +15 -0
- package/dist/types/deploy/platforms/vercel.d.ts +3 -2
- package/dist/types/deploy/redirects.d.ts +31 -15
- package/dist/types/deploy/vercel-negotiation.d.ts +3 -2
- package/dist/types/markdown/directives.d.ts +62 -0
- package/dist/types/markdown/features.d.ts +21 -0
- package/dist/types/markdown/mdast.d.ts +63 -0
- package/dist/types/openapi/asyncapi.d.ts +4 -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 +20 -9
- package/docs/08-faq.mdx +10 -3
- package/docs/advanced/changelog.mdx +1 -1
- package/docs/advanced/custom-pages.mdx +7 -5
- package/docs/cli/audit.mdx +19 -3
- package/docs/cli/doctor.mdx +2 -2
- package/docs/cli/evals.mdx +4 -4
- package/docs/cli/index.mdx +4 -1
- package/docs/cli/translate.mdx +4 -4
- package/docs/cli/version.mdx +1 -1
- package/docs/configuration/analytics.mdx +42 -2
- package/docs/configuration/assistant.mdx +1 -1
- package/docs/configuration/customization.mdx +6 -3
- package/docs/configuration/index.mdx +5 -3
- package/docs/configuration/search.mdx +2 -2
- package/docs/content/components.mdx +2 -2
- package/docs/content/frontmatter.mdx +5 -1
- package/docs/content/i18n.mdx +3 -1
- package/docs/content/index.mdx +8 -4
- package/docs/content/islands.mdx +1 -1
- package/docs/content/meta.mdx +7 -3
- package/docs/content/navigation.mdx +29 -4
- package/docs/content/sources.mdx +18 -16
- package/docs/content/syntax.mdx +14 -0
- package/docs/content/versioning.mdx +2 -1
- package/docs/discoverability/agent-discovery.mdx +22 -8
- package/docs/discoverability/index.mdx +4 -3
- package/docs/discoverability/llms-txt.mdx +2 -5
- package/docs/discoverability/markdown.mdx +6 -4
- package/docs/discoverability/mcp.mdx +4 -0
- package/docs/discoverability/metadata.mdx +3 -2
- package/docs/discoverability/open-graph.mdx +8 -4
- package/docs/discoverability/rss.mdx +4 -2
- package/docs/references/asyncapi.mdx +2 -2
- package/docs/references/graphql.mdx +2 -2
- package/docs/references/openapi.mdx +6 -4
- 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/fumadocs.md +1 -1
- 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 +2 -2
- package/skills/blume-migrate/references/starlight.md +5 -5
- package/skills/blume-migrate/scripts/mintlify-codemod.mjs +9 -4
- package/skills/blume-update-docs/references/audit-checklist.md +1 -1
- package/src/ai/agent-readability.ts +2 -2
- package/src/ai/agent-surface.ts +56 -0
- package/src/ai/ai-catalog.ts +6 -2
- package/src/ai/api/handlers.ts +2 -2
- package/src/ai/api/spec.ts +17 -4
- package/src/ai/api-catalog.ts +13 -3
- package/src/ai/ask-context.ts +14 -2
- package/src/ai/ask-data.ts +26 -12
- package/src/ai/changelog-markdown.ts +2 -2
- package/src/ai/component-markdown.ts +43 -37
- package/src/ai/link-headers.ts +22 -8
- package/src/ai/llms.ts +22 -10
- package/src/ai/markdown.ts +23 -11
- package/src/ai/mcp/discovery.ts +2 -2
- package/src/ai/mcp/query.ts +2 -2
- package/src/ai/mcp/server.ts +80 -9
- package/src/ai/openapi-components.ts +22 -6
- package/src/ai/relative-links.ts +99 -15
- package/src/ai/serializers.ts +2 -1
- package/src/ai/skills.ts +14 -1
- package/src/ai/static-expression.ts +416 -0
- package/src/ai/tar.ts +139 -12
- package/src/ai/visibility.ts +45 -14
- package/src/analytics/databuddy.ts +67 -0
- package/src/analytics/head.ts +8 -0
- package/src/analytics/index.ts +7 -0
- package/src/analytics/one-dollar-stats.ts +86 -0
- package/src/analytics/posthog.ts +21 -3
- package/src/analytics/schema.ts +4 -0
- package/src/astro/generate.ts +72 -28
- package/src/astro/include-hmr.ts +31 -12
- package/src/astro/include-refresh.ts +19 -8
- package/src/astro/integration.ts +108 -14
- package/src/astro/runtime-modules.ts +28 -13
- package/src/astro/templates.ts +139 -57
- 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 +22 -1
- 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/graph.ts +3 -1
- package/src/audit/report.ts +54 -17
- package/src/audit/run.ts +13 -9
- package/src/audit/snapshot.ts +5 -0
- package/src/audit/types.ts +18 -0
- package/src/audit/url.ts +37 -6
- package/src/cli/args.ts +32 -0
- package/src/cli/build-failure.ts +50 -0
- package/src/cli/commands/audit.ts +11 -1
- package/src/cli/commands/build.ts +19 -5
- package/src/cli/commands/check.ts +2 -0
- package/src/cli/commands/doctor.ts +3 -1
- package/src/cli/commands/eval.ts +20 -18
- 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 +18 -18
- package/src/cli/commands/upgrade.ts +11 -0
- package/src/cli/commands/validate.ts +3 -1
- package/src/cli/dev-lock.ts +157 -37
- 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/cli/report-format.ts +11 -6
- package/src/components/colors.ts +19 -0
- package/src/components/content/AccordionItem.astro +26 -22
- package/src/components/content/Badge.astro +7 -12
- package/src/components/content/Card.astro +2 -2
- package/src/components/content/Component.astro +11 -5
- 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/Tab.astro +0 -1
- package/src/components/content/Tabs.astro +58 -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 +20 -0
- package/src/components/content/base-href.ts +14 -22
- package/src/components/content/inline-markdown.ts +27 -7
- package/src/components/copy-feedback.ts +35 -8
- package/src/components/islands/assistant.tsx +37 -6
- package/src/components/islands/base-path.ts +47 -10
- package/src/components/islands/hooks.ts +42 -21
- package/src/components/islands/webmcp.ts +16 -10
- package/src/components/layout/Banner.astro +23 -4
- package/src/components/layout/Breadcrumbs.astro +5 -2
- package/src/components/layout/DiscoveryLinks.astro +9 -5
- package/src/components/layout/Header.astro +23 -10
- package/src/components/layout/LanguageSwitcher.astro +2 -2
- package/src/components/layout/Logo.astro +5 -0
- package/src/components/layout/NavSelector.astro +8 -3
- package/src/components/layout/NavTabMenu.astro +133 -0
- package/src/components/layout/NavTree.astro +31 -12
- package/src/components/layout/NavTreeCache.astro +5 -2
- package/src/components/layout/NavTreeScript.astro +45 -5
- package/src/components/layout/PageActions.astro +4 -3
- package/src/components/layout/PageLayout.astro +28 -13
- package/src/components/layout/Pagination.astro +3 -3
- package/src/components/layout/ReferenceLayout.astro +4 -1
- package/src/components/layout/RootLayout.astro +25 -12
- package/src/components/layout/Search.astro +38 -14
- package/src/components/layout/VersionBanner.astro +2 -2
- package/src/components/layout/analytics-client.ts +14 -0
- package/src/components/layout/toc-active.ts +41 -0
- package/src/components/layout/toc-element.ts +8 -14
- package/src/components/openapi/ApiTagOperations.astro +2 -2
- package/src/components/openapi/AsyncApiOperation.astro +7 -4
- package/src/components/openapi/GraphqlChip.astro +2 -2
- package/src/components/openapi/GraphqlType.astro +11 -3
- package/src/components/openapi/MessageComposer.astro +1 -1
- package/src/components/openapi/Operation.astro +23 -5
- package/src/components/openapi/PanelTabs.astro +4 -1
- package/src/components/openapi/Playground.astro +8 -2
- package/src/components/openapi/RequestPanel.astro +13 -6
- package/src/components/openapi/SchemaProperty.astro +11 -33
- package/src/components/openapi/SchemaTable.astro +34 -52
- package/src/components/openapi/async.ts +38 -6
- package/src/components/openapi/helpers.ts +100 -12
- package/src/components/openapi/message-composer.ts +14 -1
- package/src/components/openapi/message-model.ts +12 -2
- package/src/components/openapi/message.ts +21 -3
- package/src/components/openapi/operation-model.ts +119 -17
- package/src/components/openapi/panel.ts +31 -5
- package/src/components/openapi/param-style.ts +181 -0
- package/src/components/openapi/playground-client.ts +110 -23
- package/src/components/openapi/playground-schema.ts +25 -6
- package/src/components/openapi/request.ts +192 -26
- package/src/components/openapi/schema-tree.ts +209 -0
- package/src/components/openapi/snippets.ts +113 -19
- package/src/components/openapi/validate-json.ts +1 -1
- package/src/components/openapi/ws-client.ts +18 -2
- package/src/core/base-path.ts +69 -8
- package/src/core/config-input.ts +13 -11
- package/src/core/config.ts +18 -4
- package/src/core/diagnostics.ts +219 -30
- package/src/core/directive-diagnostics.ts +99 -0
- package/src/core/frontmatter.ts +21 -18
- package/src/core/graph.ts +120 -11
- package/src/core/heading-markers.ts +5 -18
- package/src/core/i18n-ui.ts +39 -2
- package/src/core/i18n.ts +44 -5
- package/src/core/last-modified.ts +25 -3
- package/src/core/links.ts +7 -4
- package/src/core/load-module.ts +20 -0
- package/src/core/locale-links.ts +22 -18
- package/src/core/manifest.ts +3 -2
- package/src/core/meta.ts +89 -12
- package/src/core/nav-diagnostics.ts +56 -1
- package/src/core/navigation.ts +272 -83
- package/src/core/ordering-prefix.ts +27 -0
- package/src/core/project-graph.ts +50 -6
- package/src/core/safe-href.ts +53 -1
- package/src/core/safe-links.ts +11 -2
- package/src/core/schema.ts +94 -18
- package/src/core/sources/assets.ts +83 -41
- package/src/core/sources/contentful-rich-text.ts +28 -20
- package/src/core/sources/contentful.ts +25 -12
- package/src/core/sources/filesystem.ts +25 -3
- package/src/core/sources/github-releases.ts +131 -11
- package/src/core/sources/lexical.ts +30 -20
- package/src/core/sources/lower.ts +276 -56
- package/src/core/sources/mdx-remote.ts +52 -16
- package/src/core/sources/normalize.ts +456 -119
- package/src/core/sources/notion.ts +77 -34
- package/src/core/sources/obsidian.ts +40 -9
- package/src/core/sources/payload.ts +1 -0
- package/src/core/sources/portable-text.ts +85 -49
- 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 +28 -19
- package/src/core/sources/strapi.ts +1 -0
- package/src/core/sources/types.ts +31 -0
- package/src/core/sources/watch.ts +20 -7
- package/src/core/standard-schema.ts +10 -6
- 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 +69 -17
- 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 +52 -11
- package/src/deploy/cloudflare-negotiation.ts +27 -16
- package/src/deploy/headers.ts +75 -55
- package/src/deploy/node-headers.ts +148 -27
- package/src/deploy/platforms/cloudflare.ts +13 -3
- package/src/deploy/platforms/netlify.ts +83 -5
- package/src/deploy/platforms/node.ts +8 -5
- package/src/deploy/platforms/static.ts +2 -0
- package/src/deploy/platforms/types.ts +15 -0
- package/src/deploy/platforms/vercel.ts +10 -3
- package/src/deploy/redirects.ts +68 -21
- package/src/deploy/robots.ts +2 -2
- package/src/deploy/rss.ts +4 -3
- package/src/deploy/sitemap.ts +7 -5
- 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 +74 -29
- package/src/markdown/directives.ts +242 -36
- package/src/markdown/features.ts +17 -0
- package/src/markdown/index.ts +13 -9
- package/src/markdown/mdast.ts +5 -2
- package/src/markdown/relative-links.ts +15 -27
- package/src/markdown/route-snapshot.ts +37 -0
- package/src/og/card.ts +149 -9
- package/src/og/derive.ts +156 -5
- package/src/og/index.ts +1 -0
- package/src/openapi/asyncapi.ts +4 -2
- package/src/openapi/graphql-build.ts +28 -2
- package/src/openapi/model.ts +99 -27
- package/src/openapi/proxy.ts +63 -10
- package/src/openapi/render-mdx.ts +51 -3
- package/src/registry/eject.ts +297 -43
- package/src/search/adapters/version-scope.ts +30 -0
- package/src/search/documents.ts +89 -27
- package/src/search/popular.ts +2 -1
- package/src/search/sync/algolia.ts +36 -2
- package/src/seo/jsonld.ts +7 -3
- 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 +76 -24
- package/src/translate/run.ts +14 -8
- package/src/translate/validate.ts +14 -2
- package/src/translate/work-list.ts +91 -19
- package/src/upgrade/upgrade.ts +36 -4
- package/dist/cli/chunk-27g6wdth.js.map +0 -10
- 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-ce574jw2.js +0 -23
- package/dist/cli/chunk-ce574jw2.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-jwyddg7y.js.map +0 -15
- 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-yw7dm696.js.map +0 -14
- /package/dist/cli/{chunk-s6jhgk0q.js.map → chunk-2eytanqx.js.map} +0 -0
- /package/dist/cli/{chunk-hdpx1tax.js.map → chunk-2z928egk.js.map} +0 -0
- /package/dist/cli/{chunk-ah61y8py.js.map → chunk-364znk6q.js.map} +0 -0
- /package/dist/cli/{chunk-vtk4a6dg.js.map → chunk-8ktnccpt.js.map} +0 -0
- /package/dist/cli/{chunk-fxypxtvm.js.map → chunk-a58773jm.js.map} +0 -0
- /package/dist/cli/{chunk-wm7js3j9.js.map → chunk-b07cmahc.js.map} +0 -0
- /package/dist/cli/{chunk-zxcczpyx.js.map → chunk-r20tn01b.js.map} +0 -0
- /package/dist/cli/{chunk-5shv93fd.js.map → chunk-tkacnehg.js.map} +0 -0
- /package/dist/cli/{chunk-zxh4d9vy.js.map → chunk-tzmab476.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,27 +94,50 @@ 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
|
|
|
118
|
+
/**
|
|
119
|
+
* The name inside a group folder's parentheses — `(guides)`, or `01-(guides)`
|
|
120
|
+
* with its ordering prefix outside them — else undefined. A prefix written
|
|
121
|
+
* inside (`(01-guides)`) stays in the name, where it sorts and drops like any
|
|
122
|
+
* folder's.
|
|
123
|
+
*/
|
|
124
|
+
const groupName = (raw: string): string | undefined =>
|
|
125
|
+
(raw.match(GROUP_FOLDER) ?? stripOrderingPrefix(raw).match(GROUP_FOLDER))
|
|
126
|
+
?.groups?.label;
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* A folder's sidebar order: its ordering prefix, or — for a group folder
|
|
130
|
+
* without one outside the parentheses — the prefix inside them, so
|
|
131
|
+
* `01-(guides)` and `(01-guides)` both sort like `01-guides`.
|
|
132
|
+
*/
|
|
133
|
+
const folderOrder = (raw: string): number =>
|
|
134
|
+
numericOrder(
|
|
135
|
+
orderingPrefix(raw) === undefined ? (groupName(raw) ?? raw) : raw
|
|
136
|
+
);
|
|
137
|
+
|
|
113
138
|
/** The nav key of a raw path segment: group label or numeric-stripped name. */
|
|
114
|
-
const segmentKey = (raw: string): string =>
|
|
115
|
-
|
|
116
|
-
return (group ?? raw).replace(NUMERIC_PREFIX, "");
|
|
117
|
-
};
|
|
139
|
+
const segmentKey = (raw: string): string =>
|
|
140
|
+
stripOrderingPrefix(groupName(raw) ?? raw);
|
|
118
141
|
|
|
119
142
|
/**
|
|
120
143
|
* Whether a filename stem is a directory index, ignoring an ordering prefix:
|
|
@@ -122,7 +145,7 @@ const segmentKey = (raw: string): string => {
|
|
|
122
145
|
* exactly like `index` and must be treated as one here too.
|
|
123
146
|
*/
|
|
124
147
|
const isIndexStem = (stem: string): boolean =>
|
|
125
|
-
stem
|
|
148
|
+
stripOrderingPrefix(stem) === "index";
|
|
126
149
|
|
|
127
150
|
/** A filename's stem: the name with its extension stripped. */
|
|
128
151
|
const stemOf = (filename: string): string =>
|
|
@@ -158,6 +181,8 @@ interface MutablePage {
|
|
|
158
181
|
* so they're excluded from the duplicate-order diagnostic.
|
|
159
182
|
*/
|
|
160
183
|
orderIsAuthored: boolean;
|
|
184
|
+
/** Whether `order` is the node's position in its folder meta `pages` list. */
|
|
185
|
+
ranked?: boolean;
|
|
161
186
|
}
|
|
162
187
|
|
|
163
188
|
interface MutableGroup {
|
|
@@ -166,6 +191,12 @@ interface MutableGroup {
|
|
|
166
191
|
path: string;
|
|
167
192
|
/** The group's URL path (folder route prefix); set as pages are inserted. */
|
|
168
193
|
routePath?: string;
|
|
194
|
+
/**
|
|
195
|
+
* The group's URL path read off its folder names, from a page whose
|
|
196
|
+
* frontmatter `slug` moved it off the folder's path. Fills `routePath` only
|
|
197
|
+
* when no other page under the folder supplies one.
|
|
198
|
+
*/
|
|
199
|
+
folderPath?: string;
|
|
169
200
|
/** The folder's index page route, when it has one; the group row's link. */
|
|
170
201
|
route?: string;
|
|
171
202
|
label: string;
|
|
@@ -173,6 +204,8 @@ interface MutableGroup {
|
|
|
173
204
|
collapsed?: boolean;
|
|
174
205
|
display?: SidebarDisplay;
|
|
175
206
|
order: number;
|
|
207
|
+
/** Whether `order` is the node's position in its folder meta `pages` list. */
|
|
208
|
+
ranked?: boolean;
|
|
176
209
|
children: MutableNode[];
|
|
177
210
|
index: Map<string, MutableGroup>;
|
|
178
211
|
}
|
|
@@ -206,8 +239,8 @@ const ensureGroup = (
|
|
|
206
239
|
const group = createGroup(
|
|
207
240
|
segmentKey(rawSegment),
|
|
208
241
|
path,
|
|
209
|
-
humanize(rawSegment
|
|
210
|
-
|
|
242
|
+
humanize(groupName(rawSegment) ?? rawSegment),
|
|
243
|
+
folderOrder(rawSegment)
|
|
211
244
|
);
|
|
212
245
|
parent.index.set(rawSegment, group);
|
|
213
246
|
parent.children.push(group);
|
|
@@ -240,7 +273,8 @@ const pageOrder = (page: PageRecord, filename: string): PageOrder => {
|
|
|
240
273
|
}
|
|
241
274
|
}
|
|
242
275
|
// An undated changelog entry's numeric filename prefix is usually a date
|
|
243
|
-
// (`
|
|
276
|
+
// (`20240105-release.md`) rather than a rank, so it is derived too. An ISO
|
|
277
|
+
// date (`2024-01-05-release.md`) is no ordering prefix at all.
|
|
244
278
|
const order = numericOrder(filename);
|
|
245
279
|
return {
|
|
246
280
|
order,
|
|
@@ -260,6 +294,17 @@ const metaKey = (path: string, metaPrefix: string): string => {
|
|
|
260
294
|
return path ? `${metaPrefix}/${path}` : metaPrefix;
|
|
261
295
|
};
|
|
262
296
|
|
|
297
|
+
/**
|
|
298
|
+
* What an i18n locale's tree mirrors from the fallback locale for a folder it
|
|
299
|
+
* has no meta of its own for: the fallback locale's folder meta, and the
|
|
300
|
+
* `sidebar.display` of a fallback-filled index page. Empty for the fallback
|
|
301
|
+
* locale itself and for single-locale sites.
|
|
302
|
+
*/
|
|
303
|
+
interface FallbackFolderMeta {
|
|
304
|
+
indexDisplay: Map<string, SidebarDisplay>;
|
|
305
|
+
meta: (path: string) => FolderMeta | undefined;
|
|
306
|
+
}
|
|
307
|
+
|
|
263
308
|
/**
|
|
264
309
|
* Apply folder meta (title/order/icon/collapsed/display and explicit page
|
|
265
310
|
* order), plus the index-frontmatter display sugar collected per folder path.
|
|
@@ -271,41 +316,40 @@ const applyFolderMeta = (
|
|
|
271
316
|
metaPrefix: string,
|
|
272
317
|
sharedMetaPrefix: string,
|
|
273
318
|
indexDisplay: Map<string, SidebarDisplay>,
|
|
274
|
-
indexRoute: Map<string, string
|
|
319
|
+
indexRoute: Map<string, string>,
|
|
320
|
+
fallback: FallbackFolderMeta
|
|
275
321
|
): void => {
|
|
276
322
|
// A folder with an index page links its group row to it — the same shape
|
|
277
323
|
// as an explicit-config group's `root`, and the only sidebar link to the
|
|
278
324
|
// section's own page once the index row is hidden. Index-less folders keep
|
|
279
325
|
// no link: their row would 404.
|
|
280
326
|
group.route = indexRoute.get(group.path);
|
|
327
|
+
group.routePath ??= group.folderPath;
|
|
281
328
|
// Locale-specific meta wins; a shared `meta.$.*` (keyed by the locale-stripped
|
|
282
329
|
// group path — version-prefixed inside a snapshot) applies to every locale
|
|
283
330
|
// otherwise.
|
|
284
|
-
const
|
|
331
|
+
const localeMeta =
|
|
285
332
|
folderMeta.get(metaKey(group.path, metaPrefix)) ??
|
|
286
333
|
sharedMeta.get(metaKey(group.path, sharedMetaPrefix));
|
|
334
|
+
// A locale with neither mirrors the fallback locale's group, the way its
|
|
335
|
+
// tree mirrors that locale's untranslated pages: the fallback's folder
|
|
336
|
+
// meta, and — ahead of it, as in the fallback's own tree — the display its
|
|
337
|
+
// fallback-filled index page sets. A locale's own meta owns its group, so
|
|
338
|
+
// the fallback's index frontmatter never overrides it.
|
|
339
|
+
const mirrored = localeMeta === undefined;
|
|
340
|
+
const meta = localeMeta ?? fallback.meta(group.path);
|
|
287
341
|
// The group's own render mode, resolved index frontmatter first, then folder
|
|
288
342
|
// meta; `toNavNode` falls back to the global mode. Applies to this group
|
|
289
343
|
// only — nested subgroups resolve their own value through the same chain.
|
|
290
|
-
group.display =
|
|
344
|
+
group.display =
|
|
345
|
+
indexDisplay.get(group.path) ??
|
|
346
|
+
(mirrored ? fallback.indexDisplay.get(group.path) : undefined) ??
|
|
347
|
+
meta?.display;
|
|
291
348
|
if (meta) {
|
|
292
349
|
group.label = meta.title ?? group.label;
|
|
293
350
|
group.icon = meta.icon ?? group.icon;
|
|
294
351
|
group.order = meta.order ?? group.order;
|
|
295
352
|
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
353
|
}
|
|
310
354
|
|
|
311
355
|
for (const child of group.children) {
|
|
@@ -317,10 +361,29 @@ const applyFolderMeta = (
|
|
|
317
361
|
metaPrefix,
|
|
318
362
|
sharedMetaPrefix,
|
|
319
363
|
indexDisplay,
|
|
320
|
-
indexRoute
|
|
364
|
+
indexRoute,
|
|
365
|
+
fallback
|
|
321
366
|
);
|
|
322
367
|
}
|
|
323
368
|
}
|
|
369
|
+
|
|
370
|
+
// Ranked after the children resolved their own meta: the `pages` list is
|
|
371
|
+
// this folder's word on its children's order, so it wins over a listed
|
|
372
|
+
// subfolder's own `order` just as it wins over a listed page's
|
|
373
|
+
// `sidebar.order`.
|
|
374
|
+
if (meta?.pages) {
|
|
375
|
+
const rank = new Map(meta.pages.map((key, i) => [key, i]));
|
|
376
|
+
for (const child of group.children) {
|
|
377
|
+
const position = rank.get(child.key);
|
|
378
|
+
if (position !== undefined) {
|
|
379
|
+
child.order = position;
|
|
380
|
+
child.ranked = true;
|
|
381
|
+
if (child.kind === "page") {
|
|
382
|
+
child.orderIsAuthored = true;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
324
387
|
};
|
|
325
388
|
|
|
326
389
|
/**
|
|
@@ -457,22 +520,27 @@ const isAuthoredOrder = (node: MutableNode): boolean =>
|
|
|
457
520
|
* excluded: that's the common, intentional case of "just sort alphabetically."
|
|
458
521
|
* So is a derived, non-authored order (e.g. two changelog entries published
|
|
459
522
|
* on the same day) — not an authoring mistake.
|
|
523
|
+
*
|
|
524
|
+
* A folder meta `pages` rank is a list position, not a number the author
|
|
525
|
+
* picked, so it only ties with another rank: a listed page at position 1
|
|
526
|
+
* beside an unlisted `01-setup.md` is not a conflict anyone wrote.
|
|
460
527
|
*/
|
|
461
528
|
const duplicateOrderDiagnostics = (nodes: MutableNode[]): Diagnostic[] => {
|
|
462
|
-
const byOrder = new Map<
|
|
529
|
+
const byOrder = new Map<string, { order: number; tied: MutableNode[] }>();
|
|
463
530
|
for (const node of nodes) {
|
|
464
531
|
if (!Number.isFinite(node.order) || !isAuthoredOrder(node)) {
|
|
465
532
|
continue;
|
|
466
533
|
}
|
|
467
|
-
const
|
|
468
|
-
|
|
469
|
-
|
|
534
|
+
const key = `${node.ranked === true}:${node.order}`;
|
|
535
|
+
const entry = byOrder.get(key);
|
|
536
|
+
if (entry) {
|
|
537
|
+
entry.tied.push(node);
|
|
470
538
|
} else {
|
|
471
|
-
byOrder.set(node.order, [node]);
|
|
539
|
+
byOrder.set(key, { order: node.order, tied: [node] });
|
|
472
540
|
}
|
|
473
541
|
}
|
|
474
542
|
const diagnostics: Diagnostic[] = [];
|
|
475
|
-
for (const
|
|
543
|
+
for (const { order, tied } of byOrder.values()) {
|
|
476
544
|
if (tied.length > 1) {
|
|
477
545
|
const names = tied.map((node) => `"${node.label}"`);
|
|
478
546
|
const list =
|
|
@@ -596,13 +664,23 @@ const buildFileSystemSidebar = (
|
|
|
596
664
|
sharedMetaPrefix: string,
|
|
597
665
|
display: SidebarDisplay,
|
|
598
666
|
tabPaths: Set<string>,
|
|
599
|
-
diagnostics: Diagnostic[] = []
|
|
667
|
+
diagnostics: Diagnostic[] = [],
|
|
668
|
+
fallbackMetaPrefix?: string
|
|
600
669
|
): NavNode[] => {
|
|
601
670
|
const root = createGroup("", "", "", 0);
|
|
602
671
|
// Folder path -> `sidebar.display` from that folder's index page frontmatter.
|
|
603
672
|
// Collected before the hidden filter (like the title check): hiding the index
|
|
604
673
|
// row from the panel shouldn't stop it configuring its group.
|
|
605
674
|
const indexDisplay = new Map<string, SidebarDisplay>();
|
|
675
|
+
// What a group with no meta of this locale's mirrors from the fallback
|
|
676
|
+
// locale (see `applyFolderMeta`).
|
|
677
|
+
const fallback: FallbackFolderMeta = {
|
|
678
|
+
indexDisplay: new Map(),
|
|
679
|
+
meta: (path) =>
|
|
680
|
+
fallbackMetaPrefix === undefined
|
|
681
|
+
? undefined
|
|
682
|
+
: folderMeta.get(metaKey(path, fallbackMetaPrefix)),
|
|
683
|
+
};
|
|
606
684
|
// Folder path -> that folder's index page route, for the group row's link.
|
|
607
685
|
// Also collected before the hidden filter: hiding the index row is how a
|
|
608
686
|
// site drops the duplicate label under a linked header, so the link must
|
|
@@ -631,12 +709,16 @@ const buildFileSystemSidebar = (
|
|
|
631
709
|
if (diagnostic) {
|
|
632
710
|
diagnostics.push(diagnostic);
|
|
633
711
|
}
|
|
634
|
-
//
|
|
635
|
-
// frontmatter belongs to the fallback locale,
|
|
636
|
-
//
|
|
637
|
-
//
|
|
638
|
-
|
|
639
|
-
|
|
712
|
+
// A fallback-filled index's display is kept apart (like the title check
|
|
713
|
+
// above exempts it): its frontmatter belongs to the fallback locale, so
|
|
714
|
+
// it only speaks for a group this locale has no meta for — letting it
|
|
715
|
+
// override this locale's own authored `meta.ts` display would silently
|
|
716
|
+
// flip again the day the index gets translated.
|
|
717
|
+
if (page.meta.sidebar.display) {
|
|
718
|
+
(page.fallback ? fallback.indexDisplay : indexDisplay).set(
|
|
719
|
+
dirs.join("/"),
|
|
720
|
+
page.meta.sidebar.display
|
|
721
|
+
);
|
|
640
722
|
}
|
|
641
723
|
if (dirs.length > 0) {
|
|
642
724
|
indexRoute.set(dirs.join("/"), page.route);
|
|
@@ -658,17 +740,39 @@ const buildFileSystemSidebar = (
|
|
|
658
740
|
const folderParts = isIndexStem(stem)
|
|
659
741
|
? routeSegments
|
|
660
742
|
: routeSegments.slice(0, -1);
|
|
661
|
-
const routeDirCount = dirs.filter(
|
|
743
|
+
const routeDirCount = dirs.filter(
|
|
744
|
+
(dir) => groupName(dir) === undefined
|
|
745
|
+
).length;
|
|
662
746
|
const offset = Math.max(0, folderParts.length - routeDirCount);
|
|
747
|
+
// A frontmatter `slug` publishes the page away from its folder, so its
|
|
748
|
+
// route says nothing about where the folder lives (`guides/a.md` with
|
|
749
|
+
// `slug: install` is `/install`). Its groups read their path off the folder
|
|
750
|
+
// names instead, behind the route's leading base/locale/version segments
|
|
751
|
+
// (whatever precedes the slug's own `versionKey`), and only where no
|
|
752
|
+
// other page under the folder supplies one.
|
|
753
|
+
const slugged = page.meta.slug !== undefined;
|
|
754
|
+
const leading = routeSegments.slice(
|
|
755
|
+
0,
|
|
756
|
+
Math.max(
|
|
757
|
+
0,
|
|
758
|
+
routeSegments.length - page.versionKey.split("/").filter(Boolean).length
|
|
759
|
+
)
|
|
760
|
+
);
|
|
663
761
|
|
|
664
762
|
let parent = root;
|
|
665
763
|
let consumed = offset;
|
|
764
|
+
const folders: string[] = [];
|
|
666
765
|
for (const dir of dirs) {
|
|
667
766
|
parent = ensureGroup(parent, dir);
|
|
668
|
-
if (
|
|
767
|
+
if (groupName(dir) === undefined) {
|
|
669
768
|
consumed += 1;
|
|
769
|
+
folders.push(stripOrderingPrefix(dir));
|
|
770
|
+
}
|
|
771
|
+
if (slugged) {
|
|
772
|
+
parent.folderPath ??= `/${[...leading, ...folders].join("/")}`;
|
|
773
|
+
} else {
|
|
774
|
+
parent.routePath ??= `/${folderParts.slice(0, consumed).join("/")}`;
|
|
670
775
|
}
|
|
671
|
-
parent.routePath ??= `/${folderParts.slice(0, consumed).join("/")}`;
|
|
672
776
|
}
|
|
673
777
|
|
|
674
778
|
const { order, orderIsAuthored } = pageOrder(page, filename);
|
|
@@ -695,7 +799,8 @@ const buildFileSystemSidebar = (
|
|
|
695
799
|
metaPrefix,
|
|
696
800
|
sharedMetaPrefix,
|
|
697
801
|
indexDisplay,
|
|
698
|
-
indexRoute
|
|
802
|
+
indexRoute,
|
|
803
|
+
fallback
|
|
699
804
|
);
|
|
700
805
|
sortNodes(root.children, diagnostics);
|
|
701
806
|
hoistPages(root.children, display, true);
|
|
@@ -703,7 +808,11 @@ const buildFileSystemSidebar = (
|
|
|
703
808
|
return root.children.map((child) => toNavNode(child, display));
|
|
704
809
|
};
|
|
705
810
|
|
|
706
|
-
|
|
811
|
+
/**
|
|
812
|
+
* An explicit-sidebar ref (`"guides/"`, `"/guides/index"`, `"index"`) in the
|
|
813
|
+
* slashless route form pages are keyed by (`/guides`, `/`).
|
|
814
|
+
*/
|
|
815
|
+
export const normalizeRef = (ref: string): string => {
|
|
707
816
|
if (ref === "index") {
|
|
708
817
|
return "/";
|
|
709
818
|
}
|
|
@@ -734,9 +843,36 @@ const routeForRef = (
|
|
|
734
843
|
};
|
|
735
844
|
|
|
736
845
|
/** An explicit-config sidebar item written as a bare page-ref string. */
|
|
737
|
-
const isPageRef = (item: SidebarItemConfig): item is string =>
|
|
846
|
+
export const isPageRef = (item: SidebarItemConfig): item is string =>
|
|
738
847
|
typeof item === "string";
|
|
739
848
|
|
|
849
|
+
/**
|
|
850
|
+
* Pages keyed the way explicit-sidebar refs look them up: by the
|
|
851
|
+
* locale-agnostic `translationKey` under i18n (`byLogical`), so one authored
|
|
852
|
+
* sidebar maps onto every locale's pages, else by route. Refs are authored as
|
|
853
|
+
* if mounted at root while `page.route` carries the base, so each page is also
|
|
854
|
+
* aliased under its base-less route (the `translationKey` is base-less
|
|
855
|
+
* already).
|
|
856
|
+
*/
|
|
857
|
+
export const pagesByRef = (
|
|
858
|
+
pages: PageRecord[],
|
|
859
|
+
basePath: string,
|
|
860
|
+
byLogical: boolean
|
|
861
|
+
): Map<string, PageRecord> => {
|
|
862
|
+
const byRoute = new Map(
|
|
863
|
+
pages.map((page) => [byLogical ? page.translationKey : page.route, page])
|
|
864
|
+
);
|
|
865
|
+
if (basePath && !byLogical) {
|
|
866
|
+
for (const page of pages) {
|
|
867
|
+
const bare = stripBasePath(basePath, page.route);
|
|
868
|
+
if (!byRoute.has(bare)) {
|
|
869
|
+
byRoute.set(bare, page);
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
return byRoute;
|
|
874
|
+
};
|
|
875
|
+
|
|
740
876
|
/**
|
|
741
877
|
* Convert one non-group explicit-config sidebar item (string ref, `root`, or
|
|
742
878
|
* `href`) to a nav node, or null to skip. Group items (`item.items`) are handled
|
|
@@ -822,6 +958,35 @@ const buildConfigSidebar = (
|
|
|
822
958
|
return nodes;
|
|
823
959
|
};
|
|
824
960
|
|
|
961
|
+
/**
|
|
962
|
+
* The two roots a tab's target resolves between, both based: `tabs`, the root
|
|
963
|
+
* of the space tab paths are written in (the current docs' localized root,
|
|
964
|
+
* `/fr`), and `tree`, the root this tree's pages live under — the same route,
|
|
965
|
+
* except in an archived version tree (`/fr/v1.0`).
|
|
966
|
+
*/
|
|
967
|
+
interface TabRoots {
|
|
968
|
+
tabs: string;
|
|
969
|
+
tree: string;
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
/**
|
|
973
|
+
* `path` relative to `root` (`/fr/guides` under `/fr` is `/guides`, the root
|
|
974
|
+
* itself is `/`), or undefined when it isn't under it.
|
|
975
|
+
*/
|
|
976
|
+
const pathUnder = (path: string, root: string): string | undefined => {
|
|
977
|
+
if (root === "/") {
|
|
978
|
+
return path;
|
|
979
|
+
}
|
|
980
|
+
if (path === root) {
|
|
981
|
+
return "/";
|
|
982
|
+
}
|
|
983
|
+
return path.startsWith(`${root}/`) ? path.slice(root.length) : undefined;
|
|
984
|
+
};
|
|
985
|
+
|
|
986
|
+
/** Mount a root-relative remainder (`/guides`, never `/`) under `root`. */
|
|
987
|
+
const joinRoute = (root: string, rest: string): string =>
|
|
988
|
+
root === "/" ? rest : `${root}${rest}`;
|
|
989
|
+
|
|
825
990
|
/**
|
|
826
991
|
* Resolve a tab's clickable target. A tab's `path` scopes its sidebar section
|
|
827
992
|
* but need not be a real route — a section with no index page would 404 if the
|
|
@@ -832,30 +997,40 @@ const buildConfigSidebar = (
|
|
|
832
997
|
*
|
|
833
998
|
* Routes outside the content tree are served at their own path, not under
|
|
834
999
|
* `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
|
-
*
|
|
1000
|
+
* site) or a locale prefix, while tab paths arrive based and localized
|
|
1001
|
+
* (`/docs/fr/changelog`). So when the tab path is neither an outside route nor
|
|
1002
|
+
* a tree node, its base-less and then its root-relative form (`/changelog`)
|
|
1003
|
+
* are checked against the outside routes before the section's first page
|
|
1004
|
+
* wins.
|
|
1005
|
+
*
|
|
1006
|
+
* In an archived version tree the section is looked up under the versioned
|
|
1007
|
+
* root (`/v1.0/guides`), where that tree's pages live — tab paths stay in
|
|
1008
|
+
* current-docs space, where nothing in the tree could match.
|
|
838
1009
|
*/
|
|
839
1010
|
const resolveTabHref = (
|
|
840
1011
|
sidebar: NavNode[],
|
|
841
1012
|
path: string,
|
|
842
1013
|
extraRoutes: ReadonlySet<string>,
|
|
843
|
-
basePath: string
|
|
1014
|
+
basePath: string,
|
|
1015
|
+
roots: TabRoots
|
|
844
1016
|
): string => {
|
|
845
1017
|
if (extraRoutes.has(path)) {
|
|
846
1018
|
return path;
|
|
847
1019
|
}
|
|
1020
|
+
const rest = pathUnder(path, roots.tabs);
|
|
1021
|
+
const section =
|
|
1022
|
+
rest === undefined || rest === "/" ? path : joinRoute(roots.tree, rest);
|
|
848
1023
|
let first: string | undefined;
|
|
849
1024
|
const walk = (nodes: NavNode[]): boolean => {
|
|
850
1025
|
for (const node of nodes) {
|
|
851
1026
|
const { route } = node;
|
|
852
|
-
if (route ===
|
|
1027
|
+
if (route === section) {
|
|
853
1028
|
return true;
|
|
854
1029
|
}
|
|
855
1030
|
if (
|
|
856
1031
|
first === undefined &&
|
|
857
1032
|
route !== undefined &&
|
|
858
|
-
route.startsWith(`${
|
|
1033
|
+
route.startsWith(`${section}/`)
|
|
859
1034
|
) {
|
|
860
1035
|
first = route;
|
|
861
1036
|
}
|
|
@@ -866,10 +1041,16 @@ const resolveTabHref = (
|
|
|
866
1041
|
return false;
|
|
867
1042
|
};
|
|
868
1043
|
if (walk(sidebar)) {
|
|
869
|
-
return
|
|
1044
|
+
return section;
|
|
870
1045
|
}
|
|
871
1046
|
const bare = stripBasePath(basePath, path);
|
|
872
|
-
|
|
1047
|
+
if (extraRoutes.has(bare)) {
|
|
1048
|
+
return bare;
|
|
1049
|
+
}
|
|
1050
|
+
if (rest !== undefined && extraRoutes.has(rest)) {
|
|
1051
|
+
return rest;
|
|
1052
|
+
}
|
|
1053
|
+
return first ?? path;
|
|
873
1054
|
};
|
|
874
1055
|
|
|
875
1056
|
/**
|
|
@@ -884,11 +1065,13 @@ const withTabHrefs = (
|
|
|
884
1065
|
tabs: NavTab[],
|
|
885
1066
|
sidebar: NavNode[],
|
|
886
1067
|
extraRoutes: ReadonlySet<string>,
|
|
887
|
-
basePath: string
|
|
1068
|
+
basePath: string,
|
|
1069
|
+
roots: TabRoots
|
|
888
1070
|
): NavTab[] =>
|
|
889
1071
|
tabs.map((tab) => {
|
|
890
1072
|
const href =
|
|
891
|
-
tab.href ??
|
|
1073
|
+
tab.href ??
|
|
1074
|
+
resolveTabHref(sidebar, tab.path, extraRoutes, basePath, roots);
|
|
892
1075
|
return href === tab.path ? tab : { ...tab, href };
|
|
893
1076
|
});
|
|
894
1077
|
|
|
@@ -978,6 +1161,13 @@ export const buildNavigation = (
|
|
|
978
1161
|
* group path, e.g. `fr`, `v1.0`, or `v1.0/fr`.
|
|
979
1162
|
*/
|
|
980
1163
|
metaPrefix?: string;
|
|
1164
|
+
/**
|
|
1165
|
+
* Folder-meta lookup prefix of the i18n fallback locale, whose untranslated
|
|
1166
|
+
* pages pad this tree (`""` for the default locale of the current
|
|
1167
|
+
* version). A group with no meta of this locale's mirrors the fallback
|
|
1168
|
+
* locale's. Unset for the fallback locale itself, and without i18n.
|
|
1169
|
+
*/
|
|
1170
|
+
fallbackMetaPrefix?: string;
|
|
981
1171
|
/**
|
|
982
1172
|
* Prefix for shared `meta.$.*` lookups — the version dir inside a
|
|
983
1173
|
* snapshot (`v1.0`), since shared meta is locale-agnostic but still
|
|
@@ -999,6 +1189,14 @@ export const buildNavigation = (
|
|
|
999
1189
|
* scoping.
|
|
1000
1190
|
*/
|
|
1001
1191
|
localizedRoot?: string;
|
|
1192
|
+
/**
|
|
1193
|
+
* The root of the space tab paths are written in, before `basePath`: the
|
|
1194
|
+
* current docs' localized root (`"/"`, `/fr`). Defaults to
|
|
1195
|
+
* `localizedRoot`; an archived version tree passes it because its own
|
|
1196
|
+
* root is versionized (`/fr/v1.0`) while tab paths are not, so a tab's
|
|
1197
|
+
* section can be found under the version.
|
|
1198
|
+
*/
|
|
1199
|
+
tabRoot?: string;
|
|
1002
1200
|
/**
|
|
1003
1201
|
* Sink for diagnostics produced while building the tree (duplicate sidebar
|
|
1004
1202
|
* `order` values, index-page title/folder-meta-title mismatches). Pushed
|
|
@@ -1027,24 +1225,7 @@ export const buildNavigation = (
|
|
|
1027
1225
|
basePath,
|
|
1028
1226
|
options
|
|
1029
1227
|
);
|
|
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
|
-
}
|
|
1228
|
+
const byRoute = pagesByRef(pages, basePath, Boolean(options.refByLogical));
|
|
1048
1229
|
|
|
1049
1230
|
// A tab pointing at the tree root spans the whole sidebar rather than one
|
|
1050
1231
|
// section, so it must not feed tab-section hoisting. `tabs` carries final
|
|
@@ -1057,6 +1238,13 @@ export const buildNavigation = (
|
|
|
1057
1238
|
// paths stay in current-docs space, so root-tab checks use `isRootTab`
|
|
1058
1239
|
// containment, not equality.
|
|
1059
1240
|
const rootTabPath = withBasePath(basePath, options.localizedRoot ?? "/");
|
|
1241
|
+
const tabRoots: TabRoots = {
|
|
1242
|
+
tabs: withBasePath(
|
|
1243
|
+
basePath,
|
|
1244
|
+
options.tabRoot ?? options.localizedRoot ?? "/"
|
|
1245
|
+
),
|
|
1246
|
+
tree: rootTabPath,
|
|
1247
|
+
};
|
|
1060
1248
|
|
|
1061
1249
|
// Emitted here, before the sidebar-mode branch: an explicit config sidebar
|
|
1062
1250
|
// ignores a stray `sidebar.display` just like the filesystem sidebar does
|
|
@@ -1080,7 +1268,7 @@ export const buildNavigation = (
|
|
|
1080
1268
|
root: rootTabPath,
|
|
1081
1269
|
selectors,
|
|
1082
1270
|
sidebar,
|
|
1083
|
-
tabs: withTabHrefs(tabs, sidebar, extraRoutes, basePath),
|
|
1271
|
+
tabs: withTabHrefs(tabs, sidebar, extraRoutes, basePath, tabRoots),
|
|
1084
1272
|
};
|
|
1085
1273
|
}
|
|
1086
1274
|
|
|
@@ -1094,7 +1282,8 @@ export const buildNavigation = (
|
|
|
1094
1282
|
new Set(
|
|
1095
1283
|
tabs.flatMap((tab) => (isRootTab(tab, rootTabPath) ? [] : [tab.path]))
|
|
1096
1284
|
),
|
|
1097
|
-
diagnostics
|
|
1285
|
+
diagnostics,
|
|
1286
|
+
options.fallbackMetaPrefix
|
|
1098
1287
|
);
|
|
1099
1288
|
return {
|
|
1100
1289
|
actions,
|
|
@@ -1103,6 +1292,6 @@ export const buildNavigation = (
|
|
|
1103
1292
|
root: rootTabPath,
|
|
1104
1293
|
selectors,
|
|
1105
1294
|
sidebar,
|
|
1106
|
-
tabs: withTabHrefs(tabs, sidebar, extraRoutes, basePath),
|
|
1295
|
+
tabs: withTabHrefs(tabs, sidebar, extraRoutes, basePath, tabRoots),
|
|
1107
1296
|
};
|
|
1108
1297
|
};
|
|
@@ -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
|
+
};
|