Extract shared core module, generate browser script at build time

DRY refactor:
- detect-antipatterns-core.mjs (297 lines): shared constants (SAFE_TAGS,
  OVERUSED_FONTS, GENERIC_FONTS, ANTIPATTERNS), color utilities (parseRgb,
  relativeLuminance, contrastRatio, hasChroma, getHue, colorToHex,
  isNeutralColor), and pure detection functions (checkBorders, checkColors,
  isCardLikeFromProps).
- CLI (889 lines, was 1212): imports from core, keeps jsdom-specific
  resolveBackground, page-level analyzers, regex fallback, and CLI logic.
- Browser wrapper (335 lines): template with browser-specific DOM adapters,
  highlighting, scan loop. Core is injected at build time.
- build-browser-detector.js: reads core, strips exports, injects into
  wrapper, writes to public/js/detect-antipatterns-browser.js (generated).
- Build step added to scripts/build.js (runs before Bun bundling).

Source of truth for detection logic is now the core module. Browser script
is generated — do not edit public/js/detect-antipatterns-browser.js directly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Paul Bakaus
2026-03-17 18:04:19 -07:00
co-authored by Claude Opus 4.6
parent 7bc3a5567d
commit 623a8a8375
9 changed files with 1727 additions and 950 deletions
+37
View File
@@ -0,0 +1,37 @@
#!/usr/bin/env node
/**
* Generates public/js/detect-antipatterns-browser.js by:
* 1. Reading the core module (shared constants + pure functions)
* 2. Reading the browser wrapper template
* 3. Injecting the core into the wrapper's IIFE
*
* Run: node scripts/build-browser-detector.js
*/
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const ROOT = path.resolve(__dirname, '..');
const CORE_PATH = path.join(ROOT, 'source/skills/critique/scripts/detect-antipatterns-core.mjs');
const WRAPPER_PATH = path.join(ROOT, 'source/skills/critique/scripts/detect-antipatterns-browser-wrapper.js');
const OUTPUT_PATH = path.join(ROOT, 'public/js/detect-antipatterns-browser.js');
// Read and strip exports from core
let core = fs.readFileSync(CORE_PATH, 'utf-8');
core = core
.replace(/^export\s+/gm, '') // Remove 'export' keywords
.replace(/^\/\*\*[\s\S]*?\*\/\n/m, '') // Remove file-level JSDoc
.trim();
// Read the browser wrapper
const wrapper = fs.readFileSync(WRAPPER_PATH, 'utf-8');
// Inject core into the wrapper at the marker
const output = wrapper.replace('// {{CORE_INJECTION_POINT}}', core);
fs.writeFileSync(OUTPUT_PATH, output);
console.log(`✓ Generated ${path.relative(ROOT, OUTPUT_PATH)} (${(output.length / 1024).toFixed(1)} KB)`);
+7
View File
@@ -322,6 +322,13 @@ async function build() {
// Build CSS with Tailwind CLI (handles @theme directive)
buildTailwindCSS();
// Generate browser anti-pattern detector from core module
try {
execSync('node scripts/build-browser-detector.js', { cwd: ROOT_DIR, stdio: 'inherit' });
} catch (error) {
console.error('Failed to build browser detector:', error.message);
}
// Bundle HTML, JS, and compiled CSS with Bun
await buildStaticSite();