CodeSampleX

Beispiel

fs-extra 11.3.0: fs.move and fs.moveSync reject with 'dest already exists.' unless overwrite: true is explicitly specified, while automatically creating nested destination directories

Verifiziertes Beispiel für npm fs-extra 11.3.0: fs.move and fs.moveSync reject with 'dest already exists.' unless overwrite: true is explicitly specified…

sha256:86d5f74b0cdf68342405e8fbef8ed2ae4709675dd58a6459bd710f9420a56721

Dieses Netzwerk bietet eine Sache: ein Sample, das baut. Es hat es in einer Sandbox ausgeführt und die signierte Quittung behalten. Es bewertet nichts und garantiert nichts — ob derselbe Code bei Ihnen baut, hat es nicht gemessen. Wie viele verschiedene Signaturschlüssel eine bestandene Vertragsquittung eingereicht haben. Einer ist der Autor allein; mehr als einer heißt, jemand anderes hat es auch gebaut. Ein Schlüssel wird selbst erzeugt und hat keine registrierte Identität dahinter — gezählt werden Schlüssel, nicht Personen. MIT-0

Ausführungsbelege

Die deklarierte Umgebung und die signierten Läufe stehen getrennt, damit Sie genau sehen, was dieses Sample ausgeführt hat und wo.

Beleggrundlage
Signierter Vertrag bestanden
Verifizierungsbelege
3
Signaturschlüssel, die es gebaut haben
3
Deklarierte Umgebung node linux x64 node node npm

Umgebungen der Verifizierungsläufe

Umgebung Contract Stufen Lauf
node 22 · linux alpine/x64 · docker ed25519:d91480838ac982c9 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · node-typescript@1
2026-08-17
node 22 · linux alpine/x64 · docker ed25519:2175b912ea1c23b1 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · node-typescript@1
2026-08-18
node 22 · linux alpine/x64 · docker ed25519:c1973797be207ac4 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · node-typescript@1node:22-alpine@sha256:c610fcdfb1d5…
2026-09-08

Fall

HOW
Ziel
fs.move and fs.moveSync reject with 'dest already exists.' unless overwrite: true is explicitly specified, while automatically creating nested destination directories
Pakete
Symbole
  • fs.move
  • fs.moveSync
Umgebung
node
Erstellt
2026-08-17T19:51:25Z

Contract

  1. fs.move rejects with 'dest already exists.' when destination exists unless overwrite: true is explicitly passed, while fs.copy defaults overwrite to true
  2. fs.move with overwrite: true replaces existing destination file contents and removes the source
  3. fs.move automatically creates missing nested destination parent directories before moving
  4. fs.moveSync enforces identical overwrite: false refusal and recursive parent creation synchronously
  5. fs.move rejects when attempting to move a directory into a subdirectory of itself

Dateien

  • NOTES.md
  • csx.json
  • package-lock.json
  • package.json
  • src/index.mjs
  • test/contract.mjs

Quellartefakt herunterladen (tar.gz)

Quelltext

NOTES.md
# fs-extra `fs.move` overwrite default and directory creation semantics

## Prior Solutions Search

An initial search of known solutions returned sample `sha256:a61dda146053f4e7d4ecbf68916baffd57e0c28e2d4eab3ff805e8149e69aaa3`, which demonstrated `fs.outputJson`, `fs.readJson`, `fs.copy`, and `fs.pathExists` for atomic JSON persistence and parent directory creation.

This sample addresses a different and distinct behavior: the asymmetric overwrite defaults between `fs.move` and `fs.copy`, the safe rejection semantics of `fs.move`, and its automated parent directory creation.

## Pinned Release

- `fs-extra`: `11.3.0` (Node `>=14.14`, tested on Node 22+)

## Observed Behaviour

1. **Default Overwrite Refusal in `fs.move`**:
   - In Node's standard library `fs.rename(src, dest)` and UNIX `mv`, existing destination files are overwritten by default.
   - Within `fs-extra`, `fs.copy(src, dest)` defaults option `overwrite` to `true`.
   - However, `fs.move(src, dest)` and `fs.moveSync(src, dest)` explicitly default `overwrite` to `false` to prevent unintentional data loss. When the destination path already exists and `{ overwrite: true }` is not passed, `fs.move` rejects loudly with `Error: dest already exists.`.

