Skip to content

Commit bf63243

Browse files
authored
Add fs.cpSync, fs.cp and fs/promises cp (#1166)
2 parents 6fa1f8c + 5c39821 commit bf63243

46 files changed

Lines changed: 2398 additions & 98 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎docs/content/docs/limitations.mdx‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ title: "Limitations"
7676

7777
<include>../limitations/builtin-apis/filesystem-flags.mdx</include>
7878

79+
<include>../limitations/builtin-apis/copy.mdx</include>
80+
7981
<include>../limitations/builtin-apis/glob.mdx</include>
8082

8183
<include>../limitations/builtin-apis/filehandle-and-descriptor-i-o.mdx</include>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- **Copy:** `fs.cpSync`, `fs.cp`, and `fs/promises` `cp` support every option and follow Node's two implementations: `cpSync` reports the native binding's errors, and the callback and promise forms report `SystemError`s. Files, directories, and symbolic links are copied in directory order, as in Node. The callback and promise forms complete the copy before the callback runs or the promise settles. An async `filter` must return a promise that is already settled; a pending one throws `SC2020`. On Windows, the three functions throw `SC2020`.

‎docs/content/limitations/builtin-function-values.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@ Other immutable aliases of table-backed Node builtins support direct calls, but
1515

1616
Synchronous and promise filesystem APIs with trailing arguments that affect behavior remain call-only. This includes writers, open, directory creation/removal, metadata readers, and directory readers. Passing them as values could discard modes, flags, encodings, or options during function-signature adaptation.
1717

18-
Native error-first filesystem callbacks can be stored and passed to platform adapters. Reads support Buffer and UTF-8 results; writes preserve string flags and modes; copy supports regular files and recursive directories. Reads and writes reject already-aborted signals, but native file operations complete synchronously before the deferred callback, so cancellation cannot interrupt I/O in progress. Symbolic-link copy, copy filters and clone modes, recursive directory reads, and Dirent results report a native limitation. `fs.glob` compiles only as a direct call; a stored `fs.glob` value throws `SC2020`.
18+
Native error-first filesystem callbacks can be stored and passed to platform adapters. Reads support Buffer and UTF-8 results; writes preserve string flags and modes; copy follows `fs.cp`. Reads and writes reject already-aborted signals, but native file operations complete synchronously before the deferred callback, so cancellation cannot interrupt I/O in progress. Recursive directory reads and Dirent results report a native limitation. `fs.glob` compiles only as a direct call; a stored `fs.glob` value throws `SC2020`.

‎docs/src/generated/node-v24-compatibility-meta.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22
"schemaVersion": 3,
33
"nodeVersion": "24.22.0",
44
"nodeCommit": "f14cd3aa7bf9ca8df2cc5b8bb14beb0f335b1d93",
5-
"artifactVersion": "fba8a4c99de7c5424f50",
5+
"artifactVersion": "ae3ec89c0c8935ca6c07",
66
"rowCount": 3880
77
}

