blume 2.1.3 → 2.2.0
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 +126 -0
- package/README.md +4 -4
- package/dist/cli/{chunk-74wfhv79.js → chunk-05f21r8h.js} +1 -1
- package/dist/cli/{chunk-9qzgg6mj.js → chunk-1cm0h819.js} +3 -3
- package/dist/cli/{chunk-nbyvgnnm.js → chunk-1rkxzg3n.js} +104 -47
- package/dist/cli/chunk-1rkxzg3n.js.map +36 -0
- package/dist/cli/{chunk-desvm72z.js → chunk-2nr29y95.js} +22 -26
- package/dist/cli/chunk-2nr29y95.js.map +13 -0
- package/dist/cli/{chunk-32khwcb6.js → chunk-49r370mb.js} +42 -69
- package/dist/cli/{chunk-32khwcb6.js.map → chunk-49r370mb.js.map} +9 -10
- package/dist/cli/{chunk-ms9z66nz.js → chunk-4rftmttv.js} +2 -2
- package/dist/cli/{chunk-s0bdnc7v.js → chunk-79bzzkx4.js} +5 -4
- package/dist/cli/chunk-79bzzkx4.js.map +10 -0
- package/dist/cli/{chunk-hvcxng5f.js → chunk-7tekknev.js} +6 -6
- package/dist/cli/{chunk-dv2z0rzf.js → chunk-8b489v1b.js} +38 -13
- package/dist/cli/{chunk-dv2z0rzf.js.map → chunk-8b489v1b.js.map} +5 -4
- package/dist/cli/{chunk-0pmb4tr4.js → chunk-8f8xw4x2.js} +1195 -222
- package/dist/cli/chunk-8f8xw4x2.js.map +44 -0
- package/dist/cli/chunk-96snx8yy.js +329 -0
- package/dist/cli/chunk-96snx8yy.js.map +13 -0
- package/dist/cli/{chunk-ckrcm08h.js → chunk-a3ydrqny.js} +48 -62
- package/dist/cli/{chunk-ckrcm08h.js.map → chunk-a3ydrqny.js.map} +4 -5
- package/dist/cli/{chunk-bxwhtvw8.js → chunk-ahc5gc9h.js} +5 -5
- package/dist/cli/{chunk-dpy8qdjf.js → chunk-as09pafp.js} +73 -182
- package/dist/cli/chunk-as09pafp.js.map +18 -0
- package/dist/cli/{chunk-tfycddbt.js → chunk-bjk964fp.js} +2 -2
- package/dist/cli/{chunk-gfc5fqvf.js → chunk-br0n8n3e.js} +18 -18
- package/dist/cli/{chunk-gfc5fqvf.js.map → chunk-br0n8n3e.js.map} +1 -1
- package/dist/cli/{chunk-297ajdcv.js → chunk-c69tw29j.js} +6 -6
- package/dist/cli/{chunk-297ajdcv.js.map → chunk-c69tw29j.js.map} +3 -3
- package/dist/cli/chunk-cn3dy3hz.js +146 -0
- package/dist/cli/chunk-cn3dy3hz.js.map +11 -0
- package/dist/cli/{chunk-124fvmzq.js → chunk-dm4mg9bj.js} +11064 -8742
- package/dist/cli/{chunk-124fvmzq.js.map → chunk-dm4mg9bj.js.map} +78 -64
- package/dist/cli/{chunk-dqhg54mr.js → chunk-dmx6cezx.js} +1 -1
- package/dist/cli/{chunk-60hz265r.js → chunk-dpcahcrb.js} +24 -11
- package/dist/cli/chunk-dpcahcrb.js.map +10 -0
- package/dist/cli/{chunk-a57yh0h9.js → chunk-dyk3r7jw.js} +7 -7
- package/dist/cli/{chunk-a57yh0h9.js.map → chunk-dyk3r7jw.js.map} +1 -1
- package/dist/cli/{chunk-twy7psdz.js → chunk-enk76qd5.js} +14 -10
- package/dist/cli/chunk-enk76qd5.js.map +10 -0
- package/dist/cli/{chunk-k3gr1nm9.js → chunk-fqy4n3q2.js} +13 -16
- package/dist/cli/{chunk-k3gr1nm9.js.map → chunk-fqy4n3q2.js.map} +3 -3
- package/dist/cli/{chunk-sc93apfs.js → chunk-frk2wdq1.js} +17 -17
- package/dist/cli/{chunk-sc93apfs.js.map → chunk-frk2wdq1.js.map} +2 -2
- package/dist/cli/{chunk-kdpzw38a.js → chunk-gprwej7h.js} +95 -7
- package/dist/cli/chunk-gprwej7h.js.map +11 -0
- package/dist/cli/chunk-kkrteb15.js +157 -0
- package/dist/cli/chunk-kkrteb15.js.map +10 -0
- package/dist/cli/{chunk-ave1wcbr.js → chunk-m9bp2hc1.js} +1 -1
- package/dist/cli/{chunk-58bt3hag.js → chunk-mrvh4c88.js} +4 -4
- package/dist/cli/{chunk-5cmts6dv.js → chunk-p8zg3a2h.js} +7 -7
- package/dist/cli/{chunk-5cmts6dv.js.map → chunk-p8zg3a2h.js.map} +1 -1
- package/dist/cli/{chunk-0w6hs479.js → chunk-rkqae7kh.js} +30 -18
- package/dist/cli/chunk-rkqae7kh.js.map +10 -0
- package/dist/cli/{chunk-bqxaqn5p.js → chunk-t5xx74rd.js} +8 -8
- package/dist/cli/{chunk-bqxaqn5p.js.map → chunk-t5xx74rd.js.map} +1 -1
- package/dist/cli/{chunk-1f882900.js → chunk-th0pgaj5.js} +2 -2
- package/dist/cli/{chunk-dhwtqnnd.js → chunk-vnbvhgmd.js} +2 -2
- package/dist/cli/{chunk-gxtg6ed2.js → chunk-vzy9rh6y.js} +21 -21
- package/dist/cli/{chunk-gxtg6ed2.js.map → chunk-vzy9rh6y.js.map} +3 -3
- package/dist/cli/{chunk-j1x92mx9.js → chunk-w6e455pz.js} +1 -1
- package/dist/cli/chunk-wehv26ry.js +155 -0
- package/dist/cli/chunk-wehv26ry.js.map +11 -0
- package/dist/cli/{chunk-y4msrwm9.js → chunk-xgq4eytj.js} +15 -15
- package/dist/cli/{chunk-36bxs9c7.js → chunk-xt0qvz1t.js} +27 -23
- package/dist/cli/chunk-xt0qvz1t.js.map +10 -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 +1 -1
- package/dist/types/core/content-assets.d.ts +16 -18
- 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/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 +9 -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 +267 -7
- 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/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 +23 -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 +2 -2
- package/docs/configuration/theming.mdx +5 -1
- package/docs/content/components.mdx +26 -6
- package/docs/content/frontmatter.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/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 +49 -27
- 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 +3 -3
- 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 +2 -2
- 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 +32 -6
- 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 +89 -185
- 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/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/RootLayout.astro +18 -31
- 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 +1 -1
- package/src/core/config.ts +46 -1
- package/src/core/content-assets.ts +64 -144
- 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/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 +16 -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 +17 -3
- 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/registry/eject.ts +0 -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-74wfhv79.js.map → chunk-05f21r8h.js.map} +0 -0
- /package/dist/cli/{chunk-9qzgg6mj.js.map → chunk-1cm0h819.js.map} +0 -0
- /package/dist/cli/{chunk-ms9z66nz.js.map → chunk-4rftmttv.js.map} +0 -0
- /package/dist/cli/{chunk-hvcxng5f.js.map → chunk-7tekknev.js.map} +0 -0
- /package/dist/cli/{chunk-bxwhtvw8.js.map → chunk-ahc5gc9h.js.map} +0 -0
- /package/dist/cli/{chunk-tfycddbt.js.map → chunk-bjk964fp.js.map} +0 -0
- /package/dist/cli/{chunk-dqhg54mr.js.map → chunk-dmx6cezx.js.map} +0 -0
- /package/dist/cli/{chunk-ave1wcbr.js.map → chunk-m9bp2hc1.js.map} +0 -0
- /package/dist/cli/{chunk-58bt3hag.js.map → chunk-mrvh4c88.js.map} +0 -0
- /package/dist/cli/{chunk-1f882900.js.map → chunk-th0pgaj5.js.map} +0 -0
- /package/dist/cli/{chunk-dhwtqnnd.js.map → chunk-vnbvhgmd.js.map} +0 -0
- /package/dist/cli/{chunk-j1x92mx9.js.map → chunk-w6e455pz.js.map} +0 -0
- /package/dist/cli/{chunk-y4msrwm9.js.map → chunk-xgq4eytj.js.map} +0 -0
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
|
|
3
|
+
import type { Nodes } from "mdast";
|
|
4
|
+
import { createMdxMdastHandle, dropHandle, mdxToMdast } from "satteri";
|
|
5
|
+
|
|
6
|
+
import { MDX_BODY_FEATURES } from "../markdown/features.ts";
|
|
7
|
+
import matter from "./frontmatter.ts";
|
|
8
|
+
import { expandIncludes, hasIncludeStatements } from "./includes.ts";
|
|
9
|
+
import type { LineOrigin } from "./includes.ts";
|
|
10
|
+
import { isExpression } from "./mdx-elements.ts";
|
|
11
|
+
import { strippedLineOffset } from "./sources/normalize.ts";
|
|
12
|
+
import type { SourceEntry } from "./sources/types.ts";
|
|
13
|
+
import type { Diagnostic } from "./types.ts";
|
|
14
|
+
import { substituteVariables } from "./variables.ts";
|
|
15
|
+
import type { ContentVariables } from "./variables.ts";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* MDX that doesn't parse, found before Astro compiles it. The parse is
|
|
19
|
+
* Sätteri's, with the feature set the renderer uses, so a body that fails it
|
|
20
|
+
* here fails `astro build` too: Astro compiles every `.mdx` file in the
|
|
21
|
+
* collection, drafts and pages the scan dropped included. Parsing only (no
|
|
22
|
+
* plugins, no render) keeps it cheap enough to run on every page.
|
|
23
|
+
*
|
|
24
|
+
* - `BLUME_MDX_SYNTAX` (error): the page's own text doesn't parse, so the
|
|
25
|
+
* build fails at it.
|
|
26
|
+
* - `BLUME_MDX_SYNTAX` (warning): the page parses, but not once its
|
|
27
|
+
* `<include>`s are spliced in. Reported in the partial. The render splices a
|
|
28
|
+
* partial more leniently than this parse reads it, so the page may still
|
|
29
|
+
* build, with the partial's markup rendered wrong (an HTML comment shown as
|
|
30
|
+
* text, an unclosed `<img>` taking in what follows it).
|
|
31
|
+
*
|
|
32
|
+
* The same reading locates a build failure Astro reports with no line: an
|
|
33
|
+
* `.mdx` page that throws while it renders (a `{…}` expression reading a name
|
|
34
|
+
* nothing defines), or a partial that breaks the including page's compile.
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
/** Where MDX stopped reading a text, and why. */
|
|
38
|
+
export interface MdxSyntaxError {
|
|
39
|
+
column: number;
|
|
40
|
+
line: number;
|
|
41
|
+
/** Sätteri's message, without its leading position and trailing rule id. */
|
|
42
|
+
reason: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Sätteri's message: `line:column: reason (source:rule)`.
|
|
46
|
+
const POSITIONED =
|
|
47
|
+
/^(?<line>\d+):(?<column>\d+): (?<reason>[\s\S]*?)(?: \([\w-]+:[\w-]+\))?$/u;
|
|
48
|
+
// A position inside the reason (`… for \`<img>\` (2:1)`).
|
|
49
|
+
const INNER_POSITION = / \((?<line>\d+):(?<column>\d+)\)/gu;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The first MDX syntax error in `text`, a page body with its front matter
|
|
53
|
+
* off, or null when it parses.
|
|
54
|
+
*
|
|
55
|
+
* Only the verdict is needed, so the parse stays native: Sätteri's tree is
|
|
56
|
+
* dropped unread rather than built as JavaScript objects, which costs as much
|
|
57
|
+
* again as the parse itself on every page. A parse error carries its own
|
|
58
|
+
* position, so node positions aren't tracked either.
|
|
59
|
+
*/
|
|
60
|
+
export const mdxSyntaxError = (text: string): MdxSyntaxError | null => {
|
|
61
|
+
try {
|
|
62
|
+
dropHandle(createMdxMdastHandle(text, MDX_BODY_FEATURES, false));
|
|
63
|
+
return null;
|
|
64
|
+
} catch (error) {
|
|
65
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
66
|
+
const groups = POSITIONED.exec(message)?.groups;
|
|
67
|
+
return {
|
|
68
|
+
column: Number(groups?.column ?? 1),
|
|
69
|
+
line: Number(groups?.line ?? 1),
|
|
70
|
+
reason: groups?.reason ?? message,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/** A position in the file the author wrote. */
|
|
76
|
+
interface Site {
|
|
77
|
+
column?: number;
|
|
78
|
+
file: string;
|
|
79
|
+
line: number;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Maps a line and column of the parsed text to the file it came from. */
|
|
83
|
+
type SiteOf = (line: number, column?: number) => Site;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Where each line of a parsed text came from: the expansion's origins when
|
|
87
|
+
* `<include>`s were spliced, else the page's own file, shifted past its front
|
|
88
|
+
* matter.
|
|
89
|
+
*/
|
|
90
|
+
const sitesIn =
|
|
91
|
+
(file: string, lineOffset: number, origins?: readonly LineOrigin[]): SiteOf =>
|
|
92
|
+
(line, column) => {
|
|
93
|
+
const origin = origins?.[line - 1];
|
|
94
|
+
return origin
|
|
95
|
+
? { column, file: origin.file, line: origin.line }
|
|
96
|
+
: { column, file, line: line + lineOffset };
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* The reason with each position inside it moved to the file the diagnostic
|
|
101
|
+
* names: a position from another file (a partial's opening tag, read from
|
|
102
|
+
* the page) is dropped rather than shown as a line it isn't.
|
|
103
|
+
*/
|
|
104
|
+
const placedReason = (reason: string, at: Site, siteOf: SiteOf): string =>
|
|
105
|
+
reason.replaceAll(INNER_POSITION, (written, line: string, column: string) => {
|
|
106
|
+
const inner = siteOf(Number(line), Number(column));
|
|
107
|
+
return inner.file === at.file ? ` (${inner.line}:${column})` : "";
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// What fixes the common failures, by what the reason says.
|
|
111
|
+
const EXPRESSION_REASON = /could not parse expression/iu;
|
|
112
|
+
const COMMENT_REASON = /to create a comment in MDX/u;
|
|
113
|
+
const LINK_REASON = /to create a link in MDX/u;
|
|
114
|
+
|
|
115
|
+
/** How to fix a syntax error, read from its reason. */
|
|
116
|
+
const syntaxSuggestion = (reason: string): string => {
|
|
117
|
+
if (EXPRESSION_REASON.test(reason)) {
|
|
118
|
+
return "In .mdx, `{` starts a JavaScript expression. To show a brace as text, escape it (`\\{`) or put the text in inline code.";
|
|
119
|
+
}
|
|
120
|
+
if (COMMENT_REASON.test(reason)) {
|
|
121
|
+
return "Write the comment as `{/* … */}`.";
|
|
122
|
+
}
|
|
123
|
+
if (LINK_REASON.test(reason)) {
|
|
124
|
+
return "Write the link as `[text](https://…)`.";
|
|
125
|
+
}
|
|
126
|
+
return "MDX reads HTML as JSX: close every element (`<br />`, `<img … />`) and match each closing tag. A page that needs no components can be a .md file instead.";
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* The `BLUME_MDX_SYNTAX` diagnostic for `error`, read from a text whose lines
|
|
131
|
+
* `siteOf` places.
|
|
132
|
+
*/
|
|
133
|
+
export const mdxSyntaxDiagnostic = (
|
|
134
|
+
error: MdxSyntaxError,
|
|
135
|
+
siteOf: SiteOf,
|
|
136
|
+
options: { includedIn?: string; severity: Diagnostic["severity"] }
|
|
137
|
+
): Diagnostic => {
|
|
138
|
+
const at = siteOf(error.line, error.column);
|
|
139
|
+
const reason = placedReason(error.reason, at, siteOf);
|
|
140
|
+
return {
|
|
141
|
+
code: "BLUME_MDX_SYNTAX",
|
|
142
|
+
column: at.column,
|
|
143
|
+
file: at.file,
|
|
144
|
+
line: at.line,
|
|
145
|
+
message: options.includedIn
|
|
146
|
+
? `MDX can't parse this file once ${options.includedIn} includes it: ${reason}.`
|
|
147
|
+
: `MDX can't parse this page: ${reason}.`,
|
|
148
|
+
severity: options.severity,
|
|
149
|
+
suggestion: syntaxSuggestion(reason),
|
|
150
|
+
};
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
/** What the scan learned from parsing an `.mdx` entry. */
|
|
154
|
+
export interface MdxSyntaxCheck {
|
|
155
|
+
diagnostics: Diagnostic[];
|
|
156
|
+
/** Whether the page's own text fails to parse, so its build would fail. */
|
|
157
|
+
unparsable: boolean;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Parse an `.mdx` entry's body, and its include-expanded text when it has
|
|
162
|
+
* one (see the module comment). The body is read as the build reads it: with
|
|
163
|
+
* the site's variables substituted, which the scan has already done when
|
|
164
|
+
* there was no expansion to do it in.
|
|
165
|
+
*/
|
|
166
|
+
export const mdxSyntaxCheck = (
|
|
167
|
+
entry: SourceEntry,
|
|
168
|
+
sourceName: string,
|
|
169
|
+
variables?: ContentVariables
|
|
170
|
+
): MdxSyntaxCheck => {
|
|
171
|
+
if (entry.body.format !== "mdx") {
|
|
172
|
+
return { diagnostics: [], unparsable: false };
|
|
173
|
+
}
|
|
174
|
+
// Where the parsed text's lines sit in the files, worked out only for a
|
|
175
|
+
// page that fails: counting the lines of its front matter means splitting
|
|
176
|
+
// its whole file, which every page that parses would pay for.
|
|
177
|
+
const siteOf = (origins?: readonly LineOrigin[]): SiteOf =>
|
|
178
|
+
sitesIn(
|
|
179
|
+
entry.sourcePath ?? `${sourceName}:${entry.ref}`,
|
|
180
|
+
entry.bodyLineOffset ?? strippedLineOffset(entry.raw, entry.body.text),
|
|
181
|
+
origins
|
|
182
|
+
);
|
|
183
|
+
const { expanded } = entry;
|
|
184
|
+
const body = expanded
|
|
185
|
+
? substituteVariables(entry.body.text, variables)
|
|
186
|
+
: entry.body.text;
|
|
187
|
+
const own = mdxSyntaxError(body);
|
|
188
|
+
if (own) {
|
|
189
|
+
return {
|
|
190
|
+
diagnostics: [mdxSyntaxDiagnostic(own, siteOf(), { severity: "error" })],
|
|
191
|
+
unparsable: true,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
const spliced = expanded ? mdxSyntaxError(expanded.text) : null;
|
|
195
|
+
if (!(expanded && spliced)) {
|
|
196
|
+
return { diagnostics: [], unparsable: false };
|
|
197
|
+
}
|
|
198
|
+
return {
|
|
199
|
+
diagnostics: [
|
|
200
|
+
mdxSyntaxDiagnostic(spliced, siteOf(expanded.origins), {
|
|
201
|
+
includedIn: entry.ref,
|
|
202
|
+
severity: "warning",
|
|
203
|
+
}),
|
|
204
|
+
],
|
|
205
|
+
unparsable: false,
|
|
206
|
+
};
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
/** A page as the build failure locator reads it. */
|
|
210
|
+
export interface FailedPage {
|
|
211
|
+
/** The owning source's content root, which bounds its includes. */
|
|
212
|
+
contentRoot?: string;
|
|
213
|
+
/** How a message names the page: its path from the project root. */
|
|
214
|
+
name: string;
|
|
215
|
+
sourcePath: string;
|
|
216
|
+
variables?: ContentVariables;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/** A page's text as the build compiled it, and where its lines came from. */
|
|
220
|
+
interface CompiledText {
|
|
221
|
+
siteOf: SiteOf;
|
|
222
|
+
text: string;
|
|
223
|
+
/** The same text before variables were substituted, line for line. */
|
|
224
|
+
written: string;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Read a page the way the build compiled it: front matter off, includes
|
|
229
|
+
* spliced, variables substituted.
|
|
230
|
+
*/
|
|
231
|
+
const compiledText = async (page: FailedPage): Promise<CompiledText> => {
|
|
232
|
+
const raw = await readFile(page.sourcePath, "utf-8");
|
|
233
|
+
const body = matter(raw).content;
|
|
234
|
+
const offset = strippedLineOffset(raw, body);
|
|
235
|
+
const expansion =
|
|
236
|
+
page.contentRoot && hasIncludeStatements(body)
|
|
237
|
+
? await expandIncludes(body, {
|
|
238
|
+
contentRoot: page.contentRoot,
|
|
239
|
+
lineOffset: offset,
|
|
240
|
+
sourcePath: page.sourcePath,
|
|
241
|
+
})
|
|
242
|
+
: null;
|
|
243
|
+
const written = expansion?.text ?? body;
|
|
244
|
+
return {
|
|
245
|
+
siteOf: sitesIn(page.sourcePath, offset, expansion?.origins),
|
|
246
|
+
text: substituteVariables(written, page.variables),
|
|
247
|
+
written,
|
|
248
|
+
};
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Whether an expression's source reads `name` as a variable: as a word of its
|
|
253
|
+
* own, not a property (`user.name` reads `user`), though a spread
|
|
254
|
+
* (`...rest`) reads what it spreads.
|
|
255
|
+
*/
|
|
256
|
+
const readsName = (expression: string, name: string): boolean =>
|
|
257
|
+
new RegExp(
|
|
258
|
+
String.raw`(?<![\w$.])${name.replaceAll("$", String.raw`\$`)}(?![\w$])`,
|
|
259
|
+
"u"
|
|
260
|
+
).test(expression.replaceAll("...", " "));
|
|
261
|
+
|
|
262
|
+
/** The expressions a node holds: its own, or its JSX attributes'. */
|
|
263
|
+
const expressionsOf = (node: Nodes): string[] => {
|
|
264
|
+
if (node.type === "mdxFlowExpression" || node.type === "mdxTextExpression") {
|
|
265
|
+
return [node.value];
|
|
266
|
+
}
|
|
267
|
+
if (node.type === "mdxJsxFlowElement" || node.type === "mdxJsxTextElement") {
|
|
268
|
+
return node.attributes.flatMap((attribute) => {
|
|
269
|
+
if (attribute.type === "mdxJsxExpressionAttribute") {
|
|
270
|
+
return [attribute.value];
|
|
271
|
+
}
|
|
272
|
+
return isExpression(attribute.value) ? [attribute.value.value] : [];
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
return [];
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
/** A `{…}` expression, and the node that holds it. */
|
|
279
|
+
interface FoundExpression {
|
|
280
|
+
expression: string;
|
|
281
|
+
/** The expression's own node, or the element whose attribute holds it. */
|
|
282
|
+
node: Nodes;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/** The first `{…}` expression in `text` that reads `name`. */
|
|
286
|
+
const expressionReading = (
|
|
287
|
+
text: string,
|
|
288
|
+
name: string
|
|
289
|
+
): FoundExpression | null => {
|
|
290
|
+
let tree: Nodes;
|
|
291
|
+
try {
|
|
292
|
+
tree = mdxToMdast(text, { features: MDX_BODY_FEATURES });
|
|
293
|
+
} catch {
|
|
294
|
+
return null;
|
|
295
|
+
}
|
|
296
|
+
const visit = (node: Nodes): FoundExpression | null => {
|
|
297
|
+
const expression = expressionsOf(node).find((source) =>
|
|
298
|
+
readsName(source, name)
|
|
299
|
+
);
|
|
300
|
+
if (expression !== undefined) {
|
|
301
|
+
return { expression: `{${expression.trim()}}`, node };
|
|
302
|
+
}
|
|
303
|
+
if ("children" in node) {
|
|
304
|
+
for (const child of node.children) {
|
|
305
|
+
const found = visit(child);
|
|
306
|
+
if (found) {
|
|
307
|
+
return found;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
return null;
|
|
312
|
+
};
|
|
313
|
+
return visit(tree);
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
/** What a build failure in an `.mdx` page said about itself. */
|
|
317
|
+
export type MdxFailure =
|
|
318
|
+
| { kind: "syntax" }
|
|
319
|
+
| { kind: "undefined-name"; name: string };
|
|
320
|
+
|
|
321
|
+
/** Where a build failure happened in the files the author wrote. */
|
|
322
|
+
export interface MdxFailureSite extends Site {
|
|
323
|
+
/** For a syntax failure, the parse's own diagnostic. */
|
|
324
|
+
diagnostic?: Diagnostic;
|
|
325
|
+
/** For an undefined name, the expression that reads it, braces included. */
|
|
326
|
+
expression?: string;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Locate a build failure Astro reported against an `.mdx` page with no line:
|
|
331
|
+
* the expression that reads an undefined name, or the syntax error that
|
|
332
|
+
* stopped the compile (which, when the page's own text parsed, sits in a
|
|
333
|
+
* partial it includes). Null when the page can't be read or the cause can't
|
|
334
|
+
* be found in it.
|
|
335
|
+
*/
|
|
336
|
+
export const locateMdxFailure = async (
|
|
337
|
+
page: FailedPage,
|
|
338
|
+
failure: MdxFailure
|
|
339
|
+
): Promise<MdxFailureSite | null> => {
|
|
340
|
+
let compiled: CompiledText;
|
|
341
|
+
try {
|
|
342
|
+
compiled = await compiledText(page);
|
|
343
|
+
} catch {
|
|
344
|
+
return null;
|
|
345
|
+
}
|
|
346
|
+
const { siteOf, text, written } = compiled;
|
|
347
|
+
if (failure.kind === "undefined-name") {
|
|
348
|
+
const found = expressionReading(text, failure.name);
|
|
349
|
+
const start = found?.node.position?.start;
|
|
350
|
+
if (!(found && start)) {
|
|
351
|
+
return null;
|
|
352
|
+
}
|
|
353
|
+
// A substituted variable earlier on the line moves the column, so it's
|
|
354
|
+
// read from the line as written when the expression is there.
|
|
355
|
+
const at =
|
|
356
|
+
written.split("\n")[start.line - 1]?.indexOf(found.expression) ?? -1;
|
|
357
|
+
return {
|
|
358
|
+
...siteOf(start.line, at === -1 ? start.column : at + 1),
|
|
359
|
+
expression: found.expression,
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
const error = mdxSyntaxError(text);
|
|
363
|
+
if (!error) {
|
|
364
|
+
return null;
|
|
365
|
+
}
|
|
366
|
+
const diagnostic = mdxSyntaxDiagnostic(error, siteOf, {
|
|
367
|
+
includedIn:
|
|
368
|
+
siteOf(error.line).file === page.sourcePath ? undefined : page.name,
|
|
369
|
+
severity: "error",
|
|
370
|
+
});
|
|
371
|
+
return {
|
|
372
|
+
column: diagnostic.column,
|
|
373
|
+
diagnostic,
|
|
374
|
+
file: diagnostic.file ?? page.sourcePath,
|
|
375
|
+
line: diagnostic.line ?? 1,
|
|
376
|
+
};
|
|
377
|
+
};
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
|
|
3
|
+
import { closestMatch } from "./closest-match.ts";
|
|
4
|
+
import { locatePath } from "./diagnostics.ts";
|
|
5
|
+
import type { DiscoveredFolderMeta, UnreadFolderMeta } from "./meta.ts";
|
|
6
|
+
import { folderChildKeys, pageKey } from "./navigation.ts";
|
|
7
|
+
import type { Diagnostic, PageRecord } from "./types.ts";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Folder-meta diagnostics that need the project's pages: a `pages` entry that
|
|
11
|
+
* names no child of its folder, and a `meta.ts` Blume found but doesn't read
|
|
12
|
+
* although its folder is a sidebar group. Both would otherwise do nothing
|
|
13
|
+
* without a word.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The group path a folder-meta key configures: the key without the version
|
|
18
|
+
* and locale directories `discoverFolderMeta` hoists in front of it, since
|
|
19
|
+
* pages' nav paths carry neither.
|
|
20
|
+
*/
|
|
21
|
+
const groupPathOf = (
|
|
22
|
+
key: string,
|
|
23
|
+
versionDirs: ReadonlySet<string>,
|
|
24
|
+
localeDirs: ReadonlySet<string>
|
|
25
|
+
): string => {
|
|
26
|
+
const parts = key.split("/");
|
|
27
|
+
if (versionDirs.has(parts[0] ?? "")) {
|
|
28
|
+
parts.shift();
|
|
29
|
+
}
|
|
30
|
+
if (localeDirs.has(parts[0] ?? "")) {
|
|
31
|
+
parts.shift();
|
|
32
|
+
}
|
|
33
|
+
return parts.join("/");
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The child a mistyped entry most likely means: the entry spelled as a file
|
|
38
|
+
* name (`01-quickstart.mdx` for `quickstart`), or a near miss.
|
|
39
|
+
*/
|
|
40
|
+
const likelyChild = (
|
|
41
|
+
entry: string,
|
|
42
|
+
children: ReadonlySet<string>
|
|
43
|
+
): string | undefined => {
|
|
44
|
+
const key = pageKey(entry);
|
|
45
|
+
return key !== entry && children.has(key)
|
|
46
|
+
? key
|
|
47
|
+
: closestMatch(entry, [...children]);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const SLUG_HINT =
|
|
51
|
+
"List each child by its slug: the file or folder name without its extension, numeric prefix, or parentheses.";
|
|
52
|
+
|
|
53
|
+
/** Warn about each `pages` entry that names no child of the meta's folder. */
|
|
54
|
+
const unknownPageDiagnostics = async (
|
|
55
|
+
file: string,
|
|
56
|
+
pages: readonly string[],
|
|
57
|
+
group: string,
|
|
58
|
+
children: ReadonlySet<string>
|
|
59
|
+
): Promise<Diagnostic[]> => {
|
|
60
|
+
const unknown = pages.flatMap((entry, index) =>
|
|
61
|
+
children.has(entry) ? [] : [{ entry, index }]
|
|
62
|
+
);
|
|
63
|
+
if (unknown.length === 0) {
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
66
|
+
const source = await readFile(file, "utf-8");
|
|
67
|
+
const where = group ? `"${group}"` : "the content root";
|
|
68
|
+
return unknown.map(({ entry, index }) => {
|
|
69
|
+
const position = locatePath(source, ["pages", index]);
|
|
70
|
+
const match = likelyChild(entry, children);
|
|
71
|
+
return {
|
|
72
|
+
code: "BLUME_META_UNKNOWN_PAGE",
|
|
73
|
+
column: position?.column,
|
|
74
|
+
file,
|
|
75
|
+
line: position?.line,
|
|
76
|
+
message: `pages lists "${entry}", but no page or folder in ${where} has that slug, so the entry orders nothing.`,
|
|
77
|
+
severity: "warning",
|
|
78
|
+
suggestion: match ? `Did you mean "${match}"? ${SLUG_HINT}` : SLUG_HINT,
|
|
79
|
+
} satisfies Diagnostic;
|
|
80
|
+
});
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/** Warn about a meta file outside every `include` glob whose folder has pages. */
|
|
84
|
+
const unreadDiagnostic = (
|
|
85
|
+
{ dir, file }: UnreadFolderMeta,
|
|
86
|
+
group: string
|
|
87
|
+
): Diagnostic => ({
|
|
88
|
+
code: "BLUME_META_OUTSIDE_INCLUDE",
|
|
89
|
+
file,
|
|
90
|
+
message: `This meta file is in the "${group}" sidebar group's folder, but no include glob of its content source reaches that folder, so Blume doesn't read it.`,
|
|
91
|
+
severity: "warning",
|
|
92
|
+
suggestion: `Add an include glob that reaches the folder, such as "${dir}/**/*.{md,mdx}".`,
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
/** Folder-meta diagnostics against every page the sources produced. */
|
|
96
|
+
export const folderMetaDiagnostics = async (
|
|
97
|
+
discovered: DiscoveredFolderMeta,
|
|
98
|
+
pages: readonly PageRecord[],
|
|
99
|
+
options: {
|
|
100
|
+
localeDirs?: readonly string[];
|
|
101
|
+
versionDirs?: readonly string[];
|
|
102
|
+
} = {}
|
|
103
|
+
): Promise<Diagnostic[]> => {
|
|
104
|
+
const children = folderChildKeys(pages.map((page) => page.navPath));
|
|
105
|
+
const versionDirs = new Set(options.versionDirs);
|
|
106
|
+
const localeDirs = new Set(options.localeDirs);
|
|
107
|
+
const groupOf = (key: string): string =>
|
|
108
|
+
groupPathOf(key, versionDirs, localeDirs);
|
|
109
|
+
|
|
110
|
+
const listed = await Promise.all(
|
|
111
|
+
[...discovered.meta, ...discovered.shared].flatMap(([key, meta]) => {
|
|
112
|
+
const file = discovered.files.get(meta);
|
|
113
|
+
const group = groupOf(key);
|
|
114
|
+
return file && meta.pages
|
|
115
|
+
? [
|
|
116
|
+
unknownPageDiagnostics(
|
|
117
|
+
file,
|
|
118
|
+
meta.pages,
|
|
119
|
+
group,
|
|
120
|
+
children.get(group) ?? new Set()
|
|
121
|
+
),
|
|
122
|
+
]
|
|
123
|
+
: [];
|
|
124
|
+
})
|
|
125
|
+
);
|
|
126
|
+
// A meta file outside the include globs is only worth a word when its
|
|
127
|
+
// folder is a sidebar group some page lives in, like a reference's tag
|
|
128
|
+
// folder; elsewhere it's likely application code that shares the name.
|
|
129
|
+
const unread = discovered.unread.flatMap((entry) => {
|
|
130
|
+
const group = groupOf(entry.key);
|
|
131
|
+
return children.has(group) ? [unreadDiagnostic(entry, group)] : [];
|
|
132
|
+
});
|
|
133
|
+
return [...listed.flat(), ...unread];
|
|
134
|
+
};
|