tts-voice-creator-clone-and.../scripts/check-frontend.mjs

38 lines
1.8 KiB
JavaScript

// Fast, dependency-free frontend gate. The application deliberately shares one
// global scope, so duplicate top-level declarations are a runtime collision.
// Nested callbacks are ignored; vendor/PDF bundles are outside the manifest.
import { execFileSync } from 'node:child_process';
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
import { fileURLToPath } from 'node:url';
const root = join(fileURLToPath(new URL('.', import.meta.url)), '..');
const manifest = JSON.parse(readFileSync(join(root, 'static', 'module-manifest.json'), 'utf8'));
const modules = [...new Set([...(manifest.core || []), ...Object.values(manifest.lazy || {}).flat()])];
const seenModules = new Set();
const declarations = new Map();
const declarationPattern = /^(?:async\s+)?(?:function|class|const|let|var)\s+([A-Za-z_$][\w$]*)/gm;
for (const name of modules) {
if (seenModules.has(name)) throw new Error(`duplicate module in manifest: ${name}`);
seenModules.add(name);
const file = join(root, 'static', 'js', name + '.js');
try {
execFileSync(process.execPath, ['--check', file], { stdio: 'pipe' });
} catch (error) {
throw new Error(`JavaScript syntax check failed for ${file}: ${error.stderr?.toString().trim() || error.message}`);
}
const source = readFileSync(file, 'utf8');
for (const match of source.matchAll(declarationPattern)) {
const files = declarations.get(match[1]) || [];
files.push(file);
declarations.set(match[1], files);
}
}
const collisions = [...declarations.entries()].filter(([, files]) => files.length > 1);
if (collisions.length) {
throw new Error('duplicate top-level declarations:\n' + collisions.map(([name, files]) => ` ${name}: ${files.join(', ')}`).join('\n'));
}
console.log(`Frontend gate passed: ${modules.length} modules, no syntax errors or top-level declaration collisions.`);