‎docs/src/generated/node-v24-compatibility.json‎

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -314,9 +314,9 @@
314314
"apiEntries": 280,
315315
"static": {
316316
"supported": 7,
317-
"partial": 195,
317+
"partial": 198,
318318
"refused": 0,
319-
"not-implemented": 78,
319+
"not-implemented": 75,
320320
"by-design": 0,
321321
"unreviewed": 0,
322322
"not-applicable": 0
@@ -29667,9 +29667,9 @@
2966729667
"inherited": true
2966829668
},
2966929669
"static": {
29670-
"status": "not-implemented",
29671-
"detail": "No static compiler lowering is registered for this API yet.",
29672-
"verification": "registry-gap"
29670+
"status": "partial",
29671+
"detail": "Implemented for the documented scriptc module-loader subset.",
29672+
"verification": "test-backed"
2967329673
}
2967429674
},
2967529675
{
@@ -30449,9 +30449,9 @@
3044930449
"inherited": true
3045030450
},
3045130451
"static": {
30452-
"status": "not-implemented",
30453-
"detail": "No static compiler lowering is registered for this API yet.",
30454-
"verification": "registry-gap"
30452+
"status": "partial",
30453+
"detail": "Implemented for the documented scriptc module-loader subset.",
30454+
"verification": "test-backed"
3045530455
}
3045630456
},
3045730457
{
@@ -31714,9 +31714,9 @@
3171431714
"inherited": true
3171531715
},
3171631716
"static": {
31717-
"status": "not-implemented",
31718-
"detail": "No static compiler lowering is registered for this API yet.",
31719-
"verification": "registry-gap"
31717+
"status": "partial",
31718+
"detail": "Implemented for the documented scriptc module-loader subset.",
31719+
"verification": "test-backed"
3172031720
}
3172131721
},
3172231722
{

‎internal/compatibility/generated/node-v24-backlog.json‎

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
"tierItemCount": 3472,
88
"tiers": {
99
"static": {
10-
"audit-partial": 1058,
11-
"verify-gap": 2287,
10+
"audit-partial": 1061,
11+
"verify-gap": 2284,
1212
"replace-refusal": 40,
1313
"classify": 2,
1414
"implement": 85
@@ -26214,12 +26214,17 @@
2621426214
"priority": "high",
2621526215
"tiers": {
2621626216
"static": {
26217-
"status": "not-implemented",
26218-
"action": "verify-gap",
26219-
"verification": "registry-gap",
26220-
"confidence": "low",
26221-
"source": "compiler-unmatched:fs",
26222-
"tests": []
26217+
"status": "partial",
26218+
"action": "audit-partial",
26219+
"verification": "test-backed",
26220+
"confidence": "high",
26221+
"source": "compiler-feature:fs.fsPromises.cp",
26222+
"tests": [
26223+
"tests/corpus/5567-fs-cpsync.ts",
26224+
"tests/corpus/5568-fs-cp-errors.js",
26225+
"tests/corpus/5569-fs-cp-symlinks.ts",
26226+
"tests/corpus/5570-fs-cp-async.ts"
26227+
]
2622326228
}
2622426229
}
2622526230
},
@@ -27168,12 +27173,17 @@
2716827173
"priority": "high",
2716927174
"tiers": {
2717027175
"static": {
27171-
"status": "not-implemented",
27172-
"action": "verify-gap",
27173-
"verification": "registry-gap",
27174-
"confidence": "low",
27175-
"source": "compiler-unmatched:fs",
27176-
"tests": []
27176+
"status": "partial",
27177+
"action": "audit-partial",
27178+
"verification": "test-backed",
27179+
"confidence": "high",
27180+
"source": "compiler-feature:fs.fs.cp",
27181+
"tests": [
27182+
"tests/corpus/5567-fs-cpsync.ts",
27183+
"tests/corpus/5568-fs-cp-errors.js",
27184+
"tests/corpus/5569-fs-cp-symlinks.ts",
27185+
"tests/corpus/5570-fs-cp-async.ts"
27186+
]
2717727187
}
2717827188
}
2717927189
},
@@ -28700,12 +28710,17 @@
2870028710
"priority": "high",
2870128711
"tiers": {
2870228712
"static": {
28703-
"status": "not-implemented",
28704-
"action": "verify-gap",
28705-
"verification": "registry-gap",
28706-
"confidence": "low",
28707-
"source": "compiler-unmatched:fs",
28708-
"tests": []
28713+
"status": "partial",
28714+
"action": "audit-partial",
28715+
"verification": "test-backed",
28716+
"confidence": "high",
28717+
"source": "compiler-feature:fs.fs.cpSync",
28718+
"tests": [
28719+
"tests/corpus/5567-fs-cpsync.ts",
28720+
"tests/corpus/5568-fs-cp-errors.js",
28721+
"tests/corpus/5569-fs-cp-symlinks.ts",
28722+
"tests/corpus/5570-fs-cp-async.ts"
28723+
]
2870928724
}
2871028725
}
2871128726
},

