The previous commit appended the .nojekyll rationale to docs/vendor/README.md — a file whose own first line reads "Generated by npm run build:docs. Do not edit by hand." CI's "Landing page bundle is in sync with src/" step regenerated it, the text vanished, git diff was non-empty and all three Node jobs failed. The guard did its job; I was the one who put a hand-written paragraph in a generated file. The note now lives in the generator, and build:docs writes docs/.nojekyll itself so it is part of the generated set rather than a loose file that a docs/ rewrite could drop. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SAcqrz3FcadkYr3xG32CjK
89 lines
4.1 KiB
JavaScript
89 lines
4.1 KiB
JavaScript
/**
|
|
* Builds the assets the landing page needs, into docs/.
|
|
*
|
|
* The page is served by GitHub Pages straight from the repository, so everything it loads has
|
|
* to be committed — there is no build step on the hosting side. Everything it loads is also
|
|
* local: the previous page pulled Tailwind, CodeMirror and Babel from three CDNs, and its
|
|
* playground died silently when the unpinned `@babel/standalone` URL rolled over to Babel 8
|
|
* and the plugin list it passed stopped existing. Vendoring the compiler pins it to the
|
|
* version in package.json and to a lockfile.
|
|
*/
|
|
import { build } from 'esbuild';
|
|
import { copyFile, mkdir, readFile, writeFile, stat } from 'node:fs/promises';
|
|
import { fileURLToPath } from 'node:url';
|
|
import path from 'node:path';
|
|
import { collectDiagnostics } from './diagnostics.mjs';
|
|
|
|
const root = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
|
|
const docs = path.join(root, 'docs');
|
|
const vendor = path.join(docs, 'vendor');
|
|
|
|
const size = async (file) => {
|
|
const { size: bytes } = await stat(file);
|
|
return `${(bytes / 1024).toFixed(0)} KB`;
|
|
};
|
|
|
|
await mkdir(vendor, { recursive: true });
|
|
|
|
const pkg = JSON.parse(await readFile(path.join(root, 'package.json'), 'utf8'));
|
|
|
|
// 1. The library itself, as a browser global the playground can pull names out of.
|
|
await build({
|
|
entryPoints: [path.join(root, 'src/index.ts')],
|
|
bundle: true,
|
|
format: 'iife',
|
|
globalName: 'Cereale',
|
|
minify: true,
|
|
target: 'es2022',
|
|
tsconfigRaw: {
|
|
compilerOptions: { experimentalDecorators: false, useDefineForClassFields: true, target: 'es2022' },
|
|
},
|
|
outfile: path.join(docs, 'cereale.js'),
|
|
});
|
|
|
|
// 2. Facts the page would otherwise hard-code and then get wrong. Everything else it needs —
|
|
// the decorator count, the export list — it derives from the bundle at runtime.
|
|
await writeFile(
|
|
path.join(docs, 'meta.js'),
|
|
`// Generated by scripts/build-docs.mjs — do not edit.\n` +
|
|
`window.CEREALE_META = ${JSON.stringify({ version: pkg.version, node: pkg.engines.node }, null, 2)};\n`
|
|
);
|
|
|
|
// 3. The compiler errors the page quotes, produced by actually running the compiler. If a
|
|
// snippet the page calls a compile error ever compiles, this fails the build.
|
|
const { byCase, problems } = await collectDiagnostics(root);
|
|
if (problems.length > 0) {
|
|
console.error('The landing page makes a claim the compiler does not support:\n' +
|
|
problems.map((p) => ` - ${p}`).join('\n'));
|
|
process.exit(1);
|
|
}
|
|
await writeFile(
|
|
path.join(docs, 'diagnostics.js'),
|
|
`// Generated by scripts/build-docs.mjs from real tsc output — do not edit.\n` +
|
|
`window.CEREALE_DIAGNOSTICS = ${JSON.stringify(byCase, null, 2)};\n`
|
|
);
|
|
|
|
// 4. The playground's TypeScript compiler, pinned by package.json rather than by a CDN URL.
|
|
const babel = path.join(root, 'node_modules/@babel/standalone/babel.min.js');
|
|
await copyFile(babel, path.join(vendor, 'babel.min.js'));
|
|
await writeFile(
|
|
path.join(vendor, 'README.md'),
|
|
`# Vendored assets\n\n` +
|
|
`Generated by \`npm run build:docs\`. Do not edit by hand.\n\n` +
|
|
`- \`babel.min.js\` — @babel/standalone ${JSON.parse(await readFile(path.join(root, 'node_modules/@babel/standalone/package.json'), 'utf8')).version}, ` +
|
|
`used by the playground to compile TypeScript with standard decorators in the browser. ` +
|
|
`Vendored rather than loaded from a CDN: the previous page referenced an unpinned unpkg URL, ` +
|
|
`which silently began serving Babel 8 and broke the playground.\n\n` +
|
|
`- \`../.nojekyll\` — opts the directory out of Jekyll, so GitHub Pages serves it verbatim. ` +
|
|
`Jekyll ignores paths beginning with an underscore and carries default \`vendor/\` exclusions, ` +
|
|
`and the failure mode is an asset that silently does not publish — for this page, the ` +
|
|
`playground's compiler 404ing while everything else looks fine.\n`
|
|
);
|
|
|
|
// 5. Written rather than committed by hand so it cannot be lost in a docs/ rewrite.
|
|
await writeFile(path.join(docs, '.nojekyll'), '');
|
|
|
|
console.log(`docs/cereale.js ${await size(path.join(docs, 'cereale.js'))}`);
|
|
console.log(`docs/meta.js ${await size(path.join(docs, 'meta.js'))} (v${pkg.version})`);
|
|
console.log(`docs/vendor/babel.min.js ${await size(path.join(vendor, 'babel.min.js'))}`);
|