2. **Explicit Overwrite Handling**:
   - When `{ overwrite: true }` is provided, `fs.move(src, dest, { overwrite: true })` replaces the existing destination file and removes the original source file.

3. **Automatic Parent Directory Creation**:
   - Unlike standard `fs.rename` (which throws `ENOENT` if destination parent directories do not exist), `fs.move` and `fs.moveSync` automatically recursively create any missing parent directory hierarchy before moving.

4. **Self-Subdirectory Protection**:
   - When attempting to move a directory into a subdirectory of itself, `fs.move` rejects with `Cannot move '<src>' to a subdirectory of itself, '<dest>'.`.

## Failure Mode

When callers assume `fs.move` mirrors the overwrite behavior of `fs.copy` or Node's `fs.rename`, execution fails loudly at runtime with `Error: dest already exists.` as soon as destination files pre-exist.
csx.json
{"case":{"believed":"fs.move overwrites existing destination files by default like fs.copy and Node's fs.rename, and requires destination parent directories to already exist","caseId":"case:sha256:d67d332593724d92c547a6adc4e776bb977e233444a5cbb3771fa76adbf0dacd","contract":["fs.move rejects with 'dest already exists.' when destination exists unless overwrite: true is explicitly passed, while fs.copy defaults overwrite to true","fs.move with overwrite: true replaces existing destination file contents and removes the source","fs.move automatically creates missing nested destination parent directories before moving","fs.moveSync enforces identical overwrite: false refusal and recursive parent creation synchronously","fs.move rejects when attempting to move a directory into a subdirectory of itself"],"goal":"fs.move and fs.moveSync reject with 'dest already exists.' unless overwrite: true is explicitly specified, while automatically creating nested destination directories","kind":"HOW","packages":["pkg:npm/fs-extra@11.3.0"],"schemaVersion":1,"symbols":["fs.move","fs.moveSync"]},"contractCommand":["node","test/contract.mjs"],"environment":{"arch":"x64","ecosystem":"npm","executionContext":"node","language":"node","os":"linux","packageManager":"npm","runtime":"node","schemaVersion":1},"license":"MIT-0","packages":["pkg:npm/fs-extra@11.3.0"],"schemaVersion":1,"symbols":["fs.move","fs.moveSync"],"verifierAdapter":"node-typescript@1"}
package-lock.json
{
  "name": "csx-sample-fs-extra-move-api",
  "version": "1.0.0",
  "lockfileVersion": 3,
  "requires": true,
  "packages": {
    "": {
      "name": "csx-sample-fs-extra-move-api",
      "version": "1.0.0",
      "license": "MIT-0",
      "dependencies": {
        "fs-extra": "11.3.0"
      }
    },
    "node_modules/fs-extra": {
      "version": "11.3.0",
      "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz",
      "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==",
      "license": "MIT",
      "dependencies": {
        "graceful-fs": "^4.2.0",
        "jsonfile": "^6.0.1",
        "universalify": "^2.0.0"
      },
      "engines": {
        "node": ">=14.14"
      }
    },
    "node_modules/graceful-fs": {
      "version": "4.2.11",
      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
      "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
      "license": "ISC"
    },
    "node_modules/jsonfile": {
      "version": "6.2.1",
      "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
      "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
      "license": "MIT",
      "dependencies": {
        "universalify": "^2.0.0"
      },
      "optionalDependencies": {
        "graceful-fs": "^4.1.6"
      }
    },
    "node_modules/universalify": {
      "version": "2.0.1",
      "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
      "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
      "license": "MIT",
      "engines": {
        "node": ">= 10.0.0"
      }
    }
  }
}
package.json
{
  "name": "csx-sample-fs-extra-move-api",
  "version": "1.0.0",
  "private": true,
  "type": "module",
  "license": "MIT-0",
  "dependencies": {
    "fs-extra": "11.3.0"
  }
}
src/index.mjs
import fs from 'fs-extra';

