Repository navigation
Expand file tree
/
Copy pathbuild.mjs
More file actions
91 lines (85 loc) · 4.98 KB
/
Copy pathbuild.mjs
File metadata and controls
91 lines (85 loc) · 4.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#!/usr/bin/env node
/* Build StagePlot: assembla i moduli sorgente negli artefatti di deploy.
*
* Output:
* app/index.html → shell leggera (HTML + CSS + versione + <script defer src="/app.js"> + <script async src="/icons.js">)
* app.js → tutto il JS dell'app (i blocchi <script data-app> del template, concatenati) — caricato DEFER
* biglietteria/pianta-posti.js → copia di src/pianta-posti.js (lo stesso testo è dentro app.js) per l'area della
* biglietteria: una sola fonte per la foto della sala (specifica area §4.2)
* (icons.js e' un asset statico a se', non generato qui: libreria icone caricata ASYNC)
*
* NB: l'editor vive su /app/, la root e' la landing di prodotto (index.html, scritta a mano, NON generata qui).
* Gli asset dell'app restano in radice (/app.js, /icons.js, /vendor/…) e sono referenziati con path assoluti,
* quindi il livello di annidamento della shell non conta.
*
* Perche': local-first + deploy semplice su GitHub Pages, ma per l'LCP la shell HTML deve restare piccola e
* dipingere PRIMA di eseguire ~1,4 MB di JS. Lo sviluppo resta modulare (template + src/), lo script ricompone.
*
* Uso: node build.mjs → genera app/index.html + app.js da index.template.html + src/*
* node build.mjs --check → verifica che app/index.html e app.js siano allineati ai sorgenti (CI/pre-merge)
*/
import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
import { fileURLToPath } from "node:url";
import { dirname, join } from "node:path";
const root = dirname(fileURLToPath(import.meta.url));
const r = (p) => join(root, p);
/* marcatore -> file sorgente da iniettare (inline). Aggiungere qui i moduli futuri. */
const INJECTIONS = [
{ marker: "/*__STAGEPLOT_STYLES__*/", file: "src/styles.css" },
/* la pianta dei posti: dentro l'editor E, uguale, in biglietteria/pianta-posti.js (specifica area §4.2) */
{ marker: "/*__PIANTA_POSTI__*/", file: "src/pianta-posti.js" },
];
const PIANTA_PUBBLICA = "biglietteria/pianta-posti.js";
function piantaPubblica() {
return "/* GENERATO da src/pianta-posti.js con `node build.mjs`: NON modificare questo file.\n" +
" Lo stesso testo è dentro l'editor (app.js): una sola fonte per la foto della sala (specifica area §4.2). */\n" +
readFileSync(r("src/pianta-posti.js"), "utf8");
}
function build() {
let out = readFileSync(r("index.template.html"), "utf8");
for (const { marker, file } of INJECTIONS) {
if (!out.includes(marker)) throw new Error(`Marcatore mancante nel template: ${marker}`);
const content = readFileSync(r(file), "utf8");
out = out.replace(marker, () => content); // replacer fn: nessun carattere speciale ($&, $1) interpretato
}
const version = new Date().toISOString().slice(0, 10).replace(/-/g, "."); // YYYY.MM.DD
if (!out.includes("/*__APP_VERSION__*/")) throw new Error("Marcatore mancante: /*__APP_VERSION__*/");
out = out.replaceAll("/*__APP_VERSION__*/", version);
/* Estrai i blocchi <script data-app> nel bundle app.js (defer). Sono NON contigui nel template
(in mezzo c'e' HTML: pannello SEO, feedback box) → il PRIMO diventa <script defer src="/app.js">,
gli altri spariscono, l'HTML in mezzo resta. Ordine di concatenazione = ordine nel documento. */
const appParts = [];
out = out.replace(/<script data-app>([\s\S]*?)<\/script>/g, (m, code) => {
appParts.push(code);
return appParts.length === 1 ? '<script defer src="/app.js"></script>' : "";
});
if (appParts.length === 0) throw new Error("Nessun blocco <script data-app> nel template (marcatura persa?)");
const appjs = appParts.join("\n;\n");
return { html: out, appjs };
}
const check = process.argv.includes("--check");
const { html, appjs } = build();
const stripVer = (s) => s.replace(/window\.__APP_VERSION__="[^"]*"/g, 'window.__APP_VERSION__="__VER__"');
if (check) {
let curHtml = "__MISSING__";
try { curHtml = readFileSync(r("app/index.html"), "utf8"); } catch (e) { /* shell mancante → disallineata */ }
let curApp = "__MISSING__";
try { curApp = readFileSync(r("app.js"), "utf8"); } catch (e) { /* app.js mancante → disallineato */ }
if (stripVer(curHtml) !== stripVer(html) || stripVer(curApp) !== stripVer(appjs)) {
console.error("✗ app/index.html o app.js NON allineati ai sorgenti. Esegui: node build.mjs");
process.exit(1);
}
let curPianta = "__MISSING__";
try { curPianta = readFileSync(r(PIANTA_PUBBLICA), "utf8"); } catch (e) { /* mancante → disallineata */ }
if (curPianta !== piantaPubblica()) {
console.error("✗ " + PIANTA_PUBBLICA + " NON allineato a src/pianta-posti.js. Esegui: node build.mjs");
process.exit(1);
}
console.log("✓ app/index.html + app.js + biglietteria/pianta-posti.js allineati ai sorgenti.");
} else {
mkdirSync(r("app"), { recursive: true });
writeFileSync(r("app/index.html"), html);
writeFileSync(r("app.js"), appjs);
writeFileSync(r(PIANTA_PUBBLICA), piantaPubblica());
console.log("✓ app/index.html + app.js + biglietteria/pianta-posti.js generati dai sorgenti.");
}