Files
dukang/scripts/dev-mini-user-h5.mjs
T
2026-07-14 15:16:41 +08:00

168 lines
4.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* mini-user H5 本地预览:
* Taro Vite `--watch` 在本机易 OOM(峰值 >8GB),改为:
* 1) 先完整 build(约 30s
* 2) 静态托管 dist + /api 代理
* 3) 监听 src 变更后防抖重建
*/
import { spawn } from 'node:child_process';
import fs from 'node:fs';
import http from 'node:http';
import https from 'node:https';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const APP_ROOT = path.resolve(__dirname, '../apps/mini-user');
const DIST = path.resolve(APP_ROOT, 'dist');
const PORT = Number(process.env.PORT || 5177);
const API_ORIGIN = (process.env.VITE_API_TARGET ?? 'https://dkapi.runxian.top').replace(/\/$/, '');
const TARO_BIN = path.resolve(APP_ROOT, 'node_modules/@tarojs/cli/bin/taro');
const MIME = {
'.html': 'text/html; charset=utf-8',
'.js': 'application/javascript; charset=utf-8',
'.css': 'text/css; charset=utf-8',
'.json': 'application/json; charset=utf-8',
'.png': 'image/png',
'.jpg': 'image/jpeg',
'.jpeg': 'image/jpeg',
'.svg': 'image/svg+xml',
'.ico': 'image/x-icon',
'.woff': 'font/woff',
'.woff2': 'font/woff2',
'.map': 'application/json',
};
function runBuild() {
return new Promise((resolve, reject) => {
const child = spawn(
process.execPath,
['--max-old-space-size=8192', TARO_BIN, 'build', '--type', 'h5'],
{
cwd: APP_ROOT,
stdio: 'inherit',
env: process.env,
},
);
child.on('exit', (code) => {
if (code === 0) resolve();
else reject(new Error(`taro build failed with code ${code}`));
});
});
}
function sendFile(res, filePath) {
const ext = path.extname(filePath).toLowerCase();
res.writeHead(200, { 'Content-Type': MIME[ext] || 'application/octet-stream' });
fs.createReadStream(filePath).pipe(res);
}
function proxyApi(req, res) {
const target = new URL(req.url || '/', API_ORIGIN);
const lib = target.protocol === 'https:' ? https : http;
const headers = { ...req.headers, host: target.host };
delete headers['accept-encoding'];
const upstream = lib.request(
{
protocol: target.protocol,
hostname: target.hostname,
port: target.port || undefined,
path: target.pathname + target.search,
method: req.method,
headers,
},
(up) => {
res.writeHead(up.statusCode || 502, up.headers);
up.pipe(res);
},
);
upstream.on('error', (err) => {
res.writeHead(502, { 'Content-Type': 'text/plain; charset=utf-8' });
res.end(`API proxy error: ${err.message}`);
});
req.pipe(upstream);
}
function startServer() {
const server = http.createServer((req, res) => {
const urlPath = decodeURIComponent((req.url || '/').split('?')[0]);
if (urlPath.startsWith('/api')) {
proxyApi(req, res);
return;
}
const candidates = [
path.join(DIST, urlPath),
path.join(DIST, urlPath, 'index.html'),
path.join(DIST, 'index.html'),
];
const file = candidates.find((p) => fs.existsSync(p) && fs.statSync(p).isFile());
if (!file) {
res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' });
res.end('Not Found');
return;
}
sendFile(res, file);
});
server.listen(PORT, '0.0.0.0', () => {
console.log(`\n mini-user H5 preview: http://localhost:${PORT}`);
console.log(` API proxy -> ${API_ORIGIN}`);
console.log(' Watching apps/mini-user/src for changes...\n');
});
}
function watchSrc() {
const srcDir = path.join(APP_ROOT, 'src');
let timer = null;
let building = false;
let queued = false;
const schedule = () => {
if (timer) clearTimeout(timer);
timer = setTimeout(async () => {
if (building) {
queued = true;
return;
}
building = true;
try {
console.log('\n[dev] src changed, rebuilding...');
await runBuild();
console.log('[dev] rebuild done — refresh browser');
} catch (e) {
console.error('[dev] rebuild failed:', e instanceof Error ? e.message : e);
} finally {
building = false;
if (queued) {
queued = false;
schedule();
}
}
}, 800);
};
fs.watch(srcDir, { recursive: true }, (_event, filename) => {
if (!filename) return;
if (/\.(tsx?|jsx?|css|scss|json|png|jpg|svg)$/i.test(filename)) {
schedule();
}
});
}
async function main() {
if (!fs.existsSync(TARO_BIN)) {
throw new Error(`Taro CLI not found: ${TARO_BIN}`);
}
console.log('[dev] initial H5 build...');
await runBuild();
startServer();
watchSrc();
}
main().catch((e) => {
console.error(e);
process.exit(1);
});