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,267 @@
|
|
|
1
|
+
import type { Nodes } from "mdast";
|
|
2
|
+
import { markdownToMdast, mdxToMdast } from "satteri";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
MARKDOWN_BODY_FEATURES,
|
|
6
|
+
MDX_BODY_FEATURES,
|
|
7
|
+
} from "../markdown/features.ts";
|
|
8
|
+
import { isKnownLanguage, normalizeFence } from "../markdown/fence-language.ts";
|
|
9
|
+
import {
|
|
10
|
+
metaTokens,
|
|
11
|
+
RUST_LANGUAGES,
|
|
12
|
+
RUST_META_KEYWORDS,
|
|
13
|
+
} from "../markdown/fence-meta.ts";
|
|
14
|
+
import { strippedLineOffset } from "./sources/normalize.ts";
|
|
15
|
+
import type { SourceEntry } from "./sources/types.ts";
|
|
16
|
+
import type { Diagnostic } from "./types.ts";
|
|
17
|
+
|
|
18
|
+
type Code = Extract<Nodes, { type: "code" }>;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Fence languages that render without a Shiki grammar on purpose: `math`,
|
|
22
|
+
* which Astro never highlights, and `package-install`, which `.mdx` turns
|
|
23
|
+
* into install tabs (and the docs describe as a plain block in `.md`).
|
|
24
|
+
*/
|
|
25
|
+
const BLUME_LANGUAGES: ReadonlySet<string> = new Set([
|
|
26
|
+
"math",
|
|
27
|
+
"package-install",
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
/** A fence problem at a line of the parsed text, before it's placed in a file. */
|
|
31
|
+
interface Finding {
|
|
32
|
+
code: string;
|
|
33
|
+
line: number;
|
|
34
|
+
message: string;
|
|
35
|
+
suggestion: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** A fence option another docs tool reads and Blume doesn't. */
|
|
39
|
+
interface ForeignOption {
|
|
40
|
+
/** Matches the option's token in the fence's info string. */
|
|
41
|
+
pattern: RegExp;
|
|
42
|
+
/** The fence languages the option belongs to; any language when unset. */
|
|
43
|
+
languages?: ReadonlySet<string>;
|
|
44
|
+
/** What comes of the option, since Blume ignores it. */
|
|
45
|
+
effect: string;
|
|
46
|
+
/** The Blume spelling, given the option's quoted value. */
|
|
47
|
+
instead: (value: string) => string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** `hl_lines="2 4-5"` → `{2,4-5}`: MkDocs separates lines with spaces. */
|
|
51
|
+
const lineRange = (value: string): string =>
|
|
52
|
+
`{${value
|
|
53
|
+
.trim()
|
|
54
|
+
.split(/[\s,]+/u)
|
|
55
|
+
.join(",")}}`;
|
|
56
|
+
|
|
57
|
+
const FOREIGN_OPTIONS: readonly ForeignOption[] = [
|
|
58
|
+
{
|
|
59
|
+
effect: "the block highlights no lines",
|
|
60
|
+
instead: (value) =>
|
|
61
|
+
`Put the lines in braces after the language instead: \`${lineRange(value)}\`.`,
|
|
62
|
+
pattern: /^hl_lines=["'](?<value>[^"']*)["']$/u,
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
effect: "the block shows no line numbers",
|
|
66
|
+
instead: (value) =>
|
|
67
|
+
`Write \`lineNumbers\` after the language instead.${
|
|
68
|
+
value.trim() === "1" ? "" : " Blume numbers every block from 1."
|
|
69
|
+
}`,
|
|
70
|
+
pattern: /^linenums=["'](?<value>[^"']*)["']$/u,
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
effect: "the block shows no line numbers",
|
|
74
|
+
instead: () => "Write `lineNumbers` after the language instead.",
|
|
75
|
+
pattern: /^showLineNumbers(?:\{\d*\})?$/u,
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
// Mintlify's. Like `showLineNumbers` and `wordWrap`, it never joins the
|
|
79
|
+
// title (see `markdown/fence-meta.ts`).
|
|
80
|
+
effect: "the block shows no line numbers",
|
|
81
|
+
instead: () =>
|
|
82
|
+
'Write `lineNumbers` after the language instead. To keep the word in the block\'s title, set the title with `title="…"`.',
|
|
83
|
+
pattern: /^lines$/u,
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
effect: "the block doesn't wrap its long lines",
|
|
87
|
+
instead: () => "Write `wrap` after the language instead.",
|
|
88
|
+
pattern: /^wordWrap$/u,
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
effect: "the block has no title",
|
|
92
|
+
instead: (value) => `Write \`title="${value}"\` instead.`,
|
|
93
|
+
pattern: /^filename=["'](?<value>[^"']*)["']$/u,
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
// rustdoc's and mdBook's, on a Rust fence. They never join the title.
|
|
97
|
+
effect:
|
|
98
|
+
"it does nothing: Blume shows a code block as static text, and never compiles, tests, or runs it",
|
|
99
|
+
instead: () => "Remove it.",
|
|
100
|
+
languages: RUST_LANGUAGES,
|
|
101
|
+
pattern: new RegExp(`^(?:${[...RUST_META_KEYWORDS].join("|")})$`, "u"),
|
|
102
|
+
},
|
|
103
|
+
];
|
|
104
|
+
|
|
105
|
+
/** The option `token` is in a fence whose language is `lang`, if any. */
|
|
106
|
+
const foreignOption = (
|
|
107
|
+
token: string,
|
|
108
|
+
lang: string
|
|
109
|
+
): { option: ForeignOption; value: string } | null => {
|
|
110
|
+
for (const option of FOREIGN_OPTIONS) {
|
|
111
|
+
const match = option.pattern.exec(token);
|
|
112
|
+
if (match && (option.languages?.has(lang) ?? true)) {
|
|
113
|
+
return { option, value: match.groups?.value ?? "" };
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return null;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
/** Whether a fence's language renders as the author meant: highlighted, or plain on purpose. */
|
|
120
|
+
const rendersLanguage = (lang: string): boolean =>
|
|
121
|
+
isKnownLanguage(lang) || BLUME_LANGUAGES.has(lang);
|
|
122
|
+
|
|
123
|
+
/** A fence's language and option tokens, as the renderer reads them. */
|
|
124
|
+
const fenceParts = (
|
|
125
|
+
lang: string | null | undefined,
|
|
126
|
+
meta: string | null | undefined
|
|
127
|
+
) => {
|
|
128
|
+
const fence = normalizeFence(lang, meta) ?? {
|
|
129
|
+
lang: lang ?? "",
|
|
130
|
+
meta: meta ?? null,
|
|
131
|
+
};
|
|
132
|
+
return { lang: fence.lang, meta: fence.meta, tokens: metaTokens(fence.meta) };
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const unknownLanguage = (node: Code, line: number): Finding[] => {
|
|
136
|
+
const { lang } = fenceParts(node.lang, node.meta);
|
|
137
|
+
if (!lang || rendersLanguage(lang)) {
|
|
138
|
+
return [];
|
|
139
|
+
}
|
|
140
|
+
return [
|
|
141
|
+
{
|
|
142
|
+
code: "BLUME_UNKNOWN_CODE_LANGUAGE",
|
|
143
|
+
line,
|
|
144
|
+
message: `\`${lang}\` isn't a language Blume can highlight, so the code block renders as plain text.`,
|
|
145
|
+
suggestion:
|
|
146
|
+
'Use a Shiki language id or alias (https://shiki.style/languages), or `text` for plain text. Options like a title go after the language, as in `text title="notes.txt"`.',
|
|
147
|
+
},
|
|
148
|
+
];
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const foreignOptions = (node: Code, line: number): Finding[] => {
|
|
152
|
+
const { lang, tokens } = fenceParts(node.lang, node.meta);
|
|
153
|
+
// A fence with options and no language reads its first option as the
|
|
154
|
+
// language, so that token counts too.
|
|
155
|
+
return [node.lang ?? "", ...tokens].flatMap((token) => {
|
|
156
|
+
const found = foreignOption(token, lang);
|
|
157
|
+
if (!found) {
|
|
158
|
+
return [];
|
|
159
|
+
}
|
|
160
|
+
return [
|
|
161
|
+
{
|
|
162
|
+
code: "BLUME_CODE_FENCE_OPTION",
|
|
163
|
+
line,
|
|
164
|
+
message: `\`${token}\` isn't a Blume code block option, so ${found.option.effect}.`,
|
|
165
|
+
suggestion: found.option.instead(found.value),
|
|
166
|
+
},
|
|
167
|
+
];
|
|
168
|
+
});
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
// A fence opener's info string: the text after a run of three or more
|
|
172
|
+
// backticks or tildes. Matched anywhere in a line, so it also sees fences in
|
|
173
|
+
// list items and quotes (and fence-like inline code, which the parse below
|
|
174
|
+
// sorts out).
|
|
175
|
+
const FENCE_INFO = /(?:`{3,}|~{3,})[\t ]*(?<info>[^`\n]*)/gu;
|
|
176
|
+
|
|
177
|
+
/** Whether a fence's info string holds anything these checks report. */
|
|
178
|
+
const suspectInfo = (info: string): boolean => {
|
|
179
|
+
const [lang, ...rest] = info.trim().split(/\s+/u);
|
|
180
|
+
if (!lang) {
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
const parts = fenceParts(lang, rest.join(" ") || null);
|
|
184
|
+
return (
|
|
185
|
+
!rendersLanguage(parts.lang) ||
|
|
186
|
+
[lang, ...parts.tokens].some(
|
|
187
|
+
(token) => foreignOption(token, parts.lang) !== null
|
|
188
|
+
)
|
|
189
|
+
);
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
const parse = (text: string, format: "md" | "mdx"): Nodes | null => {
|
|
193
|
+
try {
|
|
194
|
+
return format === "mdx"
|
|
195
|
+
? mdxToMdast(text, { features: MDX_BODY_FEATURES })
|
|
196
|
+
: markdownToMdast(text, { features: MARKDOWN_BODY_FEATURES });
|
|
197
|
+
} catch {
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* The fence problems in a page body, in source order. The body is parsed as
|
|
204
|
+
* the renderer reads it, so a fence shown inside another code block never
|
|
205
|
+
* counts. A page is parsed only when an info string looks like one of these
|
|
206
|
+
* problems, so pages without one cost a scan. A body MDX can't parse fails to
|
|
207
|
+
* render anyway, with its own error.
|
|
208
|
+
*/
|
|
209
|
+
const fenceFindings = (text: string, format: "md" | "mdx"): Finding[] => {
|
|
210
|
+
const suspect = [...text.matchAll(FENCE_INFO)].some((match) =>
|
|
211
|
+
suspectInfo(match.groups?.info ?? "")
|
|
212
|
+
);
|
|
213
|
+
const tree = suspect ? parse(text, format) : null;
|
|
214
|
+
if (!tree) {
|
|
215
|
+
return [];
|
|
216
|
+
}
|
|
217
|
+
const found: Finding[] = [];
|
|
218
|
+
const walk = (node: Nodes): void => {
|
|
219
|
+
if (node.type === "code") {
|
|
220
|
+
const line = node.position?.start.line ?? 1;
|
|
221
|
+
found.push(...unknownLanguage(node, line), ...foreignOptions(node, line));
|
|
222
|
+
}
|
|
223
|
+
if ("children" in node) {
|
|
224
|
+
for (const child of node.children) {
|
|
225
|
+
walk(child);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
walk(tree);
|
|
230
|
+
return found;
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Warn about code fences that don't render as their author meant:
|
|
235
|
+
*
|
|
236
|
+
* - `BLUME_UNKNOWN_CODE_LANGUAGE`: a language Shiki doesn't know, so the block
|
|
237
|
+
* renders as plain text. Shiki itself only logs a console line naming the
|
|
238
|
+
* language, with no page or line.
|
|
239
|
+
* - `BLUME_CODE_FENCE_OPTION`: an option another docs tool reads, like MkDocs'
|
|
240
|
+
* `hl_lines="2 3"`, Docusaurus' `showLineNumbers`, Mintlify's `lines`, or
|
|
241
|
+
* rustdoc's `no_run`, which Blume ignores. None of them becomes part of the
|
|
242
|
+
* block's title. The suggestion gives the Blume spelling, or says to remove
|
|
243
|
+
* an option Blume has no use for.
|
|
244
|
+
*
|
|
245
|
+
* Lines point into the file the author wrote, a partial's own file for a
|
|
246
|
+
* fence an `<include>` brought in.
|
|
247
|
+
*/
|
|
248
|
+
export const codeFenceDiagnostics = (
|
|
249
|
+
entry: SourceEntry,
|
|
250
|
+
sourceName: string
|
|
251
|
+
): Diagnostic[] => {
|
|
252
|
+
const page = entry.sourcePath ?? `${sourceName}:${entry.ref}`;
|
|
253
|
+
const offset =
|
|
254
|
+
entry.bodyLineOffset ?? strippedLineOffset(entry.raw, entry.body.text);
|
|
255
|
+
return fenceFindings(
|
|
256
|
+
entry.expanded?.text ?? entry.body.text,
|
|
257
|
+
entry.body.format
|
|
258
|
+
).map(({ line, ...finding }) => {
|
|
259
|
+
const origin = entry.expanded?.origins[line - 1];
|
|
260
|
+
return {
|
|
261
|
+
...finding,
|
|
262
|
+
file: origin?.file ?? page,
|
|
263
|
+
line: origin?.line ?? line + offset,
|
|
264
|
+
severity: "warning",
|
|
265
|
+
};
|
|
266
|
+
});
|
|
267
|
+
};
|
package/src/core/code-fences.ts
CHANGED
|
@@ -12,37 +12,73 @@
|
|
|
12
12
|
*/
|
|
13
13
|
const CODE_FENCE = /^(?<run>`{3,}|~{3,})/u;
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
// One block-quote marker. A fence inside a quote (`> ```ts`) is written after
|
|
16
|
+
// one marker per level of nesting, and so is every line of its code.
|
|
17
|
+
const QUOTE_MARKER = /^[ \t]*>/u;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The open fence's delimiter char, its run length, and the block-quote depth
|
|
21
|
+
* it opened at, or null outside one.
|
|
22
|
+
*/
|
|
23
|
+
export type FenceState = {
|
|
24
|
+
delimiter: "`" | "~";
|
|
25
|
+
length: number;
|
|
26
|
+
quotes: number;
|
|
27
|
+
} | null;
|
|
28
|
+
|
|
29
|
+
/** Strip up to `max` leading block-quote markers from `line`. */
|
|
30
|
+
const stripQuotes = (line: string, max: number) => {
|
|
31
|
+
let rest = line;
|
|
32
|
+
let depth = 0;
|
|
33
|
+
while (depth < max) {
|
|
34
|
+
const marker = QUOTE_MARKER.exec(rest);
|
|
35
|
+
if (!marker) {
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
rest = rest.slice(marker[0].length);
|
|
39
|
+
depth += 1;
|
|
40
|
+
}
|
|
41
|
+
return { depth, rest };
|
|
42
|
+
};
|
|
17
43
|
|
|
18
44
|
/**
|
|
19
45
|
* Advance the fenced-code state for one line: an opening fence records its
|
|
20
46
|
* delimiter and run length, only a bare run of the same character at least as
|
|
21
47
|
* long closes it (CommonMark), and any other line leaves the state untouched.
|
|
48
|
+
* A fence inside block quotes is read past the quote markers it opened under,
|
|
49
|
+
* and a line without them ends the quote, and the fence with it: code takes
|
|
50
|
+
* no lazy continuation lines (CommonMark).
|
|
22
51
|
*/
|
|
23
52
|
export const nextFenceState = (line: string, fence: FenceState): FenceState => {
|
|
24
|
-
const
|
|
53
|
+
const { depth, rest } = stripQuotes(
|
|
54
|
+
line,
|
|
55
|
+
fence === null ? Number.POSITIVE_INFINITY : fence.quotes
|
|
56
|
+
);
|
|
57
|
+
if (fence !== null && depth < fence.quotes) {
|
|
58
|
+
return nextFenceState(line, null);
|
|
59
|
+
}
|
|
60
|
+
const trimmed = rest.trimStart();
|
|
25
61
|
const run = trimmed.match(CODE_FENCE)?.groups?.run;
|
|
26
62
|
if (run === undefined) {
|
|
27
63
|
return fence;
|
|
28
64
|
}
|
|
29
|
-
const
|
|
65
|
+
const info = trimmed.slice(run.length);
|
|
30
66
|
const delimiter = run.startsWith("`") ? ("`" as const) : ("~" as const);
|
|
31
67
|
if (fence === null) {
|
|
32
68
|
// A backtick fence's info string cannot itself contain a backtick
|
|
33
69
|
// (CommonMark) — a line-leading ```inline``` span is a paragraph, and
|
|
34
70
|
// opening a phantom fence on it would swallow every heading and link
|
|
35
71
|
// after it. Tilde fences carry no such rule.
|
|
36
|
-
if (delimiter === "`" &&
|
|
72
|
+
if (delimiter === "`" && info.includes("`")) {
|
|
37
73
|
return fence;
|
|
38
74
|
}
|
|
39
|
-
return { delimiter, length: run.length };
|
|
75
|
+
return { delimiter, length: run.length, quotes: depth };
|
|
40
76
|
}
|
|
41
77
|
// A closing fence may be followed only by whitespace: a same-length
|
|
42
78
|
// ```js line inside an open ``` block is content, not a closer.
|
|
43
79
|
return fence.delimiter === delimiter &&
|
|
44
80
|
run.length >= fence.length &&
|
|
45
|
-
|
|
81
|
+
info.trim() === ""
|
|
46
82
|
? null
|
|
47
83
|
: fence;
|
|
48
84
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { relative } from "pathe";
|
|
2
2
|
|
|
3
|
-
import { BUILTIN_MDX_TAGS } from "./builtin-tags.ts";
|
|
3
|
+
import { BUILTIN_CHILD_PROPS, BUILTIN_MDX_TAGS } from "./builtin-tags.ts";
|
|
4
4
|
import type { Diagnostic, PageRecord } from "./types.ts";
|
|
5
5
|
|
|
6
6
|
/** `CardGroup` → `card-group`, matching registry item names. */
|
|
@@ -9,36 +9,89 @@ const toKebab = (tag: string): string =>
|
|
|
9
9
|
.replaceAll(/(?<lower>[a-z0-9])(?<upper>[A-Z])/gu, "$<lower>-$<upper>")
|
|
10
10
|
.toLowerCase();
|
|
11
11
|
|
|
12
|
+
const LIST = new Intl.ListFormat("en", { type: "conjunction" });
|
|
13
|
+
|
|
12
14
|
/**
|
|
13
15
|
* Warn when an `.mdx` page uses a `<Component>` tag that resolves to nothing —
|
|
14
16
|
* a built-in, an island, or a `components.ts` override — so a typo surfaces as a
|
|
15
17
|
* friendly diagnostic (with a `blume add` hint where one exists) instead of a raw
|
|
16
18
|
* MDX "X is not defined" build error. `extraTags` are the project's own known
|
|
17
19
|
* components (islands + overrides); `registryNames` gates the install hint.
|
|
20
|
+
* One warning per tag names every page that uses it, anchored to the first.
|
|
18
21
|
*/
|
|
19
22
|
export const validateUsedComponents = (
|
|
20
23
|
pages: PageRecord[],
|
|
21
24
|
extraTags: Set<string>,
|
|
22
25
|
registryNames: Set<string>
|
|
26
|
+
): Diagnostic[] => {
|
|
27
|
+
const users = new Map<string, PageRecord[]>();
|
|
28
|
+
for (const page of pages) {
|
|
29
|
+
for (const tag of page.componentsUsed ?? []) {
|
|
30
|
+
if (!(BUILTIN_MDX_TAGS.has(tag) || extraTags.has(tag))) {
|
|
31
|
+
users.set(tag, [...(users.get(tag) ?? []), page]);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return [...users].map(([tag, using]) => {
|
|
36
|
+
const name = toKebab(tag);
|
|
37
|
+
const suggestion = registryNames.has(name)
|
|
38
|
+
? `Run \`blume add ${name}\` to install it, or register <${tag}> in components.ts (mdx).`
|
|
39
|
+
: `Register <${tag}> in components.ts (mdx), or add an islands/${tag}.tsx component.`;
|
|
40
|
+
const routes = using.map((page) => page.route);
|
|
41
|
+
const [first] = using;
|
|
42
|
+
return {
|
|
43
|
+
code: "BLUME_UNKNOWN_COMPONENT",
|
|
44
|
+
file: first?.sourcePath ?? first?.id,
|
|
45
|
+
message:
|
|
46
|
+
routes.length === 1
|
|
47
|
+
? `<${tag}> is used in ${LIST.format(routes)} but isn't a known component.`
|
|
48
|
+
: `<${tag}> is used on ${routes.length} pages but isn't a known component: ${LIST.format(routes)}.`,
|
|
49
|
+
severity: "warning",
|
|
50
|
+
suggestion,
|
|
51
|
+
};
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const EITHER = new Intl.ListFormat("en", { type: "disjunction" });
|
|
56
|
+
const quoted = (names: readonly string[]): string[] =>
|
|
57
|
+
names.map((name) => `\`${name}\``);
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Warn about each childless built-in in an `.mdx` page given props it doesn't
|
|
61
|
+
* take (`<Badge type="tip" text="beta" />`, VitePress's Badge). Blume's
|
|
62
|
+
* components ignore unknown props, so the element renders without the
|
|
63
|
+
* content those props carried. `extraTags` are the project's own components
|
|
64
|
+
* (islands + overrides): one that replaces a built-in takes its own props,
|
|
65
|
+
* so its uses aren't checked. A file shared by several locales is reported
|
|
66
|
+
* once.
|
|
67
|
+
*/
|
|
68
|
+
export const unknownPropDiagnostics = (
|
|
69
|
+
pages: readonly PageRecord[],
|
|
70
|
+
extraTags: ReadonlySet<string>
|
|
23
71
|
): Diagnostic[] => {
|
|
24
72
|
const diagnostics: Diagnostic[] = [];
|
|
25
73
|
const seen = new Set<string>();
|
|
26
74
|
for (const page of pages) {
|
|
27
|
-
for (const
|
|
28
|
-
|
|
75
|
+
for (const use of page.unknownProps ?? []) {
|
|
76
|
+
const file = use.file ?? page.sourcePath ?? page.id;
|
|
77
|
+
const key = `${file}:${use.line}:${use.column}`;
|
|
78
|
+
if (extraTags.has(use.tag) || seen.has(key)) {
|
|
29
79
|
continue;
|
|
30
80
|
}
|
|
31
|
-
seen.add(
|
|
32
|
-
const
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
81
|
+
seen.add(key);
|
|
82
|
+
const accepted = BUILTIN_CHILD_PROPS.get(use.tag) ?? [];
|
|
83
|
+
const takes =
|
|
84
|
+
accepted.length > 0
|
|
85
|
+
? `, and use the props ${use.tag} takes: ${LIST.format(quoted(accepted))}`
|
|
86
|
+
: `; ${use.tag} takes no props`;
|
|
36
87
|
diagnostics.push({
|
|
37
|
-
code: "
|
|
38
|
-
|
|
39
|
-
|
|
88
|
+
code: "BLUME_UNKNOWN_PROP",
|
|
89
|
+
column: use.column,
|
|
90
|
+
file,
|
|
91
|
+
line: use.line,
|
|
92
|
+
message: `<${use.tag}> in ${page.route} has no children, and ${use.tag} doesn't take ${EITHER.format(quoted(use.props))}, so it renders without what ${use.props.length === 1 ? "that prop" : "those props"} meant to show.`,
|
|
40
93
|
severity: "warning",
|
|
41
|
-
suggestion
|
|
94
|
+
suggestion: `Put the content between the tags (<${use.tag}>…</${use.tag}>)${takes}.`,
|
|
42
95
|
});
|
|
43
96
|
}
|
|
44
97
|
}
|
package/src/core/config-input.ts
CHANGED
|
@@ -181,7 +181,7 @@ export interface ChangelogConfig {
|
|
|
181
181
|
export interface ContentConfig {
|
|
182
182
|
/** Default page `type` for content that sets none. Defaults to `doc`. */
|
|
183
183
|
defaultType?: string;
|
|
184
|
-
/** Glob patterns to ignore
|
|
184
|
+
/** Glob patterns to ignore, on top of `["**\/_*", "**\/.*"]`; a `!pattern` entry stops ignoring that pattern, a default included. Shorthand; not allowed beside `sources`. */
|
|
185
185
|
exclude?: string[];
|
|
186
186
|
/** Glob patterns to include. Defaults to `["**\/*.{md,mdx}"]`. Shorthand; not allowed beside `sources`. */
|
|
187
187
|
include?: string[];
|
package/src/core/config.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
3
3
|
import { basename } from "pathe";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
|
|
6
|
+
import { ACCENTS, invalidColorSettings } from "../theme/palette.ts";
|
|
6
7
|
import type { BlumeConfig } from "./config-input.ts";
|
|
7
8
|
import { applyDeploymentEnv } from "./deployment-env.ts";
|
|
8
9
|
import {
|
|
@@ -261,6 +262,50 @@ const configIssues = (
|
|
|
261
262
|
);
|
|
262
263
|
};
|
|
263
264
|
|
|
265
|
+
const COLOR_FORMS =
|
|
266
|
+
"a hex value like #6340ac, rgb(), hsl(), oklch(), or a color name like rebeccapurple";
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* A warning for each configured color that isn't a CSS color, at the line
|
|
270
|
+
* that sets it. The schema has always taken any string, so this warns rather
|
|
271
|
+
* than failing a build that used to pass.
|
|
272
|
+
*/
|
|
273
|
+
const colorDiagnostics = (
|
|
274
|
+
config: ResolvedConfig,
|
|
275
|
+
configFile: string | null
|
|
276
|
+
): Diagnostic[] => {
|
|
277
|
+
const invalid = invalidColorSettings(config);
|
|
278
|
+
if (invalid.length === 0) {
|
|
279
|
+
return [];
|
|
280
|
+
}
|
|
281
|
+
const source =
|
|
282
|
+
configFile && existsSync(configFile)
|
|
283
|
+
? readFileSync(configFile, "utf-8")
|
|
284
|
+
: undefined;
|
|
285
|
+
const located = diagnosticsFromIssues(
|
|
286
|
+
invalid.map((setting) => ({
|
|
287
|
+
message: `${JSON.stringify(setting.value)} isn't a CSS color, so ${
|
|
288
|
+
setting.path[0] === "seo"
|
|
289
|
+
? "the social card renderer rejects it"
|
|
290
|
+
: "browsers ignore the styles that use it"
|
|
291
|
+
}.`,
|
|
292
|
+
path: setting.path,
|
|
293
|
+
})),
|
|
294
|
+
{
|
|
295
|
+
code: "BLUME_THEME_COLOR_INVALID",
|
|
296
|
+
file: configFile ?? undefined,
|
|
297
|
+
source,
|
|
298
|
+
}
|
|
299
|
+
);
|
|
300
|
+
return located.map((diagnostic, index) => ({
|
|
301
|
+
...diagnostic,
|
|
302
|
+
severity: "warning",
|
|
303
|
+
suggestion: invalid[index]?.presets
|
|
304
|
+
? `Use a preset (${Object.keys(ACCENTS).join(", ")}) or a CSS color: ${COLOR_FORMS}.`
|
|
305
|
+
: `Use a CSS color: ${COLOR_FORMS}.`,
|
|
306
|
+
}));
|
|
307
|
+
};
|
|
308
|
+
|
|
264
309
|
/**
|
|
265
310
|
* Load and validate the project config. When no config file exists, schema
|
|
266
311
|
* defaults produce a fully resolved config so the zero-boilerplate path works.
|
|
@@ -364,7 +409,7 @@ export const loadConfig = async (
|
|
|
364
409
|
seo: { ...config.seo, og: { ...config.seo.og, enabled: ogEnabled } },
|
|
365
410
|
},
|
|
366
411
|
configFile,
|
|
367
|
-
diagnostics:
|
|
412
|
+
diagnostics: colorDiagnostics(config, configFile),
|
|
368
413
|
themeFontsConfigured,
|
|
369
414
|
};
|
|
370
415
|
};
|