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/config.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
|
|
3
|
+
import { basename } from "pathe";
|
|
3
4
|
import { z } from "zod";
|
|
4
5
|
|
|
5
6
|
import type { BlumeConfig } from "./config-input.ts";
|
|
@@ -9,7 +10,7 @@ import {
|
|
|
9
10
|
diagnosticsFromIssues,
|
|
10
11
|
diagnosticsFromZod,
|
|
11
12
|
} from "./diagnostics.ts";
|
|
12
|
-
import {
|
|
13
|
+
import { createDefaultExportLoader } from "./load-module.ts";
|
|
13
14
|
import { findConfigFile } from "./project.ts";
|
|
14
15
|
import { blumeConfigSchema } from "./schema.ts";
|
|
15
16
|
import type { ResolvedConfig } from "./schema.ts";
|
|
@@ -68,8 +69,8 @@ import type { Diagnostic } from "./types.ts";
|
|
|
68
69
|
* - `theme` — `accent` color, `fonts` (curated slugs, any provider family, or
|
|
69
70
|
* local font files), `radius`,
|
|
70
71
|
* `mode` (`system`/`light`/`dark`), and `background`.
|
|
71
|
-
* - `markdown` — `code` (language icons,
|
|
72
|
-
* `
|
|
72
|
+
* - `markdown` — `code` (language icons, light/dark Shiki `theme`, line
|
|
73
|
+
* `wrap`), `externalLinks`, `headingAnchors`, and `imageZoom`.
|
|
73
74
|
* - `toc` — on-page table of contents; `true`/`false` or a heading-level range.
|
|
74
75
|
* - `lastModified` — "Last updated" stamps from `git` history or frontmatter.
|
|
75
76
|
* - `feedback` — the per-page "Was this helpful?" widget (on by default).
|
|
@@ -199,7 +200,7 @@ export interface ConfigLoadResult {
|
|
|
199
200
|
themeFontsConfigured: boolean;
|
|
200
201
|
}
|
|
201
202
|
|
|
202
|
-
const importConfigModule =
|
|
203
|
+
const importConfigModule = createDefaultExportLoader();
|
|
203
204
|
|
|
204
205
|
/**
|
|
205
206
|
* The slice of a user config module probed before schema defaults apply:
|
|
@@ -290,6 +291,19 @@ export const loadConfig = async (
|
|
|
290
291
|
severity: "error",
|
|
291
292
|
});
|
|
292
293
|
}
|
|
294
|
+
// Validating whatever else the module holds would build a silent default
|
|
295
|
+
// site (a bare `defineConfig({…})` call) or reject a named `config` export
|
|
296
|
+
// as an unknown key, neither of which names the actual mistake.
|
|
297
|
+
if (raw === undefined) {
|
|
298
|
+
throw new BlumeError({
|
|
299
|
+
code: "BLUME_CONFIG_INVALID",
|
|
300
|
+
file: configFile,
|
|
301
|
+
message: `${basename(configFile)} has no default export, so Blume can't read its config.`,
|
|
302
|
+
severity: "error",
|
|
303
|
+
suggestion:
|
|
304
|
+
"Export the config as the file's default: `export default defineConfig({ … })`.",
|
|
305
|
+
});
|
|
306
|
+
}
|
|
293
307
|
}
|
|
294
308
|
|
|
295
309
|
// Read before parsing: schema defaults erase the set-vs-defaulted distinction.
|
package/src/core/diagnostics.ts
CHANGED
|
@@ -98,50 +98,237 @@ const isKeySegment = (segment: string | number): segment is string =>
|
|
|
98
98
|
typeof segment === "string";
|
|
99
99
|
|
|
100
100
|
/**
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
* or undefined when nothing matches.
|
|
101
|
+
* Where a located value's children are searched. `object` and `array` are the
|
|
102
|
+
* inside of a `{…}`/`[…]` literal, searched entry by entry so a key or an index
|
|
103
|
+
* only matches a direct child. `block` has no brackets — the whole file at the
|
|
104
|
+
* top, or a YAML key's indented lines — and is searched at any depth.
|
|
106
105
|
*/
|
|
107
|
-
|
|
106
|
+
interface Scope {
|
|
107
|
+
end: number;
|
|
108
|
+
/** A literal's direct entries (keys or elements), in source order. */
|
|
109
|
+
entries: number[];
|
|
110
|
+
kind: "array" | "block" | "object";
|
|
111
|
+
start: number;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** A located key or element: where it starts, and where its value starts. */
|
|
115
|
+
interface Step {
|
|
116
|
+
index: number;
|
|
117
|
+
next: number;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const QUOTES = new Set(['"', "'", "`"]);
|
|
121
|
+
const OPENERS = new Set(["(", "[", "{"]);
|
|
122
|
+
const CLOSERS = new Set([")", "]", "}"]);
|
|
123
|
+
const WHITESPACE = /\s/u;
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* The index just past a comment starting at `index`, or `index` when none does.
|
|
127
|
+
* A `//` after a `:` is a URL scheme in YAML (`href: https://…`), not a comment.
|
|
128
|
+
*/
|
|
129
|
+
const skipComment = (source: string, index: number): number => {
|
|
130
|
+
if (source.startsWith("//", index) && source.charAt(index - 1) !== ":") {
|
|
131
|
+
const end = source.indexOf("\n", index);
|
|
132
|
+
return end === -1 ? source.length : end;
|
|
133
|
+
}
|
|
134
|
+
if (source.startsWith("/*", index)) {
|
|
135
|
+
const end = source.indexOf("*/", index + 2);
|
|
136
|
+
return end === -1 ? source.length : end + 2;
|
|
137
|
+
}
|
|
138
|
+
return index;
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* The index just past a string literal starting at `index`, or `index` when
|
|
143
|
+
* none does. A `'`/`"` that doesn't close on its own line is an apostrophe in
|
|
144
|
+
* plain YAML text, not a string.
|
|
145
|
+
*/
|
|
146
|
+
const skipString = (source: string, index: number): number => {
|
|
147
|
+
const quote = source.charAt(index);
|
|
148
|
+
if (!QUOTES.has(quote)) {
|
|
149
|
+
return index;
|
|
150
|
+
}
|
|
151
|
+
let cursor = index + 1;
|
|
152
|
+
while (cursor < source.length) {
|
|
153
|
+
const char = source.charAt(cursor);
|
|
154
|
+
if (char === quote) {
|
|
155
|
+
return cursor + 1;
|
|
156
|
+
}
|
|
157
|
+
if (char === "\n" && quote !== "`") {
|
|
158
|
+
return index;
|
|
159
|
+
}
|
|
160
|
+
cursor += char === "\\" ? 2 : 1;
|
|
161
|
+
}
|
|
162
|
+
return index;
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
/** Where a literal closes, and where each of its direct entries starts. */
|
|
166
|
+
interface LiteralScan {
|
|
167
|
+
end: number;
|
|
168
|
+
entries: number[];
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Walk the `{…}`/`[…]` literal opened at `open`: where each direct entry starts
|
|
173
|
+
* (a key, or an array element) and where the literal closes. Nested brackets,
|
|
174
|
+
* strings, and comments are stepped over, so only the literal's own commas
|
|
175
|
+
* separate its entries.
|
|
176
|
+
*/
|
|
177
|
+
const scanLiteral = (source: string, open: number): LiteralScan => {
|
|
178
|
+
const entries: number[] = [];
|
|
179
|
+
let depth = 0;
|
|
180
|
+
let pending = true;
|
|
181
|
+
let index = open + 1;
|
|
182
|
+
while (index < source.length) {
|
|
183
|
+
const afterComment = skipComment(source, index);
|
|
184
|
+
const char = source.charAt(index);
|
|
185
|
+
if (afterComment !== index || WHITESPACE.test(char)) {
|
|
186
|
+
index = Math.max(afterComment, index + 1);
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
if (depth === 0 && CLOSERS.has(char)) {
|
|
190
|
+
return { end: index, entries };
|
|
191
|
+
}
|
|
192
|
+
if (depth === 0 && pending) {
|
|
193
|
+
entries.push(index);
|
|
194
|
+
pending = false;
|
|
195
|
+
}
|
|
196
|
+
const afterString = skipString(source, index);
|
|
197
|
+
if (afterString !== index) {
|
|
198
|
+
index = afterString;
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
if (OPENERS.has(char)) {
|
|
202
|
+
depth += 1;
|
|
203
|
+
} else if (CLOSERS.has(char)) {
|
|
204
|
+
depth -= 1;
|
|
205
|
+
} else if (char === "," && depth === 0) {
|
|
206
|
+
pending = true;
|
|
207
|
+
}
|
|
208
|
+
index += 1;
|
|
209
|
+
}
|
|
210
|
+
return { end: source.length, entries };
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* The lines indented deeper than the key at `anchor` — a YAML mapping's
|
|
215
|
+
* children — up to the first line that isn't, or `limit`.
|
|
216
|
+
*/
|
|
217
|
+
const indentedBlock = (
|
|
108
218
|
source: string,
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
) => {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
219
|
+
anchor: number,
|
|
220
|
+
limit: number
|
|
221
|
+
): Scope => {
|
|
222
|
+
const column = anchor - (source.lastIndexOf("\n", anchor - 1) + 1);
|
|
223
|
+
const lineEnd = source.indexOf("\n", anchor);
|
|
224
|
+
const start = lineEnd === -1 ? limit : Math.min(lineEnd + 1, limit);
|
|
225
|
+
let end = start;
|
|
226
|
+
while (end < limit) {
|
|
227
|
+
const next = source.indexOf("\n", end);
|
|
228
|
+
const after = next === -1 || next >= limit ? limit : next + 1;
|
|
229
|
+
const line = source.slice(end, after);
|
|
230
|
+
if (line.trim() !== "" && line.length - line.trimStart().length <= column) {
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
233
|
+
end = after;
|
|
115
234
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
235
|
+
return { end, entries: [], kind: "block", start };
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
/** A value's lead-in before its literal: spaces, or an adapter call's `vercel(`. */
|
|
239
|
+
const VALUE_LEAD = /[\s\w$.(]*/uy;
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* The scope holding the children of the key or element at `anchor`, whose
|
|
243
|
+
* value starts at `from`. A `{…}`/`[…]` literal — written directly, or as the
|
|
244
|
+
* first argument of a call like `vercel({…})` — is walked entry by entry;
|
|
245
|
+
* anything else falls back to the lines indented under `anchor` (YAML's
|
|
246
|
+
* nesting), within the enclosing scope's `limit`.
|
|
247
|
+
*/
|
|
248
|
+
const valueScope = (source: string, step: Step, limit: number): Scope => {
|
|
249
|
+
VALUE_LEAD.lastIndex = step.next;
|
|
250
|
+
VALUE_LEAD.exec(source);
|
|
251
|
+
const open = VALUE_LEAD.lastIndex;
|
|
252
|
+
const char = source.charAt(open);
|
|
253
|
+
if (open < limit && (char === "{" || char === "[")) {
|
|
254
|
+
const literal = scanLiteral(source, open);
|
|
255
|
+
return {
|
|
256
|
+
end: literal.end,
|
|
257
|
+
entries: literal.entries,
|
|
258
|
+
kind: char === "{" ? "object" : "array",
|
|
259
|
+
start: open,
|
|
260
|
+
};
|
|
126
261
|
}
|
|
127
|
-
return
|
|
262
|
+
return indentedBlock(source, step.index, limit);
|
|
128
263
|
};
|
|
129
264
|
|
|
265
|
+
/**
|
|
266
|
+
* Find `segment` as a `key:`/`key =` (bare or quoted) in `scope`: among an
|
|
267
|
+
* object literal's own entries, or anywhere in a block. The negative lookbehind
|
|
268
|
+
* keeps `title` from matching the tail of an unrelated key such as `subtitle:`.
|
|
269
|
+
*/
|
|
270
|
+
const findKey = (
|
|
271
|
+
source: string,
|
|
272
|
+
scope: Scope,
|
|
273
|
+
segment: string
|
|
274
|
+
): Step | undefined => {
|
|
275
|
+
const key = escapeRegExp(segment);
|
|
276
|
+
const pattern = `(?<![\\w$])(?:${key}|"${key}"|'${key}')\\s*[:=]`;
|
|
277
|
+
if (scope.kind === "block") {
|
|
278
|
+
const matcher = new RegExp(pattern, "gu");
|
|
279
|
+
matcher.lastIndex = scope.start;
|
|
280
|
+
const match = matcher.exec(source);
|
|
281
|
+
return match && match.index < scope.end
|
|
282
|
+
? { index: match.index, next: matcher.lastIndex }
|
|
283
|
+
: undefined;
|
|
284
|
+
}
|
|
285
|
+
// Sticky: the key must start exactly where the entry does.
|
|
286
|
+
const matcher = new RegExp(pattern, "uy");
|
|
287
|
+
for (const entry of scope.kind === "object" ? scope.entries : []) {
|
|
288
|
+
matcher.lastIndex = entry;
|
|
289
|
+
if (matcher.test(source)) {
|
|
290
|
+
return { index: entry, next: matcher.lastIndex };
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return undefined;
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
/** The element at `position` of an array literal; a block can't be indexed. */
|
|
297
|
+
const findElement = (scope: Scope, position: number): Step | undefined => {
|
|
298
|
+
const index = scope.kind === "array" ? scope.entries[position] : undefined;
|
|
299
|
+
return index === undefined ? undefined : { index, next: index };
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Best-effort source position for a Zod issue path (e.g. `["seo", "title"]`,
|
|
304
|
+
* `["redirects", 2, "status"]`) in the raw config / frontmatter text. Narrows
|
|
305
|
+
* segment by segment: a key is found among its parent's own entries (or, where
|
|
306
|
+
* there are no brackets, in the parent's indented lines), an index picks that
|
|
307
|
+
* element of an array literal. A segment that isn't there — a missing key —
|
|
308
|
+
* stops the walk at its parent, so the position never escapes into a sibling.
|
|
309
|
+
* Returns 1-based line/column, or undefined when not even the first segment
|
|
310
|
+
* matches.
|
|
311
|
+
*/
|
|
130
312
|
const locatePath = (
|
|
131
313
|
source: string,
|
|
132
314
|
path: readonly (string | number)[]
|
|
133
315
|
): { column: number; line: number } | undefined => {
|
|
134
|
-
let
|
|
316
|
+
let scope: Scope = {
|
|
317
|
+
end: source.length,
|
|
318
|
+
entries: [],
|
|
319
|
+
kind: "block",
|
|
320
|
+
start: 0,
|
|
321
|
+
};
|
|
135
322
|
let found = -1;
|
|
136
323
|
for (const segment of path) {
|
|
137
|
-
const step =
|
|
138
|
-
|
|
324
|
+
const step = isKeySegment(segment)
|
|
325
|
+
? findKey(source, scope, segment)
|
|
326
|
+
: findElement(scope, segment);
|
|
327
|
+
if (!step) {
|
|
139
328
|
break;
|
|
140
329
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
found = step.index;
|
|
144
|
-
}
|
|
330
|
+
found = step.index;
|
|
331
|
+
scope = valueScope(source, step, scope.end);
|
|
145
332
|
}
|
|
146
333
|
if (found < 0) {
|
|
147
334
|
return;
|
package/src/core/graph.ts
CHANGED
|
@@ -1,17 +1,22 @@
|
|
|
1
|
-
import { withBasePath } from "./base-path.ts";
|
|
1
|
+
import { mountBasePath, withBasePath } from "./base-path.ts";
|
|
2
2
|
import {
|
|
3
3
|
localizeInternalPath,
|
|
4
4
|
localizeRoute,
|
|
5
5
|
resolveFallbackLocale,
|
|
6
6
|
} from "./i18n.ts";
|
|
7
|
-
import {
|
|
8
|
-
|
|
7
|
+
import {
|
|
8
|
+
validateNavIcons,
|
|
9
|
+
validateNavStructure,
|
|
10
|
+
validateSidebarItems,
|
|
11
|
+
} from "./nav-diagnostics.ts";
|
|
12
|
+
import { buildNavigation, isPageRef, pagesByRef } from "./navigation.ts";
|
|
9
13
|
import type {
|
|
10
14
|
FolderMeta,
|
|
11
15
|
LocalizableLabel,
|
|
12
16
|
ResolvedConfig,
|
|
13
17
|
ResolvedI18nConfig,
|
|
14
18
|
ResolvedVersionsConfig,
|
|
19
|
+
SidebarItemConfig,
|
|
15
20
|
} from "./schema.ts";
|
|
16
21
|
import type {
|
|
17
22
|
ContentGraph,
|
|
@@ -23,6 +28,8 @@ import type {
|
|
|
23
28
|
import { versionizeRoute } from "./versions.ts";
|
|
24
29
|
|
|
25
30
|
interface BuildContentGraphOptions {
|
|
31
|
+
/** The banner's link target as configured (`banner.link.href`), if any. */
|
|
32
|
+
bannerHref?: string;
|
|
26
33
|
/** Site-wide route mount point (`""` or `/seg`); invisible to the nav tree. */
|
|
27
34
|
basePath?: string;
|
|
28
35
|
/** The header brand link as configured (`logo.href`, default `/`). */
|
|
@@ -83,7 +90,7 @@ const localePagesFor = (
|
|
|
83
90
|
...source,
|
|
84
91
|
fallback: true,
|
|
85
92
|
locale: code,
|
|
86
|
-
route:
|
|
93
|
+
route: mountBasePath(basePath, localizeRoute(key, code, i18n)),
|
|
87
94
|
});
|
|
88
95
|
}
|
|
89
96
|
}
|
|
@@ -115,6 +122,28 @@ const resolveLabel = (
|
|
|
115
122
|
);
|
|
116
123
|
};
|
|
117
124
|
|
|
125
|
+
/**
|
|
126
|
+
* An explicit sidebar with every item's `href` passed through `localize` (page
|
|
127
|
+
* refs and `root`s resolve against each locale's own pages already).
|
|
128
|
+
*/
|
|
129
|
+
const localizeSidebarHrefs = (
|
|
130
|
+
items: SidebarItemConfig[],
|
|
131
|
+
localize: (href: string) => string
|
|
132
|
+
): SidebarItemConfig[] =>
|
|
133
|
+
items.map((item) => {
|
|
134
|
+
if (isPageRef(item)) {
|
|
135
|
+
return item;
|
|
136
|
+
}
|
|
137
|
+
const localized = { ...item };
|
|
138
|
+
if (item.href !== undefined) {
|
|
139
|
+
localized.href = localize(item.href);
|
|
140
|
+
}
|
|
141
|
+
if (item.items) {
|
|
142
|
+
localized.items = localizeSidebarHrefs(item.items, localize);
|
|
143
|
+
}
|
|
144
|
+
return localized;
|
|
145
|
+
});
|
|
146
|
+
|
|
118
147
|
/** Resolve every localizable label in the configured tabs for one locale. */
|
|
119
148
|
const resolveTabLabels = (
|
|
120
149
|
tabs: BuildContentGraphOptions["navigation"]["tabs"],
|
|
@@ -130,6 +159,18 @@ const resolveTabLabels = (
|
|
|
130
159
|
label: resolveLabel(tab.label, locale, defaultLocale),
|
|
131
160
|
}));
|
|
132
161
|
|
|
162
|
+
/**
|
|
163
|
+
* Mount a configured link authored as if the site were served at root (the
|
|
164
|
+
* banner's): under `basePath`, except a route served outside the content tree
|
|
165
|
+
* — a custom page, the generated changelog index — which answers only at its
|
|
166
|
+
* own path.
|
|
167
|
+
*/
|
|
168
|
+
const mountConfiguredLink = (
|
|
169
|
+
path: string,
|
|
170
|
+
basePath: string,
|
|
171
|
+
extraRoutes: ReadonlySet<string>
|
|
172
|
+
): string => (extraRoutes.has(path) ? path : withBasePath(basePath, path));
|
|
173
|
+
|
|
133
174
|
/**
|
|
134
175
|
* Build one locale's navigation tree from its own pages and folder meta.
|
|
135
176
|
* `version` is the archived version id when building a snapshot's tree
|
|
@@ -217,8 +258,15 @@ const buildLocaleNavigation = (
|
|
|
217
258
|
// next to the originals and `guides/meta.ts` applies to every locale —
|
|
218
259
|
// prefixing would look up keys that can never exist. Inside a snapshot the
|
|
219
260
|
// version dir is hoisted in front (`v1.0/fr`), matching `discoverFolderMeta`.
|
|
220
|
-
const
|
|
221
|
-
i18n.parser === "dir" &&
|
|
261
|
+
const localeDirOf = (locale: string): string =>
|
|
262
|
+
i18n.parser === "dir" && locale !== i18n.defaultLocale ? locale : "";
|
|
263
|
+
const localeDir = localeDirOf(code);
|
|
264
|
+
// A locale padded from the fallback locale mirrors that locale's folder meta
|
|
265
|
+
// for any folder it has no meta of its own for.
|
|
266
|
+
const fallbackMetaPrefix =
|
|
267
|
+
fallback && code !== fallback
|
|
268
|
+
? [version, localeDirOf(fallback)].filter(Boolean).join("/")
|
|
269
|
+
: undefined;
|
|
222
270
|
// Internal featured and header hrefs are localized like tab paths — a pinned
|
|
223
271
|
// `/changelog` link rendered on `/fr/…` pages must stay inside the reader's
|
|
224
272
|
// locale, not kick them back to the default one.
|
|
@@ -227,6 +275,7 @@ const buildLocaleNavigation = (
|
|
|
227
275
|
href: localizeServed(item.href, false),
|
|
228
276
|
});
|
|
229
277
|
const { actions, cta, featured } = options.navigation;
|
|
278
|
+
const sidebarItems = options.navigation.sidebar.items;
|
|
230
279
|
|
|
231
280
|
const navigation = buildNavigation(localePages, {
|
|
232
281
|
actions: actions?.map(localizeHref),
|
|
@@ -235,6 +284,7 @@ const buildLocaleNavigation = (
|
|
|
235
284
|
diagnostics,
|
|
236
285
|
display: options.navigation.sidebar.display,
|
|
237
286
|
extraRoutes: options.extraRoutes,
|
|
287
|
+
fallbackMetaPrefix,
|
|
238
288
|
featured: featured?.map(localizeHref),
|
|
239
289
|
folderMeta: options.folderMeta,
|
|
240
290
|
// The localized tree root ("/" for the hidden default, "/fr" otherwise;
|
|
@@ -250,13 +300,43 @@ const buildLocaleNavigation = (
|
|
|
250
300
|
sharedMetaPrefix: version,
|
|
251
301
|
// A configured explicit sidebar describes the current docs; a frozen
|
|
252
302
|
// snapshot's structure comes from the snapshot itself, so archived trees
|
|
253
|
-
// always build from the filesystem.
|
|
254
|
-
|
|
303
|
+
// always build from the filesystem. Its `href` links are localized like
|
|
304
|
+
// featured links.
|
|
305
|
+
sidebar:
|
|
306
|
+
version || !sidebarItems
|
|
307
|
+
? undefined
|
|
308
|
+
: localizeSidebarHrefs(sidebarItems, (href) =>
|
|
309
|
+
localizeServed(href, false)
|
|
310
|
+
),
|
|
311
|
+
// Tab paths are written against the current docs' root, which a
|
|
312
|
+
// snapshot's versionized `localizedRoot` no longer is.
|
|
313
|
+
tabRoot: localizeRoute("/", code, i18n),
|
|
255
314
|
tabs,
|
|
256
315
|
});
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
316
|
+
// `serves` checked the localized brand link against based routes, and
|
|
317
|
+
// unlike tab and header links it isn't rebased later, so a localized one
|
|
318
|
+
// takes the base here (`/fr` is served at `/docs/fr`).
|
|
319
|
+
const localizedBrand = (href: string): string => {
|
|
320
|
+
const localized = localizeServed(href, false);
|
|
321
|
+
return localized === href ? localized : withBasePath(basePath, localized);
|
|
322
|
+
};
|
|
323
|
+
const branded =
|
|
324
|
+
options.brandHref === undefined
|
|
325
|
+
? navigation
|
|
326
|
+
: { ...navigation, brandHref: localizedBrand(options.brandHref) };
|
|
327
|
+
// The banner link is authored like a featured href — in the default
|
|
328
|
+
// locale's path space, as if mounted at root — so it is localized the same
|
|
329
|
+
// way before it is mounted.
|
|
330
|
+
return options.bannerHref === undefined
|
|
331
|
+
? branded
|
|
332
|
+
: {
|
|
333
|
+
...branded,
|
|
334
|
+
bannerHref: mountConfiguredLink(
|
|
335
|
+
localizeServed(options.bannerHref, false),
|
|
336
|
+
basePath,
|
|
337
|
+
extraRoutes
|
|
338
|
+
),
|
|
339
|
+
};
|
|
260
340
|
};
|
|
261
341
|
|
|
262
342
|
/**
|
|
@@ -348,6 +428,8 @@ const buildVersionNavigation = (
|
|
|
348
428
|
selectors: options.navigation.selectors,
|
|
349
429
|
sharedFolderMeta: options.sharedFolderMeta,
|
|
350
430
|
sharedMetaPrefix: id,
|
|
431
|
+
// Tab paths are written against the current docs' root.
|
|
432
|
+
tabRoot: "/",
|
|
351
433
|
// A configured explicit sidebar describes the current docs; a snapshot's
|
|
352
434
|
// structure comes from the snapshot itself.
|
|
353
435
|
tabs: resolveTabLabels(options.navigation.tabs, ""),
|
|
@@ -398,6 +480,16 @@ export const buildContentGraph = (
|
|
|
398
480
|
// entry on a single-locale site.
|
|
399
481
|
tabs: resolveTabLabels(options.navigation.tabs, ""),
|
|
400
482
|
});
|
|
483
|
+
if (options.bannerHref !== undefined) {
|
|
484
|
+
navigation = {
|
|
485
|
+
...navigation,
|
|
486
|
+
bannerHref: mountConfiguredLink(
|
|
487
|
+
options.bannerHref,
|
|
488
|
+
options.basePath ?? "",
|
|
489
|
+
options.extraRoutes ?? new Set<string>()
|
|
490
|
+
),
|
|
491
|
+
};
|
|
492
|
+
}
|
|
401
493
|
}
|
|
402
494
|
|
|
403
495
|
const navigationByVersion: Record<string, Record<string, Navigation>> = {};
|
|
@@ -417,6 +509,23 @@ export const buildContentGraph = (
|
|
|
417
509
|
...validateNavIcons(navigation),
|
|
418
510
|
...validateNavStructure(navigation, currentPages)
|
|
419
511
|
);
|
|
512
|
+
// Checked once against every locale's pages, not per locale tree: a page
|
|
513
|
+
// missing from one locale's sidebar is an untranslated page, not a typo.
|
|
514
|
+
const sidebarItems = options.navigation.sidebar.items;
|
|
515
|
+
if (sidebarItems) {
|
|
516
|
+
const byRef = pagesByRef(
|
|
517
|
+
currentPages,
|
|
518
|
+
options.basePath ?? "",
|
|
519
|
+
Boolean(i18n)
|
|
520
|
+
);
|
|
521
|
+
const extraRoutes = options.extraRoutes ?? new Set<string>();
|
|
522
|
+
diagnostics.push(
|
|
523
|
+
...validateSidebarItems(
|
|
524
|
+
sidebarItems,
|
|
525
|
+
(route) => byRef.has(route) || extraRoutes.has(route)
|
|
526
|
+
)
|
|
527
|
+
);
|
|
528
|
+
}
|
|
420
529
|
|
|
421
530
|
return {
|
|
422
531
|
diagnostics,
|
package/src/core/i18n-ui.ts
CHANGED
|
@@ -72,11 +72,32 @@ const uiStringsObject = z.object({
|
|
|
72
72
|
.prefault({}),
|
|
73
73
|
content: z
|
|
74
74
|
.object({
|
|
75
|
+
// `<Component>`'s source tab.
|
|
76
|
+
code: z.string().default("Code"),
|
|
75
77
|
// A `<Color.Item>` swatch's accessible name; `{name}` and `{value}` are
|
|
76
78
|
// replaced with the color's name and the value it shows.
|
|
77
79
|
copyColor: z.string().default("Copy {name} color {value}"),
|
|
78
80
|
copyPrompt: z.string().default("Copy prompt"),
|
|
81
|
+
// `<TypeTable>`: a row's default-value label.
|
|
82
|
+
default: z.string().default("Default"),
|
|
79
83
|
diagramError: z.string().default("Could not render this diagram."),
|
|
84
|
+
// `<GithubInfo>`: the screen-reader labels of the fork and star counts.
|
|
85
|
+
forks: z.string().default("Forks"),
|
|
86
|
+
// `<Component>`'s live-preview tab, also its frame's title.
|
|
87
|
+
preview: z.string().default("Preview"),
|
|
88
|
+
// `<TypeTable>`: the property-name column.
|
|
89
|
+
prop: z.string().default("Prop"),
|
|
90
|
+
// `<Tabs dropdown>`: the accessible name of the tab picker.
|
|
91
|
+
selectTab: z.string().default("Select tab"),
|
|
92
|
+
// `<Expandable>`'s toggle when it sets no `title`.
|
|
93
|
+
showMore: z.string().default("Show more"),
|
|
94
|
+
stars: z.string().default("Stars"),
|
|
95
|
+
// An untitled tab's label; `{n}` is replaced with its position.
|
|
96
|
+
tab: z.string().default("Tab {n}"),
|
|
97
|
+
// `<TypeTable>`: the type column and a row's type label.
|
|
98
|
+
type: z.string().default("Type"),
|
|
99
|
+
// `<Update>`'s heading when it sets no `label` or `title`.
|
|
100
|
+
update: z.string().default("Update"),
|
|
80
101
|
})
|
|
81
102
|
.prefault({}),
|
|
82
103
|
feedback: z
|
|
@@ -147,8 +168,13 @@ const uiStringsObject = z.object({
|
|
|
147
168
|
devOnly: z
|
|
148
169
|
.string()
|
|
149
170
|
.default("Search is available in the production build."),
|
|
171
|
+
// The section a hit falls under when its page sits in no sidebar group.
|
|
172
|
+
docs: z.string().default("Docs"),
|
|
150
173
|
error: z.string().default("Something went wrong. Please try again."),
|
|
151
174
|
label: z.string().default("Search docs"),
|
|
175
|
+
// The version tag on a result from the current docs in an all-versions
|
|
176
|
+
// search (a result from an archived version shows its version id).
|
|
177
|
+
latest: z.string().default("latest"),
|
|
152
178
|
navigate: z.string().default("navigate"),
|
|
153
179
|
noResults: z.string().default("No results found."),
|
|
154
180
|
open: z.string().default("open"),
|
|
@@ -281,11 +307,20 @@ const packFor = (code: string): UIStringsOverride | undefined => {
|
|
|
281
307
|
);
|
|
282
308
|
};
|
|
283
309
|
|
|
310
|
+
/** Whether a locale code is English (`en`, `en-GB`, `en_US`), in any case. */
|
|
311
|
+
const isEnglish = (code: string): boolean =>
|
|
312
|
+
code.toLowerCase().split(/[-_]/u)[0] === "en";
|
|
313
|
+
|
|
284
314
|
/**
|
|
285
315
|
* Resolve the active dictionary for a locale. Layers, in order:
|
|
286
316
|
* English baseline ← default-locale pack ← default-locale override ←
|
|
287
317
|
* locale pack ← locale override. So a missing key falls back to the default
|
|
288
318
|
* locale's translation, then to English.
|
|
319
|
+
*
|
|
320
|
+
* English has no pack because the baseline is its complete translation, so an
|
|
321
|
+
* English locale (`en`, `en-GB`) skips the default-locale layers: on a site
|
|
322
|
+
* whose default is German, English pages keep English chrome instead of
|
|
323
|
+
* inheriting the German strings.
|
|
289
324
|
*/
|
|
290
325
|
export const resolveUIStrings = (
|
|
291
326
|
locale: string,
|
|
@@ -296,8 +331,10 @@ export const resolveUIStrings = (
|
|
|
296
331
|
): UIStrings => {
|
|
297
332
|
const { defaultLocale, overrides } = options;
|
|
298
333
|
let dict = EN_UI;
|
|
299
|
-
|
|
300
|
-
|
|
334
|
+
if (!isEnglish(locale) || isEnglish(defaultLocale)) {
|
|
335
|
+
dict = mergeUI(dict, packFor(defaultLocale));
|
|
336
|
+
dict = mergeUI(dict, overrides?.[defaultLocale]);
|
|
337
|
+
}
|
|
301
338
|
if (locale !== defaultLocale) {
|
|
302
339
|
dict = mergeUI(dict, packFor(locale));
|
|
303
340
|
dict = mergeUI(dict, overrides?.[locale]);
|
package/src/core/i18n.ts
CHANGED
|
@@ -187,12 +187,19 @@ export const localePlacement = (
|
|
|
187
187
|
* (`x.en.mdx`) swaps it for the target's (`x.fr.mdx`) — same case-insensitive
|
|
188
188
|
* last-dot-inside-filename logic as `localePlacement`, so both spellings of a
|
|
189
189
|
* default-locale file resolve to one canonical target.
|
|
190
|
+
*
|
|
191
|
+
* Under `dir`, `folders` are the content root's existing top-level folders: one
|
|
192
|
+
* that names the locale in another casing (`pt-br/` for a configured `pt-BR`)
|
|
193
|
+
* is the locale's folder already, since {@link detectLocale} matches it case-
|
|
194
|
+
* insensitively. Writing beside it into a second `pt-BR/` would, on a case-
|
|
195
|
+
* sensitive filesystem, publish the page twice.
|
|
190
196
|
*/
|
|
191
197
|
export const localeTargetPath = (
|
|
192
198
|
rel: string,
|
|
193
199
|
ext: string,
|
|
194
200
|
locale: string,
|
|
195
|
-
i18n: ResolvedI18nConfig
|
|
201
|
+
i18n: ResolvedI18nConfig,
|
|
202
|
+
folders: readonly string[] = []
|
|
196
203
|
): string => {
|
|
197
204
|
if (i18n.parser === "dot") {
|
|
198
205
|
let base = rel.slice(0, rel.length - ext.length);
|
|
@@ -205,7 +212,11 @@ export const localeTargetPath = (
|
|
|
205
212
|
}
|
|
206
213
|
return `${base}.${locale}${ext}`;
|
|
207
214
|
}
|
|
208
|
-
|
|
215
|
+
const folder = folders.includes(locale)
|
|
216
|
+
? locale
|
|
217
|
+
: (folders.find((name) => name.toLowerCase() === locale.toLowerCase()) ??
|
|
218
|
+
locale);
|
|
219
|
+
return `${folder}/${rel}`;
|
|
209
220
|
};
|
|
210
221
|
|
|
211
222
|
/**
|