Files
web_hgcg_h5/scripts/package-standalone.mjs
谢宇宁andClaude Opus 5.5 8cdbfee08f Initial commit
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 16:23:48 +07:00

62 lines
3.7 KiB
JavaScript

import {cp, lstat, mkdir, readFile, realpath, rm, writeFile} from 'node:fs/promises';
import {basename, join, relative, resolve, sep} from 'node:path';
import {fileURLToPath} from 'node:url';
const projectRoot=fileURLToPath(new URL('../',import.meta.url));
/** Assemble a relocatable release; only the selected public environment profile is shipped. */
export async function packageStandalone(environment,{root=projectRoot}={}) {
if(!['test','production'].includes(environment))throw new Error('Usage: node scripts/package-standalone.mjs test|production');
root=await realpath(root);
const build=environment==='test'?'.next-test':'.next';
const dist=join(root,'dist'),destination=join(dist,environment),source=join(root,build,'standalone');
async function directory(path,optional=false) {
const info=await lstat(path).catch(error=>{if(optional&&error.code==='ENOENT')return null;throw error;});
if(info&&(!info.isDirectory()||info.isSymbolicLink()||await realpath(path)!==path))throw new Error(`Unsafe release directory: ${path}`);
}
for(const path of [join(root,build),source])await directory(path);
for(const file of [join(source,'server.js'),join(source,build,'BUILD_ID'),join(root,'config',environment+'.env')]) {
if(!(await lstat(file)).isFile()||!(await readFile(file)).length)throw new Error(`Missing release file: ${file}`);
}
await directory(dist,true);await mkdir(dist,{recursive:true});await directory(destination,true);
// A fixed dist/<environment> is the only location removed by this command.
await rm(destination,{recursive:true,force:true});
const filter=async path=>{
const name=basename(path);
if(name.startsWith('.env')||name.endsWith('.local.env')||name.startsWith('._')||name==='.DS_Store'||name.endsWith('.log'))return false;
// Dynamic filesystem reads can make Next trace sibling build/cache/test directories.
// Keep this project's runtime roots, never recursively include earlier dist releases.
if(path.startsWith(source+sep)&&!['server.js','package.json','package-lock.json',build,'node_modules','src'].includes(relative(source,path).split(sep)[0]))return false;
if(path===join(source,build,'cache'))return false;
if((await lstat(path)).isSymbolicLink())throw new Error(`Release source contains a symbolic link: ${path}`);
return true;
};
await cp(source,destination,{recursive:true,filter});
for(const [from,to] of [[join(root,'public'),'public'],[join(root,build,'static'),build+'/static'],
[join(root,'node_modules/dotenv'),'node_modules/dotenv']]) {
await cp(from,join(destination,to),{recursive:true,filter});
}
await mkdir(join(destination,'config'),{recursive:true});await mkdir(join(destination,'scripts'),{recursive:true});
for(const file of ['package.json','package-lock.json','scripts/next-env.mjs','scripts/check-health.mjs','config/'+environment+'.env']) {
await cp(join(root,file),join(destination,file));
}
await writeFile(join(destination,'start.mjs'),`import {fileURLToPath} from 'node:url';
import {nextCommand} from './scripts/next-env.mjs';
const root=fileURLToPath(new URL('./',import.meta.url));
process.chdir(root);
const {env}=nextCommand(${JSON.stringify(environment)},'start',[],{root});
Object.assign(process.env,env);
process.env.HOSTNAME=process.env.HOSTNAME||'0.0.0.0';
await import('./server.js');
`);
console.log(`Standalone release ready: ${destination}`);
return destination;
}
if(process.argv[1]&&resolve(process.argv[1])===fileURLToPath(import.meta.url)) {
try {
if(process.argv.length!==3)throw new Error('Usage: node scripts/package-standalone.mjs test|production');
await packageStandalone(process.argv[2]);
}catch(error){console.error(error.message);process.exitCode=1;}
}