blume 2.1.3 → 2.2.1
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/AGENTS.md +1 -1
- package/CHANGELOG.md +134 -0
- package/README.md +4 -4
- package/dist/cli/{chunk-y4msrwm9.js → chunk-080mfch6.js} +15 -15
- package/dist/cli/{chunk-gfc5fqvf.js → chunk-1ch3ct3r.js} +18 -18
- package/dist/cli/{chunk-gfc5fqvf.js.map → chunk-1ch3ct3r.js.map} +1 -1
- package/dist/cli/{chunk-32khwcb6.js → chunk-2ssh60qh.js} +42 -69
- package/dist/cli/{chunk-32khwcb6.js.map → chunk-2ssh60qh.js.map} +9 -10
- package/dist/cli/{chunk-9qzgg6mj.js → chunk-34wap3ed.js} +3 -3
- package/dist/cli/{chunk-k3gr1nm9.js → chunk-40vk8kgf.js} +13 -16
- package/dist/cli/{chunk-k3gr1nm9.js.map → chunk-40vk8kgf.js.map} +3 -3
- package/dist/cli/chunk-487b95qy.js +329 -0
- package/dist/cli/chunk-487b95qy.js.map +13 -0
- package/dist/cli/chunk-4zxh25hc.js +157 -0
- package/dist/cli/chunk-4zxh25hc.js.map +10 -0
- package/dist/cli/chunk-69rjf786.js +146 -0
- package/dist/cli/chunk-69rjf786.js.map +11 -0
- package/dist/cli/{chunk-dqhg54mr.js → chunk-9h8mh2tk.js} +3 -3
- package/dist/cli/{chunk-dqhg54mr.js.map → chunk-9h8mh2tk.js.map} +2 -2
- package/dist/cli/{chunk-bqxaqn5p.js → chunk-9jqmq5hv.js} +8 -8
- package/dist/cli/{chunk-bqxaqn5p.js.map → chunk-9jqmq5hv.js.map} +1 -1
- package/dist/cli/{chunk-dv2z0rzf.js → chunk-aafhwx74.js} +38 -13
- package/dist/cli/{chunk-dv2z0rzf.js.map → chunk-aafhwx74.js.map} +5 -4
- package/dist/cli/{chunk-desvm72z.js → chunk-b680sv30.js} +22 -26
- package/dist/cli/chunk-b680sv30.js.map +13 -0
- package/dist/cli/{chunk-0w6hs479.js → chunk-beb0z983.js} +30 -18
- package/dist/cli/chunk-beb0z983.js.map +10 -0
- package/dist/cli/{chunk-1f882900.js → chunk-c5bjxq8v.js} +2 -2
- package/dist/cli/chunk-e1n41w9m.js +155 -0
- package/dist/cli/chunk-e1n41w9m.js.map +11 -0
- package/dist/cli/{chunk-sc93apfs.js → chunk-etckvbrh.js} +17 -17
- package/dist/cli/{chunk-sc93apfs.js.map → chunk-etckvbrh.js.map} +2 -2
- package/dist/cli/{chunk-bxwhtvw8.js → chunk-f260jdrn.js} +5 -5
- package/dist/cli/{chunk-74wfhv79.js → chunk-fnxgtjeq.js} +1 -1
- package/dist/cli/{chunk-ms9z66nz.js → chunk-gwy55cx3.js} +2 -2
- package/dist/cli/{chunk-ms9z66nz.js.map → chunk-gwy55cx3.js.map} +2 -2
- package/dist/cli/{chunk-dhwtqnnd.js → chunk-gxhcf2hs.js} +2 -2
- package/dist/cli/{chunk-tfycddbt.js → chunk-h0qwdbem.js} +2 -2
- package/dist/cli/{chunk-nbyvgnnm.js → chunk-hktq4j5r.js} +109 -49
- package/dist/cli/chunk-hktq4j5r.js.map +36 -0
- package/dist/cli/{chunk-twy7psdz.js → chunk-hn3g2b89.js} +14 -10
- package/dist/cli/chunk-hn3g2b89.js.map +10 -0
- package/dist/cli/{chunk-j1x92mx9.js → chunk-j4nfgn2j.js} +1 -1
- package/dist/cli/{chunk-dpy8qdjf.js → chunk-k45sq16q.js} +74 -183
- package/dist/cli/chunk-k45sq16q.js.map +18 -0
- package/dist/cli/{chunk-5cmts6dv.js → chunk-kmswjxj9.js} +7 -7
- package/dist/cli/{chunk-5cmts6dv.js.map → chunk-kmswjxj9.js.map} +1 -1
- package/dist/cli/{chunk-36bxs9c7.js → chunk-nqna8dzp.js} +27 -23
- package/dist/cli/chunk-nqna8dzp.js.map +10 -0
- package/dist/cli/{chunk-a57yh0h9.js → chunk-p834rkcn.js} +7 -7
- package/dist/cli/{chunk-a57yh0h9.js.map → chunk-p834rkcn.js.map} +1 -1
- package/dist/cli/{chunk-60hz265r.js → chunk-ppa6mg4d.js} +24 -11
- package/dist/cli/chunk-ppa6mg4d.js.map +10 -0
- package/dist/cli/{chunk-s0bdnc7v.js → chunk-q1wwrxzw.js} +5 -4
- package/dist/cli/chunk-q1wwrxzw.js.map +10 -0
- package/dist/cli/{chunk-ave1wcbr.js → chunk-qcyxkj44.js} +1 -1
- package/dist/cli/{chunk-124fvmzq.js → chunk-r5nm9w0c.js} +11183 -8806
- package/dist/cli/{chunk-124fvmzq.js.map → chunk-r5nm9w0c.js.map} +82 -66
- package/dist/cli/{chunk-ckrcm08h.js → chunk-rtvgmg2j.js} +48 -62
- package/dist/cli/{chunk-ckrcm08h.js.map → chunk-rtvgmg2j.js.map} +4 -5
- package/dist/cli/{chunk-297ajdcv.js → chunk-t2h8pwy1.js} +6 -6
- package/dist/cli/{chunk-297ajdcv.js.map → chunk-t2h8pwy1.js.map} +3 -3
- package/dist/cli/{chunk-gxtg6ed2.js → chunk-x60e15st.js} +21 -21
- package/dist/cli/{chunk-gxtg6ed2.js.map → chunk-x60e15st.js.map} +3 -3
- package/dist/cli/{chunk-58bt3hag.js → chunk-x7bh6d9j.js} +4 -4
- package/dist/cli/{chunk-hvcxng5f.js → chunk-ydqzkhjn.js} +6 -6
- package/dist/cli/{chunk-kdpzw38a.js → chunk-yg8ckc3g.js} +95 -7
- package/dist/cli/chunk-yg8ckc3g.js.map +11 -0
- package/dist/cli/{chunk-0pmb4tr4.js → chunk-zxd5jrgx.js} +1205 -222
- package/dist/cli/chunk-zxd5jrgx.js.map +44 -0
- package/dist/cli/index.js +78 -42
- package/dist/cli/index.js.map +7 -6
- package/dist/types/ai/changelog-components.d.ts +18 -0
- package/dist/types/ai/changelog-markdown.d.ts +12 -0
- package/dist/types/ai/openapi-components.d.ts +7 -7
- package/dist/types/ai/operation-details.d.ts +39 -0
- package/dist/types/ai/relative-links.d.ts +3 -1
- package/dist/types/ai/serializers.d.ts +6 -4
- package/dist/types/analytics/head.d.ts +4 -2
- package/dist/types/analytics/script.d.ts +8 -2
- package/dist/types/components/content/youtube.d.ts +20 -0
- package/dist/types/components/layout/nav-utils.d.ts +8 -0
- package/dist/types/components/openapi/example-panels.d.ts +33 -0
- package/dist/types/components/openapi/helpers.d.ts +53 -11
- package/dist/types/components/openapi/snippets.d.ts +7 -1
- package/dist/types/core/alert-diagnostics.d.ts +10 -0
- package/dist/types/core/builtin-tags.d.ts +16 -0
- package/dist/types/core/changelog-list.d.ts +48 -0
- package/dist/types/core/closest-match.d.ts +6 -0
- package/dist/types/core/code-fence-diagnostics.d.ts +18 -0
- package/dist/types/core/code-fences.d.ts +8 -1
- package/dist/types/core/config-input.d.ts +38 -6
- package/dist/types/core/content-assets.d.ts +16 -18
- package/dist/types/core/data.d.ts +6 -2
- package/dist/types/core/diagnostics.d.ts +14 -0
- package/dist/types/core/directive-diagnostics.d.ts +22 -8
- package/dist/types/core/former-routes.d.ts +14 -0
- package/dist/types/core/github.d.ts +5 -4
- package/dist/types/core/heading-markers.d.ts +65 -2
- package/dist/types/core/hidden-pages.d.ts +3 -0
- package/dist/types/core/includes.d.ts +23 -0
- package/dist/types/core/last-modified.d.ts +18 -6
- package/dist/types/core/links.d.ts +42 -2
- package/dist/types/core/load-module.d.ts +19 -4
- package/dist/types/core/manifest.d.ts +14 -1
- package/dist/types/core/markdown-mirrors.d.ts +22 -0
- package/dist/types/core/mcp-clients.d.ts +47 -0
- package/dist/types/core/mdx-elements.d.ts +32 -0
- package/dist/types/core/mdx-syntax.d.ts +100 -0
- package/dist/types/core/meta-diagnostics.d.ts +7 -0
- package/dist/types/core/meta.d.ts +32 -5
- package/dist/types/core/nav-diagnostics.d.ts +16 -2
- package/dist/types/core/navigation.d.ts +13 -0
- package/dist/types/core/ordering-prefix.d.ts +6 -0
- package/dist/types/core/pinned-ids.d.ts +23 -0
- package/dist/types/core/project-graph.d.ts +9 -1
- package/dist/types/core/redirect-patterns.d.ts +18 -0
- package/dist/types/core/relative-files.d.ts +31 -0
- package/dist/types/core/schema.d.ts +322 -11
- package/dist/types/core/sources/normalize.d.ts +101 -9
- package/dist/types/core/sources/types.d.ts +16 -0
- package/dist/types/core/static-files.d.ts +10 -0
- package/dist/types/core/static-host.d.ts +42 -0
- package/dist/types/core/svg-images.d.ts +26 -0
- package/dist/types/core/syntax-diagnostics.d.ts +14 -0
- package/dist/types/core/types.d.ts +42 -1
- package/dist/types/deploy/generated-files.d.ts +41 -0
- package/dist/types/deploy/redirects.d.ts +21 -7
- package/dist/types/markdown/code-title.d.ts +9 -5
- package/dist/types/markdown/directives.d.ts +8 -0
- package/dist/types/markdown/fence-language.d.ts +51 -0
- package/dist/types/markdown/fence-meta.d.ts +5 -0
- package/dist/types/markdown/github-alerts.d.ts +35 -0
- package/dist/types/openapi/asyncapi.d.ts +6 -5
- package/dist/types/openapi/checks.d.ts +10 -5
- package/dist/types/openapi/model.d.ts +55 -8
- package/dist/types/openapi/parse.d.ts +4 -1
- package/dist/types/ratelimit/index.d.ts +2 -0
- package/dist/types/ratelimit/memory.d.ts +1 -1
- package/dist/types/ratelimit/schema.d.ts +40 -0
- package/dist/types/ratelimit/unkey.d.ts +48 -0
- package/dist/types/sources/filesystem.d.ts +14 -4
- package/dist/types/sources/mdx-remote.d.ts +6 -3
- package/dist/types/sources/registry.d.ts +3 -3
- package/dist/types/theme/color.d.ts +33 -0
- package/dist/types/theme/palette.d.ts +122 -0
- package/docs/01-quickstart.mdx +1 -1
- package/docs/02-deployment.mdx +24 -8
- package/docs/04-migrating.mdx +16 -4
- package/docs/advanced/changelog.mdx +27 -1
- package/docs/advanced/skills.mdx +1 -1
- package/docs/cli/audit.mdx +1 -1
- package/docs/cli/doctor.mdx +2 -2
- package/docs/cli/evals.mdx +1 -1
- package/docs/cli/index.mdx +4 -2
- package/docs/cli/validate.mdx +10 -7
- package/docs/configuration/analytics.mdx +2 -0
- package/docs/configuration/customization.mdx +3 -1
- package/docs/configuration/index.mdx +7 -6
- package/docs/configuration/rate-limiting.mdx +20 -1
- package/docs/configuration/theming.mdx +5 -1
- package/docs/content/components.mdx +26 -6
- package/docs/content/frontmatter.mdx +1 -1
- package/docs/content/i18n.mdx +1 -1
- package/docs/content/includes.mdx +6 -2
- package/docs/content/index.mdx +11 -1
- package/docs/content/islands.mdx +2 -0
- package/docs/content/meta.mdx +9 -3
- package/docs/content/navigation.mdx +14 -6
- package/docs/content/sources/obsidian.mdx +1 -1
- package/docs/content/sources/remote-mdx.mdx +3 -1
- package/docs/content/syntax.mdx +76 -8
- package/docs/content/variables.mdx +3 -1
- package/docs/discoverability/agent-discovery.mdx +1 -1
- package/docs/discoverability/json-api.mdx +1 -1
- package/docs/discoverability/llms-txt.mdx +1 -1
- package/docs/discoverability/markdown.mdx +2 -2
- package/docs/discoverability/mcp.mdx +40 -8
- package/docs/discoverability/open-graph.mdx +1 -1
- package/docs/discoverability/sitemap-and-robots.mdx +1 -1
- package/docs/references/graphql.mdx +2 -0
- package/docs/references/openapi.mdx +45 -7
- package/package.json +4 -4
- package/skills/blume/SKILL.md +2 -2
- package/skills/blume-migrate/SKILL.md +51 -29
- package/skills/blume-migrate/references/docsify.md +262 -0
- package/skills/blume-migrate/references/docus.md +298 -0
- package/skills/blume-migrate/references/docusaurus.md +5 -5
- package/skills/blume-migrate/references/fern.md +327 -0
- package/skills/blume-migrate/references/fumadocs.md +1 -1
- package/skills/blume-migrate/references/gitbook.md +267 -0
- package/skills/blume-migrate/references/github-wiki.md +259 -0
- package/skills/blume-migrate/references/jekyll.md +253 -0
- package/skills/blume-migrate/references/mdbook.md +256 -0
- package/skills/blume-migrate/references/mintlify.md +5 -5
- package/skills/blume-migrate/references/mkdocs.md +236 -0
- package/skills/blume-migrate/references/monorepo.md +20 -8
- package/skills/blume-migrate/references/nextra.md +5 -5
- package/skills/blume-migrate/references/readme.md +257 -0
- package/skills/blume-migrate/references/redocly.md +198 -0
- package/skills/blume-migrate/references/starlight.md +3 -3
- package/skills/blume-migrate/references/vitepress.md +204 -0
- package/skills/blume-migrate/references/vuepress.md +271 -0
- package/skills/blume-migrate/scripts/docsify-codemod.mjs +2387 -0
- package/skills/blume-migrate/scripts/docus-codemod.mjs +2542 -0
- package/skills/blume-migrate/scripts/fern-codemod.mjs +2633 -0
- package/skills/blume-migrate/scripts/github-wiki-codemod.mjs +2808 -0
- package/skills/blume-migrate/scripts/include-excerpts.mjs +649 -0
- package/skills/blume-migrate/scripts/jekyll-codemod.mjs +3649 -0
- package/skills/blume-migrate/scripts/mintlify-codemod.mjs +17 -13
- package/skills/blume-migrate/scripts/mkdocs-codemod.mjs +2526 -0
- package/skills/blume-migrate/scripts/operation-routes.mjs +508 -0
- package/skills/blume-migrate/scripts/pin-heading-ids.mjs +1049 -0
- package/skills/blume-migrate/scripts/readme-codemod.mjs +4139 -0
- package/skills/blume-migrate/scripts/vitepress-codemod.mjs +1511 -0
- package/src/ai/changelog-components.ts +24 -0
- package/src/ai/changelog-markdown.ts +45 -41
- package/src/ai/component-markdown.ts +22 -9
- package/src/ai/llms.ts +36 -5
- package/src/ai/markdown.ts +2 -2
- package/src/ai/openapi-components.ts +25 -9
- package/src/ai/operation-details.ts +208 -0
- package/src/ai/relative-links.ts +48 -8
- package/src/ai/serializers.ts +7 -6
- package/src/ai/site-skill.ts +6 -2
- package/src/analytics/head.ts +11 -5
- package/src/analytics/script.ts +13 -3
- package/src/astro/generate.ts +47 -11
- package/src/astro/index.ts +1 -0
- package/src/astro/integration.ts +27 -1
- package/src/astro/mdx-source-errors.ts +46 -0
- package/src/astro/templates.ts +93 -189
- package/src/audit/checks/redirects.ts +5 -1
- package/src/audit/redirects.ts +36 -19
- package/src/audit/report.ts +22 -9
- package/src/audit/run.ts +12 -28
- package/src/audit/terms.ts +23 -0
- package/src/audit/url.ts +2 -2
- package/src/cli/build-failure.ts +256 -7
- package/src/cli/command-meta.ts +1 -1
- package/src/cli/commands/audit.ts +1 -1
- package/src/cli/commands/build.ts +12 -3
- package/src/cli/commands/check.ts +13 -2
- package/src/cli/commands/dev.ts +1 -1
- package/src/cli/commands/doctor.ts +4 -2
- package/src/cli/commands/init.ts +3 -3
- package/src/cli/commands/preview.ts +17 -1
- package/src/cli/commands/validate.ts +22 -10
- package/src/cli/prepare.ts +1 -1
- package/src/cli/preview-pages.ts +161 -0
- package/src/cli/required-secrets.ts +0 -1
- package/src/cli/unknown-flags.ts +1 -42
- package/src/components/code-copy.ts +51 -0
- package/src/components/code-language.ts +75 -0
- package/src/components/content/AccordionItem.astro +16 -1
- package/src/components/content/Callout.astro +11 -1
- package/src/components/content/Changelog.astro +189 -0
- package/src/components/content/CodeBlock.astro +8 -11
- package/src/components/content/Tabs.astro +16 -2
- package/src/components/content/YouTube.astro +6 -4
- package/src/components/content/code-block.ts +33 -0
- package/src/components/content/content-strings.ts +5 -1
- package/src/components/content/inline-markdown.ts +35 -8
- package/src/components/content/youtube.ts +49 -7
- package/src/components/layout/Analytics.astro +5 -1
- package/src/components/layout/NavTree.astro +8 -3
- package/src/components/layout/PageActions.astro +81 -23
- package/src/components/layout/RootLayout.astro +35 -32
- package/src/components/layout/SiteFooter.astro +31 -8
- package/src/components/layout/nav-utils.ts +14 -0
- package/src/components/layout/search/pagefind.ts +2 -1
- package/src/components/openapi/Operation.astro +4 -0
- package/src/components/openapi/PanelTabs.astro +9 -3
- package/src/components/openapi/RequestBody.astro +42 -8
- package/src/components/openapi/RequestPanel.astro +34 -53
- package/src/components/openapi/example-panels.ts +110 -0
- package/src/components/openapi/helpers.ts +195 -20
- package/src/components/openapi/operation-model.ts +63 -26
- package/src/components/openapi/request.ts +13 -0
- package/src/components/openapi/snippets.ts +14 -2
- package/src/core/alert-diagnostics.ts +89 -0
- package/src/core/builtin-tags.ts +76 -0
- package/src/core/changelog-list.ts +87 -0
- package/src/core/closest-match.ts +41 -0
- package/src/core/code-fence-diagnostics.ts +267 -0
- package/src/core/code-fences.ts +43 -7
- package/src/core/component-diagnostics.ts +65 -12
- package/src/core/config-input.ts +39 -5
- package/src/core/config.ts +46 -1
- package/src/core/content-assets.ts +64 -144
- package/src/core/data.ts +10 -3
- package/src/core/diagnostics.ts +36 -3
- package/src/core/directive-diagnostics.ts +265 -47
- package/src/core/former-routes.ts +46 -0
- package/src/core/git-ignored.ts +53 -0
- package/src/core/github.ts +5 -4
- package/src/core/heading-markers.ts +156 -2
- package/src/core/hidden-pages.ts +64 -0
- package/src/core/includes.ts +181 -41
- package/src/core/last-modified.ts +47 -10
- package/src/core/links.ts +438 -83
- package/src/core/load-module.ts +98 -9
- package/src/core/manifest.ts +12 -9
- package/src/core/markdown-mirrors.ts +52 -0
- package/src/core/mcp-clients.ts +87 -0
- package/src/core/mdx-elements.ts +153 -0
- package/src/core/mdx-syntax.ts +377 -0
- package/src/core/meta-diagnostics.ts +134 -0
- package/src/core/meta.ts +154 -55
- package/src/core/nav-diagnostics.ts +41 -4
- package/src/core/navigation.ts +70 -10
- package/src/core/ordering-prefix.ts +22 -2
- package/src/core/pinned-ids.ts +141 -0
- package/src/core/project-graph.ts +221 -29
- package/src/core/redirect-patterns.ts +41 -0
- package/src/core/relative-files.ts +145 -0
- package/src/core/schema.ts +110 -6
- package/src/core/sources/mdx-remote.ts +75 -14
- package/src/core/sources/normalize.ts +885 -179
- package/src/core/sources/notion.ts +5 -4
- package/src/core/sources/obsidian.ts +2 -5
- package/src/core/sources/types.ts +17 -0
- package/src/core/static-files.ts +40 -0
- package/src/core/static-host.ts +125 -0
- package/src/core/svg-images.ts +98 -0
- package/src/core/syntax-diagnostics.ts +491 -0
- package/src/core/types.ts +43 -1
- package/src/deploy/artifacts.ts +2 -28
- package/src/deploy/generated-files.ts +128 -0
- package/src/deploy/platforms/vercel.ts +25 -1
- package/src/deploy/redirects.ts +43 -16
- package/src/deploy/sitemap.ts +2 -1
- package/src/markdown/base-links.ts +87 -6
- package/src/markdown/code-title.ts +29 -13
- package/src/markdown/content-assets.ts +210 -0
- package/src/markdown/directives.ts +69 -5
- package/src/markdown/fence-language.ts +106 -0
- package/src/markdown/fence-meta.ts +42 -0
- package/src/markdown/github-alerts.ts +172 -0
- package/src/markdown/heading-anchors.ts +265 -56
- package/src/markdown/include.ts +30 -3
- package/src/markdown/index.ts +145 -40
- package/src/markdown/prompt-text.ts +95 -0
- package/src/markdown/relative-links.ts +37 -2
- package/src/markdown/route-snapshot.ts +22 -12
- package/src/markdown/svg-fallback.ts +51 -0
- package/src/markdown/title-heading.ts +134 -0
- package/src/migrate/migrate.ts +127 -2
- package/src/og/card.ts +14 -4
- package/src/openapi/asyncapi.ts +14 -6
- package/src/openapi/checks.ts +160 -15
- package/src/openapi/model.ts +172 -33
- package/src/openapi/parse.ts +13 -4
- package/src/openapi/render-mdx.ts +55 -9
- package/src/openapi/source.ts +87 -6
- package/src/ratelimit/index.ts +2 -0
- package/src/ratelimit/memory.ts +1 -1
- package/src/ratelimit/runtime.ts +81 -0
- package/src/ratelimit/schema.ts +2 -0
- package/src/ratelimit/unkey.ts +61 -0
- package/src/registry/eject.ts +0 -1
- package/src/registry/registry.ts +1 -1
- package/src/search/documents.ts +10 -2
- package/src/sources/filesystem.ts +28 -3
- package/src/sources/mdx-remote.ts +7 -15
- package/src/theme/color.ts +14 -0
- package/src/theme/entry.ts +22 -1
- package/src/theme/palette.ts +74 -1
- package/dist/cli/chunk-0pmb4tr4.js.map +0 -35
- package/dist/cli/chunk-0w6hs479.js.map +0 -10
- package/dist/cli/chunk-150jk7km.js +0 -52
- package/dist/cli/chunk-150jk7km.js.map +0 -10
- package/dist/cli/chunk-36bxs9c7.js.map +0 -10
- package/dist/cli/chunk-53jqw8z0.js +0 -89
- package/dist/cli/chunk-53jqw8z0.js.map +0 -10
- package/dist/cli/chunk-60hz265r.js.map +0 -10
- package/dist/cli/chunk-desvm72z.js.map +0 -14
- package/dist/cli/chunk-dpy8qdjf.js.map +0 -17
- package/dist/cli/chunk-es8h0v3j.js +0 -441
- package/dist/cli/chunk-es8h0v3j.js.map +0 -11
- package/dist/cli/chunk-kdpzw38a.js.map +0 -11
- package/dist/cli/chunk-nbyvgnnm.js.map +0 -36
- package/dist/cli/chunk-s0bdnc7v.js.map +0 -10
- package/dist/cli/chunk-twy7psdz.js.map +0 -10
- package/dist/cli/chunk-ydp3065p.js +0 -64
- package/dist/cli/chunk-ydp3065p.js.map +0 -10
- /package/dist/cli/{chunk-y4msrwm9.js.map → chunk-080mfch6.js.map} +0 -0
- /package/dist/cli/{chunk-9qzgg6mj.js.map → chunk-34wap3ed.js.map} +0 -0
- /package/dist/cli/{chunk-1f882900.js.map → chunk-c5bjxq8v.js.map} +0 -0
- /package/dist/cli/{chunk-bxwhtvw8.js.map → chunk-f260jdrn.js.map} +0 -0
- /package/dist/cli/{chunk-74wfhv79.js.map → chunk-fnxgtjeq.js.map} +0 -0
- /package/dist/cli/{chunk-dhwtqnnd.js.map → chunk-gxhcf2hs.js.map} +0 -0
- /package/dist/cli/{chunk-tfycddbt.js.map → chunk-h0qwdbem.js.map} +0 -0
- /package/dist/cli/{chunk-j1x92mx9.js.map → chunk-j4nfgn2j.js.map} +0 -0
- /package/dist/cli/{chunk-ave1wcbr.js.map → chunk-qcyxkj44.js.map} +0 -0
- /package/dist/cli/{chunk-58bt3hag.js.map → chunk-x7bh6d9j.js.map} +0 -0
- /package/dist/cli/{chunk-hvcxng5f.js.map → chunk-ydqzkhjn.js.map} +0 -0
|
@@ -0,0 +1,491 @@
|
|
|
1
|
+
import { slug } from "github-slugger";
|
|
2
|
+
import type { Nodes } from "mdast";
|
|
3
|
+
import { markdownToMdast } from "satteri";
|
|
4
|
+
|
|
5
|
+
import { MARKDOWN_BODY_FEATURES } from "../markdown/features.ts";
|
|
6
|
+
import { nextFenceState } from "./code-fences.ts";
|
|
7
|
+
import type { FenceState } from "./code-fences.ts";
|
|
8
|
+
import {
|
|
9
|
+
ATTRIBUTE,
|
|
10
|
+
HTML_COMMENT,
|
|
11
|
+
INLINE_CODE,
|
|
12
|
+
strippedLineOffset,
|
|
13
|
+
trailingCurlyMarker,
|
|
14
|
+
} from "./sources/normalize.ts";
|
|
15
|
+
import type { SourceEntry } from "./sources/types.ts";
|
|
16
|
+
import type { Diagnostic, PageRecord } from "./types.ts";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Syntax from other docs tools that Blume doesn't read, found in a page body
|
|
20
|
+
* outside code and comments. Each one either ships as literal text on a green
|
|
21
|
+
* build or, in `.mdx`, breaks the page only once it renders, so it's reported
|
|
22
|
+
* at its line instead:
|
|
23
|
+
*
|
|
24
|
+
* - `BLUME_TEMPLATE_TAG`: a Liquid or Markdoc tag (`{% include x %}`) or a
|
|
25
|
+
* Liquid output (`{{ site.title }}`) in `.md`. A `{{name}}` that could be a
|
|
26
|
+
* Blume variable is left to the variables pass.
|
|
27
|
+
* - `BLUME_WIKILINK_UNSUPPORTED`: a wiki link (`[[Page]]`, `[[Text|Page]]`)
|
|
28
|
+
* to one of the site's pages, or any with a `|`. A bare `[[Name]]` that
|
|
29
|
+
* names no page is left alone: JavaScript docs write the spec's internal
|
|
30
|
+
* slots that way (`[[Prototype]]`). The Obsidian source rewrites its own
|
|
31
|
+
* links before this sees them.
|
|
32
|
+
* - `BLUME_MDC_SYNTAX`: a Nuxt Content (MDC) block (`::callout` … `::`) or
|
|
33
|
+
* inline component (`:badge[New]{color="primary"}`).
|
|
34
|
+
* - `BLUME_MDX_ATTRIBUTE_LIST`: an attribute list (`{ width="300" }`,
|
|
35
|
+
* `{ .class }`) in `.mdx`, which MDX reads as JavaScript.
|
|
36
|
+
* - `BLUME_MD_ATTRIBUTE_LIST`: an attribute list in `.md`, kramdown's IAL
|
|
37
|
+
* (`{: .note }`) or MkDocs' `attr_list`, which shows as written. A heading's
|
|
38
|
+
* `{#id}` marker is left to `BLUME_MD_CURLY_ANCHOR`.
|
|
39
|
+
* - `BLUME_MDX_UNCLOSED_ELEMENT`: a void element written as HTML (`<img …>`)
|
|
40
|
+
* in `.mdx`, which MDX reads as an unclosed JSX element.
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
/** A problem at a line of the scanned text, before it's placed in a file. */
|
|
44
|
+
interface Finding {
|
|
45
|
+
code: string;
|
|
46
|
+
column: number;
|
|
47
|
+
line: number;
|
|
48
|
+
message: string;
|
|
49
|
+
suggestion: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** A match's position in the scanned text. */
|
|
53
|
+
interface Site {
|
|
54
|
+
column: number;
|
|
55
|
+
line: number;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** The route of the page a wiki link names, if one does. */
|
|
59
|
+
type PageRoute = (name: string) => string | undefined;
|
|
60
|
+
|
|
61
|
+
/** What a check reads: the masked body, where its lines start, and the site's pages. */
|
|
62
|
+
interface Scan {
|
|
63
|
+
pageRoute: PageRoute;
|
|
64
|
+
starts: number[];
|
|
65
|
+
text: string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** The site's pages by the names a wiki link can call them (see {@link wikiKey}). */
|
|
69
|
+
export type PageNameIndex = ReadonlyMap<string, readonly PageRecord[]>;
|
|
70
|
+
|
|
71
|
+
/** A backslash escapes the character after it, so only an even run is text. */
|
|
72
|
+
const UNESCAPED = String.raw`(?<=(?:^|[^\\])(?:\\\\)*)`;
|
|
73
|
+
|
|
74
|
+
// Liquid and Markdoc tags, and Liquid output, on one line.
|
|
75
|
+
const TEMPLATE_TAG = new RegExp(String.raw`${UNESCAPED}\{%[^\n]*?%\}`, "gu");
|
|
76
|
+
const TEMPLATE_OUTPUT = new RegExp(
|
|
77
|
+
String.raw`${UNESCAPED}\{\{(?<expression>[^\n{}]*)\}\}`,
|
|
78
|
+
"gu"
|
|
79
|
+
);
|
|
80
|
+
// What a Blume variable reference holds (see `variables.ts`).
|
|
81
|
+
const VARIABLE_REFERENCE = /^\s*[\w-]+\s*$/u;
|
|
82
|
+
|
|
83
|
+
// `[[Page]]` or `[[Text|Page]]`. An embed (`![[x]]`) and a link whose label
|
|
84
|
+
// is in brackets (`[[x]](/url)`, `[[x]][ref]`) aren't wiki links.
|
|
85
|
+
const WIKILINK = /(?<![!\\[])\[\[(?<inner>[^[\]\n]+)\]\](?![([:])/gu;
|
|
86
|
+
|
|
87
|
+
// An MDC block component opener on its own line: two colons, a name, and an
|
|
88
|
+
// optional `[slot]` and `{props}`. Three colons is a `:::` container.
|
|
89
|
+
const MDC_BLOCK =
|
|
90
|
+
/^[\t ]*(?<written>::(?<name>[a-z][\w-]*)(?:\[[^\]\n]*\])?(?:\{[^}\n]*\})?)[\t ]*$/gimu;
|
|
91
|
+
// An inline MDC component: a colon and a name, then a `[slot]`, `{props}`,
|
|
92
|
+
// or both. A word character before the colon (`og:image`, `16:9`) is prose.
|
|
93
|
+
// Two colons is a block opener that shares its line with text, which no
|
|
94
|
+
// parser reads as a block.
|
|
95
|
+
const MDC_INLINE =
|
|
96
|
+
/(?<![\w:\\])(?<written>:{1,2}(?<name>[a-z][\w-]*)(?:\[[^\]\n]*\](?:\{[^}\n]*\})?|\{[^}\n]*\}))/giu;
|
|
97
|
+
|
|
98
|
+
// A brace-wrapped attribute list (`{ width="300" }`, kramdown's `{: .note }`).
|
|
99
|
+
// After `=` it's a JSX attribute's value (`style={…}`).
|
|
100
|
+
const ATTRIBUTE_LIST = new RegExp(
|
|
101
|
+
String.raw`(?<![=\\])\{:?[\t ]*${ATTRIBUTE}(?:[\t ]+${ATTRIBUTE})*[\t ]*\}`,
|
|
102
|
+
"gu"
|
|
103
|
+
);
|
|
104
|
+
// A directive's `{attributes}` (`:::note{title="x"}`, `::name{a="b"}`), which
|
|
105
|
+
// the directive parser reads before MDX can.
|
|
106
|
+
const DIRECTIVE_ATTRIBUTES = /:+[a-z][\w-]*(?:\[[^\]\n]*\])?\{[^}\n]*\}/giu;
|
|
107
|
+
const ATX_HEADING = /^ {0,3}#{1,6}[\t ]/u;
|
|
108
|
+
const SETEXT_UNDERLINE = /^ {0,3}(?:=+|-+)[\t ]*$/u;
|
|
109
|
+
|
|
110
|
+
// An HTML void element not closed with `/>`. Attribute values may hold `/`
|
|
111
|
+
// and `>`, so quoted ones are skipped whole.
|
|
112
|
+
const VOID_ELEMENT = new RegExp(
|
|
113
|
+
String.raw`${UNESCAPED}<(?<tag>area|base|br|col|embed|hr|img|input|link|meta|param|source|track|wbr)(?=[\s/>])(?:[^<>"'/]|"[^"]*"|'[^']*'|\/(?!>))*>`,
|
|
114
|
+
"gu"
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
// The cheap tests that gate the scans both formats share.
|
|
118
|
+
const SUSPECT_WIKILINK = /\[\[/u;
|
|
119
|
+
const SUSPECT_MDC = /(?:^|[^\w:]):{1,2}[a-z][\w-]*[[{]|^[\t ]*::[a-z]/imu;
|
|
120
|
+
|
|
121
|
+
// An MDX comment, `{/* … */}`, which renders nothing.
|
|
122
|
+
const MDX_COMMENT = /\{\s*\/\*[\s\S]*?\*\/\s*\}/gu;
|
|
123
|
+
|
|
124
|
+
// A link or image destination in angle brackets, after `](` or a reference
|
|
125
|
+
// definition's `]:` (``, `[ref]: <br/x.png>`). Both
|
|
126
|
+
// formats read it as a URL, never as HTML, though it can open with a tag name.
|
|
127
|
+
const ANGLE_DESTINATION =
|
|
128
|
+
/(?<=\]\([\t ]*|^ {0,3}\[[^\]\n]+\]:[\t ]*)<[^<>\n]*>/gmu;
|
|
129
|
+
|
|
130
|
+
/** `text` with every character but line breaks turned into a space. */
|
|
131
|
+
const blank = (text: string): string => text.replaceAll(/[^\n]/gu, " ");
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* An `.md` body with its code (fenced, indented, and inline) and HTML
|
|
135
|
+
* comments blanked, exactly as the renderer's parser reads them.
|
|
136
|
+
*/
|
|
137
|
+
const maskMarkdown = (text: string): string => {
|
|
138
|
+
const ranges: [number, number][] = [];
|
|
139
|
+
const walk = (node: Nodes): void => {
|
|
140
|
+
const start = node.position?.start.offset ?? 0;
|
|
141
|
+
const end = node.position?.end.offset ?? start;
|
|
142
|
+
if (
|
|
143
|
+
node.type === "code" ||
|
|
144
|
+
node.type === "inlineCode" ||
|
|
145
|
+
(node.type === "html" && node.value.startsWith("<!--"))
|
|
146
|
+
) {
|
|
147
|
+
ranges.push([start, end]);
|
|
148
|
+
} else if ("children" in node) {
|
|
149
|
+
for (const child of node.children) {
|
|
150
|
+
walk(child);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
walk(
|
|
155
|
+
markdownToMdast(text, {
|
|
156
|
+
features: { ...MARKDOWN_BODY_FEATURES, gfm: true },
|
|
157
|
+
})
|
|
158
|
+
);
|
|
159
|
+
let masked = "";
|
|
160
|
+
let cursor = 0;
|
|
161
|
+
for (const [start, end] of ranges) {
|
|
162
|
+
masked += text.slice(cursor, start) + blank(text.slice(start, end));
|
|
163
|
+
cursor = end;
|
|
164
|
+
}
|
|
165
|
+
return masked + text.slice(cursor);
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* An `.mdx` body with fenced code, inline code, comments, and angle-bracket
|
|
170
|
+
* link destinations blanked, line by line rather than parsed: the problems
|
|
171
|
+
* this looks for are what keeps MDX from parsing a page. MDX has no indented
|
|
172
|
+
* code blocks.
|
|
173
|
+
*/
|
|
174
|
+
const maskMdx = (text: string): string => {
|
|
175
|
+
let fence: FenceState = null;
|
|
176
|
+
return text
|
|
177
|
+
.split("\n")
|
|
178
|
+
.map((line) => {
|
|
179
|
+
const next = nextFenceState(line, fence);
|
|
180
|
+
const inFence = fence !== null || next !== null;
|
|
181
|
+
fence = next;
|
|
182
|
+
return inFence ? blank(line) : line.replaceAll(INLINE_CODE, blank);
|
|
183
|
+
})
|
|
184
|
+
.join("\n")
|
|
185
|
+
.replaceAll(MDX_COMMENT, blank)
|
|
186
|
+
.replaceAll(HTML_COMMENT, blank)
|
|
187
|
+
.replaceAll(ANGLE_DESTINATION, blank);
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
/** Where each line of `text` starts. */
|
|
191
|
+
const lineStartsOf = (text: string): number[] => {
|
|
192
|
+
const starts = [0];
|
|
193
|
+
for (
|
|
194
|
+
let index = text.indexOf("\n");
|
|
195
|
+
index !== -1;
|
|
196
|
+
index = text.indexOf("\n", index + 1)
|
|
197
|
+
) {
|
|
198
|
+
starts.push(index + 1);
|
|
199
|
+
}
|
|
200
|
+
return starts;
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
/** The 1-based line and column of offset `at`. */
|
|
204
|
+
const siteOf = (starts: readonly number[], at: number): Site => {
|
|
205
|
+
const line = starts.findLastIndex((start) => start <= at);
|
|
206
|
+
return { column: at - (starts[line] ?? 0) + 1, line: line + 1 };
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
const templateFindings = ({ starts, text }: Scan): Finding[] => [
|
|
210
|
+
...[...text.matchAll(TEMPLATE_TAG)].map((match) => ({
|
|
211
|
+
...siteOf(starts, match.index),
|
|
212
|
+
code: "BLUME_TEMPLATE_TAG",
|
|
213
|
+
message: `\`${match[0]}\` is a Liquid or Markdoc tag, which Blume doesn't run, so the page shows it as written.`,
|
|
214
|
+
suggestion:
|
|
215
|
+
"Rewrite what it produced as Markdown or a Blume component, or remove it. To show it as text, put it in inline code.",
|
|
216
|
+
})),
|
|
217
|
+
...[...text.matchAll(TEMPLATE_OUTPUT)]
|
|
218
|
+
.filter((match) => !VARIABLE_REFERENCE.test(match.groups?.expression ?? ""))
|
|
219
|
+
.map((match) => ({
|
|
220
|
+
...siteOf(starts, match.index),
|
|
221
|
+
code: "BLUME_TEMPLATE_TAG",
|
|
222
|
+
message: `\`${match[0]}\` is a Liquid output tag, which Blume doesn't fill in, so the page shows it as written.`,
|
|
223
|
+
suggestion:
|
|
224
|
+
"Write the value in its place, or define it under `variables` in blume.config.ts and write `{{name}}`. To show it as text, put it in inline code.",
|
|
225
|
+
})),
|
|
226
|
+
];
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* A page name the way wiki links match it: GitHub wikis link `[[Getting
|
|
230
|
+
* Started]]` to `Getting-Started.md`, and case doesn't count.
|
|
231
|
+
*/
|
|
232
|
+
const wikiKey = (name: string): string =>
|
|
233
|
+
name
|
|
234
|
+
.trim()
|
|
235
|
+
.toLowerCase()
|
|
236
|
+
.replaceAll(/[\s_-]+/gu, "-");
|
|
237
|
+
|
|
238
|
+
/** Index pages by file name and title, for {@link syntaxDiagnostics}. */
|
|
239
|
+
export const indexPageNames = (pages: readonly PageRecord[]): PageNameIndex => {
|
|
240
|
+
const index = new Map<string, PageRecord[]>();
|
|
241
|
+
for (const page of pages) {
|
|
242
|
+
const stem = (page.source.ref.split("/").at(-1) ?? "").replace(
|
|
243
|
+
/\.mdx?$/iu,
|
|
244
|
+
""
|
|
245
|
+
);
|
|
246
|
+
for (const key of new Set([wikiKey(stem), wikiKey(page.title)])) {
|
|
247
|
+
const named = index.get(key);
|
|
248
|
+
if (named) {
|
|
249
|
+
named.push(page);
|
|
250
|
+
} else {
|
|
251
|
+
index.set(key, [page]);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return index;
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
/** One side of a wiki link's `|`, and the route it names, if any. */
|
|
259
|
+
interface WikiPart {
|
|
260
|
+
route?: string;
|
|
261
|
+
text: string;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/** A wiki link side's route, with a `#heading` as the anchor it slugs to. */
|
|
265
|
+
const partRoute = (text: string, pageRoute: PageRoute): string | undefined => {
|
|
266
|
+
const [name = "", heading] = text.split("#", 2);
|
|
267
|
+
const route = pageRoute(name);
|
|
268
|
+
return route && heading ? `${route}#${slug(heading)}` : route;
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
const wikilinkFindings = ({ pageRoute, starts, text }: Scan): Finding[] =>
|
|
272
|
+
[...text.matchAll(WIKILINK)].flatMap((match) => {
|
|
273
|
+
const parts: WikiPart[] = (match.groups?.inner ?? "")
|
|
274
|
+
.split("|")
|
|
275
|
+
.map((part) => ({
|
|
276
|
+
route: partRoute(part, pageRoute),
|
|
277
|
+
text: part.trim(),
|
|
278
|
+
}));
|
|
279
|
+
const target = parts.find((part) => part.route !== undefined);
|
|
280
|
+
if (!target && parts.length === 1) {
|
|
281
|
+
return [];
|
|
282
|
+
}
|
|
283
|
+
const label = parts.find((part) => part !== target) ?? target;
|
|
284
|
+
return [
|
|
285
|
+
{
|
|
286
|
+
...siteOf(starts, match.index),
|
|
287
|
+
code: "BLUME_WIKILINK_UNSUPPORTED",
|
|
288
|
+
message: `\`${match[0]}\` is a wiki link, which Blume reads only in an Obsidian vault source, so the page shows it as written${target ? ` instead of linking to ${target.route}` : ""}.`,
|
|
289
|
+
suggestion: target
|
|
290
|
+
? `Rewrite it as a Markdown link: \`[${label?.text}](${target.route})\`.`
|
|
291
|
+
: "Rewrite it as a Markdown link to the page, like `[Text](./page.md)`.",
|
|
292
|
+
},
|
|
293
|
+
];
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
const mdcFindings = ({ starts, text }: Scan): Finding[] => {
|
|
297
|
+
const blocks = [...text.matchAll(MDC_BLOCK)].map((match) => ({
|
|
298
|
+
at: match.index + match[0].indexOf("::"),
|
|
299
|
+
written: match.groups?.written,
|
|
300
|
+
}));
|
|
301
|
+
const opened = new Set(blocks.map(({ at }) => at));
|
|
302
|
+
return [
|
|
303
|
+
...blocks.map(({ at, written }) => ({
|
|
304
|
+
...siteOf(starts, at),
|
|
305
|
+
code: "BLUME_MDC_SYNTAX",
|
|
306
|
+
message: `\`${written}\` opens a Nuxt Content (MDC) block component, which Blume doesn't render, so the page shows its \`::\` lines as text.`,
|
|
307
|
+
suggestion:
|
|
308
|
+
"Rewrite the block as the matching Blume component (`<Callout>`, `<Card>`, `<Tabs>`, …) or a `:::` callout.",
|
|
309
|
+
})),
|
|
310
|
+
...[...text.matchAll(MDC_INLINE)]
|
|
311
|
+
.filter((match) => !opened.has(match.index))
|
|
312
|
+
.map((match) => ({
|
|
313
|
+
...siteOf(starts, match.index),
|
|
314
|
+
code: "BLUME_MDC_SYNTAX",
|
|
315
|
+
message: `\`${match.groups?.written}\` is an inline component in Nuxt Content (MDC) syntax, which Blume doesn't render, so the page shows it as written.`,
|
|
316
|
+
suggestion:
|
|
317
|
+
"Rewrite it as the matching Blume component (`<Badge>`, `<Icon>`, …) or as plain text.",
|
|
318
|
+
})),
|
|
319
|
+
];
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Whether the list at `column` of `line` is a heading's trailing `{#id}`
|
|
324
|
+
* marker, which `BLUME_MDX_CURLY_ANCHOR` or `BLUME_MD_CURLY_ANCHOR` already
|
|
325
|
+
* reports (an unspaced `{#id}` pins the anchor in `.md`). In `.mdx`, one that
|
|
326
|
+
* also sets classes or attributes (`{#id .wide}`) is reported here instead.
|
|
327
|
+
*/
|
|
328
|
+
const isHeadingMarker = (
|
|
329
|
+
lines: readonly string[],
|
|
330
|
+
site: Site,
|
|
331
|
+
written: string,
|
|
332
|
+
format: "md" | "mdx"
|
|
333
|
+
): boolean => {
|
|
334
|
+
const line = lines[site.line - 1] ?? "";
|
|
335
|
+
const heading =
|
|
336
|
+
ATX_HEADING.test(line) || SETEXT_UNDERLINE.test(lines[site.line] ?? "");
|
|
337
|
+
const marker = trailingCurlyMarker(line);
|
|
338
|
+
return (
|
|
339
|
+
heading &&
|
|
340
|
+
marker?.marker === written &&
|
|
341
|
+
marker.index === site.column - 1 &&
|
|
342
|
+
(format === "md" || !marker.attributes)
|
|
343
|
+
);
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
/** What an attribute list does to a page in each format, and the way out. */
|
|
347
|
+
const ATTRIBUTE_LIST_PROBLEMS = {
|
|
348
|
+
md: {
|
|
349
|
+
code: "BLUME_MD_ATTRIBUTE_LIST",
|
|
350
|
+
effect: "which Blume doesn't read, so the page shows it as written",
|
|
351
|
+
suggestion:
|
|
352
|
+
'Remove it. To keep the attributes, write the element as HTML (`<img src="…" width="300">`), or pin a heading\'s anchor with `[#id]`. To show the braces as text, put them in inline code.',
|
|
353
|
+
},
|
|
354
|
+
mdx: {
|
|
355
|
+
code: "BLUME_MDX_ATTRIBUTE_LIST",
|
|
356
|
+
effect:
|
|
357
|
+
"which MDX reads as a JavaScript expression, so the page fails to build",
|
|
358
|
+
suggestion:
|
|
359
|
+
'Remove it. To keep the attributes, write the element as JSX (`<img src="…" width="300" />`), or pin a heading\'s anchor with `[#id]`. To show the braces as text, escape them: `\\{…\\}`.',
|
|
360
|
+
},
|
|
361
|
+
} as const;
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* The attribute lists in a body in `format`. A directive's own attributes and
|
|
365
|
+
* a `{{ }}` variable or Liquid output aren't one.
|
|
366
|
+
*/
|
|
367
|
+
const attributeListFindings =
|
|
368
|
+
(format: "md" | "mdx") =>
|
|
369
|
+
({ starts, text }: Scan): Finding[] => {
|
|
370
|
+
const { code, effect, suggestion } = ATTRIBUTE_LIST_PROBLEMS[format];
|
|
371
|
+
const scanned = text
|
|
372
|
+
.replaceAll(DIRECTIVE_ATTRIBUTES, blank)
|
|
373
|
+
.replaceAll(TEMPLATE_OUTPUT, blank);
|
|
374
|
+
const lines = text.split("\n");
|
|
375
|
+
return [...scanned.matchAll(ATTRIBUTE_LIST)]
|
|
376
|
+
.map((match) => ({
|
|
377
|
+
site: siteOf(starts, match.index),
|
|
378
|
+
written: match[0],
|
|
379
|
+
}))
|
|
380
|
+
.filter(
|
|
381
|
+
({ site, written }) => !isHeadingMarker(lines, site, written, format)
|
|
382
|
+
)
|
|
383
|
+
.map(({ site, written }) => ({
|
|
384
|
+
...site,
|
|
385
|
+
code,
|
|
386
|
+
message: `\`${written}\` is an attribute list, ${effect}.`,
|
|
387
|
+
suggestion,
|
|
388
|
+
}));
|
|
389
|
+
};
|
|
390
|
+
|
|
391
|
+
// An attribute list's opening brace and first attribute.
|
|
392
|
+
const SUSPECT_ATTRIBUTE_LIST = /\{:?[\t ]*(?:[#.][A-Za-z_-]|[\w-]+=)/u;
|
|
393
|
+
|
|
394
|
+
const voidElementFindings = ({ starts, text }: Scan): Finding[] =>
|
|
395
|
+
[...text.matchAll(VOID_ELEMENT)]
|
|
396
|
+
.filter(
|
|
397
|
+
(match) =>
|
|
398
|
+
!text
|
|
399
|
+
.slice(match.index + match[0].length)
|
|
400
|
+
.trimStart()
|
|
401
|
+
.startsWith(`</${match.groups?.tag}>`)
|
|
402
|
+
)
|
|
403
|
+
.map((match) => ({
|
|
404
|
+
...siteOf(starts, match.index),
|
|
405
|
+
code: "BLUME_MDX_UNCLOSED_ELEMENT",
|
|
406
|
+
message: `\`<${match.groups?.tag}>\` isn't closed. MDX reads HTML as JSX, where it needs \`/>\`: unclosed, it fails the page, or in an included file it takes in everything after it.`,
|
|
407
|
+
suggestion: `Close it: \`<${match.groups?.tag} … />\`.`,
|
|
408
|
+
}));
|
|
409
|
+
|
|
410
|
+
/** A scan over masked text, and the cheap check that says it's worth running. */
|
|
411
|
+
interface Check {
|
|
412
|
+
find: (scan: Scan) => Finding[];
|
|
413
|
+
suspect: RegExp;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/** The checks each body format runs. */
|
|
417
|
+
const CHECKS: Record<"md" | "mdx", Check[]> = {
|
|
418
|
+
md: [
|
|
419
|
+
{ find: templateFindings, suspect: /\{[%{]/u },
|
|
420
|
+
{ find: wikilinkFindings, suspect: SUSPECT_WIKILINK },
|
|
421
|
+
{ find: mdcFindings, suspect: SUSPECT_MDC },
|
|
422
|
+
{ find: attributeListFindings("md"), suspect: SUSPECT_ATTRIBUTE_LIST },
|
|
423
|
+
],
|
|
424
|
+
mdx: [
|
|
425
|
+
{ find: wikilinkFindings, suspect: SUSPECT_WIKILINK },
|
|
426
|
+
{ find: mdcFindings, suspect: SUSPECT_MDC },
|
|
427
|
+
{ find: attributeListFindings("mdx"), suspect: SUSPECT_ATTRIBUTE_LIST },
|
|
428
|
+
{
|
|
429
|
+
find: voidElementFindings,
|
|
430
|
+
suspect:
|
|
431
|
+
/<(?:area|base|br|col|embed|hr|img|input|link|meta|param|source|track|wbr)[\s/>]/u,
|
|
432
|
+
},
|
|
433
|
+
],
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Every finding in `text`, a body in `format`, in source order. The body is
|
|
438
|
+
* masked only when a check's cheap test matches, so most pages cost a few
|
|
439
|
+
* regex tests.
|
|
440
|
+
*/
|
|
441
|
+
const syntaxFindings = (
|
|
442
|
+
text: string,
|
|
443
|
+
format: "md" | "mdx",
|
|
444
|
+
pageRoute: PageRoute
|
|
445
|
+
): Finding[] => {
|
|
446
|
+
const checks = CHECKS[format].filter((check) => check.suspect.test(text));
|
|
447
|
+
if (checks.length === 0) {
|
|
448
|
+
return [];
|
|
449
|
+
}
|
|
450
|
+
const masked = format === "mdx" ? maskMdx(text) : maskMarkdown(text);
|
|
451
|
+
const scan = { pageRoute, starts: lineStartsOf(masked), text: masked };
|
|
452
|
+
return checks
|
|
453
|
+
.flatMap((check) => check.find(scan))
|
|
454
|
+
.toSorted((a, b) => a.line - b.line || a.column - b.column);
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* Warn about syntax from other docs tools in an entry's body (see the module
|
|
459
|
+
* comment). `pages` are the site's pages by name, which a wiki link resolves
|
|
460
|
+
* against, preferring one in the entry's own `locale`. Lines point into the
|
|
461
|
+
* file the author wrote, a partial's own file for syntax an `<include>`
|
|
462
|
+
* brought in.
|
|
463
|
+
*/
|
|
464
|
+
export const syntaxDiagnostics = (
|
|
465
|
+
entry: SourceEntry,
|
|
466
|
+
sourceName: string,
|
|
467
|
+
pages: PageNameIndex = new Map(),
|
|
468
|
+
locale = ""
|
|
469
|
+
): Diagnostic[] => {
|
|
470
|
+
const page = entry.sourcePath ?? `${sourceName}:${entry.ref}`;
|
|
471
|
+
const offset =
|
|
472
|
+
entry.bodyLineOffset ?? strippedLineOffset(entry.raw, entry.body.text);
|
|
473
|
+
const pageRoute = (name: string): string | undefined => {
|
|
474
|
+
const named = pages.get(wikiKey(name));
|
|
475
|
+
return (named?.find((other) => other.locale === locale) ?? named?.[0])
|
|
476
|
+
?.route;
|
|
477
|
+
};
|
|
478
|
+
return syntaxFindings(
|
|
479
|
+
entry.expanded?.text ?? entry.body.text,
|
|
480
|
+
entry.body.format,
|
|
481
|
+
pageRoute
|
|
482
|
+
).map(({ line, ...finding }) => {
|
|
483
|
+
const origin = entry.expanded?.origins[line - 1];
|
|
484
|
+
return {
|
|
485
|
+
...finding,
|
|
486
|
+
file: origin?.file ?? page,
|
|
487
|
+
line: origin?.line ?? line + offset,
|
|
488
|
+
severity: "warning",
|
|
489
|
+
};
|
|
490
|
+
});
|
|
491
|
+
};
|
package/src/core/types.ts
CHANGED
|
@@ -56,8 +56,13 @@ export interface Heading {
|
|
|
56
56
|
|
|
57
57
|
/** A link target discovered in page content, anchored to its source line. */
|
|
58
58
|
export interface PageLink {
|
|
59
|
-
/** Raw link target as written, e.g. `./foo`, `/api#auth`, `https://x.dev`.
|
|
59
|
+
/** Raw link target as written, e.g. `./foo`, `/api#auth`, `https://x.dev`.
|
|
60
|
+
* A Markdown destination is read as the renderer reads it: without its
|
|
61
|
+
* `<…>` brackets, and with backslash escapes resolved. */
|
|
60
62
|
target: string;
|
|
63
|
+
/** Length of the target's text in the source, when escapes make it differ
|
|
64
|
+
* from `target`'s (`image\(1\).png` is read as `image(1).png`). */
|
|
65
|
+
sourceLength?: number;
|
|
61
66
|
/** Set when the target was written as an image embed (``) —
|
|
62
67
|
* only those go through the image pipeline; a plain link to the same path
|
|
63
68
|
* resolves as a site route. */
|
|
@@ -66,6 +71,11 @@ export interface PageLink {
|
|
|
66
71
|
* the Markdown pipeline neither resolves its relative path file-style nor
|
|
67
72
|
* mounts the base path on it, so it's checked the way a browser reads it. */
|
|
68
73
|
raw?: boolean;
|
|
74
|
+
/** The element, when the target is a media element's `src` (`img`,
|
|
75
|
+
* `source`, `video`, `audio`). It's `raw` too: the browser resolves it as
|
|
76
|
+
* written, unless it names a file beside the page, which the build
|
|
77
|
+
* publishes and points it at. */
|
|
78
|
+
src?: string;
|
|
69
79
|
/** 1-based line number in the source file. */
|
|
70
80
|
line: number;
|
|
71
81
|
/** 1-based column of the target within the line. */
|
|
@@ -88,6 +98,26 @@ export interface ExampleUse {
|
|
|
88
98
|
file?: string;
|
|
89
99
|
}
|
|
90
100
|
|
|
101
|
+
/**
|
|
102
|
+
* A JSX element in an `.mdx` body whose props don't render as written (see
|
|
103
|
+
* `core/mdx-elements.ts`), located like a {@link PageLink}: an HTML element
|
|
104
|
+
* with a JavaScript event handler (`handler`), or a childless built-in given
|
|
105
|
+
* props it doesn't take (`prop`).
|
|
106
|
+
*/
|
|
107
|
+
export interface ElementUse {
|
|
108
|
+
/** 1-based column of the element's `<` within the line. */
|
|
109
|
+
column: number;
|
|
110
|
+
/** Absolute path of the included partial it was written in, if not the page. */
|
|
111
|
+
file?: string;
|
|
112
|
+
kind: "handler" | "prop";
|
|
113
|
+
/** 1-based line number in the source file. */
|
|
114
|
+
line: number;
|
|
115
|
+
/** The handler props, or the props the built-in doesn't take. */
|
|
116
|
+
props: string[];
|
|
117
|
+
/** The element's name as written (`button`, `Badge`). */
|
|
118
|
+
tag: string;
|
|
119
|
+
}
|
|
120
|
+
|
|
91
121
|
/**
|
|
92
122
|
* Resolved project paths. Computed once per CLI invocation and threaded
|
|
93
123
|
* through the core pipeline.
|
|
@@ -170,6 +200,12 @@ export interface PageRecord {
|
|
|
170
200
|
* checks skip these.
|
|
171
201
|
*/
|
|
172
202
|
fallback?: boolean;
|
|
203
|
+
/**
|
|
204
|
+
* The route an earlier Blume published the page at, when it differs from
|
|
205
|
+
* `route`: a date-named file or folder (`12-05-2022`) lost its first number
|
|
206
|
+
* as an ordering prefix. The scan redirects it to `route`.
|
|
207
|
+
*/
|
|
208
|
+
formerRoute?: string;
|
|
173
209
|
/**
|
|
174
210
|
* True for pages from a source that publishes one language only (GitHub
|
|
175
211
|
* Releases): they render without a language switcher.
|
|
@@ -212,6 +248,12 @@ export interface PageRecord {
|
|
|
212
248
|
componentsUsed?: string[];
|
|
213
249
|
/** The string `path` of each `<Component>` example in the body (`.mdx` only). */
|
|
214
250
|
examplesUsed?: ExampleUse[];
|
|
251
|
+
/**
|
|
252
|
+
* Childless built-ins given props they don't take (`.mdx` only). Reported
|
|
253
|
+
* once the project's own components are known, since an override takes
|
|
254
|
+
* whatever props it declares.
|
|
255
|
+
*/
|
|
256
|
+
unknownProps?: ElementUse[];
|
|
215
257
|
/** Resolved "last updated" ISO date, when the feature is enabled. */
|
|
216
258
|
lastModified?: string;
|
|
217
259
|
/** Absolute paths of files this page `<include>`s, transitively. Drives the
|
package/src/deploy/artifacts.ts
CHANGED
|
@@ -5,12 +5,6 @@ import { dirname, join, resolve } from "pathe";
|
|
|
5
5
|
|
|
6
6
|
import { buildAgentReadability } from "../ai/agent-readability.ts";
|
|
7
7
|
import { advertisedConfig, servesMcp } from "../ai/agent-surface.ts";
|
|
8
|
-
import {
|
|
9
|
-
AI_CATALOG_PATH,
|
|
10
|
-
ARD_MANIFEST_PATH,
|
|
11
|
-
buildAiCatalog,
|
|
12
|
-
} from "../ai/ai-catalog.ts";
|
|
13
|
-
import { API_CATALOG_PATH, buildApiCatalog } from "../ai/api-catalog.ts";
|
|
14
8
|
import { buildHomeLinkHeader } from "../ai/link-headers.ts";
|
|
15
9
|
import { buildLlmsFiles } from "../ai/llms.ts";
|
|
16
10
|
import { markdownRoutePaths } from "../ai/markdown.ts";
|
|
@@ -26,10 +20,6 @@ import {
|
|
|
26
20
|
collectSkills,
|
|
27
21
|
} from "../ai/skills.ts";
|
|
28
22
|
import type { SkillArtifact } from "../ai/skills.ts";
|
|
29
|
-
import {
|
|
30
|
-
buildSignaturesDirectory,
|
|
31
|
-
SIGNATURES_DIRECTORY_PATH,
|
|
32
|
-
} from "../ai/web-bot-auth.ts";
|
|
33
23
|
import { normalizeBasePath } from "../core/base-path.ts";
|
|
34
24
|
import { discoverPagesSync } from "../core/custom-pages.ts";
|
|
35
25
|
import type { BlumeProject } from "../core/project-graph.ts";
|
|
@@ -38,6 +28,7 @@ import { buildNarration } from "../narration/build.ts";
|
|
|
38
28
|
import { buildSearchIndex } from "../search/build.ts";
|
|
39
29
|
import { syncSearchProvider } from "../search/sync/index.ts";
|
|
40
30
|
import { readsHeaderFiles } from "./adapter-output.ts";
|
|
31
|
+
import { wellKnownFiles } from "./generated-files.ts";
|
|
41
32
|
import { buildNetlifyHeaders, buildVercelHeaders } from "./headers.ts";
|
|
42
33
|
import { deployPlatform } from "./platforms/index.ts";
|
|
43
34
|
import { VERCEL_JSON_FILE } from "./platforms/vercel.ts";
|
|
@@ -352,24 +343,7 @@ const emitWellKnownFiles = async (
|
|
|
352
343
|
skills: readonly SkillArtifact[],
|
|
353
344
|
logger: ArtifactLogger
|
|
354
345
|
): Promise<void> => {
|
|
355
|
-
|
|
356
|
-
// ARD v0.91 one (see `ai/ai-catalog.ts`).
|
|
357
|
-
const aiCatalog = buildAiCatalog(config, skills);
|
|
358
|
-
const files = [
|
|
359
|
-
{
|
|
360
|
-
content: buildSignaturesDirectory(config),
|
|
361
|
-
label: "Web Bot Auth",
|
|
362
|
-
path: SIGNATURES_DIRECTORY_PATH,
|
|
363
|
-
},
|
|
364
|
-
{
|
|
365
|
-
content: buildApiCatalog(config),
|
|
366
|
-
label: "RFC 9727",
|
|
367
|
-
path: API_CATALOG_PATH,
|
|
368
|
-
},
|
|
369
|
-
{ content: aiCatalog, label: "AI Catalog", path: AI_CATALOG_PATH },
|
|
370
|
-
{ content: aiCatalog, label: "ARD manifest", path: ARD_MANIFEST_PATH },
|
|
371
|
-
];
|
|
372
|
-
for (const file of files) {
|
|
346
|
+
for (const file of wellKnownFiles(config, skills)) {
|
|
373
347
|
const target = join(distDir, file.path.slice(1));
|
|
374
348
|
if (!file.content || existsSync(target)) {
|
|
375
349
|
continue;
|