77 lines
5.2 KiB
JavaScript
77 lines
5.2 KiB
JavaScript
#!/usr/bin/env node
|
|
// /income (LG-1, 7 October 2026): docs/analysis/income-tiers.md published as a site page. The document is the generator's
|
|
// output (tools/launch/income-tiers.mjs, checked on the gate); this script turns its tables and tier lines into
|
|
// site/income.html inside the site's shell markers, so the page and the document cannot disagree. Labels on every
|
|
// number, no price of IGN anywhere (the page says so). node tools/launch/income-page.mjs [--check]
|
|
import { readFileSync, writeFileSync } from 'node:fs';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { dirname, join } from 'node:path';
|
|
const here = dirname(fileURLToPath(import.meta.url)), root = join(here, '../..');
|
|
const md = readFileSync(join(root, 'docs/analysis/income-tiers.md'), 'utf8');
|
|
const esc = (s) => s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
const inline = (s) => esc(s).replace(/\*\*(.+?)\*\*/g, '<b>$1</b>').replace(/`(.+?)`/g, '<code>$1</code>');
|
|
const section = (title) => { const i = md.indexOf('## ' + title); if (i < 0) throw new Error('section missing: ' + title); const j = md.indexOf('\n## ', i + 3); return md.slice(i, j < 0 ? md.length : j); };
|
|
const table = (text) => { const rows = text.split('\n').filter((l) => l.startsWith('|')); if (rows.length < 3) return ''; const cells = (l) => l.slice(1, -1).split('|').map((c) => c.trim()); const head = cells(rows[0]); const body = rows.slice(2).map(cells); return '<div class="tbl"><table><thead><tr>' + head.map((h, i) => `<th${i ? ' class="num"' : ''}>${inline(h)}</th>`).join('') + '</tr></thead><tbody>' + body.map((r) => '<tr>' + r.map((c, i) => `<td${i >= 2 ? ' class="num"' : ''}>${inline(c)}</td>`).join('') + '</tr>').join('') + '</tbody></table></div>'; };
|
|
const paras = (text) => text.split('\n').filter((l) => l.trim() && !l.startsWith('|') && !l.startsWith('#') && !l.startsWith('- ')).map((l) => `<p>${inline(l)}</p>`).join('\n');
|
|
const bullets = (text) => { const items = text.split('\n').filter((l) => l.startsWith('- ')); return items.length ? '<ul class="tiers">' + items.map((l) => `<li>${inline(l.slice(2))}</li>`).join('\n') + '</ul>' : ''; };
|
|
const sched = section('The schedule the figures use'), cards = section('The cards, measured'), ign = section('IGN a day per card, solo, after the ramp (day 90)'), elec = section('Electricity a day, and the electricity cost of one mined IGN'), means = section('What it means per tier, and what is being done'), owed = section('Owed');
|
|
const generated = /Generated by[^\n]*/.exec(md)?.[0] || '';
|
|
const html = `<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
|
<title>Mining income per tier</title>
|
|
<meta name="description" content="What one card, a rig and a pool user mine on Igneum: IGN a day at three network sizes from measured cards, and the electricity a day at three tariffs. Labels on every number, no price of IGN.">
|
|
<link rel="canonical" href="https://igneum.network/income">
|
|
<!-- head:start -->
|
|
<!-- head:end -->
|
|
</head>
|
|
<body>
|
|
<!-- nav:start -->
|
|
<!-- nav:end -->
|
|
<main id="main">
|
|
<section class="page-hero"><div class="container">
|
|
<div class="breadcrumb"><a href="/">Igneum</a><span>/</span><span>Income per tier</span></div>
|
|
<h1>What a card mines, by tier.</h1>
|
|
<p class="lead">One 8, 12, 16, 24 or 32 GB card, a rig and a pool user: IGN a day at three network sizes from measured cards, and the electricity a day at three tariffs. Every number carries its label. No price of IGN appears here: nobody sells IGN and the project quotes none.</p>
|
|
</div></section>
|
|
<section class="section"><div class="container">
|
|
<h2>The schedule the figures use</h2>
|
|
${paras(sched)}
|
|
${table(sched)}
|
|
</div></section>
|
|
<section class="section"><div class="container">
|
|
<h2>The cards, measured</h2>
|
|
${paras(cards)}
|
|
${table(cards)}
|
|
</div></section>
|
|
<section class="section"><div class="container">
|
|
<h2>IGN a day per card, solo, after the ramp (day 90)</h2>
|
|
${paras(ign)}
|
|
${table(ign)}
|
|
</div></section>
|
|
<section class="section"><div class="container">
|
|
<h2>Electricity a day, and the electricity cost of one mined IGN</h2>
|
|
${paras(elec)}
|
|
${table(elec)}
|
|
</div></section>
|
|
<section class="section"><div class="container">
|
|
<h2>What it means per tier, and what is being done</h2>
|
|
${bullets(means)}
|
|
</div></section>
|
|
<section class="section compact"><div class="container">
|
|
<h2>Owed</h2>
|
|
${bullets(owed)}
|
|
<p class="note">${inline(generated)}</p>
|
|
</div></section>
|
|
</main>
|
|
<!-- footer:start -->
|
|
<!-- footer:end -->
|
|
</body>
|
|
</html>
|
|
`;
|
|
const out = join(root, 'site/income.html');
|
|
// --check: the build decorates card names with the GPU marks; the words are what the document owns, so they are compared bare
|
|
if (process.argv.includes('--check')) { let cur = ''; try { cur = readFileSync(out, 'utf8'); } catch (e) { } const main = (s) => { const m = /<main id="main">[\s\S]*?<\/main>/.exec(s); return (m ? m[0] : '').replace(/<span class="cardname">(?:<div class="badge[\s\S]*?<\/div>)([\s\S]*?)<\/span>/g, '$1'); }; if (main(cur) !== main(html)) { console.error('site/income.html is not the generator\'s output: node tools/launch/income-page.mjs'); process.exit(1); } console.log('income page: equals the document'); }
|
|
else { writeFileSync(out, html); console.log('wrote site/income.html'); }
|