‎internal/compatibility/generated/node-v24-internal.json‎

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -326,9 +326,9 @@
326326
"apiEntries": 280,
327327
"static": {
328328
"supported": 7,
329-
"partial": 195,
329+
"partial": 198,
330330
"refused": 0,
331-
"not-implemented": 78,
331+
"not-implemented": 75,
332332
"by-design": 0,
333333
"unreviewed": 0,
334334
"not-applicable": 0
@@ -44021,6 +44021,10 @@
4402144021
"tests/corpus/5548-fs-callback-arguments.cjs",
4402244022
"tests/corpus/5549-fs-callback-option-checks.cjs",
4402344023
"tests/corpus/5554-fs-streams-typed.ts",
44024+
"tests/corpus/5567-fs-cpsync.ts",
44025+
"tests/corpus/5568-fs-cp-errors.js",
44026+
"tests/corpus/5569-fs-cp-symlinks.ts",
44027+
"tests/corpus/5570-fs-cp-async.ts",
4402444028
"tests/corpus/fs-native-realpath.ts",
4402544029
"tests/corpus/node24-fs-links-callbacks.cjs",
4402644030
"tests/corpus/node24-fs-links-errors.cjs",
@@ -45067,8 +45071,14 @@
4506745071
"inherited": true
4506845072
},
4506945073
"static": {
45070-
"status": "not-implemented",
45071-
"evidence": "compiler-unmatched:fs"
45074+
"status": "partial",
45075+
"evidence": "compiler-feature:fs.fsPromises.cp",
45076+
"tests": [
45077+
"tests/corpus/5567-fs-cpsync.ts",
45078+
"tests/corpus/5568-fs-cp-errors.js",
45079+
"tests/corpus/5569-fs-cp-symlinks.ts",
45080+
"tests/corpus/5570-fs-cp-async.ts"
45081+
]
4507245082
},
4507345083
"anchorSource": "exact"
4507445084
},
@@ -46027,8 +46037,14 @@
4602746037
"inherited": true
4602846038
},
4602946039
"static": {
46030-
"status": "not-implemented",
46031-
"evidence": "compiler-unmatched:fs"
46040+
"status": "partial",
46041+
"evidence": "compiler-feature:fs.fs.cp",
46042+
"tests": [
46043+
"tests/corpus/5567-fs-cpsync.ts",
46044+
"tests/corpus/5568-fs-cp-errors.js",
46045+
"tests/corpus/5569-fs-cp-symlinks.ts",
46046+
"tests/corpus/5570-fs-cp-async.ts"
46047+
]
4603246048
},
4603346049
"anchorSource": "exact"
4603446050
},
@@ -47655,8 +47671,14 @@
4765547671
"inherited": true
4765647672
},
4765747673
"static": {
47658-
"status": "not-implemented",
47659-
"evidence": "compiler-unmatched:fs"
47674+
"status": "partial",
47675+
"evidence": "compiler-feature:fs.fs.cpSync",
47676+
"tests": [
47677+
"tests/corpus/5567-fs-cpsync.ts",
47678+
"tests/corpus/5568-fs-cp-errors.js",
47679+
"tests/corpus/5569-fs-cp-symlinks.ts",
47680+
"tests/corpus/5570-fs-cp-async.ts"
47681+
]
4766047682
},
4766147683
"anchorSource": "exact"
4766247684
},

‎internal/compatibility/static-support.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,7 @@
312312
{ "chapter": "child_process", "signatures": ["`stdin` Type: {stream.Writable|null|undefined}"], "status": "partial", "evidence": ["tests/corpus/2893-child-stdin-duplex.ts", "tests/corpus/2894-child-stdin-backpressure.ts", "tests/corpus/2895-child-stdin-write-after-end.ts", "tests/corpus/2896-child-stdin-write-after-finish.ts", "tests/corpus/2897-child-stdin-destroy.ts", "tests/corpus/2898-child-stdin-spawn-failure.ts"] },
313313
{ "chapter": "fs", "signatures": ["`fsPromises.lstat(path[, options])`"], "status": "partial", "evidence": ["tests/corpus/2941-fsp-lstat-identity.ts"] },
314314
{ "chapter": "fs", "signatures": ["`fs.globSync(pattern[, options])`", "`fs.glob(pattern[, options], callback)`", "`fsPromises.glob(pattern[, options])`"], "status": "partial", "evidence": ["tests/corpus/5448-fs-glob-patterns.ts", "tests/corpus/5449-fs-glob-options.ts", "tests/corpus/5450-fs-glob-validation.js", "tests/corpus/5451-fs-promises-glob.ts", "tests/corpus/5452-fs-glob-callback.ts", "tests/fixtures/node-types/fs-glob.ts"] },
315+
{ "chapter": "fs", "signatures": ["`fs.cpSync(src, dest[, options])`", "`fs.cp(src, dest[, options], callback)`", "`fsPromises.cp(src, dest[, options])`"], "status": "partial", "evidence": ["tests/corpus/5567-fs-cpsync.ts", "tests/corpus/5568-fs-cp-errors.js", "tests/corpus/5569-fs-cp-symlinks.ts", "tests/corpus/5570-fs-cp-async.ts"] },
315316
{ "chapter": "process", "signatures": ["`process.send(message[, sendHandle[, options]][, callback])`", "`process.disconnect()`", "`connected` Type: {boolean}", "Event: `'message'`", "Event: `'disconnect'`"], "status": "partial", "evidence": ["tests/corpus/2946-child-fork-ipc/main.ts"] },
316317
{ "chapter": "fs", "symbols": ["fs.Stats", "stats.isBlockDevice", "stats.isCharacterDevice", "stats.isDirectory", "stats.isFIFO", "stats.isFile", "stats.isSocket", "stats.isSymbolicLink"], "status": "partial", "evidence": ["tests/corpus/5541-fs-stats-fields.cjs"] },
317318
{ "chapter": "fs", "parent": "Class: `fs.Stats`", "signatures": ["`mode` Type: {number|bigint}", "`nlink` Type: {number|bigint}", "`uid` Type: {number|bigint}", "`gid` Type: {number|bigint}", "`rdev` Type: {number|bigint}", "`size` Type: {number|bigint}", "`blksize` Type: {number|bigint}", "`blocks` Type: {number|bigint}", "`atimeMs` Type: {number|bigint}", "`mtimeMs` Type: {number|bigint}", "`birthtimeMs` Type: {number|bigint}", "`atimeNs` Type: {bigint}", "`mtimeNs` Type: {bigint}", "`ctimeNs` Type: {bigint}", "`birthtimeNs` Type: {bigint}", "`atime` Type: {Date}", "`mtime` Type: {Date}", "`ctime` Type: {Date}", "`birthtime` Type: {Date}"], "status": "partial", "evidence": ["tests/corpus/5541-fs-stats-fields.cjs"] },

