blume 2.0.2 → 2.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +65 -0
- package/dist/cli/{chunk-6k4ftwze.js → chunk-1d7ve1dm.js} +1 -1
- package/dist/cli/{chunk-zp79m0ts.js → chunk-2eytanqx.js} +2 -2
- package/dist/cli/{chunk-8g8ytmgx.js → chunk-2hsdwb9n.js} +19 -19
- package/dist/cli/{chunk-8g8ytmgx.js.map → chunk-2hsdwb9n.js.map} +1 -1
- package/dist/cli/{chunk-g698a744.js → chunk-2z928egk.js} +5 -5
- package/dist/cli/{chunk-ppzjqwx2.js → chunk-35d4wj9f.js} +17 -18
- package/dist/cli/{chunk-ppzjqwx2.js.map → chunk-35d4wj9f.js.map} +3 -3
- package/dist/cli/{chunk-mqc662a6.js → chunk-364znk6q.js} +2 -2
- package/dist/cli/{chunk-gs7r695n.js → chunk-3em5wd2y.js} +21 -8
- package/dist/cli/{chunk-gs7r695n.js.map → chunk-3em5wd2y.js.map} +3 -3
- package/dist/cli/{chunk-k7pj68a8.js → chunk-5f86nr5m.js} +15 -15
- package/dist/cli/{chunk-hr8ne106.js → chunk-5m5nmvyq.js} +68 -43
- package/dist/cli/chunk-5m5nmvyq.js.map +13 -0
- package/dist/cli/{chunk-91ws1n6j.js → chunk-5xvm6tfj.js} +14 -14
- package/dist/cli/{chunk-w4bxdvsa.js → chunk-6dsbexzp.js} +14 -14
- package/dist/cli/{chunk-00gs3wqs.js → chunk-8ktnccpt.js} +1 -1
- package/dist/cli/{chunk-j85scx15.js → chunk-9t7a85s3.js} +130 -44
- package/dist/cli/chunk-9t7a85s3.js.map +10 -0
- package/dist/cli/{chunk-bbnwccaz.js → chunk-a58773jm.js} +2 -2
- package/dist/cli/{chunk-nfcyttvj.js → chunk-acanzt5p.js} +9 -9
- package/dist/cli/{chunk-nfcyttvj.js.map → chunk-acanzt5p.js.map} +1 -1
- package/dist/cli/{chunk-wdrt2k2v.js → chunk-akbpwfxc.js} +90 -26
- package/dist/cli/chunk-akbpwfxc.js.map +10 -0
- package/dist/cli/{chunk-sqw4ekg1.js → chunk-b07cmahc.js} +2 -2
- package/dist/cli/{chunk-xh43dwgw.js → chunk-c8chx29p.js} +42 -29
- package/dist/cli/{chunk-xh43dwgw.js.map → chunk-c8chx29p.js.map} +9 -9
- package/dist/cli/{chunk-7mbqtmgb.js → chunk-crgn1q09.js} +21 -12
- package/dist/cli/chunk-crgn1q09.js.map +10 -0
- package/dist/cli/chunk-e04dxsz1.js +39 -0
- package/dist/cli/chunk-e04dxsz1.js.map +10 -0
- package/dist/cli/{chunk-4e9b9ra6.js → chunk-ey84smr6.js} +3 -3
- package/dist/cli/{chunk-d1v5rhy0.js → chunk-g4hq16wv.js} +13 -13
- package/dist/cli/{chunk-273ygyr4.js → chunk-ga0pf4aj.js} +7 -7
- package/dist/cli/{chunk-273ygyr4.js.map → chunk-ga0pf4aj.js.map} +3 -3
- package/dist/cli/{chunk-pbg5a4s3.js → chunk-hm3vjy5s.js} +64 -45
- package/dist/cli/chunk-hm3vjy5s.js.map +19 -0
- package/dist/cli/{chunk-5r8g91qn.js → chunk-j85vccga.js} +306 -107
- package/dist/cli/chunk-j85vccga.js.map +36 -0
- package/dist/cli/{chunk-n1yg3tj3.js → chunk-p3v96n38.js} +6 -6
- package/dist/cli/{chunk-n1yg3tj3.js.map → chunk-p3v96n38.js.map} +3 -3
- package/dist/cli/{chunk-7vtckvaw.js → chunk-p73c0m7w.js} +14 -14
- package/dist/cli/{chunk-7vtckvaw.js.map → chunk-p73c0m7w.js.map} +1 -1
- package/dist/cli/{chunk-qkb5a8sa.js → chunk-pehfxfta.js} +3 -3
- package/dist/cli/{chunk-v6ya5kcb.js → chunk-pv29h0wf.js} +1223 -978
- package/dist/cli/{chunk-v6ya5kcb.js.map → chunk-pv29h0wf.js.map} +47 -46
- package/dist/cli/{chunk-6dtt0zfn.js → chunk-q58y5e6a.js} +10 -10
- package/dist/cli/{chunk-6dtt0zfn.js.map → chunk-q58y5e6a.js.map} +3 -3
- package/dist/cli/{chunk-fsmrqk8a.js → chunk-r20tn01b.js} +1 -1
- package/dist/cli/{chunk-bfwp9vp6.js → chunk-r9rcc4w7.js} +7 -7
- package/dist/cli/{chunk-h7k3nq3v.js → chunk-tkacnehg.js} +2 -2
- package/dist/cli/{chunk-h2ez8dzb.js → chunk-tzmab476.js} +4 -4
- package/dist/cli/{chunk-3yce002v.js → chunk-vg9r4eb9.js} +6 -6
- package/dist/cli/{chunk-3yce002v.js.map → chunk-vg9r4eb9.js.map} +4 -4
- package/dist/cli/{chunk-hqp2ajnh.js → chunk-wrr3j9w9.js} +16 -17
- package/dist/cli/{chunk-hqp2ajnh.js.map → chunk-wrr3j9w9.js.map} +7 -7
- package/dist/cli/{chunk-ddndchfr.js → chunk-yfyb25rh.js} +38 -26
- package/dist/cli/chunk-yfyb25rh.js.map +14 -0
- package/dist/cli/index.js +20 -18
- package/dist/cli/index.js.map +3 -3
- package/dist/types/ai/link-headers.d.ts +8 -1
- package/dist/types/ai/openapi-components.d.ts +5 -2
- package/dist/types/ai/relative-links.d.ts +9 -7
- package/dist/types/ai/skills.d.ts +4 -1
- package/dist/types/ai/tar.d.ts +1 -3
- package/dist/types/analytics/index.d.ts +2 -0
- package/dist/types/analytics/one-dollar-stats.d.ts +48 -0
- package/dist/types/analytics/schema.d.ts +14 -0
- package/dist/types/astro/integration.d.ts +3 -2
- package/dist/types/core/base-path.d.ts +21 -7
- package/dist/types/core/config-input.d.ts +9 -7
- package/dist/types/core/directive-diagnostics.d.ts +12 -0
- package/dist/types/core/heading-markers.d.ts +5 -7
- package/dist/types/core/i18n.d.ts +2 -0
- package/dist/types/core/last-modified.d.ts +10 -0
- package/dist/types/core/meta.d.ts +8 -0
- package/dist/types/core/schema.d.ts +7 -0
- package/dist/types/core/sources/lower.d.ts +29 -16
- package/dist/types/core/sources/normalize.d.ts +13 -1
- package/dist/types/core/sources/watch.d.ts +12 -5
- package/dist/types/core/standard-schema.d.ts +5 -0
- package/dist/types/deploy/artifacts.d.ts +6 -4
- package/dist/types/deploy/headers.d.ts +5 -0
- package/dist/types/deploy/platforms/types.d.ts +8 -0
- package/dist/types/deploy/redirects.d.ts +24 -13
- package/dist/types/markdown/directives.d.ts +62 -0
- package/dist/types/markdown/features.d.ts +21 -0
- package/dist/types/markdown/mdast.d.ts +63 -0
- package/dist/types/openapi/asyncapi.d.ts +4 -2
- package/docs/02-deployment.mdx +4 -2
- package/docs/08-faq.mdx +1 -1
- package/docs/advanced/custom-pages.mdx +3 -3
- package/docs/cli/audit.mdx +17 -1
- package/docs/cli/evals.mdx +3 -3
- package/docs/cli/translate.mdx +3 -3
- package/docs/cli/version.mdx +1 -1
- package/docs/configuration/analytics.mdx +20 -1
- package/docs/configuration/customization.mdx +2 -0
- package/docs/configuration/search.mdx +1 -1
- package/docs/content/components.mdx +1 -1
- package/docs/content/frontmatter.mdx +1 -1
- package/docs/content/i18n.mdx +2 -0
- package/docs/content/index.mdx +4 -2
- package/docs/content/islands.mdx +1 -1
- package/docs/content/meta.mdx +3 -1
- package/docs/content/sources.mdx +1 -5
- package/docs/content/syntax.mdx +14 -0
- package/docs/content/versioning.mdx +1 -1
- package/docs/discoverability/agent-discovery.mdx +1 -1
- package/docs/discoverability/index.mdx +2 -1
- package/docs/discoverability/markdown.mdx +2 -2
- package/docs/discoverability/open-graph.mdx +5 -3
- package/docs/discoverability/rss.mdx +1 -1
- package/docs/references/asyncapi.mdx +1 -1
- package/docs/references/graphql.mdx +1 -1
- package/docs/references/openapi.mdx +5 -3
- package/package.json +1 -1
- package/skills/blume-migrate/references/fumadocs.md +1 -1
- package/skills/blume-migrate/references/nextra.md +1 -1
- package/skills/blume-migrate/scripts/mintlify-codemod.mjs +7 -2
- package/src/ai/agent-readability.ts +2 -2
- package/src/ai/ai-catalog.ts +6 -2
- package/src/ai/api/handlers.ts +2 -2
- package/src/ai/api/spec.ts +2 -2
- package/src/ai/api-catalog.ts +6 -2
- package/src/ai/changelog-markdown.ts +2 -2
- package/src/ai/link-headers.ts +14 -5
- package/src/ai/llms.ts +18 -7
- package/src/ai/mcp/discovery.ts +2 -2
- package/src/ai/mcp/query.ts +2 -2
- package/src/ai/mcp/server.ts +2 -2
- package/src/ai/openapi-components.ts +22 -6
- package/src/ai/relative-links.ts +42 -26
- package/src/ai/serializers.ts +2 -1
- package/src/ai/skills.ts +14 -1
- package/src/ai/tar.ts +139 -12
- package/src/analytics/head.ts +4 -0
- package/src/analytics/index.ts +5 -0
- package/src/analytics/one-dollar-stats.ts +86 -0
- package/src/analytics/schema.ts +2 -0
- package/src/astro/generate.ts +1 -1
- package/src/astro/include-hmr.ts +31 -12
- package/src/astro/include-refresh.ts +19 -8
- package/src/astro/integration.ts +108 -14
- package/src/astro/runtime-modules.ts +28 -13
- package/src/astro/templates.ts +81 -37
- package/src/audit/checks/links.ts +8 -1
- package/src/audit/graph.ts +3 -1
- package/src/audit/run.ts +12 -9
- package/src/audit/snapshot.ts +5 -0
- package/src/cli/args.ts +32 -0
- package/src/cli/commands/audit.ts +4 -1
- package/src/cli/commands/doctor.ts +3 -1
- package/src/cli/commands/eval.ts +19 -11
- package/src/cli/commands/translate.ts +9 -12
- package/src/cli/commands/validate.ts +3 -1
- package/src/cli/dev-lock.ts +157 -37
- package/src/cli/report-format.ts +11 -6
- package/src/components/colors.ts +19 -0
- package/src/components/content/Badge.astro +5 -3
- package/src/components/content/Component.astro +2 -2
- package/src/components/content/Tab.astro +0 -1
- package/src/components/content/Tabs.astro +4 -0
- package/src/components/content/badge-color.ts +5 -2
- package/src/components/content/base-href.ts +13 -36
- package/src/components/islands/assistant.tsx +21 -4
- package/src/components/islands/base-path.ts +47 -10
- package/src/components/islands/hooks.ts +35 -19
- package/src/components/islands/webmcp.ts +4 -2
- package/src/components/layout/Breadcrumbs.astro +5 -2
- package/src/components/layout/DiscoveryLinks.astro +9 -5
- package/src/components/layout/Header.astro +2 -2
- package/src/components/layout/LanguageSwitcher.astro +2 -2
- package/src/components/layout/NavSelector.astro +2 -2
- package/src/components/layout/NavTabMenu.astro +3 -3
- package/src/components/layout/NavTree.astro +16 -6
- package/src/components/layout/PageActions.astro +4 -3
- package/src/components/layout/PageLayout.astro +7 -6
- package/src/components/layout/Pagination.astro +3 -3
- package/src/components/layout/RootLayout.astro +5 -5
- package/src/components/layout/Search.astro +33 -13
- package/src/components/layout/VersionBanner.astro +2 -2
- package/src/components/layout/analytics-client.ts +12 -0
- package/src/components/layout/toc-active.ts +41 -0
- package/src/components/layout/toc-element.ts +8 -14
- package/src/components/openapi/ApiTagOperations.astro +2 -2
- package/src/components/openapi/AsyncApiOperation.astro +7 -4
- package/src/components/openapi/GraphqlChip.astro +2 -2
- package/src/components/openapi/Operation.astro +2 -0
- package/src/components/openapi/Playground.astro +4 -4
- package/src/components/openapi/RequestPanel.astro +5 -2
- package/src/components/openapi/SchemaTable.astro +7 -0
- package/src/components/openapi/async.ts +38 -6
- package/src/components/openapi/helpers.ts +19 -9
- package/src/components/openapi/message-composer.ts +6 -1
- package/src/components/openapi/message.ts +17 -2
- package/src/components/openapi/operation-model.ts +81 -11
- package/src/components/openapi/panel.ts +4 -2
- package/src/components/openapi/param-style.ts +181 -0
- package/src/components/openapi/playground-client.ts +73 -16
- package/src/components/openapi/request.ts +190 -26
- package/src/components/openapi/schema-tree.ts +30 -24
- package/src/components/openapi/snippets.ts +85 -6
- package/src/components/openapi/ws-client.ts +18 -2
- package/src/core/base-path.ts +62 -16
- package/src/core/config-input.ts +9 -7
- package/src/core/diagnostics.ts +2 -0
- package/src/core/directive-diagnostics.ts +99 -0
- package/src/core/frontmatter.ts +21 -18
- package/src/core/heading-markers.ts +5 -18
- package/src/core/i18n.ts +31 -3
- package/src/core/last-modified.ts +25 -3
- package/src/core/locale-links.ts +5 -1
- package/src/core/meta.ts +33 -19
- package/src/core/navigation.ts +28 -8
- package/src/core/project-graph.ts +27 -5
- package/src/core/schema.ts +24 -4
- package/src/core/sources/contentful-rich-text.ts +27 -20
- package/src/core/sources/filesystem.ts +20 -2
- package/src/core/sources/github-releases.ts +25 -6
- package/src/core/sources/lexical.ts +23 -18
- package/src/core/sources/lower.ts +201 -34
- package/src/core/sources/mdx-remote.ts +51 -16
- package/src/core/sources/normalize.ts +325 -90
- package/src/core/sources/notion.ts +52 -25
- package/src/core/sources/obsidian.ts +23 -6
- package/src/core/sources/portable-text.ts +38 -21
- package/src/core/sources/strapi-blocks.ts +20 -16
- package/src/core/sources/watch.ts +20 -7
- package/src/core/standard-schema.ts +10 -6
- package/src/core/version-cut.ts +52 -15
- package/src/deploy/artifacts.ts +27 -6
- package/src/deploy/cloudflare-negotiation.ts +4 -12
- package/src/deploy/headers.ts +8 -4
- package/src/deploy/node-headers.ts +1 -1
- package/src/deploy/platforms/cloudflare.ts +12 -3
- package/src/deploy/platforms/netlify.ts +1 -0
- package/src/deploy/platforms/node.ts +1 -0
- package/src/deploy/platforms/static.ts +1 -0
- package/src/deploy/platforms/types.ts +8 -0
- package/src/deploy/platforms/vercel.ts +1 -0
- package/src/deploy/redirects.ts +54 -18
- package/src/deploy/robots.ts +2 -2
- package/src/deploy/rss.ts +4 -3
- package/src/deploy/sitemap.ts +7 -5
- package/src/markdown/base-links.ts +34 -36
- package/src/markdown/directives.ts +242 -36
- package/src/markdown/features.ts +17 -0
- package/src/markdown/index.ts +7 -6
- package/src/markdown/mdast.ts +5 -2
- package/src/markdown/relative-links.ts +12 -4
- package/src/og/card.ts +129 -5
- package/src/og/derive.ts +41 -31
- package/src/og/index.ts +1 -0
- package/src/openapi/asyncapi.ts +4 -2
- package/src/openapi/model.ts +22 -14
- package/src/openapi/proxy.ts +63 -10
- package/src/openapi/render-mdx.ts +41 -2
- package/src/registry/eject.ts +181 -24
- package/src/search/adapters/version-scope.ts +30 -0
- package/src/search/documents.ts +67 -26
- package/src/search/popular.ts +2 -1
- package/src/seo/jsonld.ts +7 -3
- package/src/translate/meta.ts +68 -24
- package/src/translate/run.ts +3 -3
- package/src/translate/validate.ts +4 -1
- package/src/translate/work-list.ts +56 -17
- package/dist/cli/chunk-5r8g91qn.js.map +0 -34
- package/dist/cli/chunk-7mbqtmgb.js.map +0 -10
- package/dist/cli/chunk-ddndchfr.js.map +0 -14
- package/dist/cli/chunk-esh98wmb.js +0 -23
- package/dist/cli/chunk-esh98wmb.js.map +0 -10
- package/dist/cli/chunk-hr8ne106.js.map +0 -13
- package/dist/cli/chunk-j85scx15.js.map +0 -10
- package/dist/cli/chunk-pbg5a4s3.js.map +0 -19
- package/dist/cli/chunk-wdrt2k2v.js.map +0 -10
- /package/dist/cli/{chunk-6k4ftwze.js.map → chunk-1d7ve1dm.js.map} +0 -0
- /package/dist/cli/{chunk-zp79m0ts.js.map → chunk-2eytanqx.js.map} +0 -0
- /package/dist/cli/{chunk-g698a744.js.map → chunk-2z928egk.js.map} +0 -0
- /package/dist/cli/{chunk-mqc662a6.js.map → chunk-364znk6q.js.map} +0 -0
- /package/dist/cli/{chunk-k7pj68a8.js.map → chunk-5f86nr5m.js.map} +0 -0
- /package/dist/cli/{chunk-91ws1n6j.js.map → chunk-5xvm6tfj.js.map} +0 -0
- /package/dist/cli/{chunk-w4bxdvsa.js.map → chunk-6dsbexzp.js.map} +0 -0
- /package/dist/cli/{chunk-00gs3wqs.js.map → chunk-8ktnccpt.js.map} +0 -0
- /package/dist/cli/{chunk-bbnwccaz.js.map → chunk-a58773jm.js.map} +0 -0
- /package/dist/cli/{chunk-sqw4ekg1.js.map → chunk-b07cmahc.js.map} +0 -0
- /package/dist/cli/{chunk-4e9b9ra6.js.map → chunk-ey84smr6.js.map} +0 -0
- /package/dist/cli/{chunk-d1v5rhy0.js.map → chunk-g4hq16wv.js.map} +0 -0
- /package/dist/cli/{chunk-qkb5a8sa.js.map → chunk-pehfxfta.js.map} +0 -0
- /package/dist/cli/{chunk-fsmrqk8a.js.map → chunk-r20tn01b.js.map} +0 -0
- /package/dist/cli/{chunk-bfwp9vp6.js.map → chunk-r9rcc4w7.js.map} +0 -0
- /package/dist/cli/{chunk-h7k3nq3v.js.map → chunk-tkacnehg.js.map} +0 -0
- /package/dist/cli/{chunk-h2ez8dzb.js.map → chunk-tzmab476.js.map} +0 -0
package/src/deploy/headers.ts
CHANGED
|
@@ -200,6 +200,13 @@ export interface VercelHeader {
|
|
|
200
200
|
/** The characters `path-to-regexp` would read as syntax in a literal path. */
|
|
201
201
|
const SOURCE_SYNTAX = /[\\(){}?+:]/gu;
|
|
202
202
|
|
|
203
|
+
/**
|
|
204
|
+
* A literal path as `path-to-regexp` source text — what a `vercel.json`
|
|
205
|
+
* `source` is — with each syntax character escaped, so it matches only itself.
|
|
206
|
+
*/
|
|
207
|
+
export const escapeVercelSource = (path: string): string =>
|
|
208
|
+
path.replaceAll(SOURCE_SYNTAX, String.raw`\$&`);
|
|
209
|
+
|
|
203
210
|
/**
|
|
204
211
|
* A `_headers` path as a `vercel.json` `source`, which `path-to-regexp` reads:
|
|
205
212
|
* the literal parts are escaped, and a `*` glob becomes the `(.*)` group that
|
|
@@ -209,10 +216,7 @@ const SOURCE_SYNTAX = /[\\(){}?+:]/gu;
|
|
|
209
216
|
const vercelSource = (path: string): string => {
|
|
210
217
|
const trimmed =
|
|
211
218
|
path.length > 1 && path.endsWith("/") ? path.slice(0, -1) : path;
|
|
212
|
-
return trimmed
|
|
213
|
-
.split("*")
|
|
214
|
-
.map((part) => part.replaceAll(SOURCE_SYNTAX, String.raw`\$&`))
|
|
215
|
-
.join("(.*)");
|
|
219
|
+
return trimmed.split("*").map(escapeVercelSource).join("(.*)");
|
|
216
220
|
};
|
|
217
221
|
|
|
218
222
|
/**
|
|
@@ -124,7 +124,7 @@ export const nodeRedirects = (project: BlumeProject): NodeRedirects => {
|
|
|
124
124
|
)
|
|
125
125
|
);
|
|
126
126
|
return Object.fromEntries(
|
|
127
|
-
platformRedirects(
|
|
127
|
+
platformRedirects(project)
|
|
128
128
|
.filter((redirect) => !pages.has(normalizePath(redirect.from)))
|
|
129
129
|
.map((redirect) => [
|
|
130
130
|
normalizePath(redirect.from),
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
markdownRoutePaths,
|
|
12
12
|
markdownTokenCount,
|
|
13
13
|
} from "../../ai/markdown.ts";
|
|
14
|
+
import { normalizeBasePath } from "../../core/base-path.ts";
|
|
14
15
|
import type { BlumeProject } from "../../core/project-graph.ts";
|
|
15
16
|
import type { ProjectContext } from "../../core/types.ts";
|
|
16
17
|
import { CLOUDFLARE_ADAPTER_PACKAGE } from "../adapters/cloudflare.ts";
|
|
@@ -90,8 +91,13 @@ export const emitCloudflareNegotiation = async (
|
|
|
90
91
|
const home = rawMarkdown["/"];
|
|
91
92
|
// The 404 twins the wrapper may substitute for the HTML shell; only wired
|
|
92
93
|
// when the build actually emitted them (a project that owns `/404` gets
|
|
93
|
-
// neither), like the Vercel routing config.
|
|
94
|
-
|
|
94
|
+
// neither), like the Vercel routing config. The adapter writes the
|
|
95
|
+
// prerendered files under the deployment base (`dist/client/<base>/`),
|
|
96
|
+
// where the wrapper fetches them from.
|
|
97
|
+
const staticDir = join(
|
|
98
|
+
clientDir(context),
|
|
99
|
+
normalizeBasePath(config.deployment.options.base)
|
|
100
|
+
);
|
|
95
101
|
const injected = injectWorkerNegotiation(
|
|
96
102
|
await readFile(wranglerPath, "utf-8"),
|
|
97
103
|
{
|
|
@@ -119,7 +125,7 @@ export const emitCloudflareNegotiation = async (
|
|
|
119
125
|
// based the same way the platform files are — it answers any the
|
|
120
126
|
// worker-first rules claim, where `_redirects` is never consulted and
|
|
121
127
|
// Astro would default their status.
|
|
122
|
-
redirects: platformRedirects(
|
|
128
|
+
redirects: platformRedirects(project),
|
|
123
129
|
routePaths,
|
|
124
130
|
}
|
|
125
131
|
);
|
|
@@ -308,6 +314,9 @@ export const cloudflarePlatform: DeployPlatform = {
|
|
|
308
314
|
previewDeploy: null,
|
|
309
315
|
readsHeaderFiles: { server: true, static: true },
|
|
310
316
|
redirectFiles: [REDIRECTS_FILE],
|
|
317
|
+
// The adapter points `build.client` at `dist/client/<base>/` for a server
|
|
318
|
+
// build, and hoists its own `_headers`/`_redirects` back up to `dist/client`.
|
|
319
|
+
serverClientUnderBase: true,
|
|
311
320
|
serverOutputDir: distDir,
|
|
312
321
|
serverStaticDir: clientDir,
|
|
313
322
|
};
|
|
@@ -124,6 +124,7 @@ export const netlifyPlatform: DeployPlatform = {
|
|
|
124
124
|
// same rules into that tree's config.
|
|
125
125
|
readsHeaderFiles: { server: false, static: true },
|
|
126
126
|
redirectFiles: [NETLIFY_REDIRECTS_FILE],
|
|
127
|
+
serverClientUnderBase: false,
|
|
127
128
|
serverOutputDir: distDir,
|
|
128
129
|
serverStaticDir: distDir,
|
|
129
130
|
};
|
|
@@ -25,6 +25,7 @@ export const staticPlatform: DeployPlatform = {
|
|
|
25
25
|
previewDeploy: null,
|
|
26
26
|
readsHeaderFiles: { server: false, static: true },
|
|
27
27
|
redirectFiles: [REDIRECTS_FILE, VERCEL_JSON_FILE],
|
|
28
|
+
serverClientUnderBase: false,
|
|
28
29
|
serverOutputDir: distDir,
|
|
29
30
|
serverStaticDir: distDir,
|
|
30
31
|
};
|
|
@@ -112,6 +112,14 @@ export interface DeployPlatform {
|
|
|
112
112
|
readsHeaderFiles: Record<DeployOutput, boolean>;
|
|
113
113
|
/** The redirect files a static build writes, beside the manifest. */
|
|
114
114
|
redirectFiles: RedirectFile[];
|
|
115
|
+
/**
|
|
116
|
+
* Whether the adapter moves a server build's client output under
|
|
117
|
+
* `deployment.base` (`dist/client/<base>/`, the directory Astro then hands
|
|
118
|
+
* `astro:build:done`) while the platform keeps serving `serverStaticDir`
|
|
119
|
+
* as the assets root. The files the platform reads there, like `_headers`,
|
|
120
|
+
* belong above the directory Astro reports.
|
|
121
|
+
*/
|
|
122
|
+
serverClientUnderBase: boolean;
|
|
115
123
|
/** Where a server build's deploy bundle lands. */
|
|
116
124
|
serverOutputDir: (context: ProjectContext) => string;
|
|
117
125
|
/** The directory a server build serves as static files. */
|
|
@@ -194,6 +194,7 @@ export const vercelPlatform: DeployPlatform = {
|
|
|
194
194
|
previewDeploy: "vercel deploy",
|
|
195
195
|
readsHeaderFiles: { server: false, static: false },
|
|
196
196
|
redirectFiles: [VERCEL_JSON_FILE],
|
|
197
|
+
serverClientUnderBase: false,
|
|
197
198
|
serverOutputDir: buildOutputDir,
|
|
198
199
|
serverStaticDir: (context) => join(buildOutputDir(context), "static"),
|
|
199
200
|
};
|
package/src/deploy/redirects.ts
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
normalizeBasePath,
|
|
3
|
+
withAuthoredBasePath,
|
|
3
4
|
withBasePath,
|
|
4
5
|
withComposedBasePath,
|
|
5
6
|
} from "../core/base-path.ts";
|
|
7
|
+
import { routeSetFor, servesRoute } from "../core/locale-links.ts";
|
|
8
|
+
import type { RouteSet } from "../core/locale-links.ts";
|
|
9
|
+
import type { BlumeProject } from "../core/project-graph.ts";
|
|
6
10
|
import type { ResolvedConfig } from "../core/schema.ts";
|
|
11
|
+
import { escapeVercelSource } from "./headers.ts";
|
|
7
12
|
import type { VercelHeader } from "./headers.ts";
|
|
8
13
|
|
|
9
14
|
/**
|
|
@@ -16,6 +21,23 @@ import type { VercelHeader } from "./headers.ts";
|
|
|
16
21
|
|
|
17
22
|
type Redirect = ResolvedConfig["redirects"][number];
|
|
18
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Base a redirect destination authored as if mounted at root by what it
|
|
26
|
+
* names (see `withAuthoredBasePath`): a page gains the full
|
|
27
|
+
* `{deployment.base}{basePath}` stack, a public file (`/files/spec.pdf`, with
|
|
28
|
+
* no page in `routes`) only the deployment base — `public/` is served there
|
|
29
|
+
* whatever `basePath` is. External URLs pass through.
|
|
30
|
+
*/
|
|
31
|
+
const baseRedirectTarget = (
|
|
32
|
+
to: string,
|
|
33
|
+
basePath: string,
|
|
34
|
+
base: string,
|
|
35
|
+
routes: RouteSet
|
|
36
|
+
): string =>
|
|
37
|
+
withAuthoredBasePath(base, basePath, to, (route) =>
|
|
38
|
+
servesRoute(routes, route)
|
|
39
|
+
);
|
|
40
|
+
|
|
19
41
|
/**
|
|
20
42
|
* Base a redirect for Astro's `redirects` config, where the two sides are not
|
|
21
43
|
* symmetric:
|
|
@@ -23,19 +45,23 @@ type Redirect = ResolvedConfig["redirects"][number];
|
|
|
23
45
|
* - `from` gains only `basePath`. Astro builds the match pattern with
|
|
24
46
|
* `deployment.base` already applied (`getPattern(segments, config.base)`), so
|
|
25
47
|
* adding it here would serve the redirect at `{base}{base}/from`.
|
|
26
|
-
* - `to` gains the full `{deployment.base}{basePath}` stack
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
48
|
+
* - `to` gains the full `{deployment.base}{basePath}` stack, or the deployment
|
|
49
|
+
* base alone for a public file (see {@link baseRedirectTarget}). Astro
|
|
50
|
+
* resolves a destination that matches a known route by regenerating it from
|
|
51
|
+
* that route's segments, which carry no base — and passes an unmatched
|
|
52
|
+
* destination through verbatim. Neither path prepends `base`, so a
|
|
53
|
+
* root-relative `to` escapes the base entirely (withastro/astro#7774, still
|
|
54
|
+
* the behavior in Astro 7).
|
|
31
55
|
*
|
|
32
|
-
* Both sides are authored as if mounted at root;
|
|
33
|
-
*
|
|
56
|
+
* Both sides are authored as if mounted at root; `routes` (the served page
|
|
57
|
+
* routes, carrying `basePath`) tell a dotted page route like `/releases/v1.2`
|
|
58
|
+
* from a file. Idempotent, so a hand-written base isn't doubled.
|
|
34
59
|
*/
|
|
35
60
|
export const applyBaseToAstroRedirects = (
|
|
36
61
|
redirects: Redirect[],
|
|
37
62
|
basePath: string,
|
|
38
|
-
deployBase: string
|
|
63
|
+
deployBase: string,
|
|
64
|
+
routes: RouteSet
|
|
39
65
|
): Redirect[] => {
|
|
40
66
|
// `deployment.base` arrives as the user wrote it (Astro accepts `/base/`,
|
|
41
67
|
// even `base`); normalizing here keeps the composed paths well-formed.
|
|
@@ -44,27 +70,29 @@ export const applyBaseToAstroRedirects = (
|
|
|
44
70
|
? redirects.map((redirect) => ({
|
|
45
71
|
...redirect,
|
|
46
72
|
from: withBasePath(basePath, redirect.from),
|
|
47
|
-
to:
|
|
73
|
+
to: baseRedirectTarget(redirect.to, basePath, base, routes),
|
|
48
74
|
}))
|
|
49
75
|
: redirects;
|
|
50
76
|
};
|
|
51
77
|
|
|
52
78
|
/**
|
|
53
79
|
* Base a redirect for the host platform files below. Unlike Astro's config,
|
|
54
|
-
* these are matched against the real served URL, so
|
|
55
|
-
* `{deployment.base}{basePath}` stack
|
|
80
|
+
* these are matched against the real served URL, so `from` carries the full
|
|
81
|
+
* `{deployment.base}{basePath}` stack, and so does `to` unless it names a
|
|
82
|
+
* public file (see {@link baseRedirectTarget}).
|
|
56
83
|
*/
|
|
57
84
|
export const applyBaseToPlatformRedirects = (
|
|
58
85
|
redirects: Redirect[],
|
|
59
86
|
basePath: string,
|
|
60
|
-
deployBase: string
|
|
87
|
+
deployBase: string,
|
|
88
|
+
routes: RouteSet
|
|
61
89
|
): Redirect[] => {
|
|
62
90
|
const base = normalizeBasePath(deployBase);
|
|
63
91
|
return basePath || base
|
|
64
92
|
? redirects.map((redirect) => ({
|
|
65
93
|
...redirect,
|
|
66
94
|
from: withComposedBasePath(base, basePath, redirect.from),
|
|
67
|
-
to:
|
|
95
|
+
to: baseRedirectTarget(redirect.to, basePath, base, routes),
|
|
68
96
|
}))
|
|
69
97
|
: redirects;
|
|
70
98
|
};
|
|
@@ -75,12 +103,18 @@ export const applyBaseToPlatformRedirects = (
|
|
|
75
103
|
* share: the emitted redirect files and the Cloudflare worker-first redirect
|
|
76
104
|
* exemptions both compare these paths against real served URLs.
|
|
77
105
|
*/
|
|
78
|
-
export const platformRedirects = (
|
|
79
|
-
|
|
106
|
+
export const platformRedirects = (project: {
|
|
107
|
+
config: ResolvedConfig;
|
|
108
|
+
manifest: Pick<BlumeProject["manifest"], "routes">;
|
|
109
|
+
}): Redirect[] => {
|
|
110
|
+
const { config } = project;
|
|
111
|
+
return applyBaseToPlatformRedirects(
|
|
80
112
|
config.redirects,
|
|
81
113
|
config.basePath,
|
|
82
|
-
config.deployment.options.base ?? ""
|
|
114
|
+
config.deployment.options.base ?? "",
|
|
115
|
+
routeSetFor(project.manifest.routes)
|
|
83
116
|
);
|
|
117
|
+
};
|
|
84
118
|
|
|
85
119
|
/**
|
|
86
120
|
* `_redirects` text (Netlify + Cloudflare Pages): `from to status` per line.
|
|
@@ -110,7 +144,9 @@ interface VercelConfig {
|
|
|
110
144
|
* header rules are given (see `buildVercelHeaders`). Uses `statusCode` (Vercel's
|
|
111
145
|
* alternative to the boolean `permanent`) so the configured code ships exactly:
|
|
112
146
|
* `permanent` would silently coerce a 301 to 308 and a 302 to 307, diverging
|
|
113
|
-
* from the `_redirects` file, which preserves exact codes.
|
|
147
|
+
* from the `_redirects` file, which preserves exact codes. A `source` is a
|
|
148
|
+
* `path-to-regexp` pattern, so each exact `from` path is escaped: unescaped,
|
|
149
|
+
* `/c++-guide` fails the whole config and `/faq(old)` never matches.
|
|
114
150
|
*/
|
|
115
151
|
export const buildVercelConfig = (
|
|
116
152
|
redirects: Redirect[],
|
|
@@ -119,7 +155,7 @@ export const buildVercelConfig = (
|
|
|
119
155
|
const config: VercelConfig = {
|
|
120
156
|
redirects: redirects.map((redirect) => ({
|
|
121
157
|
destination: redirect.to,
|
|
122
|
-
source: redirect.from,
|
|
158
|
+
source: escapeVercelSource(redirect.from),
|
|
123
159
|
statusCode: redirect.status,
|
|
124
160
|
})),
|
|
125
161
|
};
|
package/src/deploy/robots.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { mountBasePath, normalizeBasePath } from "../core/base-path.ts";
|
|
2
2
|
import type { BlumeProject } from "../core/project-graph.ts";
|
|
3
3
|
import type { ContentSignalPolicy, ContentSignals } from "../core/schema.ts";
|
|
4
4
|
import { absoluteUrl } from "../core/site-url.ts";
|
|
@@ -49,7 +49,7 @@ export const buildRobots = (project: BlumeProject): string | null => {
|
|
|
49
49
|
|
|
50
50
|
const { site } = config.deployment.options;
|
|
51
51
|
if (site && config.seo.sitemap) {
|
|
52
|
-
const sitemapPath =
|
|
52
|
+
const sitemapPath = mountBasePath(
|
|
53
53
|
normalizeBasePath(config.deployment.options.base),
|
|
54
54
|
"/sitemap.xml"
|
|
55
55
|
);
|
package/src/deploy/rss.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// `'` reference rather than `'`, which XML accepts equally.
|
|
3
3
|
import { escape as escapeXml } from "html-escaper";
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { mountBasePath, normalizeBasePath } from "../core/base-path.ts";
|
|
6
6
|
import type { BlumeProject } from "../core/project-graph.ts";
|
|
7
7
|
import { siteRoot } from "../core/site-url.ts";
|
|
8
8
|
import type { PageRecord } from "../core/types.ts";
|
|
@@ -57,7 +57,8 @@ export const buildRssFeeds = (project: BlumeProject): RssFeed[] => {
|
|
|
57
57
|
return [];
|
|
58
58
|
}
|
|
59
59
|
const base = siteRoot(site);
|
|
60
|
-
// Routes carry `basePath`; a `deployment.base` subdirectory is
|
|
60
|
+
// Routes carry `basePath`; a `deployment.base` subdirectory is mounted on top
|
|
61
|
+
// of every one, even a route whose first segment matches it (see sitemap.ts).
|
|
61
62
|
// The feed's own `link`/self URL points at the docs root under that base, while
|
|
62
63
|
// `path` stays base-less (it's also the on-disk output location).
|
|
63
64
|
const deployBase = normalizeBasePath(config.deployment.options.base);
|
|
@@ -80,7 +81,7 @@ export const buildRssFeeds = (project: BlumeProject): RssFeed[] => {
|
|
|
80
81
|
description: page.description,
|
|
81
82
|
// Encode like the sitemap does: a route with spaces or non-ASCII
|
|
82
83
|
// must still yield a valid <link>/<guid> URL after XML decoding.
|
|
83
|
-
link: encodeURI(`${base}${
|
|
84
|
+
link: encodeURI(`${base}${mountBasePath(deployBase, page.route)}`),
|
|
84
85
|
title: page.title,
|
|
85
86
|
}))
|
|
86
87
|
.toSorted((a, b) => (b.date?.getTime() ?? 0) - (a.date?.getTime() ?? 0))
|
package/src/deploy/sitemap.ts
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
discoverPagesSync,
|
|
7
7
|
hasGeneratedChangelog,
|
|
8
8
|
} from "../astro/pages.ts";
|
|
9
|
-
import {
|
|
9
|
+
import { mountBasePath, normalizeBasePath } from "../core/base-path.ts";
|
|
10
10
|
import type { BlumeProject } from "../core/project-graph.ts";
|
|
11
11
|
import { siteRoot } from "../core/site-url.ts";
|
|
12
12
|
|
|
@@ -78,7 +78,9 @@ export const buildSitemapFiles = (
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
const base = siteRoot(site);
|
|
81
|
-
// Routes carry `basePath`; a `deployment.base` subdirectory is
|
|
81
|
+
// Routes carry `basePath`; a `deployment.base` subdirectory is mounted on
|
|
82
|
+
// top of every one, even a route whose first segment matches it: under base
|
|
83
|
+
// `/guides`, `guides/setup.md` is served at `/guides/guides/setup`.
|
|
82
84
|
const deployBase = normalizeBasePath(project.config.deployment.options.base);
|
|
83
85
|
|
|
84
86
|
// Archived-version pages leave the sitemap when the version is noindexed,
|
|
@@ -135,7 +137,7 @@ export const buildSitemapFiles = (
|
|
|
135
137
|
) {
|
|
136
138
|
continue;
|
|
137
139
|
}
|
|
138
|
-
pushUrl(
|
|
140
|
+
pushUrl(mountBasePath(deployBase, page.route), page.lastModified);
|
|
139
141
|
}
|
|
140
142
|
// Custom `.astro` pages and the generated changelog index mount outside
|
|
141
143
|
// `basePath` (they're injected at their pattern — see `blumeIntegration`), so
|
|
@@ -150,7 +152,7 @@ export const buildSitemapFiles = (
|
|
|
150
152
|
extraRoutes.push("/changelog");
|
|
151
153
|
}
|
|
152
154
|
for (const route of extraRoutes) {
|
|
153
|
-
pushUrl(
|
|
155
|
+
pushUrl(mountBasePath(deployBase, route));
|
|
154
156
|
}
|
|
155
157
|
urls.sort();
|
|
156
158
|
|
|
@@ -169,7 +171,7 @@ export const buildSitemapFiles = (
|
|
|
169
171
|
// Chunks sit next to sitemap.xml, so their URLs layer the same deployment
|
|
170
172
|
// base robots.txt uses for the index.
|
|
171
173
|
const loc = escapeXml(
|
|
172
|
-
encodeURI(`${base}${
|
|
174
|
+
encodeURI(`${base}${mountBasePath(deployBase, `/${name}`)}`)
|
|
173
175
|
);
|
|
174
176
|
references.push(` <sitemap><loc>${loc}</loc></sitemap>`);
|
|
175
177
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
isInternalPath,
|
|
3
|
+
withAuthoredBasePath,
|
|
3
4
|
withBasePath,
|
|
4
|
-
withComposedBasePath,
|
|
5
5
|
} from "../core/base-path.ts";
|
|
6
6
|
import { servesRoute } from "../core/locale-links.ts";
|
|
7
7
|
import type { MdastNode } from "./mdast.ts";
|
|
@@ -20,18 +20,6 @@ interface MdastUrlContext {
|
|
|
20
20
|
setProperty: (node: MdastNode, key: "url", value: string) => void;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
/**
|
|
24
|
-
* A path whose final segment carries a file extension (`/spec.pdf`, `/logo.svg`)
|
|
25
|
-
* — a public asset, which Blume serves from `public/` at the site root and
|
|
26
|
-
* does *not* move under `basePath`, unless a page is served there: a dotted
|
|
27
|
-
* route (`/releases/v1.2`) is based like any other page link, the same
|
|
28
|
-
* served-route test the link checker and the locale rewrite apply.
|
|
29
|
-
*/
|
|
30
|
-
const DOTTED_PATH = /\.[a-z0-9]+$/iu;
|
|
31
|
-
|
|
32
|
-
/** Strip any `#fragment`/`?query` so only the path is extension-tested. */
|
|
33
|
-
const pathOf = (url: string): string => url.replace(/[#?].*$/u, "");
|
|
34
|
-
|
|
35
23
|
/** Only a string URL can be rebased; MDAST allows null or absent urls. */
|
|
36
24
|
const isUrl = (url: string | null | undefined): url is string =>
|
|
37
25
|
typeof url === "string";
|
|
@@ -45,13 +33,15 @@ export interface BaseLinksPluginOptions {
|
|
|
45
33
|
}
|
|
46
34
|
|
|
47
35
|
/**
|
|
48
|
-
* Satteri MDAST plugin that prepends the served-URL base
|
|
49
|
-
*
|
|
50
|
-
* (`[x](/guide)`
|
|
51
|
-
*
|
|
52
|
-
* `/
|
|
53
|
-
*
|
|
54
|
-
* (
|
|
36
|
+
* Satteri MDAST plugin that prepends the served-URL base to root-relative
|
|
37
|
+
* internal links, so authors write links as if mounted at root. A page link
|
|
38
|
+
* gains `deployment.base` layered over the site-wide `basePath` (`[x](/guide)`
|
|
39
|
+
* -> `/base/docs/guide`); a public asset — an image, or a link to a file like
|
|
40
|
+
* `/spec.pdf` (see `withAuthoredBasePath`) — gains `deployment.base` alone,
|
|
41
|
+
* since Astro serves `public/` under it but never under `basePath`.
|
|
42
|
+
* Idempotent per layer (a hand-written `/docs/x` isn't double-prefixed) and
|
|
43
|
+
* inert for external URLs, fragments, and relative paths. Only constructed
|
|
44
|
+
* when a base is set (see `markdown/index.ts`).
|
|
55
45
|
*/
|
|
56
46
|
export const baseLinksPlugin = (
|
|
57
47
|
deployBase: string,
|
|
@@ -78,30 +68,38 @@ export const baseLinksPlugin = (
|
|
|
78
68
|
return cached.routes;
|
|
79
69
|
};
|
|
80
70
|
|
|
81
|
-
/** Whether
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
return (
|
|
85
|
-
!DOTTED_PATH.test(path) ||
|
|
86
|
-
servesRoute(servedRoutes(), withBasePath(basePath, path))
|
|
87
|
-
);
|
|
88
|
-
};
|
|
71
|
+
/** Whether a page is served at a based, fragment-less path. */
|
|
72
|
+
const servesPage = (route: string): boolean =>
|
|
73
|
+
servesRoute(servedRoutes(), route);
|
|
89
74
|
|
|
90
|
-
|
|
75
|
+
/** Record `based(url)` on the node when its root-relative URL changes. */
|
|
76
|
+
const rebase = (
|
|
77
|
+
node: UrlNode,
|
|
78
|
+
ctx: MdastUrlContext,
|
|
79
|
+
based: (url: string) => string
|
|
80
|
+
): void => {
|
|
91
81
|
const { url } = node;
|
|
92
|
-
if (isUrl(url) && isInternalPath(url)
|
|
93
|
-
const next =
|
|
82
|
+
if (isUrl(url) && isInternalPath(url)) {
|
|
83
|
+
const next = based(url);
|
|
94
84
|
if (next !== url) {
|
|
95
85
|
ctx.setProperty(node, "url", next);
|
|
96
86
|
}
|
|
97
87
|
}
|
|
98
88
|
};
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
89
|
+
const rebaseLink = (node: UrlNode, ctx: MdastUrlContext): void =>
|
|
90
|
+
rebase(node, ctx, (url) =>
|
|
91
|
+
withAuthoredBasePath(deployBase, basePath, url, servesPage)
|
|
92
|
+
);
|
|
93
|
+
// An image is always a file, never a page: `public/` (or a generated asset
|
|
94
|
+
// endpoint), served under the deployment base but not `basePath`.
|
|
95
|
+
const rebaseImage = (node: UrlNode, ctx: MdastUrlContext): void =>
|
|
96
|
+
rebase(node, ctx, (url) => withBasePath(deployBase, url));
|
|
97
|
+
// `link` covers inline links; `definition` covers reference-style
|
|
98
|
+
// definitions (`[x]: /guide`), which a link or an image may cite.
|
|
102
99
|
return {
|
|
103
|
-
definition:
|
|
104
|
-
|
|
100
|
+
definition: rebaseLink,
|
|
101
|
+
image: rebaseImage,
|
|
102
|
+
link: rebaseLink,
|
|
105
103
|
name: "blume-base-links",
|
|
106
104
|
};
|
|
107
105
|
};
|