ontoref-outreach/scripts/decks/new-deck.nu
2026-07-17 01:00:32 +01:00

131 lines
5.9 KiB
Text

#!/usr/bin/env nu
# Register a new Slidev deck as a site page.
#
# The projection REPLACES a page's body and PRESERVES its frontmatter — so the page has to exist
# first, and it must carry title, slug, thumbnail and og_image. That is the one thing the
# projection cannot invent, and the one step nobody remembers: without it, `just decks` fails with
# "la página de destino no tiene frontmatter; no la sobrescribo a ciegas".
#
# This scaffolds those pages and prints the decks.ncl entry to add. It deliberately does NOT edit
# decks.ncl: that file is the manifest a human curates (which deck, which slugs, which language),
# and a script that rewrites a manifest is a script that decides what should be published.
#
# Usage (from outreach/):
# nu scripts/decks/new-deck.nu rustikon.md rustikon-why-i-needed-rust \
# --title-es "Rustikon: por qué necesité Rust" --slug-es rustikon-por-que-necesite-rust \
# --title-en "Rustikon: Why I Needed Rust" --slug-en rustikon-why-i-needed-rust
def outreach [] { $env.FILE_PWD | path dirname | path dirname }
def main [
source: string # the Slidev md, relative to outreach/presentation/
id: string # the deck id: file name of the pages, and the asset dir
--title-es: string = "" # omite el par title/slug de un idioma y ese idioma NO tendrá página
--slug-es: string = ""
--title-en: string = ""
--slug-en: string = ""
--source-en: string = "" # fuente PROPIA de la página EN cuando el deck es bilingüe nativo
# (dos charlas reales, no una traducción): p. ej. rustikon.md (EN)
# + rustikon_es.md (ES). Omítelo si una sola fuente sirve ambos idiomas.
--date: string = "" # ISO; defaults to today
--author: string = "Jesús Pérez"
] {
let src = [(outreach) "presentation" $source] | path join
if not ($src | path exists) { error make { msg: $"no existe el deck: ($src)" } }
# A separate EN source must exist and must actually differ — a page's `source` equal to the
# deck's is just noise in the manifest, and a non-existent one fails the projection later, so
# catch both here where the message can name the file.
if (not ($source_en | is-empty)) {
let esrc = [(outreach) "presentation" $source_en] | path join
if not ($esrc | path exists) { error make { msg: $"no existe la fuente EN: ($esrc)" } }
if ($source_en == $source) { error make { msg: "--source-en es igual a la fuente del deck; omítelo" } }
}
let when = if ($date | is-empty) { (date now | format date "%Y-%m-%d") } else { $date }
# `source` per page: EN uses --source-en when given, otherwise the deck's own. Only a source
# that DIFFERS from the deck default is emitted in the manifest below.
let pages = [
{ lang: "es", title: $title_es, slug: $slug_es, source: $source }
{ lang: "en", title: $title_en, slug: $slug_en, source: (if ($source_en | is-empty) { $source } else { $source_en }) }
]
# A language with no title/slug gets NO page. A deck that exists only in Spanish must not have
# an English URL serving Spanish slides: a page that lies about its language is worse than a
# page that does not exist.
let wanted = ($pages | where {|p| not (($p.title | is-empty) or ($p.slug | is-empty)) })
if ($wanted | is-empty) { error make { msg: "necesitas al menos --title-es/--slug-es o --title-en/--slug-en" } }
# The URL category is localized (decks.ncl `category`): ES → presentaciones, EN → decks. The
# loader resolves the body by {lang}/{category}/{id}.md, so BOTH the directory and the
# frontmatter category must be the localized value, or the ES page 404s.
let cat = (^nickel export --format json ([(outreach) "scripts" "decks" "decks.ncl"] | path join)
| from json | get category)
for p in $wanted {
let category = ($cat | get $p.lang)
let dir = [(outreach) "site" "site" "content" "resources" $p.lang $category] | path join
let file = [$dir $"($id).md"] | path join
if ($file | path exists) {
print $"(ansi yellow)ya existe, no lo toco: ($file)(ansi reset)"
continue
}
mkdir $dir
# Only the frontmatter. The body is the projection's, and it is fenced by its own markers —
# anything written here below the frontmatter would be overwritten on the first `just decks`.
$"---
id: \"($id)\"
title: \"($p.title)\"
slug: \"($p.slug)\"
subtitle: \"\"
excerpt: \"\"
author: \"($author)\"
date: \"($when)\"
published: true
featured: false
category: \"($category)\"
tags: [\"ontoref\", \"slides\"]
# Card + share image. The webp carousel is no longer the CONTENT — the slides carry real text
# now — but slide 0 still makes the best thumbnail.
thumbnail: \"/images/decks/($id)/0.webp\"
image_url: \"/images/decks/($id)/0.webp\"
og_image: \"/images/og/decks-($id)-0.png\"
sort_order: 1
---
" | save --force $file
print $"(ansi green)creada:(ansi reset) ($file)"
}
# Emit `source = …` per page ONLY where it differs from the deck default — that is exactly the
# bilingual-native case (rustikon.md + rustikon_es.md), and it is the line deck-new used to make
# you add by hand.
let entries = ($wanted | each {|p|
if ($p.source == $source) {
$" { lang = \"($p.lang)\", slug = \"($p.slug)\" },"
} else {
$" { lang = \"($p.lang)\", slug = \"($p.slug)\", source = \"($p.source)\" },"
}
} | str join "\n")
print $"
(ansi cyan)Añade esta entrada a scripts/decks/decks.ncl(ansi reset) — el manifiesto lo curas tú,
no un script: qué deck se publica, con qué slug y en qué idioma es una decisión, no un derivado.
{
source = \"($source)\",
id = \"($id)\",
pages = [
($entries)
],
},
(ansi cyan)Y luego:(ansi reset)
just decks # build + captura + proyección + puertas de artefacto
just decks-served # las puertas sobre la PÁGINA servida
"
}