Skip to content

Commit 52abf27

Browse files
committed
fs: add docs and tests for AsyncIterable support in fh.writeFile
Refs: #37490 PR-URL: #39836 Reviewed-By: Nitzan Uziely <linkgoron@gmail.com>
1 parent 6fdd582 commit 52abf27

File tree

3 files changed

+177
-18
lines changed

3 files changed

+177
-18
lines changed

doc/api/fs.md

+8-3
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,9 @@ the end of the file.
545545
<!-- YAML
546546
added: v10.0.0
547547
changes:
548+
- version: v15.14.0
549+
pr-url: https://github.com/nodejs/node/pull/37490
550+
description: The `data` argument supports `AsyncIterable`, `Iterable` and `Stream`.
548551
- version: v14.12.0
549552
pr-url: https://github.com/nodejs/node/pull/34993
550553
description: The `data` parameter will stringify an object with an
@@ -555,14 +558,16 @@ changes:
555558
strings anymore.
556559
-->
557560
558-
* `data` {string|Buffer|TypedArray|DataView|Object}
561+
* `data` {string|Buffer|TypedArray|DataView|Object|AsyncIterable|Iterable
562+
|Stream}
559563
* `options` {Object|string}
560564
* `encoding` {string|null} The expected character encoding when `data` is a
561565
string. **Default:** `'utf8'`
562566
* Returns: {Promise}
563567
564568
Asynchronously writes data to a file, replacing the file if it already exists.
565-
`data` can be a string, a buffer, or an object with an own `toString` function
569+
`data` can be a string, a buffer, an {AsyncIterable} or {Iterable} object, or an
570+
object with an own `toString` function
566571
property. The promise is resolved with no arguments upon success.
567572
568573
If `options` is a string, then it specifies the `encoding`.
@@ -1341,7 +1346,7 @@ added: v10.0.0
13411346
changes:
13421347
- version: v15.14.0
13431348
pr-url: https://github.com/nodejs/node/pull/37490
1344-
description: The `data` argument supports `AsyncIterable`, `Iterable` & `Stream`.
1349+
description: The `data` argument supports `AsyncIterable`, `Iterable` and `Stream`.
13451350
- version: v15.2.0
13461351
pr-url: https://github.com/nodejs/node/pull/35993
13471352
description: The options argument may include an AbortSignal to abort an

test/parallel/test-fs-promises-file-handle-writeFile.js

+161-9
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const common = require('../common');
88
const fs = require('fs');
99
const { open, writeFile } = fs.promises;
1010
const path = require('path');
11+
const { Readable } = require('stream');
1112
const tmpdir = require('../common/tmpdir');
1213
const assert = require('assert');
1314
const tmpDir = tmpdir.path;
@@ -17,13 +18,15 @@ tmpdir.refresh();
1718
async function validateWriteFile() {
1819
const filePathForHandle = path.resolve(tmpDir, 'tmp-write-file2.txt');
1920
const fileHandle = await open(filePathForHandle, 'w+');
20-
const buffer = Buffer.from('Hello world'.repeat(100), 'utf8');
21-
22-
await fileHandle.writeFile(buffer);
23-
const readFileData = fs.readFileSync(filePathForHandle);
24-
assert.deepStrictEqual(buffer, readFileData);
21+
try {
22+
const buffer = Buffer.from('Hello world'.repeat(100), 'utf8');
2523

26-
await fileHandle.close();
24+
await fileHandle.writeFile(buffer);
25+
const readFileData = fs.readFileSync(filePathForHandle);
26+
assert.deepStrictEqual(buffer, readFileData);
27+
} finally {
28+
await fileHandle.close();
29+
}
2730
}
2831

2932
// Signal aborted while writing file
@@ -43,6 +46,155 @@ async function doWriteAndCancel() {
4346
}
4447
}
4548

