mirror of
https://github.com/crmeb/CRMEB.git
synced 2026-10-28 13:31:04 +00:00
149 lines
5.2 KiB
JavaScript
149 lines
5.2 KiB
JavaScript
/**
|
||
* 发现并编译前端扩展
|
||
* 扫描 src/pages/product/type/ 和 src/pages/plugin/ 下所有带 ext.json + index.umd.js 的目录,
|
||
* 逐个执行 vite build(scripts/vite.ext.config.js,UMD lib 模式,externals vue/element-plus)。
|
||
* 产物输出到 dist/static/extensions/{key}/app.umd.js + app.umd.css
|
||
*
|
||
* 用法:
|
||
* node scripts/build-extensions.js # 编译所有扩展(并行)
|
||
* node scripts/build-extensions.js --ext agent # 只编译指定扩展
|
||
* node scripts/build-extensions.js --ext agent --watch # 监控模式
|
||
*/
|
||
import { spawn } from 'node:child_process';
|
||
import fs from 'node:fs';
|
||
import path from 'node:path';
|
||
import { fileURLToPath, URL } from 'node:url';
|
||
|
||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||
const ROOT = path.resolve(__dirname, '..');
|
||
|
||
const scanDirs = [path.resolve(ROOT, 'src/pages/product/type'), path.resolve(ROOT, 'src/pages/plugin')];
|
||
const args = process.argv.slice(2);
|
||
const isWatch = args.includes('--watch');
|
||
const extFilterIndex = args.indexOf('--ext');
|
||
const extFilter = extFilterIndex !== -1 ? args[extFilterIndex + 1] : null;
|
||
|
||
function getExtensions() {
|
||
const results = [];
|
||
scanDirs.forEach((scanDir) => {
|
||
if (!fs.existsSync(scanDir)) return;
|
||
fs.readdirSync(scanDir).forEach((name) => {
|
||
const configPath = path.join(scanDir, name, 'ext.json');
|
||
const entryPath = path.join(scanDir, name, 'index.umd.js');
|
||
if (!fs.existsSync(configPath) || !fs.existsSync(entryPath)) return;
|
||
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
||
results.push({ ...config, entry: entryPath, dir: path.join(scanDir, name) });
|
||
});
|
||
});
|
||
return results;
|
||
}
|
||
|
||
function buildExtension(ext) {
|
||
return new Promise((resolve, reject) => {
|
||
const targetDir = path.resolve(ROOT, `dist/static/extensions/${ext.key}`);
|
||
|
||
// 构建前清空旧目录,避免残留文件
|
||
if (fs.existsSync(targetDir)) {
|
||
fs.rmSync(targetDir, { recursive: true });
|
||
}
|
||
|
||
console.log(`\n━━━ 编译扩展:${ext.title} (${ext.key}) ━━━`);
|
||
const viteBin = path.resolve(ROOT, 'node_modules/.bin/vite');
|
||
const child = spawn(
|
||
viteBin,
|
||
['build', '-c', path.resolve(__dirname, 'vite.ext.config.js')],
|
||
{
|
||
stdio: 'inherit',
|
||
cwd: ROOT,
|
||
env: {
|
||
...process.env,
|
||
EXT_ENTRY: ext.entry,
|
||
EXT_NAME: `__ext_${ext.key}`,
|
||
EXT_OUT: targetDir,
|
||
},
|
||
},
|
||
);
|
||
|
||
child.on('close', (code) => {
|
||
if (code !== 0) {
|
||
reject(new Error(`${ext.title} 编译失败,exit code: ${code}`));
|
||
return;
|
||
}
|
||
|
||
try {
|
||
// 白名单清理:只保留 app.umd.js + app.umd.css(rollup cssCodeSplit:false 单文件输出,
|
||
// lib 单入口下动态 import 自动内联,理论上无额外 chunk;防御性清理以防万一)
|
||
const keep = new Set(['app.umd.js', 'app.umd.css']);
|
||
if (fs.existsSync(targetDir)) {
|
||
fs.readdirSync(targetDir).forEach((f) => {
|
||
if (keep.has(f)) return;
|
||
const fp = path.join(targetDir, f);
|
||
fs.rmSync(fp, { recursive: true });
|
||
});
|
||
}
|
||
if (!fs.existsSync(path.join(targetDir, 'app.umd.js'))) {
|
||
throw new Error(`${ext.title} 产物 app.umd.js 未生成`);
|
||
}
|
||
console.log(` \u2713 ${ext.title} 编译完成 \u2192 dist/static/extensions/${ext.key}/`);
|
||
resolve();
|
||
} catch (err) {
|
||
reject(err);
|
||
}
|
||
});
|
||
|
||
child.on('error', (err) => {
|
||
reject(err);
|
||
});
|
||
});
|
||
}
|
||
|
||
async function main() {
|
||
let extensions = getExtensions();
|
||
|
||
if (extFilter) {
|
||
extensions = extensions.filter((e) => e.key === extFilter);
|
||
if (extensions.length === 0) {
|
||
const available = getExtensions().map((e) => e.key + '(' + e.title + ')').join(', ');
|
||
console.log('\n未找到扩展 "' + extFilter + '",可用扩展:' + available);
|
||
process.exit(1);
|
||
}
|
||
}
|
||
|
||
if (extensions.length === 0) {
|
||
console.log('\n未发现扩展(没有带 ext.json + index.umd.js 的目录)');
|
||
process.exit(0);
|
||
}
|
||
|
||
console.log(`\n发现 ${extensions.length} 个扩展:`, extensions.map((e) => `${e.title}(${e.key})`).join(', '));
|
||
|
||
if (isWatch) {
|
||
// 监控模式:先并行编译一次
|
||
await Promise.all(extensions.map(buildExtension));
|
||
|
||
console.log('\n初始编译完成,进入监控模式,文件变更时自动重建...\n');
|
||
const watchDirs = extensions.map((ext) => ext.dir);
|
||
const watchSet = new Set(watchDirs);
|
||
watchSet.forEach((dir) => {
|
||
fs.watch(dir, { recursive: true }, (eventType, filename) => {
|
||
if (!filename) return;
|
||
if (!filename.endsWith('.vue') && !filename.endsWith('.js')) return;
|
||
const ext = extensions.find((e) => filename.startsWith(e.key + '/') || filename.startsWith(e.dir));
|
||
if (ext) {
|
||
console.log(`\n ${ext.title} 文件变更: ${filename}`);
|
||
buildExtension(ext).catch((err) => console.error(err.message));
|
||
}
|
||
});
|
||
});
|
||
console.log('按 Ctrl+C 停止监控');
|
||
} else {
|
||
// 并行编译所有扩展
|
||
await Promise.all(extensions.map(buildExtension));
|
||
console.log('\n所有扩展编译完成!');
|
||
}
|
||
}
|
||
|
||
main().catch((err) => {
|
||
console.error(err);
|
||
process.exit(1);
|
||
});
|