/**
 * Moves a file or directory to a target destination with fs-extra's safe overwrite semantics.
 * By default, fs.move refuses to overwrite an existing destination file/directory unless
 * options.overwrite is explicitly set to true. However, it automatically creates any missing
 * parent directories for the destination path.
 */
export async function relocateFile(src, dest, options = {}) {
  return fs.move(src, dest, options);
}

/**
 * Synchronously moves a file or directory to a target destination.
 */
export function relocateFileSync(src, dest, options = {}) {
  return fs.moveSync(src, dest, options);
}
test/contract.mjs
import os from 'node:os';
import path from 'node:path';
import { strict as assert } from 'node:assert';
import fs from 'fs-extra';
import { relocateFile, relocateFileSync } from '../src/index.mjs';

const root = await fs.mkdtemp(path.join(os.tmpdir(), 'fsx-move-'));

try {
  // 1. fs.copy defaults overwrite to true, but fs.move defaults overwrite to false
  const src1 = path.join(root, 'src1.txt');
  const dest1 = path.join(root, 'dest1.txt');
  await fs.writeFile(src1, 'source payload');
  await fs.writeFile(dest1, 'original destination');

  // fs.copy overwrites by default
  await fs.copy(src1, dest1);
  assert.equal(await fs.readFile(dest1, 'utf8'), 'source payload');

  // fs.move fails by default when destination exists
  await assert.rejects(
    async () => relocateFile(src1, dest1),
    (err) => {
      assert.equal(err.message, 'dest already exists.');
      return true;
    }
  );
  // Destination and source remain unchanged after rejected move
  assert.equal(await fs.readFile(dest1, 'utf8'), 'source payload');
  assert.equal(await fs.pathExists(src1), true);

  // 2. Explicit overwrite: true allows fs.move to replace destination and remove source
  await relocateFile(src1, dest1, { overwrite: true });
  assert.equal(await fs.readFile(dest1, 'utf8'), 'source payload');
  assert.equal(await fs.pathExists(src1), false);

  // 3. fs.move automatically creates missing nested destination parent directories
  const src2 = path.join(root, 'src2.txt');
  const deepDest = path.join(root, 'deep', 'nested', 'structure', 'target.txt');
  await fs.writeFile(src2, 'deep payload');
  assert.equal(await fs.pathExists(path.dirname(deepDest)), false);

  await relocateFile(src2, deepDest);
  assert.equal(await fs.pathExists(deepDest), true);
  assert.equal(await fs.readFile(deepDest, 'utf8'), 'deep payload');
  assert.equal(await fs.pathExists(src2), false);

  // 4. fs.moveSync mirrors the safe default and recursive parent creation synchronously
  const syncSrc = path.join(root, 'sync-src.txt');
  const syncDest = path.join(root, 'sync-dest.txt');
  fs.writeFileSync(syncSrc, 'sync payload');
  fs.writeFileSync(syncDest, 'sync existing');

  assert.throws(
    () => relocateFileSync(syncSrc, syncDest),
    (err) => {
      assert.equal(err.message, 'dest already exists.');
      return true;
    }
  );

  const syncDeepDest = path.join(root, 'sync', 'deep', 'target.txt');
  relocateFileSync(syncSrc, syncDeepDest);
  assert.equal(fs.readFileSync(syncDeepDest, 'utf8'), 'sync payload');
  assert.equal(fs.pathExistsSync(syncSrc), false);

  // 5. fs.move rejects when moving a directory into a subdirectory of itself
  const parentDir = path.join(root, 'parent-dir');
  const subDir = path.join(parentDir, 'sub-dir');
  await fs.ensureDir(parentDir);

  await assert.rejects(
    async () => fs.move(parentDir, subDir),
    (err) => {
      assert(err.message.includes('Cannot move'));
      assert(err.message.includes('to a subdirectory of itself'));
      return true;
    }
  );

  console.log('CONTRACT PASS: fs.move overwrite defaults and path creation verified');
} finally {
  await fs.remove(root);
}

Ursprungs-Seeder

csx-seed