‎packages/compiler/ambient/scriptc-node-fallback.d.ts‎

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1251,6 +1251,45 @@ declare module "node:fs" {
12511251
path: string | Uint8Array | URL,
12521252
type?: string | null,
12531253
): void;
1254+
/* fs.cpSync / fs.cp: copies a file, symlink or (with recursive) a
1255+
* directory tree. filter decides per path; the callback and promise
1256+
* forms complete the copy before reporting. */
1257+
export interface CopySyncOptions {
1258+
dereference?: boolean;
1259+
errorOnExist?: boolean;
1260+
filter?: (source: string, destination: string) => boolean;
1261+
force?: boolean;
1262+
mode?: number;
1263+
preserveTimestamps?: boolean;
1264+
recursive?: boolean;
1265+
verbatimSymlinks?: boolean;
1266+
}
1267+
export interface CopyOptions {
1268+
dereference?: boolean;
1269+
errorOnExist?: boolean;
1270+
filter?: (source: string, destination: string) => boolean | Promise<boolean>;
1271+
force?: boolean;
1272+
mode?: number;
1273+
preserveTimestamps?: boolean;
1274+
recursive?: boolean;
1275+
verbatimSymlinks?: boolean;
1276+
}
1277+
export function cpSync(
1278+
source: string | URL,
1279+
destination: string | URL,
1280+
options?: CopySyncOptions,
1281+
): void;
1282+
export function cp(
1283+
source: string | URL,
1284+
destination: string | URL,
1285+
callback: (err: NodeJS.ErrnoException | null) => void,
1286+
): void;
1287+
export function cp(
1288+
source: string | URL,
1289+
destination: string | URL,
1290+
options: CopyOptions,
1291+
callback: (err: NodeJS.ErrnoException | null) => void,
1292+
): void;
12541293
export function symlink(
12551294
target: string | Uint8Array | URL,
12561295
path: string | Uint8Array | URL,
@@ -1441,6 +1480,9 @@ declare module "node:fs" {
14411480
readonly O_NONBLOCK: number;
14421481
readonly O_TRUNC: number;
14431482
readonly O_APPEND: number;
1483+
readonly COPYFILE_EXCL: number;
1484+
readonly COPYFILE_FICLONE: number;
1485+
readonly COPYFILE_FICLONE_FORCE: number;
14441486
};
14451487
/* A stat(2) snapshot (statSync follows symlinks, lstatSync does not —
14461488
* Node's split) — immutable; the supported surface is exactly these
@@ -1789,6 +1831,11 @@ declare module "fs/promises" {
17891831
path: string | Uint8Array | URL,
17901832
type?: string | null,
17911833
): Promise<void>;
1834+
export function cp(
1835+
source: string | URL,
1836+
destination: string | URL,
1837+
options?: import("node:fs").CopyOptions,
1838+
): Promise<void>;
17921839
export function readlink(
17931840
path: string | Uint8Array | URL,
17941841
options: "buffer" | { encoding: "buffer" },

‎packages/compiler/src/backend/llvm/lib-shared.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,7 @@ export const LIB_FN_SYMS: Record<string, string> = {
461461
"fs.writevSync": "scr_fs_writev_sync",
462462
"fs.linkSync": "scr_fs_link_checked",
463463
"fs.symlinkSync": "scr_fs_symlink_checked",
464+
"fs.cpSync": "scr_fs_cp_sync",
464465
"fs.readlinkSync": "scr_fs_readlink_dyn",
465466
"fs.statfsSync": "scr_fs_statfs_checked",
466467
"fs.readlinkSyncStr": "scr_fs_readlink_str",
@@ -712,6 +713,7 @@ export const LIB_FN_SYMS: Record<string, string> = {
712713
"fsp.rmdir": "scr_fsp_rmdir",
713714
"fsp.link": "scr_fsp_link",
714715
"fsp.symlink": "scr_fsp_symlink",
716+
"fsp.cp": "scr_fsp_cp",
715717
"fsp.readlink": "scr_fsp_readlink_dyn",
716718
"fsp.statfs": "scr_fsp_statfs",
717719
"fsp.readlinkStr": "scr_fsp_readlink_str",

0 commit comments

Comments
 (0)