Tweaks, fixes, and launch prep

This commit is contained in:
2026-02-06 19:09:48 -03:00
parent 22b21d254c
commit 2959360d65
34 changed files with 496 additions and 81 deletions

View File

@@ -1,35 +1,46 @@
/**
* Post-build step: inline critical CSS in built HTML.
* Reads build/index.html, extracts critical CSS, inlines in <head>,
* Reads <buildDir>/index.html, extracts critical CSS, inlines in <head>,
* and defers non-critical styles (preload + link at end of body).
*
* Usage: node scripts/critical-css.mjs [buildDir]
* buildDir: path to build output (default: "build"). Use from repo root.
*/
import { readFileSync, writeFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import { dirname, join } from 'node:path';
import { join } from 'node:path';
import { cwd } from 'node:process';
const __dirname = dirname(fileURLToPath(import.meta.url));
const buildDir = join(__dirname, '..', 'build');
const buildDir = join(cwd(), process.argv[2] || 'build');
const htmlPath = join(buildDir, 'index.html');
// critical/penthouse use a nested puppeteer; point it at our installed Chrome
try {
const puppeteer = await import('puppeteer');
const executablePath = puppeteer.default?.executablePath?.();
if (executablePath) {
process.env.PUPPETEER_EXECUTABLE_PATH = executablePath;
}
} catch {
// no top-level puppeteer or no executable; rely on env or default
}
try {
const { generate } = await import('critical');
const html = readFileSync(htmlPath, 'utf-8');
const { html: outHtml } = await generate({
base: buildDir,
html,
inline: true,
inline: { strategy: 'default' }, // preload in head + link at end of body (no inline JS, CSP-safe)
dimensions: [{ width: 1280, height: 720 }],
penthouse: { timeout: 30000 },
});
writeFileSync(htmlPath, outHtml, 'utf-8');
console.log('Critical CSS inlined in build/index.html');
console.log(`Critical CSS inlined in ${htmlPath}`);
} catch (err) {
const msg = err instanceof Error ? err.message : String(err);
console.error('Critical CSS step failed:', msg);
if (msg.includes('Browser is not downloaded')) {
console.error(
'Install Chromium for critical CSS: pnpm exec puppeteer browsers install chromium',
);
console.error('Install Chromium first: pnpm run critical-css:install');
console.error('Or run "pnpm run build" without critical CSS.');
}
process.exit(1);

View File

@@ -0,0 +1,93 @@
/**
* Post-build: extract SvelteKit's inline bootstrap script to an external file
* and replace it with <script src="..."> so CSP can use script-src 'self' without unsafe-inline.
*
* Usage: node scripts/externalize-inline-script.mjs [buildDir]
* buildDir: path to build output (default: "build"). Use from repo root.
*/
import { readFileSync, writeFileSync, mkdirSync } from 'node:fs';
import { createHash } from 'node:crypto';
import { join } from 'node:path';
import { cwd } from 'node:process';
const buildDir = join(cwd(), process.argv[2] || 'build');
const htmlPath = join(buildDir, 'index.html');
function findSvelteKitInlineScript(html) {
// Find <script> without src= whose content contains __sveltekit_ (the bootstrap)
let scriptStart = html.indexOf('<script>');
while (scriptStart !== -1) {
const result = extractScriptContent(html, scriptStart);
if (result && result.content.includes('__sveltekit_')) return result;
scriptStart = html.indexOf('<script>', scriptStart + 1);
}
return null;
}
function extractScriptContent(html, scriptStart) {
if (scriptStart === -1) return null;
const contentStart = scriptStart + '<script>'.length;
let pos = contentStart;
let inString = null;
let escape = false;
const endTag = '</script>';
while (pos < html.length) {
if (escape) {
escape = false;
pos++;
continue;
}
if (inString) {
if (html[pos] === '\\') {
escape = true;
pos++;
continue;
}
if (html[pos] === inString) {
inString = null;
}
pos++;
continue;
}
if (html[pos] === '"' || html[pos] === "'") {
inString = html[pos];
pos++;
continue;
}
if (html.slice(pos, pos + endTag.length) === endTag) {
return {
start: scriptStart,
end: pos + endTag.length,
content: html.slice(contentStart, pos),
};
}
pos++;
}
return null;
}
try {
let html = readFileSync(htmlPath, 'utf-8');
const found = findSvelteKitInlineScript(html);
if (!found) {
console.log('No SvelteKit inline bootstrap script found in', htmlPath);
process.exit(0);
}
const content = found.content;
const hash = createHash('sha256').update(content).digest('hex').slice(0, 8);
const filename = `bootstrap.${hash}.js`;
const immutableDir = join(buildDir, '_app', 'immutable');
mkdirSync(immutableDir, { recursive: true });
const scriptPath = join(immutableDir, filename);
writeFileSync(scriptPath, content.trimStart(), 'utf-8');
const scriptTag = `<script src="./_app/immutable/${filename}"></script>`;
html =
html.slice(0, found.start) +
scriptTag +
html.slice(found.end);
writeFileSync(htmlPath, html, 'utf-8');
console.log('Externalized SvelteKit bootstrap to', scriptPath);
} catch (err) {
console.error('externalize-inline-script failed:', err instanceof Error ? err.message : String(err));
process.exit(1);
}