Files
谢宇宁andClaude Opus 5.5 6fb2b1e486 Initial commit
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 22:26:18 +07:00

68 lines
3.8 KiB
JavaScript
Raw Permalink 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.
import {existsSync, readFileSync} from 'node:fs';
import {resolve} from 'node:path';
import {fileURLToPath} from 'node:url';
import {spawn} from 'node:child_process';
import {parse} from 'dotenv';
const projectRoot=fileURLToPath(new URL('../',import.meta.url));
const directories={development:'.next-dev',test:'.next-test',production:'.next'};
const commands=new Set(['dev','build','start']);
const readConfig=file=>existsSync(file)?parse(readFileSync(file)):{};
// 熟果短剧配置写的是 API_PROXY_TARGET,这里归一成测试服脚本读取的 UPSTREAM_API_URL。
const apiConfig=values=>({...values,...(!Object.hasOwn(values,'UPSTREAM_API_URL')&&(Object.hasOwn(values,'API_BASE_URL')||Object.hasOwn(values,'API_PROXY_TARGET'))?{UPSTREAM_API_URL:values.API_BASE_URL||values.API_PROXY_TARGET}:{})});
/** APP_ENV selects the deployment; NODE_ENV keeps Next's development/production semantics. */
export function nextCommand(environment='development',command='dev',args=[],{root=projectRoot,variables=process.env}={}) {
if(!Object.hasOwn(directories,environment)||!commands.has(command))throw new Error('Usage: node scripts/next-env.mjs development|test|production dev|build|start');
const base=resolve(root,'config',environment+'.env');
if(!existsSync(base))throw new Error(`缺少环境配置:config/${environment}.env`);
const env={
...apiConfig(readConfig(base)),
...(environment==='development'?apiConfig(readConfig(resolve(root,'.env.local'))):{}),
...apiConfig(readConfig(resolve(root,'config',environment+'.local.env'))),
...apiConfig(Object.fromEntries(Object.entries(variables).filter(([,value])=>value!==undefined))),
APP_ENV:environment,
APP_DIST_DIR:directories[environment],
NODE_ENV:command==='dev'?'development':'production',
};
try {
const value=(env.UPSTREAM_API_URL||'').trim();
if(value==='current')env.UPSTREAM_API_URL=value;
else{
const api=new URL(value);
if(!['http:','https:'].includes(api.protocol)||api.username||api.password||api.search||api.hash)throw new Error();
env.UPSTREAM_API_URL=api.href.replace(/\/+$/,'');
}
env.API_BASE_URL=env.UPSTREAM_API_URL;
}catch{throw new Error(`config/${environment}.env 的 UPSTREAM_API_URL(兼容 API_BASE_URL、API_PROXY_TARGET)必须为 current 或有效的 HTTP(S) 接口地址`);}
const port=String(env.PORT||'26149');
if(!/^\d+$/.test(port)||Number(port)<1||Number(port)>65535)throw new Error('PORT 必须在 1–65535 之间');
env.PORT=port;
const hasOption=names=>args.some(arg=>names.some(name=>arg===name||arg.startsWith(name+'=')));
const options=command==='build'?[]:[
...(hasOption(['--hostname','-H'])?[]:['--hostname','0.0.0.0']),
...(hasOption(['--port','-p'])?[]:['--port',port]),
];
return {executable:process.execPath,args:[resolve(root,'node_modules/next/dist/bin/next'),command,...options,...args],cwd:root,env};
}
export async function runNext(environment,command,args,options) {
const spec=nextCommand(environment,command,args,options);
const child=spawn(spec.executable,spec.args,{cwd:spec.cwd,env:spec.env,stdio:'inherit'});
const interrupt=()=>child.kill('SIGINT'),terminate=()=>child.kill('SIGTERM');
process.on('SIGINT',interrupt);process.on('SIGTERM',terminate);
try {
return await new Promise((resolve,reject)=>{
child.once('error',reject);
child.once('exit',(code,signal)=>resolve(code??(signal==='SIGINT'?130:143)));
});
}finally{process.off('SIGINT',interrupt);process.off('SIGTERM',terminate);}
}
if(process.argv[1]&&resolve(process.argv[1])===fileURLToPath(import.meta.url)){
try {
const [environment='development',command='dev',...args]=process.argv.slice(2);
process.exitCode=await runNext(environment,command,args);
}catch(error){console.error(error instanceof Error?error.message:'启动 Next.js 失败');process.exitCode=1;}
}