Files
soul-yongping/scripts/start-standalone.js

125 lines
3.8 KiB
JavaScript
Raw Normal View History

#!/usr/bin/env node
/**
* Standalone 模式启动脚本
* 自动复制 .next/static public standalone 目录后启动服务器
*/
const fs = require('fs');
const path = require('path');
const { spawn } = require('child_process');
const rootDir = path.join(__dirname, '..');
const standaloneDir = path.join(rootDir, '.next', 'standalone');
const staticSrc = path.join(rootDir, '.next', 'static');
const staticDst = path.join(standaloneDir, '.next', 'static');
const publicSrc = path.join(rootDir, 'public');
const publicDst = path.join(standaloneDir, 'public');
/**
* 递归复制目录
*/
function copyDir(src, dst) {
if (!fs.existsSync(src)) {
console.warn(`⚠️ 跳过复制:${src} 不存在`);
return;
}
// 删除旧目录
if (fs.existsSync(dst)) {
fs.rmSync(dst, { recursive: true, force: true });
}
// 创建目标目录
fs.mkdirSync(dst, { recursive: true });
// 复制文件
const entries = fs.readdirSync(src, { withFileTypes: true });
for (const entry of entries) {
const srcPath = path.join(src, entry.name);
const dstPath = path.join(dst, entry.name);
if (entry.isDirectory()) {
copyDir(srcPath, dstPath);
} else {
fs.copyFileSync(srcPath, dstPath);
}
}
}
console.log('🚀 准备启动 Standalone 服务器...\n');
// 检查 standalone 目录
if (!fs.existsSync(standaloneDir)) {
console.error('❌ 错误:未找到 .next/standalone 目录');
console.error(' 请先运行: pnpm build');
process.exit(1);
}
2026-02-05 11:35:57 +08:00
// 复制静态资源(缺一不可,否则部署到线上也会 404
console.log('📦 复制静态资源...');
2026-02-05 11:35:57 +08:00
if (!fs.existsSync(staticSrc)) {
console.error('❌ 错误:.next/static 不存在,请先执行 pnpm build');
process.exit(1);
}
console.log(' .next/static → .next/standalone/.next/static');
copyDir(staticSrc, staticDst);
2026-02-05 11:35:57 +08:00
const chunksDir = path.join(staticDst, 'chunks');
if (!fs.existsSync(chunksDir)) {
console.error('❌ 错误:复制后 .next/standalone/.next/static/chunks 不存在,本地会 404部署线上也会报错');
process.exit(1);
}
console.log(' public → .next/standalone/public');
copyDir(publicSrc, publicDst);
2026-02-05 11:35:57 +08:00
// 同步构建索引BUILD_ID、build-manifest 等,避免服务器用错版本导致 404
const nextRoot = path.join(rootDir, '.next');
const nextStandalone = path.join(standaloneDir, '.next');
const indexFiles = ['BUILD_ID', 'build-manifest.json', 'app-path-routes-manifest.json', 'routes-manifest.json'];
for (const name of indexFiles) {
const src = path.join(nextRoot, name);
const dst = path.join(nextStandalone, name);
if (fs.existsSync(src)) {
try {
fs.copyFileSync(src, dst);
} catch (e) {
console.warn(' [警告] 复制索引失败 %s: %s', name, e.message);
}
}
}
console.log('✅ 静态资源与构建索引已同步\n');
2026-02-05 11:35:57 +08:00
// 启动服务器(必须在 standalone 目录下运行,否则 _next/static 会 404
const serverPath = path.join(standaloneDir, 'server.js');
2026-02-05 11:35:57 +08:00
const DEFAULT_PORT = 30006;
const port = process.env.PORT || String(DEFAULT_PORT);
console.log(`🌐 启动服务器: http://localhost:${port}`);
2026-02-05 11:35:57 +08:00
console.log(' (静态资源已复制到 .next/standalone请勿直接 cd 到 standalone 用 node server.js)\n');
2026-02-05 11:35:57 +08:00
const server = spawn('node', ['server.js'], {
cwd: standaloneDir, // 关键:工作目录必须是 standalone否则找不到 .next/static
stdio: 'inherit',
env: { ...process.env, PORT: port }
});
server.on('error', (err) => {
console.error('❌ 启动失败:', err);
process.exit(1);
});
server.on('exit', (code) => {
if (code !== 0) {
console.error(`\n❌ 服务器异常退出,退出码: ${code}`);
}
process.exit(code);
});
// 处理 Ctrl+C
process.on('SIGINT', () => {
console.log('\n\n👋 停止服务器...');
server.kill('SIGINT');
});