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/diagnostics.ts
CHANGED
|
@@ -69,8 +69,10 @@ const DOCS_PATHS = new Map(
|
|
|
69
69
|
BLUME_TRANSLATE_MISSING: "/docs/cli/translate",
|
|
70
70
|
BLUME_TRANSLATE_STALE: "/docs/cli/translate",
|
|
71
71
|
BLUME_UNKNOWN_COMPONENT: "/docs/configuration/customization",
|
|
72
|
+
BLUME_UNKNOWN_DIRECTIVE: "/docs/content/syntax",
|
|
72
73
|
BLUME_UNKNOWN_ICON: DOCS_CONTENT_NAVIGATION,
|
|
73
74
|
BLUME_UNKNOWN_OPTION: "/docs/cli",
|
|
75
|
+
BLUME_UNLOADABLE_FILE_NAME: "/docs/content",
|
|
74
76
|
BLUME_WIKILINK_AMBIGUOUS: DOCS_CONTENT_SOURCES,
|
|
75
77
|
BLUME_WIKILINK_UNRESOLVED: DOCS_CONTENT_SOURCES,
|
|
76
78
|
BLUME_YARN_PNP: "/docs/quickstart",
|
|
@@ -98,50 +100,237 @@ const isKeySegment = (segment: string | number): segment is string =>
|
|
|
98
100
|
typeof segment === "string";
|
|
99
101
|
|
|
100
102
|
/**
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
* or undefined when nothing matches.
|
|
103
|
+
* Where a located value's children are searched. `object` and `array` are the
|
|
104
|
+
* inside of a `{…}`/`[…]` literal, searched entry by entry so a key or an index
|
|
105
|
+
* only matches a direct child. `block` has no brackets — the whole file at the
|
|
106
|
+
* top, or a YAML key's indented lines — and is searched at any depth.
|
|
106
107
|
*/
|
|
107
|
-
|
|
108
|
+
interface Scope {
|
|
109
|
+
end: number;
|
|
110
|
+
/** A literal's direct entries (keys or elements), in source order. */
|
|
111
|
+
entries: number[];
|
|
112
|
+
kind: "array" | "block" | "object";
|
|
113
|
+
start: number;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** A located key or element: where it starts, and where its value starts. */
|
|
117
|
+
interface Step {
|
|
118
|
+
index: number;
|
|
119
|
+
next: number;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const QUOTES = new Set(['"', "'", "`"]);
|
|
123
|
+
const OPENERS = new Set(["(", "[", "{"]);
|
|
124
|
+
const CLOSERS = new Set([")", "]", "}"]);
|
|
125
|
+
const WHITESPACE = /\s/u;
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* The index just past a comment starting at `index`, or `index` when none does.
|
|
129
|
+
* A `//` after a `:` is a URL scheme in YAML (`href: https://…`), not a comment.
|
|
130
|
+
*/
|
|
131
|
+
const skipComment = (source: string, index: number): number => {
|
|
132
|
+
if (source.startsWith("//", index) && source.charAt(index - 1) !== ":") {
|
|
133
|
+
const end = source.indexOf("\n", index);
|
|
134
|
+
return end === -1 ? source.length : end;
|
|
135
|
+
}
|
|
136
|
+
if (source.startsWith("/*", index)) {
|
|
137
|
+
const end = source.indexOf("*/", index + 2);
|
|
138
|
+
return end === -1 ? source.length : end + 2;
|
|
139
|
+
}
|
|
140
|
+
return index;
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* The index just past a string literal starting at `index`, or `index` when
|
|
145
|
+
* none does. A `'`/`"` that doesn't close on its own line is an apostrophe in
|
|
146
|
+
* plain YAML text, not a string.
|
|
147
|
+
*/
|
|
148
|
+
const skipString = (source: string, index: number): number => {
|
|
149
|
+
const quote = source.charAt(index);
|
|
150
|
+
if (!QUOTES.has(quote)) {
|
|
151
|
+
return index;
|
|
152
|
+
}
|
|
153
|
+
let cursor = index + 1;
|
|
154
|
+
while (cursor < source.length) {
|
|
155
|
+
const char = source.charAt(cursor);
|
|
156
|
+
if (char === quote) {
|
|
157
|
+
return cursor + 1;
|
|
158
|
+
}
|
|
159
|
+
if (char === "\n" && quote !== "`") {
|
|
160
|
+
return index;
|
|
161
|
+
}
|
|
162
|
+
cursor += char === "\\" ? 2 : 1;
|
|
163
|
+
}
|
|
164
|
+
return index;
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
/** Where a literal closes, and where each of its direct entries starts. */
|
|
168
|
+
interface LiteralScan {
|
|
169
|
+
end: number;
|
|
170
|
+
entries: number[];
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Walk the `{…}`/`[…]` literal opened at `open`: where each direct entry starts
|
|
175
|
+
* (a key, or an array element) and where the literal closes. Nested brackets,
|
|
176
|
+
* strings, and comments are stepped over, so only the literal's own commas
|
|
177
|
+
* separate its entries.
|
|
178
|
+
*/
|
|
179
|
+
const scanLiteral = (source: string, open: number): LiteralScan => {
|
|
180
|
+
const entries: number[] = [];
|
|
181
|
+
let depth = 0;
|
|
182
|
+
let pending = true;
|
|
183
|
+
let index = open + 1;
|
|
184
|
+
while (index < source.length) {
|
|
185
|
+
const afterComment = skipComment(source, index);
|
|
186
|
+
const char = source.charAt(index);
|
|
187
|
+
if (afterComment !== index || WHITESPACE.test(char)) {
|
|
188
|
+
index = Math.max(afterComment, index + 1);
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
if (depth === 0 && CLOSERS.has(char)) {
|
|
192
|
+
return { end: index, entries };
|
|
193
|
+
}
|
|
194
|
+
if (depth === 0 && pending) {
|
|
195
|
+
entries.push(index);
|
|
196
|
+
pending = false;
|
|
197
|
+
}
|
|
198
|
+
const afterString = skipString(source, index);
|
|
199
|
+
if (afterString !== index) {
|
|
200
|
+
index = afterString;
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
if (OPENERS.has(char)) {
|
|
204
|
+
depth += 1;
|
|
205
|
+
} else if (CLOSERS.has(char)) {
|
|
206
|
+
depth -= 1;
|
|
207
|
+
} else if (char === "," && depth === 0) {
|
|
208
|
+
pending = true;
|
|
209
|
+
}
|
|
210
|
+
index += 1;
|
|
211
|
+
}
|
|
212
|
+
return { end: source.length, entries };
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* The lines indented deeper than the key at `anchor` — a YAML mapping's
|
|
217
|
+
* children — up to the first line that isn't, or `limit`.
|
|
218
|
+
*/
|
|
219
|
+
const indentedBlock = (
|
|
108
220
|
source: string,
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
) => {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
221
|
+
anchor: number,
|
|
222
|
+
limit: number
|
|
223
|
+
): Scope => {
|
|
224
|
+
const column = anchor - (source.lastIndexOf("\n", anchor - 1) + 1);
|
|
225
|
+
const lineEnd = source.indexOf("\n", anchor);
|
|
226
|
+
const start = lineEnd === -1 ? limit : Math.min(lineEnd + 1, limit);
|
|
227
|
+
let end = start;
|
|
228
|
+
while (end < limit) {
|
|
229
|
+
const next = source.indexOf("\n", end);
|
|
230
|
+
const after = next === -1 || next >= limit ? limit : next + 1;
|
|
231
|
+
const line = source.slice(end, after);
|
|
232
|
+
if (line.trim() !== "" && line.length - line.trimStart().length <= column) {
|
|
233
|
+
break;
|
|
234
|
+
}
|
|
235
|
+
end = after;
|
|
115
236
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
237
|
+
return { end, entries: [], kind: "block", start };
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
/** A value's lead-in before its literal: spaces, or an adapter call's `vercel(`. */
|
|
241
|
+
const VALUE_LEAD = /[\s\w$.(]*/uy;
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* The scope holding the children of the key or element at `anchor`, whose
|
|
245
|
+
* value starts at `from`. A `{…}`/`[…]` literal — written directly, or as the
|
|
246
|
+
* first argument of a call like `vercel({…})` — is walked entry by entry;
|
|
247
|
+
* anything else falls back to the lines indented under `anchor` (YAML's
|
|
248
|
+
* nesting), within the enclosing scope's `limit`.
|
|
249
|
+
*/
|
|
250
|
+
const valueScope = (source: string, step: Step, limit: number): Scope => {
|
|
251
|
+
VALUE_LEAD.lastIndex = step.next;
|
|
252
|
+
VALUE_LEAD.exec(source);
|
|
253
|
+
const open = VALUE_LEAD.lastIndex;
|
|
254
|
+
const char = source.charAt(open);
|
|
255
|
+
if (open < limit && (char === "{" || char === "[")) {
|
|
256
|
+
const literal = scanLiteral(source, open);
|
|
257
|
+
return {
|
|
258
|
+
end: literal.end,
|
|
259
|
+
entries: literal.entries,
|
|
260
|
+
kind: char === "{" ? "object" : "array",
|
|
261
|
+
start: open,
|
|
262
|
+
};
|
|
126
263
|
}
|
|
127
|
-
return
|
|
264
|
+
return indentedBlock(source, step.index, limit);
|
|
128
265
|
};
|
|
129
266
|
|
|
267
|
+
/**
|
|
268
|
+
* Find `segment` as a `key:`/`key =` (bare or quoted) in `scope`: among an
|
|
269
|
+
* object literal's own entries, or anywhere in a block. The negative lookbehind
|
|
270
|
+
* keeps `title` from matching the tail of an unrelated key such as `subtitle:`.
|
|
271
|
+
*/
|
|
272
|
+
const findKey = (
|
|
273
|
+
source: string,
|
|
274
|
+
scope: Scope,
|
|
275
|
+
segment: string
|
|
276
|
+
): Step | undefined => {
|
|
277
|
+
const key = escapeRegExp(segment);
|
|
278
|
+
const pattern = `(?<![\\w$])(?:${key}|"${key}"|'${key}')\\s*[:=]`;
|
|
279
|
+
if (scope.kind === "block") {
|
|
280
|
+
const matcher = new RegExp(pattern, "gu");
|
|
281
|
+
matcher.lastIndex = scope.start;
|
|
282
|
+
const match = matcher.exec(source);
|
|
283
|
+
return match && match.index < scope.end
|
|
284
|
+
? { index: match.index, next: matcher.lastIndex }
|
|
285
|
+
: undefined;
|
|
286
|
+
}
|
|
287
|
+
// Sticky: the key must start exactly where the entry does.
|
|
288
|
+
const matcher = new RegExp(pattern, "uy");
|
|
289
|
+
for (const entry of scope.kind === "object" ? scope.entries : []) {
|
|
290
|
+
matcher.lastIndex = entry;
|
|
291
|
+
if (matcher.test(source)) {
|
|
292
|
+
return { index: entry, next: matcher.lastIndex };
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
return undefined;
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
/** The element at `position` of an array literal; a block can't be indexed. */
|
|
299
|
+
const findElement = (scope: Scope, position: number): Step | undefined => {
|
|
300
|
+
const index = scope.kind === "array" ? scope.entries[position] : undefined;
|
|
301
|
+
return index === undefined ? undefined : { index, next: index };
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Best-effort source position for a Zod issue path (e.g. `["seo", "title"]`,
|
|
306
|
+
* `["redirects", 2, "status"]`) in the raw config / frontmatter text. Narrows
|
|
307
|
+
* segment by segment: a key is found among its parent's own entries (or, where
|
|
308
|
+
* there are no brackets, in the parent's indented lines), an index picks that
|
|
309
|
+
* element of an array literal. A segment that isn't there — a missing key —
|
|
310
|
+
* stops the walk at its parent, so the position never escapes into a sibling.
|
|
311
|
+
* Returns 1-based line/column, or undefined when not even the first segment
|
|
312
|
+
* matches.
|
|
313
|
+
*/
|
|
130
314
|
const locatePath = (
|
|
131
315
|
source: string,
|
|
132
316
|
path: readonly (string | number)[]
|
|
133
317
|
): { column: number; line: number } | undefined => {
|
|
134
|
-
let
|
|
318
|
+
let scope: Scope = {
|
|
319
|
+
end: source.length,
|
|
320
|
+
entries: [],
|
|
321
|
+
kind: "block",
|
|
322
|
+
start: 0,
|
|
323
|
+
};
|
|
135
324
|
let found = -1;
|
|
136
325
|
for (const segment of path) {
|
|
137
|
-
const step =
|
|
138
|
-
|
|
326
|
+
const step = isKeySegment(segment)
|
|
327
|
+
? findKey(source, scope, segment)
|
|
328
|
+
: findElement(scope, segment);
|
|
329
|
+
if (!step) {
|
|
139
330
|
break;
|
|
140
331
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
found = step.index;
|
|
144
|
-
}
|
|
332
|
+
found = step.index;
|
|
333
|
+
scope = valueScope(source, step, scope.end);
|
|
145
334
|
}
|
|
146
335
|
if (found < 0) {
|
|
147
336
|
return;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { Nodes } from "mdast";
|
|
2
|
+
import { mdxToMdast } from "satteri";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
CALLOUT_ALIASES,
|
|
6
|
+
CALLOUT_TYPES,
|
|
7
|
+
calloutTypeFor,
|
|
8
|
+
} from "../markdown/directives.ts";
|
|
9
|
+
import { MDX_BODY_FEATURES } from "../markdown/features.ts";
|
|
10
|
+
import { strippedLineOffset } from "./sources/normalize.ts";
|
|
11
|
+
import type { SourceEntry } from "./sources/types.ts";
|
|
12
|
+
import type { Diagnostic } from "./types.ts";
|
|
13
|
+
|
|
14
|
+
// A line that may open a container directive: a colon fence and a name, after
|
|
15
|
+
// any quote or list indentation. A page is parsed only when one of these
|
|
16
|
+
// names something other than a callout, so pages without one cost a scan.
|
|
17
|
+
const CONTAINER_OPENING = /^[\t >]*:{3,}(?<name>[a-z][\w-]*)/gimu;
|
|
18
|
+
|
|
19
|
+
const codeList = (names: Iterable<string>): string =>
|
|
20
|
+
[...names].map((name) => `\`${name}\``).join(", ");
|
|
21
|
+
|
|
22
|
+
const CALLOUT_NAMES = `${codeList(CALLOUT_TYPES)} (or the aliases ${codeList(Object.keys(CALLOUT_ALIASES))})`;
|
|
23
|
+
|
|
24
|
+
/** A container directive that isn't a callout, and its line in `text`. */
|
|
25
|
+
interface UnknownContainer {
|
|
26
|
+
line: number;
|
|
27
|
+
name: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Every container directive in an `.mdx` body whose name isn't a callout, in
|
|
32
|
+
* source order. The body is parsed as the renderer reads it, so a fence in a
|
|
33
|
+
* code block or a `::: name` that isn't a directive at all never counts. A
|
|
34
|
+
* body MDX can't parse fails to render anyway, with its own error.
|
|
35
|
+
*/
|
|
36
|
+
const unknownContainers = (text: string): UnknownContainer[] => {
|
|
37
|
+
const named = [...text.matchAll(CONTAINER_OPENING)].some(
|
|
38
|
+
(match) => calloutTypeFor(match.groups?.name ?? "") === null
|
|
39
|
+
);
|
|
40
|
+
if (!named) {
|
|
41
|
+
return [];
|
|
42
|
+
}
|
|
43
|
+
let tree: Nodes;
|
|
44
|
+
try {
|
|
45
|
+
tree = mdxToMdast(text, { features: MDX_BODY_FEATURES });
|
|
46
|
+
} catch {
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
const found: UnknownContainer[] = [];
|
|
50
|
+
const walk = (node: Nodes): void => {
|
|
51
|
+
if (
|
|
52
|
+
node.type === "containerDirective" &&
|
|
53
|
+
calloutTypeFor(node.name) === null
|
|
54
|
+
) {
|
|
55
|
+
found.push({ line: node.position?.start.line ?? 1, name: node.name });
|
|
56
|
+
}
|
|
57
|
+
if ("children" in node) {
|
|
58
|
+
for (const child of node.children) {
|
|
59
|
+
walk(child);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
walk(tree);
|
|
64
|
+
return found;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Warn about each `:::name` container in an `.mdx` entry that isn't a callout.
|
|
69
|
+
* The page keeps its content — the body renders between the literal `:::`
|
|
70
|
+
* lines (see `markdown/directives.ts`) — but a typo like `:::warnig`, or a
|
|
71
|
+
* `:::details` carried over from another docs tool, should read as the
|
|
72
|
+
* mistake it is rather than as a finished page. Lines point into the file
|
|
73
|
+
* the author wrote, a partial's own file for a container an `<include>`
|
|
74
|
+
* brought in.
|
|
75
|
+
*/
|
|
76
|
+
export const unknownDirectiveDiagnostics = (
|
|
77
|
+
entry: SourceEntry,
|
|
78
|
+
sourceName: string
|
|
79
|
+
): Diagnostic[] => {
|
|
80
|
+
if (entry.body.format !== "mdx") {
|
|
81
|
+
return [];
|
|
82
|
+
}
|
|
83
|
+
const page = entry.sourcePath ?? `${sourceName}:${entry.ref}`;
|
|
84
|
+
const offset =
|
|
85
|
+
entry.bodyLineOffset ?? strippedLineOffset(entry.raw, entry.body.text);
|
|
86
|
+
return unknownContainers(entry.expanded?.text ?? entry.body.text).map(
|
|
87
|
+
({ line, name }) => {
|
|
88
|
+
const origin = entry.expanded?.origins[line - 1];
|
|
89
|
+
return {
|
|
90
|
+
code: "BLUME_UNKNOWN_DIRECTIVE",
|
|
91
|
+
file: origin?.file ?? page,
|
|
92
|
+
line: origin?.line ?? line + offset,
|
|
93
|
+
message: `\`:::${name}\` isn't a callout type, so the page shows its \`:::\` lines as written around its content.`,
|
|
94
|
+
severity: "warning",
|
|
95
|
+
suggestion: `Use a callout type — ${CALLOUT_NAMES} — or remove the \`:::\` lines to keep the content as plain prose.`,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
);
|
|
99
|
+
};
|
package/src/core/frontmatter.ts
CHANGED
|
@@ -19,13 +19,18 @@ type StringifyArgs = Parameters<typeof baseMatter.stringify>;
|
|
|
19
19
|
/** Front matter data as gray-matter types it (`GrayMatterFile["data"]`). */
|
|
20
20
|
type FrontMatterData = ReturnType<typeof baseMatter>["data"];
|
|
21
21
|
|
|
22
|
+
/** A YAML load result: a mapping (or list) Astro keeps as front matter data. */
|
|
23
|
+
const isYamlCollection = (
|
|
24
|
+
value: ReturnType<typeof load>
|
|
25
|
+
): value is FrontMatterData => typeof value === "object" && value !== null;
|
|
26
|
+
|
|
22
27
|
const yamlEngine = {
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
// A block that loads to a scalar (`title` alone, or prose between two
|
|
29
|
+
// `---` lines) is no metadata: Astro reads it as `{}`, and so does Blume.
|
|
30
|
+
parse: (input: string): FrontMatterData => {
|
|
31
|
+
const value = load(input, { schema: YAML_SCHEMA });
|
|
32
|
+
return isYamlCollection(value) ? value : {};
|
|
33
|
+
},
|
|
29
34
|
stringify: (data: FrontMatterData): string => dump(data),
|
|
30
35
|
};
|
|
31
36
|
|
|
@@ -45,23 +50,21 @@ const withYamlEngine = <O extends { engines?: object } | undefined>(
|
|
|
45
50
|
|
|
46
51
|
/**
|
|
47
52
|
* True when a document's leading `---` line is a CommonMark thematic break,
|
|
48
|
-
* not a front matter fence.
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
53
|
+
* not a front matter fence: no closing `---` line follows. Astro's own front
|
|
54
|
+
* matter match requires that close too, while gray-matter would swallow the
|
|
55
|
+
* whole document as one unclosed YAML block and hand it to js-yaml, which
|
|
56
|
+
* crashes on ordinary Markdown (`> quote` → "a line break is expected").
|
|
57
|
+
*
|
|
58
|
+
* A blank line after the opening `---` is still front matter: Astro strips
|
|
59
|
+
* the block from the page either way, so reading it as a divider here would
|
|
60
|
+
* ignore its `draft`, `slug`, and `hidden` while its YAML leaked into search
|
|
61
|
+
* and llms.txt.
|
|
56
62
|
*/
|
|
57
63
|
const opensWithThematicBreak = (input: string): boolean => {
|
|
58
|
-
const [first = ""
|
|
64
|
+
const [first = ""] = input.split(/\r?\n/u, 1);
|
|
59
65
|
if (!/^-{3}\s*$/u.test(first)) {
|
|
60
66
|
return false;
|
|
61
67
|
}
|
|
62
|
-
if (second === undefined || second.trim() === "") {
|
|
63
|
-
return true;
|
|
64
|
-
}
|
|
65
68
|
// gray-matter closes the block at the next line-leading `---`; matching its
|
|
66
69
|
// search exactly keeps this guard from firing on any document it parses.
|
|
67
70
|
return !input.includes("\n---", 1);
|
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,
|