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
|
@@ -1,5 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
isInternalPath,
|
|
3
|
+
withAuthoredBasePath,
|
|
4
|
+
withBasePath,
|
|
5
|
+
} from "../core/base-path.ts";
|
|
6
|
+
import { servesRoute } from "../core/locale-links.ts";
|
|
2
7
|
import type { MdastNode } from "./mdast.ts";
|
|
8
|
+
import { routeSnapshotReader } from "./route-snapshot.ts";
|
|
3
9
|
|
|
4
10
|
interface UrlNode extends MdastNode {
|
|
5
11
|
url?: string | null;
|
|
@@ -14,47 +20,86 @@ interface MdastUrlContext {
|
|
|
14
20
|
setProperty: (node: MdastNode, key: "url", value: string) => void;
|
|
15
21
|
}
|
|
16
22
|
|
|
17
|
-
/**
|
|
18
|
-
* A path whose final segment carries a file extension (`/spec.pdf`, `/logo.svg`)
|
|
19
|
-
* — treated as a public asset, which Blume serves from `public/` at the site
|
|
20
|
-
* root and does *not* move under `basePath`. Bare page links (`/guide`) have no
|
|
21
|
-
* extension. The rare dotted route (`/releases/v1.0`) is left un-based here; the
|
|
22
|
-
* build-time link checker still resolves it against the route set.
|
|
23
|
-
*/
|
|
24
|
-
const ASSET_PATH = /\.[a-z0-9]+$/iu;
|
|
25
|
-
|
|
26
|
-
/** Strip any `#fragment`/`?query` so only the path is extension-tested. */
|
|
27
|
-
const pathOf = (url: string): string => url.replace(/[#?].*$/u, "");
|
|
28
|
-
|
|
29
23
|
/** Only a string URL can be rebased; MDAST allows null or absent urls. */
|
|
30
24
|
const isUrl = (url: string | null | undefined): url is string =>
|
|
31
25
|
typeof url === "string";
|
|
32
26
|
|
|
27
|
+
export interface BaseLinksPluginOptions {
|
|
28
|
+
/**
|
|
29
|
+
* The `blume:data` JSON file, for an ejected app: with no CLI in the process
|
|
30
|
+
* to publish the snapshot, the plugin reads the file eject writes instead.
|
|
31
|
+
*/
|
|
32
|
+
dataFile?: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
33
35
|
/**
|
|
34
|
-
* Satteri MDAST plugin that prepends the served-URL base
|
|
35
|
-
*
|
|
36
|
-
* (`[x](/guide)`
|
|
37
|
-
*
|
|
38
|
-
* `/
|
|
39
|
-
*
|
|
40
|
-
* (
|
|
36
|
+
* Satteri MDAST plugin that prepends the served-URL base to root-relative
|
|
37
|
+
* internal links, so authors write links as if mounted at root. A page link
|
|
38
|
+
* gains `deployment.base` layered over the site-wide `basePath` (`[x](/guide)`
|
|
39
|
+
* -> `/base/docs/guide`); a public asset — an image, or a link to a file like
|
|
40
|
+
* `/spec.pdf` (see `withAuthoredBasePath`) — gains `deployment.base` alone,
|
|
41
|
+
* since Astro serves `public/` under it but never under `basePath`.
|
|
42
|
+
* Idempotent per layer (a hand-written `/docs/x` isn't double-prefixed) and
|
|
43
|
+
* inert for external URLs, fragments, and relative paths. Only constructed
|
|
44
|
+
* when a base is set (see `markdown/index.ts`).
|
|
41
45
|
*/
|
|
42
|
-
export const baseLinksPlugin = (
|
|
43
|
-
|
|
46
|
+
export const baseLinksPlugin = (
|
|
47
|
+
deployBase: string,
|
|
48
|
+
basePath: string,
|
|
49
|
+
options: BaseLinksPluginOptions = {}
|
|
50
|
+
) => {
|
|
51
|
+
const readSnapshot = routeSnapshotReader(options.dataFile);
|
|
52
|
+
// Parsed once per published snapshot, like the relative-links index.
|
|
53
|
+
let cached: { routes: Set<string>; text: string } | undefined;
|
|
54
|
+
|
|
55
|
+
/** Every route the site serves (base-prefixed), from the route snapshot. */
|
|
56
|
+
const servedRoutes = (): ReadonlySet<string> => {
|
|
57
|
+
const text = readSnapshot();
|
|
58
|
+
if (text === undefined) {
|
|
59
|
+
return new Set();
|
|
60
|
+
}
|
|
61
|
+
if (cached?.text !== text) {
|
|
62
|
+
const data: { routes: { path: string }[] } = JSON.parse(text);
|
|
63
|
+
cached = {
|
|
64
|
+
routes: new Set(data.routes.map((route) => route.path)),
|
|
65
|
+
text,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
return cached.routes;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/** Whether a page is served at a based, fragment-less path. */
|
|
72
|
+
const servesPage = (route: string): boolean =>
|
|
73
|
+
servesRoute(servedRoutes(), route);
|
|
74
|
+
|
|
75
|
+
/** Record `based(url)` on the node when its root-relative URL changes. */
|
|
76
|
+
const rebase = (
|
|
77
|
+
node: UrlNode,
|
|
78
|
+
ctx: MdastUrlContext,
|
|
79
|
+
based: (url: string) => string
|
|
80
|
+
): void => {
|
|
44
81
|
const { url } = node;
|
|
45
|
-
if (isUrl(url) && isInternalPath(url)
|
|
46
|
-
const next =
|
|
82
|
+
if (isUrl(url) && isInternalPath(url)) {
|
|
83
|
+
const next = based(url);
|
|
47
84
|
if (next !== url) {
|
|
48
85
|
ctx.setProperty(node, "url", next);
|
|
49
86
|
}
|
|
50
87
|
}
|
|
51
88
|
};
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
89
|
+
const rebaseLink = (node: UrlNode, ctx: MdastUrlContext): void =>
|
|
90
|
+
rebase(node, ctx, (url) =>
|
|
91
|
+
withAuthoredBasePath(deployBase, basePath, url, servesPage)
|
|
92
|
+
);
|
|
93
|
+
// An image is always a file, never a page: `public/` (or a generated asset
|
|
94
|
+
// endpoint), served under the deployment base but not `basePath`.
|
|
95
|
+
const rebaseImage = (node: UrlNode, ctx: MdastUrlContext): void =>
|
|
96
|
+
rebase(node, ctx, (url) => withBasePath(deployBase, url));
|
|
97
|
+
// `link` covers inline links; `definition` covers reference-style
|
|
98
|
+
// definitions (`[x]: /guide`), which a link or an image may cite.
|
|
55
99
|
return {
|
|
56
|
-
definition:
|
|
57
|
-
|
|
100
|
+
definition: rebaseLink,
|
|
101
|
+
image: rebaseImage,
|
|
102
|
+
link: rebaseLink,
|
|
58
103
|
name: "blume-base-links",
|
|
59
104
|
};
|
|
60
105
|
};
|
|
@@ -3,15 +3,30 @@ import { toString as mdastToString } from "mdast-util-to-string";
|
|
|
3
3
|
import { jsxAttribute, jsxFlowElement } from "./mdast.ts";
|
|
4
4
|
import type { MdastNode, MdastVisitorContext } from "./mdast.ts";
|
|
5
5
|
|
|
6
|
-
interface DirectiveNode extends MdastNode {
|
|
6
|
+
export interface DirectiveNode extends MdastNode {
|
|
7
7
|
attributes?: Record<string, string | null | undefined> | null;
|
|
8
8
|
// Satteri gives an empty container directive (`:::note\n:::`) `children: null`.
|
|
9
9
|
children?: MdastNode[] | null;
|
|
10
10
|
name: string;
|
|
11
|
+
position?: {
|
|
12
|
+
start?: { offset?: number };
|
|
13
|
+
end?: { offset?: number };
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The visitor-context slice the directive visitors use. `source` is the page
|
|
19
|
+
* the directive offsets index into; without it the literal fallback rebuilds
|
|
20
|
+
* a directive from its node instead. `parent` walks up to an enclosing
|
|
21
|
+
* container directive, which renders the directives inside it itself.
|
|
22
|
+
*/
|
|
23
|
+
interface DirectiveVisitorContext extends MdastVisitorContext {
|
|
24
|
+
parent?: (node: MdastNode) => MdastNode | undefined;
|
|
25
|
+
source?: string;
|
|
11
26
|
}
|
|
12
27
|
|
|
13
28
|
/** Directive names that map directly onto a Callout type. */
|
|
14
|
-
const CALLOUT_TYPES = new Set([
|
|
29
|
+
export const CALLOUT_TYPES: ReadonlySet<string> = new Set([
|
|
15
30
|
"danger",
|
|
16
31
|
"info",
|
|
17
32
|
"note",
|
|
@@ -25,7 +40,7 @@ interface CalloutAliases {
|
|
|
25
40
|
[alias: string]: string;
|
|
26
41
|
}
|
|
27
42
|
|
|
28
|
-
const
|
|
43
|
+
export const CALLOUT_ALIASES: Readonly<CalloutAliases> = {
|
|
29
44
|
caution: "warning",
|
|
30
45
|
error: "danger",
|
|
31
46
|
important: "note",
|
|
@@ -38,48 +53,239 @@ export const calloutTypeFor = (name: string): string | null => {
|
|
|
38
53
|
if (CALLOUT_TYPES.has(lower)) {
|
|
39
54
|
return lower;
|
|
40
55
|
}
|
|
41
|
-
return
|
|
56
|
+
return CALLOUT_ALIASES[lower] ?? null;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/** The markers that open a text (`:name`) and a leaf (`::name`) directive. */
|
|
60
|
+
const LITERAL_MARKERS = new Map([
|
|
61
|
+
["leafDirective", "::"],
|
|
62
|
+
["textDirective", ":"],
|
|
63
|
+
]);
|
|
64
|
+
|
|
65
|
+
/** A directive's `[label]` rebuilt from its label's text, or nothing. */
|
|
66
|
+
const labelSource = (label: MdastNode[]): string =>
|
|
67
|
+
label.length > 0
|
|
68
|
+
? `[${mdastToString(label, { includeImageAlt: false })}]`
|
|
69
|
+
: "";
|
|
70
|
+
|
|
71
|
+
/** A directive's `{attributes}` rebuilt from its node, or nothing. */
|
|
72
|
+
const attributeSource = (node: DirectiveNode): string => {
|
|
73
|
+
const attributes = Object.entries(node.attributes ?? {}).map(
|
|
74
|
+
([key, value]) => (value ? `${key}="${value}"` : key)
|
|
75
|
+
);
|
|
76
|
+
return attributes.length > 0 ? `{${attributes.join(" ")}}` : "";
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/** The source a directive node spans, when its offsets point into `source`. */
|
|
80
|
+
const spannedSource = (node: DirectiveNode, source: string): string | null => {
|
|
81
|
+
const start = node.position?.start?.offset;
|
|
82
|
+
const end = node.position?.end?.offset;
|
|
83
|
+
return start !== undefined && end !== undefined
|
|
84
|
+
? source.slice(start, end)
|
|
85
|
+
: null;
|
|
42
86
|
};
|
|
43
87
|
|
|
44
88
|
/**
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
89
|
+
* A text or leaf directive exactly as the author wrote it. The slice by
|
|
90
|
+
* offsets is the exact text — `[label]` and `{attrs}` included — and is
|
|
91
|
+
* trusted only when it opens with the directive's own marker and name;
|
|
92
|
+
* content an `<include>` spliced in carries no offsets into this page, so it
|
|
93
|
+
* is rebuilt from the node instead.
|
|
49
94
|
*/
|
|
50
|
-
export const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
95
|
+
export const directiveSource = (
|
|
96
|
+
node: DirectiveNode,
|
|
97
|
+
marker: string,
|
|
98
|
+
source: string
|
|
99
|
+
): string => {
|
|
100
|
+
const opening = `${marker}${node.name}`;
|
|
101
|
+
const slice = spannedSource(node, source);
|
|
102
|
+
if (slice?.startsWith(opening)) {
|
|
103
|
+
return slice;
|
|
104
|
+
}
|
|
105
|
+
return `${opening}${labelSource(node.children ?? [])}${attributeSource(node)}`;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* The node a text or leaf directive renders as: its literal source, as text
|
|
110
|
+
* in place of a text directive and as a paragraph in place of a leaf one.
|
|
111
|
+
*/
|
|
112
|
+
const literalDirective = (
|
|
113
|
+
node: DirectiveNode,
|
|
114
|
+
marker: string,
|
|
115
|
+
source: string
|
|
116
|
+
): MdastNode => {
|
|
117
|
+
const text = { type: "text", value: directiveSource(node, marker, source) };
|
|
118
|
+
return marker === ":" ? text : { children: [text], type: "paragraph" };
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const FENCE = /^:{3,}/u;
|
|
122
|
+
|
|
123
|
+
// The line that closes a container: a colon fence, after whatever quote or
|
|
124
|
+
// list indentation the container sits in.
|
|
125
|
+
const CLOSING_FENCE = /\n[\t >]*(?<fence>:{3,})\s*$/u;
|
|
126
|
+
|
|
127
|
+
/** A paragraph holding one line of literal text. */
|
|
128
|
+
const literalLine = (value: string): MdastNode => ({
|
|
129
|
+
children: [{ type: "text", value }],
|
|
130
|
+
type: "paragraph",
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* The fence lines of a container directive as the author wrote them — the
|
|
135
|
+
* opening one with its `[label]` and `{attrs}` — or, without offsets into
|
|
136
|
+
* this page (an `<include>`), rebuilt from the node. A container left
|
|
137
|
+
* unclosed runs to the end of its parent and has no closing line.
|
|
138
|
+
*/
|
|
139
|
+
const containerFences = (
|
|
140
|
+
node: DirectiveNode,
|
|
141
|
+
label: MdastNode | undefined,
|
|
142
|
+
source: string
|
|
143
|
+
): string[] => {
|
|
144
|
+
const slice = spannedSource(node, source) ?? "";
|
|
145
|
+
const fence = FENCE.exec(slice)?.[0];
|
|
146
|
+
if (fence === undefined || !slice.startsWith(node.name, fence.length)) {
|
|
147
|
+
const labelText = labelSource(label ? [label] : []);
|
|
148
|
+
return [`:::${node.name}${labelText}${attributeSource(node)}`, ":::"];
|
|
149
|
+
}
|
|
150
|
+
const [opening = ""] = slice.split("\n", 1);
|
|
151
|
+
const closing = CLOSING_FENCE.exec(slice.slice(opening.length))?.groups
|
|
152
|
+
?.fence;
|
|
153
|
+
// A shorter fence can't close the container; it is the body's last line.
|
|
154
|
+
return closing !== undefined && closing.length >= fence.length
|
|
155
|
+
? [opening.trimEnd(), closing]
|
|
156
|
+
: [opening.trimEnd()];
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* What a container directive renders as. A callout name becomes a
|
|
161
|
+
* `<Callout>`: the title comes from a `[label]` or a `{title="…"}` attribute,
|
|
162
|
+
* and the body becomes the callout content. Blume has no other container, and
|
|
163
|
+
* dropping one would drop its body with it, so any other name — `:::details`,
|
|
164
|
+
* a `:::warnig` typo — renders its body between its fence lines, as written.
|
|
165
|
+
* The directives inside render here too, since Satteri reaches them only
|
|
166
|
+
* after this replacement has taken their original.
|
|
167
|
+
*/
|
|
168
|
+
const renderContainer = (
|
|
169
|
+
node: DirectiveNode,
|
|
170
|
+
source: string
|
|
171
|
+
): MdastNode | MdastNode[] => {
|
|
172
|
+
const children = (node.children ?? []).flatMap((child) =>
|
|
173
|
+
// oxlint-disable-next-line no-use-before-define -- mutual recursion: a container's body holds directives, containers included
|
|
174
|
+
literalize(child, source)
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
// A leading `:::name[Label]` parses to a paragraph flagged `directiveLabel`.
|
|
178
|
+
// SAFETY: Satteri stamps `directiveLabel` on that paragraph's `data`; any
|
|
179
|
+
// other node reads undefined and fails the check.
|
|
180
|
+
const labelIndex = children.findIndex(
|
|
181
|
+
(child) =>
|
|
182
|
+
child.type === "paragraph" &&
|
|
183
|
+
(child.data as { directiveLabel?: boolean } | undefined)?.directiveLabel
|
|
184
|
+
);
|
|
185
|
+
const [label] = labelIndex === -1 ? [] : children.splice(labelIndex, 1);
|
|
186
|
+
|
|
187
|
+
const type = calloutTypeFor(node.name);
|
|
188
|
+
if (type === null) {
|
|
189
|
+
const [opening = "", closing] = containerFences(node, label, source);
|
|
190
|
+
return [
|
|
191
|
+
literalLine(opening),
|
|
192
|
+
...children,
|
|
193
|
+
...(closing === undefined ? [] : [literalLine(closing)]),
|
|
194
|
+
];
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Flatten the label's phrasing children so `:::note[Read **this**]` yields
|
|
198
|
+
// `Read this`; image alt is excluded (an image is not label text), matching
|
|
199
|
+
// the historical child-values-only behavior.
|
|
200
|
+
const title =
|
|
201
|
+
node.attributes?.title ??
|
|
202
|
+
(label ? mdastToString(label, { includeImageAlt: false }) : undefined);
|
|
203
|
+
const attributes = [jsxAttribute("type", type)];
|
|
204
|
+
if (title) {
|
|
205
|
+
attributes.push(jsxAttribute("title", title));
|
|
206
|
+
}
|
|
207
|
+
return jsxFlowElement("Callout", attributes, children);
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* `node` with every directive in it rendered: text and leaf directives as
|
|
212
|
+
* their literal source, containers as {@link renderContainer}.
|
|
213
|
+
*/
|
|
214
|
+
const literalize = (node: MdastNode, source: string): MdastNode[] => {
|
|
215
|
+
if (node.type === "containerDirective") {
|
|
216
|
+
// SAFETY: a `containerDirective` carries a `name` plus optional
|
|
217
|
+
// attributes, children, and position — the `DirectiveNode` shape.
|
|
218
|
+
return [renderContainer(node as DirectiveNode, source)].flat();
|
|
219
|
+
}
|
|
220
|
+
const marker = LITERAL_MARKERS.get(node.type);
|
|
221
|
+
if (marker) {
|
|
222
|
+
// SAFETY: only Satteri's `textDirective`/`leafDirective` nodes have a
|
|
223
|
+
// marker, and they carry a `name` plus optional attributes, children,
|
|
224
|
+
// and position — the `DirectiveNode` shape.
|
|
225
|
+
return [literalDirective(node as DirectiveNode, marker, source)];
|
|
226
|
+
}
|
|
227
|
+
// SAFETY: a parent's `children` is always a node list; leaves carry none.
|
|
228
|
+
const children = node.children as MdastNode[] | null | undefined;
|
|
229
|
+
if (!children) {
|
|
230
|
+
return [node];
|
|
231
|
+
}
|
|
232
|
+
return [
|
|
233
|
+
{
|
|
234
|
+
...node,
|
|
235
|
+
children: children.flatMap((child) => literalize(child, source)),
|
|
236
|
+
},
|
|
237
|
+
];
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
/** Whether a container directive (already rendered, body and all) holds `node`. */
|
|
241
|
+
const insideContainer = (node: MdastNode, ctx: DirectiveVisitorContext) => {
|
|
242
|
+
for (
|
|
243
|
+
let parent = ctx.parent?.(node);
|
|
244
|
+
parent !== undefined;
|
|
245
|
+
parent = ctx.parent?.(parent)
|
|
246
|
+
) {
|
|
247
|
+
if (parent.type === "containerDirective") {
|
|
248
|
+
return true;
|
|
55
249
|
}
|
|
250
|
+
}
|
|
251
|
+
return false;
|
|
252
|
+
};
|
|
56
253
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
// A
|
|
61
|
-
//
|
|
62
|
-
//
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
child.type === "paragraph" &&
|
|
66
|
-
(child.data as { directiveLabel?: boolean } | undefined)?.directiveLabel
|
|
67
|
-
);
|
|
68
|
-
if (labelIndex !== -1) {
|
|
69
|
-
const [label] = children.splice(labelIndex, 1);
|
|
70
|
-
if (label) {
|
|
71
|
-
// Flatten the label's phrasing children so `:::note[Read **this**]`
|
|
72
|
-
// yields `Read this`; image alt is excluded (an image is not label
|
|
73
|
-
// text), matching the historical child-values-only behavior.
|
|
74
|
-
title ??= mdastToString(label, { includeImageAlt: false }) || undefined;
|
|
75
|
-
}
|
|
254
|
+
/** The text and leaf visitor: render the directive as its literal source. */
|
|
255
|
+
const renderLiteral =
|
|
256
|
+
(marker: string) => (node: DirectiveNode, ctx: DirectiveVisitorContext) => {
|
|
257
|
+
// A container's body was already rendered into its replacement; a
|
|
258
|
+
// transform queued on the replaced original would be dropped with a
|
|
259
|
+
// warning.
|
|
260
|
+
if (insideContainer(node, ctx)) {
|
|
261
|
+
return;
|
|
76
262
|
}
|
|
263
|
+
ctx.replaceNode(node, literalDirective(node, marker, ctx.source ?? ""));
|
|
264
|
+
};
|
|
77
265
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
266
|
+
/**
|
|
267
|
+
* Satteri MDAST plugin for directives. Container directives (`:::note`,
|
|
268
|
+
* `:::warning`, `:::tip`, …) map onto Blume's `<Callout>` component, and any
|
|
269
|
+
* other container renders its body between its literal fence lines (see
|
|
270
|
+
* {@link renderContainer}).
|
|
271
|
+
*
|
|
272
|
+
* Blume handles no text (`:name`) or leaf (`::name`) directives, and prose is
|
|
273
|
+
* full of text that parses as one — `16:9`, `10:30am`, `og:image`,
|
|
274
|
+
* `pets:read` — so both render as the literal source the author wrote instead
|
|
275
|
+
* of vanishing.
|
|
276
|
+
*/
|
|
277
|
+
export const directiveToCalloutPlugin = () => ({
|
|
278
|
+
containerDirective(node: DirectiveNode, ctx: DirectiveVisitorContext) {
|
|
279
|
+
// A container inside another was rendered with its enclosing one.
|
|
280
|
+
if (insideContainer(node, ctx)) {
|
|
281
|
+
return;
|
|
81
282
|
}
|
|
82
|
-
ctx.replaceNode(node,
|
|
283
|
+
ctx.replaceNode(node, renderContainer(node, ctx.source ?? ""));
|
|
83
284
|
},
|
|
285
|
+
leafDirective: renderLiteral("::"),
|
|
84
286
|
name: "blume-directive-callout",
|
|
287
|
+
// Positions are opt-in since satteri 0.10; the literal fallback slices
|
|
288
|
+
// directives out of the source by offset.
|
|
289
|
+
options: { position: true },
|
|
290
|
+
textDirective: renderLiteral(":"),
|
|
85
291
|
});
|
package/src/markdown/features.ts
CHANGED
|
@@ -19,3 +19,20 @@ export const MDX_FEATURES = {
|
|
|
19
19
|
directive: true,
|
|
20
20
|
math: { singleDollarTextMath: false },
|
|
21
21
|
} satisfies Features;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The feature sets for a page body, once its front matter is off. Astro reads
|
|
25
|
+
* a page's front matter itself and hands the renderer the body alone, and the
|
|
26
|
+
* search extractor strips it first too — so front matter parsing is off here:
|
|
27
|
+
* left on, a body that opens with a `---` rule read everything up to the next
|
|
28
|
+
* `---` line as front matter and dropped it from the page.
|
|
29
|
+
*/
|
|
30
|
+
export const MARKDOWN_BODY_FEATURES = {
|
|
31
|
+
...MARKDOWN_FEATURES,
|
|
32
|
+
frontmatter: false,
|
|
33
|
+
} satisfies Features;
|
|
34
|
+
|
|
35
|
+
export const MDX_BODY_FEATURES = {
|
|
36
|
+
...MDX_FEATURES,
|
|
37
|
+
frontmatter: false,
|
|
38
|
+
} satisfies Features;
|
package/src/markdown/index.ts
CHANGED
|
@@ -13,7 +13,7 @@ import { baseLinksPlugin } from "./base-links.ts";
|
|
|
13
13
|
import { codeTitleTransformer } from "./code-title.ts";
|
|
14
14
|
import { directiveToCalloutPlugin } from "./directives.ts";
|
|
15
15
|
import { externalLinksPlugin } from "./external-links.ts";
|
|
16
|
-
import {
|
|
16
|
+
import { MARKDOWN_BODY_FEATURES, MDX_BODY_FEATURES } from "./features.ts";
|
|
17
17
|
import { headingAnchorPlugin } from "./heading-anchors.ts";
|
|
18
18
|
import { includePlugin } from "./include.ts";
|
|
19
19
|
import { inlineCodeHighlightPlugin } from "./inline-code.ts";
|
|
@@ -286,17 +286,18 @@ export interface BlumeMarkdownOptions {
|
|
|
286
286
|
contentRoot?: string;
|
|
287
287
|
/**
|
|
288
288
|
* The `blume:data` snapshot file an ejected app aliases. Relative page links
|
|
289
|
-
*
|
|
290
|
-
* process, they read this
|
|
289
|
+
* (and the base-path rewrite of dotted page routes) resolve through the
|
|
290
|
+
* snapshot the CLI publishes; with no CLI in the process, they read this
|
|
291
|
+
* file instead.
|
|
291
292
|
*/
|
|
292
293
|
dataFile?: string;
|
|
293
294
|
}
|
|
294
295
|
|
|
295
296
|
/**
|
|
296
297
|
* MDAST plugins that apply to both `.md` and `.mdx`: relative page links
|
|
297
|
-
* rewritten to the
|
|
298
|
-
* rewrite (added only when a `basePath` or
|
|
299
|
-
*
|
|
298
|
+
* rewritten to the served URL of the route they mean, then the base-path link
|
|
299
|
+
* rewrite for root-relative links (added only when a `basePath` or
|
|
300
|
+
* `deployBase` is configured).
|
|
300
301
|
*/
|
|
301
302
|
const blumeSharedMdastPlugins = (
|
|
302
303
|
options: BlumeMarkdownOptions
|
|
@@ -305,12 +306,15 @@ const blumeSharedMdastPlugins = (
|
|
|
305
306
|
relativeLinksPlugin({
|
|
306
307
|
contentRoot: options.contentRoot,
|
|
307
308
|
dataFile: options.dataFile,
|
|
309
|
+
deployBase: options.deployBase,
|
|
308
310
|
})
|
|
309
311
|
),
|
|
310
312
|
...(options.basePath || options.deployBase
|
|
311
313
|
? [
|
|
312
314
|
asMdastPlugin(
|
|
313
|
-
baseLinksPlugin(options.deployBase ?? "", options.basePath ?? ""
|
|
315
|
+
baseLinksPlugin(options.deployBase ?? "", options.basePath ?? "", {
|
|
316
|
+
dataFile: options.dataFile,
|
|
317
|
+
})
|
|
314
318
|
),
|
|
315
319
|
]
|
|
316
320
|
: []),
|
|
@@ -327,7 +331,7 @@ const blumeIncludePlugin = (options: BlumeMarkdownOptions): MdastPlugin =>
|
|
|
327
331
|
/** Sätteri processor for plain `.md`, with Blume's curated feature set. */
|
|
328
332
|
export const blumeMarkdownProcessor = (options: BlumeMarkdownOptions = {}) =>
|
|
329
333
|
satteri({
|
|
330
|
-
features: { ...
|
|
334
|
+
features: { ...MARKDOWN_BODY_FEATURES },
|
|
331
335
|
hastPlugins: blumeHastPlugins(options),
|
|
332
336
|
mdastPlugins: [
|
|
333
337
|
blumeIncludePlugin(options),
|
|
@@ -356,7 +360,7 @@ export type BlumeMdxOptions = BlumeMarkdownOptions;
|
|
|
356
360
|
*/
|
|
357
361
|
export const blumeMdxProcessor = (options: BlumeMdxOptions = {}) =>
|
|
358
362
|
satteri({
|
|
359
|
-
features: { ...
|
|
363
|
+
features: { ...MDX_BODY_FEATURES },
|
|
360
364
|
hastPlugins: blumeHastPlugins(options),
|
|
361
365
|
mdastPlugins: [
|
|
362
366
|
blumeIncludePlugin(options),
|
package/src/markdown/mdast.ts
CHANGED
|
@@ -19,9 +19,12 @@ export type MdastValue =
|
|
|
19
19
|
| MdastValue[]
|
|
20
20
|
| { [key: string]: MdastValue };
|
|
21
21
|
|
|
22
|
-
/**
|
|
22
|
+
/**
|
|
23
|
+
* The visitor context Blume's plugins use to mutate the tree. A list of
|
|
24
|
+
* nodes takes the replaced node's place in order.
|
|
25
|
+
*/
|
|
23
26
|
export interface MdastVisitorContext {
|
|
24
|
-
replaceNode: (node: MdastNode, replacement: MdastNode) => void;
|
|
27
|
+
replaceNode: (node: MdastNode, replacement: MdastNode | MdastNode[]) => void;
|
|
25
28
|
}
|
|
26
29
|
|
|
27
30
|
/** Any MDAST node, keyed loosely since we build a small subset by hand. */
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { readFileSync, statSync } from "node:fs";
|
|
2
1
|
import { fileURLToPath } from "node:url";
|
|
3
2
|
|
|
4
3
|
import { dirname, normalize, relative, resolve } from "pathe";
|
|
5
4
|
|
|
6
|
-
import {
|
|
5
|
+
import { mountBasePath } from "../core/base-path.ts";
|
|
7
6
|
import { isIndexFileName, resolveRelativeHref } from "../core/links.ts";
|
|
8
7
|
import type { RelativeLinkBase } from "../core/links.ts";
|
|
9
8
|
import type { MdastNode, MdastValue } from "./mdast.ts";
|
|
9
|
+
import { routeSnapshotReader } from "./route-snapshot.ts";
|
|
10
10
|
|
|
11
11
|
interface UrlNode extends MdastNode {
|
|
12
12
|
url?: string | null;
|
|
@@ -131,6 +131,8 @@ export interface RelativeLinksPluginOptions {
|
|
|
131
131
|
* to publish the snapshot, the plugin reads the file eject writes instead.
|
|
132
132
|
*/
|
|
133
133
|
dataFile?: string;
|
|
134
|
+
/** Astro's `deployment.base` subdirectory (`""` or `/seg`). */
|
|
135
|
+
deployBase?: string;
|
|
134
136
|
}
|
|
135
137
|
|
|
136
138
|
/**
|
|
@@ -146,9 +148,11 @@ export interface RelativeLinksPluginOptions {
|
|
|
146
148
|
* entries (remote sources) by the longest trailing path that names one. A file
|
|
147
149
|
* the snapshot doesn't know keeps its links as written.
|
|
148
150
|
*
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
* `
|
|
151
|
+
* A rewritten route already carries `basePath`, and the `deployment.base`
|
|
152
|
+
* prefix goes on here, unconditionally: the route is one Blume serves, so
|
|
153
|
+
* `guides/setup.md` under base `/guides` is linked at `/guides/guides/setup`.
|
|
154
|
+
* The base-links plugin that runs next is idempotent per layer, so it leaves
|
|
155
|
+
* the based route alone.
|
|
152
156
|
*/
|
|
153
157
|
export const relativeLinksPlugin = (
|
|
154
158
|
options: RelativeLinksPluginOptions = {}
|
|
@@ -156,34 +160,16 @@ export const relativeLinksPlugin = (
|
|
|
156
160
|
const contentRoot = options.contentRoot
|
|
157
161
|
? resolve(options.contentRoot)
|
|
158
162
|
: undefined;
|
|
159
|
-
const
|
|
163
|
+
const deployBase = options.deployBase ?? "";
|
|
164
|
+
const readSnapshot = routeSnapshotReader(options.dataFile);
|
|
160
165
|
|
|
161
166
|
// Parsed once per published snapshot: the CLI republishes on regeneration,
|
|
162
167
|
// and an unchanged snapshot is the same string, so every page compiled
|
|
163
168
|
// between regenerations reuses one index.
|
|
164
169
|
let cached: { index: RouteIndex; text: string } | undefined;
|
|
165
|
-
let fileStamp: number | undefined;
|
|
166
|
-
let fileText: string | undefined;
|
|
167
|
-
|
|
168
|
-
/** The ejected snapshot file's text, re-read only when it changes. */
|
|
169
|
-
const readDataFile = (path: string): string | undefined => {
|
|
170
|
-
let stamp: number;
|
|
171
|
-
try {
|
|
172
|
-
stamp = statSync(path).mtimeMs;
|
|
173
|
-
} catch {
|
|
174
|
-
return undefined;
|
|
175
|
-
}
|
|
176
|
-
if (stamp !== fileStamp) {
|
|
177
|
-
fileStamp = stamp;
|
|
178
|
-
fileText = readFileSync(path, "utf-8");
|
|
179
|
-
}
|
|
180
|
-
return fileText;
|
|
181
|
-
};
|
|
182
170
|
|
|
183
171
|
const routeIndex = (): RouteIndex | undefined => {
|
|
184
|
-
const text =
|
|
185
|
-
readRuntimeModule("blume:data") ??
|
|
186
|
-
(dataFile ? readDataFile(dataFile) : undefined);
|
|
172
|
+
const text = readSnapshot();
|
|
187
173
|
if (text === undefined) {
|
|
188
174
|
return undefined;
|
|
189
175
|
}
|
|
@@ -265,7 +251,9 @@ export const relativeLinksPlugin = (
|
|
|
265
251
|
const next = resolveRelativeHref(url, page, page.routeOf, (route) =>
|
|
266
252
|
page.routes.has(route)
|
|
267
253
|
);
|
|
268
|
-
return next ===
|
|
254
|
+
return next === undefined || next === url
|
|
255
|
+
? undefined
|
|
256
|
+
: mountBasePath(deployBase, next);
|
|
269
257
|
};
|
|
270
258
|
|
|
271
259
|
const rewrite = (node: UrlNode, ctx: RelativeLinksContext): void => {
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { readFileSync, statSync } from "node:fs";
|
|
2
|
+
|
|
3
|
+
import { resolve } from "pathe";
|
|
4
|
+
|
|
5
|
+
import { readRuntimeModule } from "../astro/runtime-modules.ts";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A reader for the `blume:data` snapshot the Markdown link plugins consult for
|
|
9
|
+
* the site's routes: the text the CLI publishes, or — in an ejected app, with
|
|
10
|
+
* no CLI in the process — the snapshot file eject writes (`dataFile`), re-read
|
|
11
|
+
* only when it changes. Resolves to `undefined` when neither is available.
|
|
12
|
+
*/
|
|
13
|
+
export const routeSnapshotReader = (
|
|
14
|
+
dataFile?: string
|
|
15
|
+
): (() => string | undefined) => {
|
|
16
|
+
const file = dataFile ? resolve(dataFile) : undefined;
|
|
17
|
+
let fileStamp: number | undefined;
|
|
18
|
+
let fileText: string | undefined;
|
|
19
|
+
|
|
20
|
+
/** The ejected snapshot file's text, re-read only when it changes. */
|
|
21
|
+
const readDataFile = (path: string): string | undefined => {
|
|
22
|
+
let stamp: number;
|
|
23
|
+
try {
|
|
24
|
+
stamp = statSync(path).mtimeMs;
|
|
25
|
+
} catch {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
if (stamp !== fileStamp) {
|
|
29
|
+
fileStamp = stamp;
|
|
30
|
+
fileText = readFileSync(path, "utf-8");
|
|
31
|
+
}
|
|
32
|
+
return fileText;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
return () =>
|
|
36
|
+
readRuntimeModule("blume:data") ?? (file ? readDataFile(file) : undefined);
|
|
37
|
+
};
|