Files
armandine/scripts/externalize-bootstrap.js
mifi 4f863e5686
Some checks failed
ci/woodpecker/pr/ci Pipeline failed
Finished the Sveltification
2026-02-15 23:01:16 -03:00

80 lines
2.8 KiB
JavaScript

/**
* Move SvelteKit's inline bootstrap script to an external file for CSP (no unsafe-inline).
* Run after vite build; reads/writes build/.
* Finds <script>...</script> containing __sveltekit_, minifies it, writes to _app/immutable/entry/bootstrap.js,
* and replaces the inline script with <script src="...">.
*/
import { readFileSync, writeFileSync, readdirSync, mkdirSync } from 'fs';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
import { minify } from 'terser';
const __dirname = dirname(fileURLToPath(import.meta.url));
const buildDir = join(__dirname, '..', 'build');
const entryDir = join(buildDir, '_app', 'immutable', 'entry');
const bootstrapPath = join(entryDir, 'bootstrap.js');
const scriptSrc = './_app/immutable/entry/bootstrap.js';
function getFiles(dir, ext, files = []) {
for (const name of readdirSync(dir, { withFileTypes: true })) {
const full = join(dir, name.name);
if (name.isDirectory()) getFiles(full, ext, files);
else if (name.name.endsWith(ext)) files.push(full);
}
return files;
}
// Find first <script>...</script> that contains __sveltekit_
function findInlineBootstrap(html) {
const scriptOpen = html.indexOf('<script>');
if (scriptOpen === -1) return null;
const scriptClose = html.indexOf('</script>', scriptOpen);
if (scriptClose === -1) return null;
const content = html.slice(scriptOpen + '<script>'.length, scriptClose);
if (!content.includes('__sveltekit_')) return null;
return { content: content.trim(), start: scriptOpen, end: scriptClose + '</script>'.length };
}
const SCRIPT_TAG = `<script src="${scriptSrc}"></script>`;
async function main() {
const htmlFiles = getFiles(buildDir, '.html');
let bootstrapWritten = false;
let count = 0;
for (const htmlFile of htmlFiles) {
let html = readFileSync(htmlFile, 'utf8');
const found = findInlineBootstrap(html);
if (!found) continue;
if (!bootstrapWritten) {
// Imports relative to script location when in _app/immutable/entry/
let scriptContent = found.content.replace(
/import\("\.\/_app\/immutable\/entry\/([^"]+)"\)/g,
'import("./$1")'
);
const result = await minify(scriptContent, {
format: { comments: false },
compress: { passes: 1 }
});
if (result.code) scriptContent = result.code;
mkdirSync(entryDir, { recursive: true });
writeFileSync(bootstrapPath, scriptContent, 'utf8');
bootstrapWritten = true;
}
html = html.slice(0, found.start) + SCRIPT_TAG + html.slice(found.end);
writeFileSync(htmlFile, html, 'utf8');
count++;
}
if (count > 0) {
console.log('Bootstrap script externalized (minified):', scriptSrc, `(${count} HTML file(s))`);
} else if (htmlFiles.length > 0) {
console.log('No SvelteKit inline script found in HTML (bootstrap already external?)');
}
}
main().catch((err) => {
console.error(err);
process.exit(1);
});