Skip to content

[Feature]: content metadata validation #59

Description

@NTBBloodbath

Issues

  • I have checked existing issues and there no existing ones with the same request.

Feature description

Sometimes we may enforce certain metadata fields in a path of our content, for example we add categories to the posts. However, an about page in our blog doesn't need to have categories so we can safely skip said field there.

One could forget about adding a metadata field in different scenarios. Due to this possibility, we may want to have schemas to validate the metadata fields before building our sites for production environments.

For example, we could add schemas directly to the norgolith.toml file like this:

# Sets global required metadata fields
[content_schema]
required = ["title", "created_at"]

# Sets properties for the metadata fields
[content_schema.fields]
title = { type = "string", max_length = 100 }
created_at = { type = "date", format = "iso8601" }

# '/posts' norg files should have categories in their metadata
[content_schema.posts]
required = ["categories"] # this will also inherit the global required metadata fields

[content_schema.posts.fields]
categories = { type = "array", items = { type = "string" }, min_items = 1 }

# '/posts' also require a `publish_date` if they are not `draft`s
[content_schema.posts.rules]
if = { draft = false }
then = { required = ["publish_date"] } # Again, this gets merged

# '/posts/this-week-in-norgolith' posts should contain a "norgolith" category
#
# this 'categories' will get merged with the '/posts' specs as well
[content_schema.posts.this_week_in_norgolith.fields.categories]
must_contain = ["norgolith"]

# '/about' should have a long description
[content_schema.about]
required = ["description"] # this will also inherit the global required metadata fields

[content_schema.about.fields]
description = { type = "string", max_length = 200 }

The validation (using the validator crate) workflow would be the following:

  1. Resolve schema: for each .norg file, determine which schema applies based on its path.
  2. Validate metadata:
  • Check required fields.
  • Validate field types and constraints.
  1. Error reporting:
  • Highlight mismatches (e.g. "posts/hello.norg is missing required field categories").
  • Suggest fixes (e.g. "Add categories: [rust] to metadata").

Note

The validation process should throw an Error on lith build and throw a Warning on lith serve.

Error message example (it could probably not look exactly like this on the final implementation):

[build] Error: Validation failed for 'content/posts/2025-02-14.norg'
  → Missing required field: 'categories'
  → Schema applied: 'posts/*.norg'
  → Required fields: title, date, categories

Tip

Additionally, the lith new command could receive a --schema option to scaffold .norg files with valid metadata pre-filled (e.g. lith new --schema=posts hello.norg.


Now it's time for a small FAQ I can think of about the idea:

Why schemas matter for a static site?

  • Consistency: enforce metadata conventions across teams, organizations or communities.
  • Safety: catch typos (e.g. taags instead of tags) early, and prevent them from reaching production.
  • Documentation: schemas act as living docs for content authors and themes.

Ok, but why tie schemas to paths?

By tying schemas to content paths, Norgolith becomes opinionated about structure without sacrificing flexibility, which is ideal for users who value Norg's format formalism but want guardrails for large sites. This also opens the door to seamless integration with plugins and themes later.

Help

Yes

Implementation help

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions