From 400b4a236e7cbaa7f250d4a4ec7e81218158f2df Mon Sep 17 00:00:00 2001 From: Jordan Gillman Date: Thu, 15 Oct 2026 03:28:36 +0000 Subject: [PATCH] Document metadata for source-backed WordPress posts --- content/(dynamic)/content-sync.mdx | 37 ++++++++++++++++++++++++++++++ content/(dynamic)/meta.ts | 2 +- content/(dynamic)/wordpress.mdx | 2 ++ content/(dynamic)/zero-runtime.mdx | 4 ++++ scripts/verify-routes.mjs | 1 + 5 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 content/(dynamic)/content-sync.mdx diff --git a/content/(dynamic)/content-sync.mdx b/content/(dynamic)/content-sync.mdx new file mode 100644 index 0000000..5f2eec3 --- /dev/null +++ b/content/(dynamic)/content-sync.mdx @@ -0,0 +1,37 @@ +--- +title: Content source metadata +description: Set the title, publication date, slug, and status of a source-backed WordPress document. +--- + +When Content management is enabled for your team, Markdown and HTML files bound to WordPress documents can carry their own metadata. Use the document header below to preserve titles and publication dates during a migration. + +## Document metadata + +Put a single-line JSON object inside a `spacefast:document` HTML comment at the very start of the file, before its body. Do not put a blank line or other text before the comment. + +```markdown content/posts/first-post.md + + +The post body starts here. +``` + +The same header works before an HTML document body. It stores WordPress fields separately from the editable content; it is not a visible heading or a page layout. + +| Field | Required | Meaning | +| --- | --- | --- | +| `version` | Yes | Must be `1`. | +| `title` | Yes | The WordPress document title, up to 2,000 UTF-8 bytes. | +| `slug` | Yes | The WordPress document slug, up to 200 UTF-8 bytes. | +| `status` | Yes | `draft`, `pending`, `publish`, `private`, `future`, or `trash`. | +| `dateGmt` | No | Publication time in UTC, such as `2026-01-15T09:30:00Z`. Required when `status` is `future`. | +| `componentSource` | No | The original component path recorded by a source conversion. Preserve it when it is already present. | + +`dateGmt` must end in `Z`. Fractional seconds are accepted but stored to whole seconds. Do not add other keys to the JSON object. + +## Editing and migration + +WordPress editor saves write the metadata header back to the source file alongside the body. Preserve that header when you edit the file locally. Its JSON keys may be reordered when the source is saved. + +Without this header, the source does not set the document's metadata. A new document gets a title derived from its filename or binding slug. Existing WordPress metadata is retained during source adoption. + +YAML front matter is not read as document metadata. A `title:` or `date:` field between `---` lines does not set the WordPress title or publication date. Replace that front matter with the JSON header before publishing source-backed documents. Otherwise a migrated post's original date is not supplied to WordPress, which can change date-based query ordering. diff --git a/content/(dynamic)/meta.ts b/content/(dynamic)/meta.ts index d0c68f8..5c178b5 100644 --- a/content/(dynamic)/meta.ts +++ b/content/(dynamic)/meta.ts @@ -4,5 +4,5 @@ export default defineMeta({ title: "Dynamic", collapsed: false, order: 5, - pages: ["zero-runtime", "functions", "database", "environment-variables", "crons", "object-storage", "logs", "wordpress"], + pages: ["zero-runtime", "content-sync", "functions", "database", "environment-variables", "crons", "object-storage", "logs", "wordpress"], }); diff --git a/content/(dynamic)/wordpress.mdx b/content/(dynamic)/wordpress.mdx index ac6799f..3369f45 100644 --- a/content/(dynamic)/wordpress.mdx +++ b/content/(dynamic)/wordpress.mdx @@ -7,6 +7,8 @@ Every Space includes a WordPress install behind its published files. Reach it th Your published files serve in front of WordPress, so a static Space never touches it and never pays for it. It is there because it is what makes a Space more than a bucket: [Zero](/zero-runtime) apps run on it, [Storage](/object-storage) is its media library, and [Database](/database) is its MySQL. This page covers the two ways to reach that WordPress directly. +For source-backed WordPress documents, see [content source metadata](/content-sync) to set titles, publication dates, and status from Markdown or HTML. + ## `sf wp` `sf wp` is the escape hatch under every named surface: whatever WP-CLI can do that Spacefast has not given its own command. diff --git a/content/(dynamic)/zero-runtime.mdx b/content/(dynamic)/zero-runtime.mdx index 572cf1c..647d7bb 100644 --- a/content/(dynamic)/zero-runtime.mdx +++ b/content/(dynamic)/zero-runtime.mdx @@ -255,6 +255,10 @@ export default capsule({ Call them from the client with `useAction("invite")`. `scope: "/"` covers the whole space; `/docs` covers one subtree. Both routes return the Person in `{ data }`. The invite link itself only ever travels by email. +## Document metadata + +For Markdown or HTML files bound to WordPress documents, set titles and publication dates with [content source metadata](/content-sync). YAML front matter does not set those fields. + ## Styling Write Tailwind classes in your JSX. Zero compiles them at publish, so do not add a CSS, PostCSS, or Tailwind pipeline. `@plugin` and `@config` directives are rejected, and `theme.json` plus utility classes are the whole styling story. diff --git a/scripts/verify-routes.mjs b/scripts/verify-routes.mjs index 5d49e86..b99221a 100644 --- a/scripts/verify-routes.mjs +++ b/scripts/verify-routes.mjs @@ -143,6 +143,7 @@ const representativePages = [ ["errors/rate_limited/index.html", `${docsRoot}/errors/rate_limited`, "rate_limited"], ["recipes/vite/index.html", `${docsRoot}/recipes/vite`, "Vite"], ["zero-runtime/index.html", `${docsRoot}/zero-runtime`, "What Zero is"], + ["content-sync/index.html", `${docsRoot}/content-sync`, "Document metadata"], ]; for (const page of representativePages) { await requirePage(...page);