/* smoke.js — esegue DAVVERO lo script di una pagina, con un DOM finto. * * Perche' serve: `node --check` valida la SINTASSI e non tocca l'esecuzione. Ho pubblicato una * pagina che passava il controllo di sintassi e moriva alla prima riga utile, perche' le chiavi * dei pesi arrivavano da Python come "0.0"/"1.0" e la pagina le ricostruiva con String(0) -> "0". * Un test che non esegue non vede niente di tutto cio'. * * node smoke.js pagina.html [...] */ const fs = require("fs"); function makeEl(tag) { const el = { tagName: tag, _html: "", textContent: "", value: "", dataset: {}, style: new Proxy({}, { get: () => "", set: () => true }), children: [], attributes: {}, min: "0", max: "1000000", setAttribute(k, v) { this.attributes[k] = v; }, getAttribute(k) { return this.attributes[k]; }, appendChild(c) { this.children.push(c); return c; }, addEventListener() {}, removeEventListener() {}, getBoundingClientRect() { return { left: 0, top: 0, width: 100, height: 40, right: 100, bottom: 40 }; }, insertAdjacentHTML(pos, html) { this._html += html; }, querySelectorAll() { return { forEach() {} }; }, querySelector() { return makeEl("div"); }, contains() { return false; }, focus() {} }; Object.defineProperty(el, "innerHTML", { get() { return this._html; }, set(v) { if (v === undefined || String(v).includes("undefined")) el._undef = true; this._html = String(v); } }); return el; } const known = new Map(); global.document = { _byId: known, getElementById(id) { if (!known.has(id)) known.set(id, makeEl("div")); return known.get(id); }, createElementNS(ns, tag) { return makeEl(tag); }, createElement(tag) { return makeEl(tag); }, querySelectorAll() { return { forEach() {} }; }, addEventListener() {} }; global.window = { innerWidth: 1400, innerHeight: 900, addEventListener() {} }; global.requestAnimationFrame = fn => { /* una sola passata: basta per vedere se esplode */ }; global.setTimeout = (fn, ms) => { try { fn(); } catch (e) { throw e; } return 0; }; global.clearTimeout = () => {}; let bad = 0; for (const file of process.argv.slice(2)) { known.clear(); const html = fs.readFileSync(file, "utf8"); const blocchi = [...html.matchAll(/]*)?>([\s\S]*?)<\/script>/g)]; /* i blocchi type="application/json" sono DATI: il DOM finto deve restituirli */ for (const m of blocchi) { const tagAttr = m[0].slice(0, m[0].indexOf(">")); const idm = tagAttr.match(/id="([^"]+)"/); if (tagAttr.includes("application/json") && idm) { const el = document.getElementById(idm[1]); el.textContent = m[1]; } } const code = blocchi.filter(m => !m[0].includes("application/json")).map(m => m[1]).join("\n"); try { new Function(code)(); /* un id mai riempito, o riempito con "undefined", e' un difetto silenzioso */ const vuoti = [...known.entries()].filter(([, e]) => e._undef); if (vuoti.length) { console.log("⚠ " + file + " — gira, ma scrive 'undefined' in: " + vuoti.map(([k]) => k).join(", ")); bad++; } else { console.log("✓ " + file + " — esegue senza errori (" + known.size + " elementi toccati)"); } } catch (e) { console.log("✗ " + file + " — " + e.constructor.name + ": " + e.message); bad++; } } process.exit(bad ? 1 : 0);