Skip to content

Commit bea3f1c

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

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
@@ -495,6 +495,9 @@ the end of the file.
495495
<!-- YAML
496496
added: v10.0.0
497497
changes:
498+
- version: REPLACEME
499+
pr-url: https://github.com/nodejs/node/pull/37490
500+
description: The `data` argument supports `AsyncIterable`, `Iterable` and `Stream`.
498501
- version: v14.12.0
499502
pr-url: https://github.com/nodejs/node/pull/34993
500503
description: The `data` parameter will stringify an object with an
@@ -505,14 +508,16 @@ changes:
505508
strings anymore.
506509
-->
507510
508-
* `data` {string|Buffer|Uint8Array|Object}
511+
* `data` {string|Buffer|Uint8Array|Object|AsyncIterable|Iterable
512+
|Stream}
509513
* `options` {Object|string}
510514
* `encoding` {string|null} The expected character encoding when `data` is a
511515
string. **Default:** `'utf8'`
512516
* Returns: {Promise}
513517
514518
Asynchronously writes data to a file, replacing the file if it already exists.
515-
`data` can be a string, a buffer, or an object with an own `toString` function
519+
`data` can be a string, a buffer, an {AsyncIterable} or {Iterable} object, or an
520+
object with an own `toString` function
516521
property. The promise is resolved with no arguments upon success.
517522
518523
If `options` is a string, then it specifies the `encoding`.
@@ -1187,7 +1192,7 @@ added: v10.0.0
11871192
changes:
11881193
- version: REPLACEME
11891194
pr-url: https://github.com/nodejs/node/pull/37490
1190-
description: The `data` argument supports `AsyncIterable`, `Iterable` & `Stream`.
1195+
description: The `data` argument supports `AsyncIterable`, `Iterable` and `Stream`.
11911196
- version: v14.17.0
11921197
pr-url: https://github.com/nodejs/node/pull/35993
11931198
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
@@ -9,6 +9,7 @@ const common = require('../common');
99
const fs = require('fs');
1010
const { open, writeFile } = fs.promises;
1111
const path = require('path');
12+
const { Readable } = require('stream');
1213
const tmpdir = require('../common/tmpdir');
1314
const assert = require('assert');
1415
const tmpDir = tmpdir.path;
@@ -18,13 +19,15 @@ tmpdir.refresh();
1819
async function validateWriteFile() {
1920
const filePathForHandle = path.resolve(tmpDir, 'tmp-write-file2.txt');
2021
const fileHandle = await open(filePathForHandle, 'w+');
21-
const buffer = Buffer.from('Hello world'.repeat(100), 'utf8');
22+
try {
23+
const buffer = Buffer.from('Hello world'.repeat(100), 'utf8');
2224

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

3033
// Signal aborted while writing file
@@ -40,6 +43,155 @@ async function doWriteAndCancel() {
4043
});
4144
}
4245

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

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

+8-6
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,10 @@ async function doWriteStreamWithCancel() {
6767
const controller = new AbortController();
6868
const { signal } = controller;
6969
process.nextTick(() => controller.abort());
70-
assert.rejects(fsPromises.writeFile(otherDest, stream, { signal }), {
71-
name: 'AbortError'
72-
});
70+
await assert.rejects(
71+
fsPromises.writeFile(otherDest, stream, { signal }),
72+
{ name: 'AbortError' }
73+
);
7374
}
7475

7576
async function doWriteIterable() {
@@ -121,9 +122,10 @@ async function doWriteWithCancel() {
121122
const controller = new AbortController();
122123
const { signal } = controller;
123124
process.nextTick(() => controller.abort());
124-
assert.rejects(fsPromises.writeFile(otherDest, buffer, { signal }), {
125-
name: 'AbortError'
126-
});
125+
await assert.rejects(
126+
fsPromises.writeFile(otherDest, buffer, { signal }),
127+
{ name: 'AbortError' }
128+
);
127129
}
128130

129131
async function doAppend() {

0 commit comments

Comments
 (0)