///|
fn post_layout() -> String {
  (
    #|
    #|
    #|
    #|  
    #|  {{title}}
    #|
    #|
    #|  
#|

{{title}}

#|

{{date}}

#| {{categories}} #|
{{content}}
#| {{tags}} #|
#| #| ) } ///| fn index_layout() -> String { ( #| #| #| #| #| {{title}} #| #| #|

{{title}}

#| {{posts}} #| {{pagination}} #| #| ) } ///| fn tag_layout() -> String { ( #| #| #|Tag {{tag}} #|

Tag {{tag}}

{{posts}} #| ) } ///| fn category_layout() -> String { ( #| #| #|Category {{category}} #|

Category {{category}}

{{posts}} #| ) } ///| fn page_source( title : String, date : String, tags : String, categories : String, body : String, ) -> String { "---\n" + "title: " + title + "\n" + "date: " + date + "\n" + "tags: [" + tags + "]\n" + "categories: [" + categories + "]\n" + "---\n" + body } ///| fn draft_source() -> String { ( #|--- #|title: Future Draft #|date: 2026-08-01 #|draft: true #|tags: [Draft] #|categories: [Planning] #|--- #|# Future Draft #| #|This draft should only appear when explicitly requested. ) } ///| fn showcase_sources() -> Array[(String, String)] { [ ( "getting-started.md", page_source( "Getting Started", "2026-07-01", "MoonBit, Guide", "Docs", "# Getting Started\n\nInstall the toolchain and run `moon test`.", ), ), ( "cli-reference.md", page_source( "CLI Reference", "2026-07-02", "CLI, Tooling", "Reference", "# CLI Reference\n\nUse `moon run src/main -- --help` for options.", ), ), ( "markdown-pipeline.md", page_source( "Markdown Pipeline", "2026-07-03", "Markdown, Parser", "Architecture", "# Markdown Pipeline\n\nThe parser emits blocks, events, and HTML.", ), ), ( "security-hardening.md", page_source( "Security Hardening", "2026-07-04", "Security, HTML", "Hardening", "# Security\n\nUnsafe links such as [bad](javascript:alert(1)) are blocked.", ), ), ( "taxonomy-pages.md", page_source( "Taxonomy Pages", "2026-07-05", "Tags, Categories", "Features", "# Taxonomy\n\nTags and categories generate listing pages.", ), ), ( "rss-search-sitemap.md", page_source( "RSS Search Sitemap", "2026-07-06", "RSS, Search, Sitemap", "Features", "# Feeds\n\nRSS, search JSON, and sitemap XML are generated together.", ), ), ( "pagination.md", page_source( "Pagination", "2026-07-07", "Pagination, UX", "Features", "# Pagination\n\nIndex pages split posts using `posts_per_page`.", ), ), ( "moonbit-targets.md", page_source( "MoonBit Targets", "2026-07-08", "MoonBit, CI", "Tooling", "# Targets\n\nThe project is checked against all MoonBit backends.", ), ), ( "release-process.md", page_source( "Release Process", "2026-07-09", "Release, Mooncakes", "Governance", "# Release\n\nRun checks before publishing to Mooncakes.", ), ), ("future-draft.md", draft_source()), ] } ///| fn config() -> @ssg.Config { @ssg.Config::make( "Moon SSG Showcase", "A complete in-memory example site", "https://example.com/showcase", 3, "content", "layouts", "public", "static", ) } ///| fn generate_showcase(include_drafts : Bool) -> Array[@ssg.GeneratedPage] { @ssg.generate_site( showcase_sources(), post_layout(), index_layout(), tag_layout(), category_layout(), config(), include_drafts, ) } ///| fn find_page(pages : Array[@ssg.GeneratedPage], path : String) -> String { for page in pages { if page.path == path { return page.html } } abort("missing page: " + path) } ///| fn assert_contains(label : String, text : String, expected : String) -> Unit { if !text.contains(expected) { abort(label + " missing expected fragment: " + expected) } } ///| fn assert_not_contains(label : String, text : String, blocked : String) -> Unit { if text.contains(blocked) { abort(label + " contained blocked fragment: " + blocked) } } ///| fn assert_page_exists(pages : Array[@ssg.GeneratedPage], path : String) -> Unit { ignore(find_page(pages, path)) } ///| fn assert_page_missing( pages : Array[@ssg.GeneratedPage], path : String, ) -> Unit { for page in pages { if page.path == path { abort("unexpected page: " + path) } } } ///| fn validate_core_pages(pages : Array[@ssg.GeneratedPage]) -> Unit { assert_page_exists(pages, "getting-started.html") assert_page_exists(pages, "cli-reference.html") assert_page_exists(pages, "markdown-pipeline.html") assert_page_exists(pages, "security-hardening.html") assert_page_exists(pages, "taxonomy-pages.html") assert_page_exists(pages, "rss-search-sitemap.html") assert_page_exists(pages, "pagination.html") assert_page_exists(pages, "moonbit-targets.html") assert_page_exists(pages, "release-process.html") assert_page_exists(pages, "index.html") assert_page_exists(pages, "index2.html") assert_page_exists(pages, "index3.html") assert_page_exists(pages, "rss.xml") assert_page_exists(pages, "search.json") assert_page_exists(pages, "sitemap.xml") } ///| fn validate_taxonomies(pages : Array[@ssg.GeneratedPage]) -> Unit { assert_page_exists(pages, "tag-moonbit.html") assert_page_exists(pages, "tag-guide.html") assert_page_exists(pages, "tag-cli.html") assert_page_exists(pages, "tag-tooling.html") assert_page_exists(pages, "tag-markdown.html") assert_page_exists(pages, "tag-parser.html") assert_page_exists(pages, "tag-security.html") assert_page_exists(pages, "tag-html.html") assert_page_exists(pages, "tag-tags.html") assert_page_exists(pages, "tag-categories.html") assert_page_exists(pages, "tag-rss.html") assert_page_exists(pages, "tag-search.html") assert_page_exists(pages, "tag-sitemap.html") assert_page_exists(pages, "tag-pagination.html") assert_page_exists(pages, "tag-ux.html") assert_page_exists(pages, "tag-ci.html") assert_page_exists(pages, "tag-release.html") assert_page_exists(pages, "tag-mooncakes.html") assert_page_exists(pages, "category-docs.html") assert_page_exists(pages, "category-reference.html") assert_page_exists(pages, "category-architecture.html") assert_page_exists(pages, "category-hardening.html") assert_page_exists(pages, "category-features.html") assert_page_exists(pages, "category-tooling.html") assert_page_exists(pages, "category-governance.html") } ///| fn validate_security_output(pages : Array[@ssg.GeneratedPage]) -> Unit { let security = find_page(pages, "security-hardening.html") assert_contains("security page", security, "href=\"#unsafe-url\"") assert_not_contains("security page", security, "href=\"javascript:") } ///| fn validate_indexes(pages : Array[@ssg.GeneratedPage]) -> Unit { let index1 = find_page(pages, "index.html") let index2 = find_page(pages, "index2.html") let index3 = find_page(pages, "index3.html") assert_contains("index1", index1, "release-process.html") assert_contains("index1", index1, "moonbit-targets.html") assert_contains("index1", index1, "pagination.html") assert_contains("index2", index2, "rss-search-sitemap.html") assert_contains("index2", index2, "taxonomy-pages.html") assert_contains("index2", index2, "security-hardening.html") assert_contains("index3", index3, "markdown-pipeline.html") assert_contains("index3", index3, "cli-reference.html") assert_contains("index3", index3, "getting-started.html") } ///| fn validate_machine_indexes(pages : Array[@ssg.GeneratedPage]) -> Unit { let rss = find_page(pages, "rss.xml") let search = find_page(pages, "search.json") let sitemap = find_page(pages, "sitemap.xml") assert_contains( "rss", rss, "https://example.com/showcase/release-process.html", ) assert_contains("rss", rss, "moon-ssg") assert_contains("search", search, "\"title\": \"Release Process\"") assert_contains("search", search, "\"url\": \"release-process.html\"") assert_contains( "sitemap", sitemap, "https://example.com/showcase/release-process.html", ) assert_not_contains("sitemap", sitemap, "rss.xml") assert_not_contains("sitemap", sitemap, "search.json") } ///| fn validate_without_drafts(pages : Array[@ssg.GeneratedPage]) -> Unit { assert_page_missing(pages, "future-draft.html") let search = find_page(pages, "search.json") assert_not_contains("search", search, "Future Draft") } ///| fn validate_with_drafts(pages : Array[@ssg.GeneratedPage]) -> Unit { assert_page_exists(pages, "future-draft.html") let draft = find_page(pages, "future-draft.html") assert_contains("draft", draft, "This draft should only appear") } ///| fn report_pages(pages : Array[@ssg.GeneratedPage]) -> Unit { println("showcase generated pages: " + pages.length().to_string()) println("sample post: release-process.html") println("sample taxonomy: tag-mooncakes.html") println("machine indexes: rss.xml, search.json, sitemap.xml") } ///| fn main { let pages = generate_showcase(false) validate_core_pages(pages) validate_taxonomies(pages) validate_security_output(pages) validate_indexes(pages) validate_machine_indexes(pages) validate_without_drafts(pages) let draft_pages = generate_showcase(true) validate_with_drafts(draft_pages) report_pages(pages) }