fix(installer): add move() and overwrite support to fs-native

Add missing move() with cross-device fallback (rename → copy+rm on
EXDEV), needed by OfficialModules.createModuleDirectories for directory
migrations during upgrades.

Honor overwrite/errorOnExist options in copy() to match fs-extra
behavior for callers that pass these flags.
This commit is contained in:
Brian Madison 2026-04-13 00:52:41 -05:00
parent a6d075bd0b
commit c6c8301ea1
1 changed files with 24 additions and 0 deletions

View File

@ -24,11 +24,21 @@ async function remove(p) {
async function copy(src, dest, options = {}) { async function copy(src, dest, options = {}) {
const filterFn = options.filter; const filterFn = options.filter;
const overwrite = options.overwrite !== false;
const srcStat = await fsp.stat(src); const srcStat = await fsp.stat(src);
if (srcStat.isFile()) { if (srcStat.isFile()) {
if (filterFn && !(await filterFn(src, dest))) return; if (filterFn && !(await filterFn(src, dest))) return;
await fsp.mkdir(path.dirname(dest), { recursive: true }); await fsp.mkdir(path.dirname(dest), { recursive: true });
if (!overwrite) {
try {
await fsp.access(dest);
if (options.errorOnExist) throw new Error(`${dest} already exists`);
return;
} catch (error) {
if (error.message.includes('already exists')) throw error;
}
}
await fsp.copyFile(src, dest); await fsp.copyFile(src, dest);
return; return;
} }
@ -43,6 +53,19 @@ async function copy(src, dest, options = {}) {
} }
} }
async function move(src, dest) {
try {
await fsp.rename(src, dest);
} catch (error) {
if (error.code === 'EXDEV') {
await copy(src, dest);
await fsp.rm(src, { recursive: true, force: true });
} else {
throw error;
}
}
}
function readJsonSync(p) { function readJsonSync(p) {
return JSON.parse(fs.readFileSync(p, 'utf8')); return JSON.parse(fs.readFileSync(p, 'utf8'));
} }
@ -72,6 +95,7 @@ module.exports = {
ensureDir, ensureDir,
remove, remove,
copy, copy,
move,
readJsonSync, readJsonSync,
writeJson, writeJson,