eureka-web/gallery/build.js
eureka cf5fbe58bd Объединить сайт и галерею в один репозиторий
Галерея переехала из eureka-gallery в gallery/ и стала страницей сайта
(/gallery/). Ссылки у неё относительные, в подкаталоге работает без правок
контента.

- gallery/: works.json, works/, build.js, work/*.html, assets/
- build.js: убран about.html (рассказ обо мне один — на главной сайта),
  «обо мне» и бренд ведут на /, футер ссылается на eureka-web
- index.html: ссылки на /gallery/
- README: новая структура и история
2026-09-20 14:08:04 +00:00

113 lines
No EOL
3.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env node
/**
* build.js — собрать статический сайт галереи из works.json.
*
* Никаких зависимостей: Node 24, стандартная библиотека. Читает works.json и
* works/*, пишет index.html, work/<slug>.html, about.html в корень.
*
* Запуск:
* node build.js
*
* Так добавление работы — это правка works.json + файл в works/, затем build.
*/
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
const ROOT = path.dirname(fileURLToPath(import.meta.url));
const data = JSON.parse(fs.readFileSync(path.join(ROOT, "works.json"), "utf8"));
function esc(s) {
return String(s)
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;");
}
function page(title, body, { base = "" } = {}) {
return `<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>${esc(title)}</title>
<meta name="description" content="${esc(data.site.intro)}">
<link rel="stylesheet" href="${base}assets/style.css">
</head>
<body>
<header class="top">
<a class="brand" href="/">Эврика<span> · галерея</span></a>
<nav>
<a href="/">обо мне</a>
</nav>
</header>
<main>
${body}
</main>
<footer class="foot">
<span>Эврика — ${new Date().getFullYear()}</span>
<a href="https://lab.athuk.space/eureka/eureka-web">исходник</a>
</footer>
</body>
</html>`;
}
function workCard(w, base = "") {
return ` <a class="card" href="${base}work/${w.slug}.html">
<img src="${base}works/${w.file}" alt="${esc(w.title)}" loading="lazy">
<span class="card-title">${esc(w.title)}</span>
</a>`;
}
function buildIndex() {
const cards = data.works.map((w) => workCard(w)).join("\n");
const body = `
<h1 class="sr-only">Галерея Эврики</h1>
<p class="lede">${esc(data.site.intro)}</p>
<section class="grid">
${cards}
</section>`;
fs.writeFileSync(path.join(ROOT, "index.html"), page(`Галерея Эврики`, body));
}
function buildWorks() {
const dir = path.join(ROOT, "work");
fs.mkdirSync(dir, { recursive: true });
const all = data.works;
for (let i = 0; i < all.length; i++) {
const w = all[i];
const prev = all[i - 1];
const next = all[i + 1];
const nav = [];
if (prev) nav.push(`<a class="pager" href="${prev.slug}.html">← ${esc(prev.title)}</a>`);
else nav.push(`<span></span>`);
if (next) nav.push(`<a class="pager" href="${next.slug}.html">${esc(next.title)} →</a>`);
else nav.push(`<span></span>`);
const body = `
<article class="work">
<figure>
<a class="zoom" href="../works/${w.file}" target="_blank" rel="noopener" title="Открыть в полном размере">
<img src="../works/${w.file}" alt="${esc(w.title)}">
</a>
</figure>
<div class="meta">
<h1>${esc(w.title)}</h1>
<p class="when">${esc(w.date)} · ${esc(w.method)}</p>
<p class="note">${esc(w.note)}</p>
<p class="full"><a href="../works/${w.file}" target="_blank" rel="noopener">открыть в полном размере →</a></p>
</div>
</article>
<nav class="worknav">
${nav.join("\n")}
</nav>`;
fs.writeFileSync(path.join(dir, `${w.slug}.html`),
page(`${w.title} — Эврика`, body, { base: "../" }));
}
}
buildIndex();
buildWorks();
console.log(`built: index.html, ${data.works.length} works`);