- Editorial web platform built with Node.js, Express, EJS, and Vanilla CSS - Markdown content engine with YAML frontmatter, bilingual variants, and hot-reload watcher - Unified SQLite with FTS5 search indexing, query logging, and schema filters - Centered editorial post layout with margin-docked left TOC and mobile bottom sheet - WordPress-grade comment system with moderation dashboard and bulk operations under /guanli - Complete SEO suite (JSON-LD structured data, RSS/Atom feeds, XML sitemap, robots.txt) - CI/CD workflows for linting, security audits (Gitleaks), automated tests, and Docker releases
111 lines
3.3 KiB
JavaScript
111 lines
3.3 KiB
JavaScript
const fs = require('fs');
|
|
const path = require('path');
|
|
const vm = require('vm');
|
|
const ejs = require('ejs');
|
|
|
|
const JS_DIRS = ['server', 'public/js', 'sample-content/plugins', 'test'];
|
|
const VIEW_DIR = 'views';
|
|
const CSS_DIR = 'public/css';
|
|
|
|
let jsFilesChecked = 0;
|
|
let ejsFilesChecked = 0;
|
|
let cssFilesChecked = 0;
|
|
let errorCount = 0;
|
|
|
|
// 1. JavaScript Checker
|
|
function checkJsFile(filePath) {
|
|
jsFilesChecked++;
|
|
const code = fs.readFileSync(filePath, 'utf8');
|
|
|
|
try {
|
|
new vm.Script(code, { filename: filePath });
|
|
} catch (err) {
|
|
console.error(`❌ Syntax Error in ${filePath}:\n ${err.message}`);
|
|
errorCount++;
|
|
return;
|
|
}
|
|
|
|
if (/\bdebugger\b/.test(code)) {
|
|
console.error(`⚠️ Warning in ${filePath}: Found 'debugger' statement.`);
|
|
errorCount++;
|
|
}
|
|
|
|
if (/-----BEGIN (RSA|EC|OPENSSH|PRIVATE) KEY-----/.test(code)) {
|
|
console.error(`🚨 Security Alert in ${filePath}: Found private key pattern!`);
|
|
errorCount++;
|
|
}
|
|
}
|
|
|
|
// 2. EJS Template Checker
|
|
function checkEjsFile(filePath) {
|
|
ejsFilesChecked++;
|
|
const content = fs.readFileSync(filePath, 'utf8');
|
|
|
|
// Verify EJS syntax compilation
|
|
try {
|
|
ejs.compile(content, { filename: filePath, client: false });
|
|
} catch (err) {
|
|
console.error(`❌ EJS Compilation Error in ${filePath}:\n ${err.message}`);
|
|
errorCount++;
|
|
return;
|
|
}
|
|
|
|
// Prevent inline style anti-patterns that break IDE CSS linters: style="... <%= ... %> ..."
|
|
const styleMatch = content.match(/style="[^"]*<[%#=][\s\S]*?"/g);
|
|
if (styleMatch) {
|
|
console.error(`❌ Linter Error in ${filePath}: Dynamic EJS expression inside style="" attribute causes IDE CSS linter errors. Use CSS utility classes instead:\n Matched: ${styleMatch[0]}`);
|
|
errorCount++;
|
|
}
|
|
}
|
|
|
|
// 3. CSS Checker (Brace matching)
|
|
function checkCssFile(filePath) {
|
|
cssFilesChecked++;
|
|
const content = fs.readFileSync(filePath, 'utf8');
|
|
let openBraces = 0;
|
|
for (let i = 0; i < content.length; i++) {
|
|
if (content[i] === '{') openBraces++;
|
|
if (content[i] === '}') openBraces--;
|
|
if (openBraces < 0) {
|
|
console.error(`❌ CSS Error in ${filePath}: Unexpected closing brace '}' at char ${i}`);
|
|
errorCount++;
|
|
return;
|
|
}
|
|
}
|
|
if (openBraces !== 0) {
|
|
console.error(`❌ CSS Error in ${filePath}: Unclosed brace '{' (mismatched by ${openBraces})`);
|
|
errorCount++;
|
|
}
|
|
}
|
|
|
|
function walkDir(dir, ext, handler) {
|
|
if (!fs.existsSync(dir)) return;
|
|
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
for (const entry of entries) {
|
|
const fullPath = path.join(dir, entry.name);
|
|
if (entry.isDirectory()) {
|
|
if (entry.name !== 'node_modules' && entry.name !== '.git') {
|
|
walkDir(fullPath, ext, handler);
|
|
}
|
|
} else if (entry.isFile() && entry.name.endsWith(ext)) {
|
|
handler(fullPath);
|
|
}
|
|
}
|
|
}
|
|
|
|
console.log('🔍 Running comprehensive project linter (JS, EJS templates, CSS)...');
|
|
JS_DIRS.forEach(d => walkDir(d, '.js', checkJsFile));
|
|
walkDir(VIEW_DIR, '.ejs', checkEjsFile);
|
|
walkDir(CSS_DIR, '.css', checkCssFile);
|
|
|
|
if (errorCount === 0) {
|
|
console.log(`✓ All files passed verification!`);
|
|
console.log(` - ${jsFilesChecked} JavaScript files`);
|
|
console.log(` - ${ejsFilesChecked} EJS templates`);
|
|
console.log(` - ${cssFilesChecked} CSS stylesheets`);
|
|
process.exit(0);
|
|
} else {
|
|
console.error(`\n❌ Linting failed with ${errorCount} error(s).`);
|
|
process.exit(1);
|
|
}
|