Files
web_hgdjc_h5/scripts/copy-release-tree.mjs
谢宇宁andClaude Opus 5.5 f65d10e998 Initial commit
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 23:07:59 +07:00

46 lines
1.8 KiB
JavaScript

import {cp, copyFile, lstat, mkdir, readdir, realpath, symlink} from 'node:fs/promises';
import {dirname, join, relative, sep} from 'node:path';
export const under=(path,base)=>path===base||path.startsWith(base+sep);
/**
* Copy a release tree for npm or pnpm layouts.
* In-tree symlinks are rewritten as relative links inside `to` so the package is relocatable.
* Symlinks that escape `from` but stay under `root` are materialized; anything else is rejected.
*/
export async function copyReleaseTree(from,to,{root=from,filter=async()=>true}={}) {
from=await realpath(from);
root=await realpath(root);
async function walk(srcDir,destDir) {
await mkdir(destDir,{recursive:true});
for(const entry of await readdir(srcDir,{withFileTypes:true})) {
const srcPath=join(srcDir,entry.name);
if(!await filter(srcPath))continue;
const destPath=join(destDir,entry.name);
if(entry.isSymbolicLink()){
const target=await realpath(srcPath).catch(()=>null);
if(!target||(!under(target,from)&&!under(target,root)))
throw new Error(`Release source contains a symbolic link: ${srcPath}`);
if(under(target,from)){
const mapped=join(to,relative(from,target));
await symlink(relative(dirname(destPath),mapped),destPath);
continue;
}
await cp(target,destPath,{recursive:true,dereference:true,filter});
continue;
}
if(entry.isDirectory()){
await walk(srcPath,destPath);
continue;
}
if(entry.isFile()){
await copyFile(srcPath,destPath);
continue;
}
// Rare non-file entries (sockets/devices) should never ship.
throw new Error(`Release source contains an unsupported file type: ${srcPath}`);
}
}
await walk(from,to);
}