46-
validateWriteFile()
47-
.then(doWriteAndCancel)
48-
.then(common.mustCall());
49+
const dest = path.resolve(tmpDir, 'tmp.txt');
50+
const otherDest = path.resolve(tmpDir, 'tmp-2.txt');
51+
const stream = Readable.from(['a', 'b', 'c']);
52+
const stream2 = Readable.from(['ümlaut', ' ', 'sechzig']);
53+
const iterable = {
54+
expected: 'abc',
55+
*[Symbol.iterator]() {
56+
yield 'a';
57+
yield 'b';
58+
yield 'c';
59+
}
60+
};
61+
function iterableWith(value) {
62+
return {
63+
*[Symbol.iterator]() {
64+
yield value;
65+
}
66+
};
67+
}
68+
const bufferIterable = {
69+
expected: 'abc',
70+
*[Symbol.iterator]() {
71+
yield Buffer.from('a');
72+
yield Buffer.from('b');
73+
yield Buffer.from('c');
74+
}
75+
};
76+
const asyncIterable = {
77+
expected: 'abc',
78+
async* [Symbol.asyncIterator]() {
79+
yield 'a';
80+
yield 'b';
81+
yield 'c';
82+
}
83+
};
84+
85+
async function doWriteStream() {
86+
const fileHandle = await open(dest, 'w+');
87+
try {
88+
await fileHandle.writeFile(stream);
89+
const expected = 'abc';
90+
const data = fs.readFileSync(dest, 'utf-8');
91+
assert.deepStrictEqual(data, expected);
92+
} finally {
93+
await fileHandle.close();
94+
}
95+
}
96+
97+
async function doWriteStreamWithCancel() {
98+
const controller = new AbortController();
99+
const { signal } = controller;
100+
process.nextTick(() => controller.abort());
101+
const fileHandle = await open(otherDest, 'w+');
102+
try {
103+
await assert.rejects(
104+
fileHandle.writeFile(stream, { signal }),
105+
{ name: 'AbortError' }
106+
);
107+
} finally {
108+
await fileHandle.close();
109+
}
110+
}
111+
112+
async function doWriteIterable() {
113+
const fileHandle = await open(dest, 'w+');
114+
try {
115+
await fileHandle.writeFile(iterable);
116+
const data = fs.readFileSync(dest, 'utf-8');
117+
assert.deepStrictEqual(data, iterable.expected);
118+
} finally {
119+
await fileHandle.close();
120+
}
121+
}
122+
123+
async function doWriteInvalidIterable() {
124+
const fileHandle = await open(dest, 'w+');
125+
try {
126+
await Promise.all(
127+
[42, 42n, {}, Symbol('42'), true, undefined, null, NaN].map((value) =>
128+
assert.rejects(
129+
fileHandle.writeFile(iterableWith(value)),
130+
{ code: 'ERR_INVALID_ARG_TYPE' }
131+
)
132+
)
133+
);
134+
} finally {
135+
await fileHandle.close();
136+
}
137+
}
138+
139+
async function doWriteIterableWithEncoding() {
140+
const fileHandle = await open(dest, 'w+');
141+
try {
142+
await fileHandle.writeFile(stream2, 'latin1');
143+
const expected = 'ümlaut sechzig';
144+
const data = fs.readFileSync(dest, 'latin1');
145+
assert.deepStrictEqual(data, expected);
146+
} finally {
147+
await fileHandle.close();
148+
}
149+
}
150+
151+
async function doWriteBufferIterable() {
152+
const fileHandle = await open(dest, 'w+');
153+
try {
154+
await fileHandle.writeFile(bufferIterable);
155+
const data = fs.readFileSync(dest, 'utf-8');
156+
assert.deepStrictEqual(data, bufferIterable.expected);
157+
} finally {
158+
await fileHandle.close();
159+
}
160+
}
161+
162+
async function doWriteAsyncIterable() {
163+
const fileHandle = await open(dest, 'w+');
164+
try {
165+
await fileHandle.writeFile(asyncIterable);
166+
const data = fs.readFileSync(dest, 'utf-8');
167+
assert.deepStrictEqual(data, asyncIterable.expected);
168+
} finally {
169+
await fileHandle.close();
170+
}
171+
}
172+
173+
async function doWriteInvalidValues() {
174+
const fileHandle = await open(dest, 'w+');
175+
try {
176+
await Promise.all(
177+
[42, 42n, {}, Symbol('42'), true, undefined, null, NaN].map((value) =>
178+
assert.rejects(
179+
fileHandle.writeFile(value),
180+
{ code: 'ERR_INVALID_ARG_TYPE' }
181+
)
182+
)
183+
);
184+
} finally {
185+
await fileHandle.close();
186+
}
187+
}
188+
189+
(async () => {
190+
await validateWriteFile();
191+
await doWriteAndCancel();
192+
await doWriteStream();
193+
await doWriteStreamWithCancel();
194+
await doWriteIterable();
195+
await doWriteInvalidIterable();
196+
await doWriteIterableWithEncoding();
197+
await doWriteBufferIterable();
198+
await doWriteAsyncIterable();
199+
await doWriteInvalidValues();
200+
})().then(common.mustCall());

test/parallel/test-fs-promises-writefile.js

+8-6
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,10 @@ async function doWriteStreamWithCancel() {
7474
const controller = new AbortController();
7575
const { signal } = controller;
7676
process.nextTick(() => controller.abort());
77-
assert.rejects(fsPromises.writeFile(otherDest, stream, { signal }), {
78-
name: 'AbortError'
79-
});
77+
await assert.rejects(
78+
fsPromises.writeFile(otherDest, stream, { signal }),
79+
{ name: 'AbortError' }
80+
);
8081
}
8182

8283
async function doWriteIterable() {
@@ -134,9 +135,10 @@ async function doWriteWithCancel() {
134135
const controller = new AbortController();
135136
const { signal } = controller;
136137
process.nextTick(() => controller.abort());
137-
assert.rejects(fsPromises.writeFile(otherDest, buffer, { signal }), {
138-
name: 'AbortError'
139-
});
138+
await assert.rejects(
139+
fsPromises.writeFile(otherDest, buffer, { signal }),
140+
{ name: 'AbortError' }
141+
);
140142
}
141143

142144
async function doAppend() {

0 commit comments

Comments
 (0)