Files
shanhe-puzzle/scripts/check.mjs
T
codex-bot 4845826deb
Shanhe source and offline package checks / verify (pull_request) Failing after 5s
feat: sync Shanhe 2.2 source and add remote package verification
2026-11-01 17:13:46 +08:00

11 lines
2.7 KiB
JavaScript

import fs from 'node:fs';import path from 'node:path';import {parse} from 'acorn';
const issues=[];const root='dist';
function walk(dir){return fs.readdirSync(dir,{withFileTypes:true}).flatMap(d=>d.isDirectory()?walk(path.join(dir,d.name)):[path.join(dir,d.name)]);}
const files=walk(root),allowed=new Set(['.html','.css','.js','.json','.png','.jpg','.jpeg','.webp','.gif','.svg','.woff','.woff2']);
if(files.length>200)issues.push('ZIP文件数量超过200个上限: '+files.length);
for(const f of files){if(!allowed.has(path.extname(f)))issues.push('不允许的文件类型: '+f);if(path.basename(f).startsWith('.'))issues.push('隐藏文件: '+f);const source=fs.readFileSync(f,'utf8');if(f.endsWith('.js')){try{parse(source,{ecmaVersion:2017});}catch(e){issues.push('非 ES2017 语法: '+f+' '+e.message);}const banned=[/\beval\s*\(/,/\bnew\s+Function\s*\(/,/\bfetch\s*\(/,/\bXMLHttpRequest\b/,/\bWebAssembly\b/,/\bnew\s+(?:SharedWorker|Worker|WebSocket|EventSource)\s*\(/,/navigator\.(?:clipboard|geolocation)/,/requestFullscreen\s*\(/,/window\.open\s*\(/];for(const re of banned)if(re.test(source))issues.push('禁用能力 '+re+' '+f);}
if(f.endsWith('.html')){for(const m of source.matchAll(/<script\b[^>]*>([\s\S]*?)<\/script>/gi))if(m[1].trim())issues.push('内联脚本');if(/\son[a-z]+\s*=/i.test(source))issues.push('行内事件');if(/<meta[^>]+Content-Security-Policy|type=[\"']module[\"']/i.test(source))issues.push('自建CSP或模块脚本');if(/<(?:iframe|object|form)\b|javascript:|\sdownload[\s=>]/i.test(source))issues.push('禁止的 HTML 能力');for(const m of source.matchAll(/(?:src|href)="([^"#]+)"/g)){if(!m[1].startsWith('./'))issues.push('非相对本地资源: '+m[1]);else if(!fs.existsSync(path.join(root,m[1])))issues.push('缺失资源: '+m[1]);}}
if(f.endsWith('.css')){for(const m of source.matchAll(/url\(([^)]+)\)/g))if(/https?:|\/\//.test(m[1]))issues.push('外部样式资源');const banned=[/[;{]\s*gap\s*:/,/\bclamp\(/,/\bcolor-mix\(/,/\bdvh\b/,/\baspect-ratio\s*:/];for(const re of banned)if(re.test(source))issues.push('无降级的现代 CSS '+re);}}
if(files.filter(f=>f.endsWith('.html')).length!==1||!fs.existsSync(root+'/index.html'))issues.push('须有且仅有根目录 index.html 入口');
const report={date:'2026-10-16',type:'本地预检(非官方 Skill 校验)',baseline:'Chrome 61 / ES2017',files:files.map(f=>({path:path.relative(root,f),bytes:fs.statSync(f).size})),issues,passed:!issues.length};fs.mkdirSync('artifacts',{recursive:true});fs.writeFileSync('artifacts/check-report.json',JSON.stringify(report,null,2));if(issues.length){console.error(issues);process.exit(1);}console.log('本地预检通过:'+files.length+' 个本地文件;ES2017 解析、离线资源、禁用 API 与入口结构检查通过。');