Some checks are pending
build-gate / build (push) Waiting to run
Driver 2026-09-21: 'a folder called deployment which will store the capacitor
folder, wordpress folder and tauri folder', and 'a folder called documentation
to store the contributing folder so docs are in one place'.
docs/ + contributing/ + CONTRIBUTING.md -> documentation/ (three doc homes into
one). capacitor/ + wordpress/ + src-tauri/ -> deployment/.
The public/ -> dist/ rename the Driver asked about is NOT here: after seeing the
measured blast radius (855 tracked files, 54 source refs, and public/ is the
served web-root four consumers point at) the Driver withdrew it - 'keep the
public folder the same'.
Path references were rewritten mechanically (258 across 87 files), but three
classes needed hand fixes because a prefix regex cannot see them:
- join(ROOT,'docs',...) in 10 internal scripts - a quoted SEGMENT, not a prefix
- tauri frontendDist '../public' -> '../../public', since src-tauri is now one
level deeper; check:tauri caught this exactly as the card predicted
- the WordPress contract test's dirname(__DIR__,2) -> 3, same reason
Also reverted one over-reach: the rewrite renamed a FINDING-CATEGORY label
'docs' in public-hygiene-check.mjs, which is a rule name in its own header, not
a path.
Verified: full build ALL GUARDS PASS, SRI consistent; check:tauri, check:mobile
(cap sync + webDir OK), check:wp-embed, quoted-scripts, capability-citations,
capability-exemplars, library-index, public-hygiene, parse and drop all pass.
120 lines
5.7 KiB
JavaScript
120 lines
5.7 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* generate-library-index.mjs — builds documentation/reference-library-index.md (the auto-generated
|
|
* companion to the hand-written documentation/reference.md) from the LIVE library/ code.
|
|
*
|
|
* WHY: a programmer (or their agent) building a new app-profile needs to know
|
|
* WHICH reusable pieces the library offers, so it can CHOOSE what to import
|
|
* instead of rebuilding. This scans library/ and emits a one-line-per-item
|
|
* catalog with the import path. Regenerate any time the library changes:
|
|
*
|
|
* node internal/generate-library-index.mjs
|
|
*
|
|
* The index is DERIVED, never hand-edited — so it can't drift from the code.
|
|
*/
|
|
import { readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
import { join, relative, basename } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const ROOT = join(fileURLToPath(new URL('.', import.meta.url)), '..');
|
|
const LIB = join(ROOT, 'library');
|
|
|
|
/** Pull the first meaningful doc line from a file's header comment. */
|
|
function firstDocLine(file) {
|
|
let text;
|
|
try { text = readFileSync(file, 'utf8'); } catch { return ''; }
|
|
const lines = text.split('\n').slice(0, 16);
|
|
for (let raw of lines) {
|
|
let l = raw.trim();
|
|
if (!l) continue;
|
|
// structural markers carry no description — skip them entirely
|
|
if (/^<template[>\s]/.test(l) || /^<\/?script/.test(l) || l === '<!--' || l === '-->') continue;
|
|
if (/^<div|^<span|^<button/.test(l)) return ''; // hit real markup → no header comment
|
|
// strip comment markers (JS block/line, HTML comment, leading stars)
|
|
l = l.replace(/^<!--+/, '').replace(/--+>$/, '')
|
|
.replace(/^\/\*+/, '').replace(/\*+\/$/, '')
|
|
.replace(/^\/\/+/, '').replace(/^\*+/, '').trim();
|
|
if (!l || l === '/' || l.startsWith('import') || l.startsWith('export') ||
|
|
l.startsWith('const ') || l.startsWith('<')) continue;
|
|
if (l.length > 3) return l.replace(/`/g, '').slice(0, 110);
|
|
}
|
|
return '';
|
|
}
|
|
|
|
/** List files (optionally recursive) matching a predicate, relative to LIB. */
|
|
function listFiles(dir, { recursive = false, ext = null } = {}) {
|
|
const out = [];
|
|
function walk(d) {
|
|
let entries;
|
|
try { entries = readdirSync(d); } catch { return; }
|
|
for (const name of entries.sort()) {
|
|
const full = join(d, name);
|
|
const st = statSync(full);
|
|
if (st.isDirectory()) { if (recursive) walk(full); continue; }
|
|
if (name.startsWith('_') || name === 'README.md') continue;
|
|
if (ext && !name.endsWith(ext)) continue;
|
|
out.push(full);
|
|
}
|
|
}
|
|
walk(dir);
|
|
return out;
|
|
}
|
|
|
|
function section(title, dir, opts, importBase) {
|
|
const files = listFiles(join(LIB, dir), opts);
|
|
if (!files.length) return '';
|
|
let md = `\n### ${title}\n\n`;
|
|
for (const f of files) {
|
|
const rel = relative(join(LIB, dir), f);
|
|
const name = basename(f).replace(/\.(vue|js|json)$/, '');
|
|
const doc = firstDocLine(f);
|
|
const imp = `${importBase}/${rel}`;
|
|
md += `- **${name}** — ${doc || '(no description)'} \n \`import … from '${imp}'\`\n`;
|
|
}
|
|
return md;
|
|
}
|
|
|
|
const now = process.env.INDEX_STAMP || 'run `node internal/generate-library-index.mjs` to refresh';
|
|
|
|
let md = `# REFERENCE-library-index — reusable pieces you can import into an app-profile
|
|
|
|
> **DERIVED FILE — do not hand-edit.** Regenerate: \`node internal/generate-library-index.mjs\`
|
|
> Generated: ${now}
|
|
|
|
**You are a programmer (or an agent) building a map.** This is the menu of REUSABLE,
|
|
parameterized pieces the shared \`library/\` offers. Import what you need with the \`@map/…\`
|
|
alias; **build your own custom code in your app-profile's \`src/\` folder.** Do NOT edit
|
|
anything listed here unless you know what you're doing — these are shared across every map,
|
|
and a change here can break other maps.
|
|
|
|
- **Three zones:** \`library/\` (this menu — reuse, don't edit) · \`plugin-designs/<map>/\` (your sandbox — build anything) · \`internal/\` (the build machine — never touch).
|
|
- **To make a new map:** copy \`plugin-designs/template-bundle/\`, read its README, list your requirements, pick from below.
|
|
`;
|
|
|
|
md += section('Components (reusable UI)', 'components', { recursive: true, ext: '.vue' }, '@map/components');
|
|
md += section('Composables (reusable logic — Vue Composition API)', 'composables', { recursive: true, ext: '.js' }, '@map/composables');
|
|
md += section('Utilities (pure helper functions)', 'utils', { ext: '.js' }, '@map/utils');
|
|
md += section('Stores (Pinia — shared state)', 'stores', { ext: '.js' }, '@map/stores');
|
|
md += section('Constants (never-change values)', 'constants', { ext: '.js' }, '@map/constants');
|
|
md += section('Data (static reference tables)', 'data', { ext: '.json' }, '@map/data');
|
|
|
|
// api/ + colors/ are registry-driven — describe the SEAM, not each file
|
|
md += `
|
|
### API / data sources (\`@map/api\`)
|
|
|
|
The library provides the data-source **mechanism**; you declare WHICH sources your map uses
|
|
in your own \`src/api/sources.json\`. Add a REST API, CSV, or MCP source by adding one entry —
|
|
no code. Mechanism (do not edit): \`@map/api/_registry.js\`, \`@map/api/DataSourceManager.js\`.
|
|
|
|
### Colors (\`@map/colors\`)
|
|
|
|
The library provides the color-strategy **registry** (the mechanism). Shared color taxonomies
|
|
(religion, adoption, engagement, language-family) live here. Your map's OWN colors live in your
|
|
app-profile's \`src/colors/\` folder — strategy \`.js\` files auto-merge OVER the shared set
|
|
for your map only, so editing them can't break other maps. Mechanism (do not edit): \`@map/colors/_registry.js\`.
|
|
`;
|
|
|
|
const outPath = join(ROOT, 'documentation', 'reference-library-index.md');
|
|
writeFileSync(outPath, md);
|
|
const count = md.split('\n').filter(l => l.startsWith('- **')).length;
|
|
console.log(`[library-index] wrote ${relative(ROOT, outPath)} — ${count} reusable items catalogued`);
|