@@ -0,0 +1,67 @@
|
||||
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,这里归一成 1047 脚本读取的 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||'26124');
|
||||
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;}
|
||||
}
|
||||
Reference in New Issue
Block a user