125 lines
5.6 KiB
Text
125 lines
5.6 KiB
Text
#!/usr/bin/env nu
|
|
|
|
# Project the Slidev decks onto the site as real HTML — text, not pixels.
|
|
#
|
|
# The site's deck pages used to be a hand-written carousel of exported PNGs: the words of
|
|
# every slide were an IMAGE of words. Nothing indexed them, no glossary tooltip could
|
|
# reach them, no screen reader read them, nobody could select or translate them, and the
|
|
# page drifted from the source the moment a slide changed.
|
|
#
|
|
# The deck is NOT re-parsed. Slidev's markdown is not standard markdown, and its meaning
|
|
# does not live in the markdown anyway — it lives in a compiled bundle of UnoCSS
|
|
# utilities, theme rules and <style scoped> blocks. Re-implementing that is re-implementing
|
|
# a renderer. So Slidev renders, and we capture what it produced (its /print route, the
|
|
# same one `slidev export` drives) and re-home it in the site.
|
|
#
|
|
# Usage:
|
|
# nu scripts/decks/gen-deck-pages.nu # build + project + gate
|
|
# nu scripts/decks/gen-deck-pages.nu --dry-run # project nothing, report
|
|
# nu scripts/decks/gen-deck-pages.nu check --url <page> # gate a served page
|
|
|
|
def outreach [] { $env.FILE_PWD | path dirname | path dirname }
|
|
|
|
def presentation [] { [(outreach) "presentation"] | path join }
|
|
|
|
def manifest [] {
|
|
let f = [(outreach) "scripts" "decks" "decks.ncl"] | path join
|
|
^nickel export --format json $f | from json
|
|
}
|
|
|
|
# The deck→artifact keys, from lib/variants.mjs — the one authority on the rule. This file used to
|
|
# mirror it by hand ("the variant key mirrors lib/project.mjs"), and a mirror is a copy that drifts:
|
|
# a THIRD copy in site/justfile never learned that a bilingual deck is one source per language, and
|
|
# gated an asset dir that does not exist. Ask, do not re-derive.
|
|
def variants [] {
|
|
let script = [(outreach) "scripts" "decks" "lib" "variants.mjs"] | path join
|
|
^node $script | from json
|
|
}
|
|
|
|
# ONE dist PER DECK. `slidev build` builds a single entry, so a shared dist/ means the second
|
|
# deck silently captures the FIRST one's slides — the projection would be wrong and nothing
|
|
# would say so. The deck's own build dir is what makes this a pipeline instead of a one-off.
|
|
#
|
|
# --force rebuilds even if the dist exists: the whole point is that the page follows the source,
|
|
# and a stale dist is a page that quietly stopped following it.
|
|
def ensure-build [id: string, source: string, --force] {
|
|
let dest = [(presentation) "builds" $id] | path join
|
|
let built = [$dest "index.html"] | path join
|
|
let src = [(presentation) $source] | path join
|
|
|
|
# Rebuild when the SOURCE is newer than the build. Without this, editing a slide and running
|
|
# `just decks` silently captured the stale build — the whole point of the projection is that the
|
|
# page follows the source, so a build older than its source is a page that stopped following it.
|
|
# --force rebuilds regardless (e.g. after a Slidev/theme upgrade the source mtime did not move).
|
|
let stale = if ($built | path exists) {
|
|
(($src | path exists) and (($src | path expand | path type) == "file")
|
|
and (((ls $src).0.modified) > ((ls $built).0.modified)))
|
|
} else { true }
|
|
|
|
if not $force and not $stale { return }
|
|
|
|
print $"(ansi cyan)slidev build ($source) → builds/($id)(ansi reset)"
|
|
cd (presentation)
|
|
# `slidev build` IGNORES --output (it is an `export` flag; on `build` it is accepted and does
|
|
# nothing). Measured: it wrote to dist/ in both flag positions. So build where it insists, then
|
|
# move. Fighting the CLI here would be fragile; moving a directory is not.
|
|
rm -rf dist
|
|
^./node_modules/.bin/slidev build $source
|
|
if not ("dist/index.html" | path exists) {
|
|
error make { msg: $"slidev build no produjo dist/ para ($source)" }
|
|
}
|
|
mkdir builds
|
|
rm -rf $dest
|
|
mv dist $dest
|
|
}
|
|
|
|
def main [--dry-run, --force] {
|
|
# One build per SOURCE, not per deck: a bilingual deck is two sources, and sharing a build is
|
|
# how an English URL ends up serving Spanish slides.
|
|
for v in (variants) {
|
|
ensure-build $v.key $v.source --force=$force
|
|
}
|
|
let flags = if $dry_run { ["--dry-run"] } else { [] }
|
|
let script = [(outreach) "scripts" "decks" "lib" "project.mjs"] | path join
|
|
|
|
cd (presentation)
|
|
let out = (^node $script ...$flags | complete)
|
|
if $out.exit_code != 0 {
|
|
print $out.stdout
|
|
print $"(ansi red)($out.stderr)(ansi reset)"
|
|
error make { msg: "la proyección del deck falló" }
|
|
}
|
|
let report = ($out.stdout | from json)
|
|
print ($report.decks | select id slides css assets fuentes | table)
|
|
|
|
if $dry_run { return }
|
|
|
|
for d in $report.decks {
|
|
check-artifact $d.id
|
|
}
|
|
print $"(ansi green)decks proyectados.(ansi reset) Verifica la PÁGINA servida con: nu scripts/decks/gen-deck-pages.nu check --url <url>"
|
|
}
|
|
|
|
# Gate the CSS artifact: containment, fidelity, no inbound leak, glossary decoration.
|
|
def check-artifact [id: string] {
|
|
let script = [(outreach) "scripts" "decks" "gates" "artifact.mjs"] | path join
|
|
cd (presentation)
|
|
let r = (^node $script $id | complete)
|
|
print $r.stdout
|
|
if $r.exit_code != 0 { error make { msg: $"puertas del artefacto FALLAN para ($id)" } }
|
|
}
|
|
|
|
# Gate the page as served. Verifying the file proves nothing: the site's content pipeline
|
|
# sits between this generator and the reader.
|
|
def "main check" [--url: string] {
|
|
if ($url | is-empty) { error make { msg: "check necesita --url <url-de-la-pagina>" } }
|
|
let script = [(outreach) "scripts" "decks" "gates" "served.mjs"] | path join
|
|
cd (presentation)
|
|
let r = (^node $script $url | complete)
|
|
print $r.stdout
|
|
if $r.exit_code != 0 {
|
|
print $"(ansi red)($r.stderr)(ansi reset)"
|
|
error make { msg: "puertas de la página servida FALLAN" }
|
|
}
|
|
print $"(ansi green)puertas de la página servida: OK(ansi reset)"
|
|
}
|