Skip to content

Commit 6bd6214

Browse files
LiviaMedeirosCeres6
authored andcommitted
fs: make mkdtemp accept buffers and URL
PR-URL: nodejs#48828 Reviewed-By: Mohammed Keyvanzadeh <mohammadkeyvanzade94@gmail.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
1 parent 7a8bb62 commit 6bd6214

File tree

5 files changed

+139
-35
lines changed

5 files changed

+139
-35
lines changed

doc/api/fs.md

+12-3
Original file line numberDiff line numberDiff line change
@@ -1163,14 +1163,17 @@ makeDirectory().catch(console.error);
11631163
<!-- YAML
11641164
added: v10.0.0
11651165
changes:
1166+
- version: REPLACEME
1167+
pr-url: https://github.com/nodejs/node/pull/48828
1168+
description: The `prefix` parameter now accepts buffers and URL.
11661169
- version:
11671170
- v16.5.0
11681171
- v14.18.0
11691172
pr-url: https://github.com/nodejs/node/pull/39028
11701173
description: The `prefix` parameter now accepts an empty string.
11711174
-->
11721175
1173-
* `prefix` {string}
1176+
* `prefix` {string|Buffer|URL}
11741177
* `options` {string|Object}
11751178
* `encoding` {string} **Default:** `'utf8'`
11761179
* Returns: {Promise} Fulfills with a string containing the file system path
@@ -3254,6 +3257,9 @@ See the POSIX mkdir(2) documentation for more details.
32543257
<!-- YAML
32553258
added: v5.10.0
32563259
changes:
3260+
- version: REPLACEME
3261+
pr-url: https://github.com/nodejs/node/pull/48828
3262+
description: The `prefix` parameter now accepts buffers and URL.
32573263
- version: v18.0.0
32583264
pr-url: https://github.com/nodejs/node/pull/41678
32593265
description: Passing an invalid callback to the `callback` argument
@@ -3277,7 +3283,7 @@ changes:
32773283
description: The `callback` parameter is optional now.
32783284
-->
32793285
3280-
* `prefix` {string}
3286+
* `prefix` {string|Buffer|URL}
32813287
* `options` {string|Object}
32823288
* `encoding` {string} **Default:** `'utf8'`
32833289
* `callback` {Function}
@@ -5566,14 +5572,17 @@ See the POSIX mkdir(2) documentation for more details.
55665572
<!-- YAML
55675573
added: v5.10.0
55685574
changes:
5575+
- version: REPLACEME
5576+
pr-url: https://github.com/nodejs/node/pull/48828
5577+
description: The `prefix` parameter now accepts buffers and URL.
55695578
- version:
55705579
- v16.5.0
55715580
- v14.18.0
55725581
pr-url: https://github.com/nodejs/node/pull/39028
55735582
description: The `prefix` parameter now accepts an empty string.
55745583
-->
55755584
5576-
* `prefix` {string}
5585+
* `prefix` {string|Buffer|URL}
55775586
* `options` {string|Object}
55785587
* `encoding` {string} **Default:** `'utf8'`
55795588
* Returns: {string}

lib/fs.js

+21-9
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,6 @@ const {
105105
getValidatedPath,
106106
getValidMode,
107107
handleErrorFromBinding,
108-
nullCheck,
109108
possiblyTransformPath,
110109
preprocessSymlinkDestination,
111110
Stats,
@@ -2900,7 +2899,7 @@ realpath.native = (path, options, callback) => {
29002899

29012900
/**
29022901
* Creates a unique temporary directory.
2903-
* @param {string} prefix
2902+
* @param {string | Buffer | URL} prefix
29042903
* @param {string | { encoding?: string; }} [options]
29052904
* @param {(
29062905
* err?: Error,
@@ -2912,27 +2911,40 @@ function mkdtemp(prefix, options, callback) {
29122911
callback = makeCallback(typeof options === 'function' ? options : callback);
29132912
options = getOptions(options);
29142913

2915-
validateString(prefix, 'prefix');
2916-
nullCheck(prefix, 'prefix');
2914+
prefix = getValidatedPath(prefix, 'prefix');
29172915
warnOnNonPortableTemplate(prefix);
2916+
2917+
let path;
2918+
if (typeof prefix === 'string') {
2919+
path = `${prefix}XXXXXX`;
2920+
} else {
2921+
path = Buffer.concat([prefix, Buffer.from('XXXXXX')]);
2922+
}
2923+
29182924
const req = new FSReqCallback();
29192925
req.oncomplete = callback;
2920-
binding.mkdtemp(`${prefix}XXXXXX`, options.encoding, req);
2926+
binding.mkdtemp(path, options.encoding, req);
29212927
}
29222928

29232929
/**
29242930
* Synchronously creates a unique temporary directory.
2925-
* @param {string} prefix
2931+
* @param {string | Buffer | URL} prefix
29262932
* @param {string | { encoding?: string; }} [options]
29272933
* @returns {string}
29282934
*/
29292935
function mkdtempSync(prefix, options) {
29302936
options = getOptions(options);
29312937

2932-
validateString(prefix, 'prefix');
2933-
nullCheck(prefix, 'prefix');
2938+
prefix = getValidatedPath(prefix, 'prefix');
29342939
warnOnNonPortableTemplate(prefix);
2935-
const path = `${prefix}XXXXXX`;
2940+
2941+
let path;
2942+
if (typeof prefix === 'string') {
2943+
path = `${prefix}XXXXXX`;
2944+
} else {
2945+
path = Buffer.concat([prefix, Buffer.from('XXXXXX')]);
2946+
}
2947+
29362948
const ctx = { path };
29372949
const result = binding.mkdtemp(path, options.encoding,
29382950
undefined, ctx);

lib/internal/fs/promises.js

+10-4
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ const {
5959
getStatsFromBinding,
6060
getValidatedPath,
6161
getValidMode,
62-
nullCheck,
6362
preprocessSymlinkDestination,
6463
stringToFlags,
6564
stringToSymlinkType,
@@ -997,10 +996,17 @@ async function realpath(path, options) {
997996
async function mkdtemp(prefix, options) {
998997
options = getOptions(options);
999998

1000-
validateString(prefix, 'prefix');
1001-
nullCheck(prefix);
999+
prefix = getValidatedPath(prefix, 'prefix');
10021000
warnOnNonPortableTemplate(prefix);
1003-
return binding.mkdtemp(`${prefix}XXXXXX`, options.encoding, kUsePromises);
1001+
1002+
let path;
1003+
if (typeof prefix === 'string') {
1004+
path = `${prefix}XXXXXX`;
1005+
} else {
1006+
path = Buffer.concat([prefix, Buffer.from('XXXXXX')]);
1007+
}
1008+
1009+
return binding.mkdtemp(path, options.encoding, kUsePromises);
10041010
}
10051011

10061012
async function writeFile(path, data, options) {

lib/internal/fs/utils.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const {
2020
StringPrototypeEndsWith,
2121
StringPrototypeIncludes,
2222
Symbol,
23+
TypedArrayPrototypeAt,
2324
TypedArrayPrototypeIncludes,
2425
} = primordials;
2526

@@ -749,7 +750,9 @@ let nonPortableTemplateWarn = true;
749750
function warnOnNonPortableTemplate(template) {
750751
// Template strings passed to the mkdtemp() family of functions should not
751752
// end with 'X' because they are handled inconsistently across platforms.
752-
if (nonPortableTemplateWarn && StringPrototypeEndsWith(template, 'X')) {
753+
if (nonPortableTemplateWarn &&
754+
((typeof template === 'string' && StringPrototypeEndsWith(template, 'X')) ||
755+
(typeof template !== 'string' && TypedArrayPrototypeAt(template, -1) === 0x58))) {
753756
process.emitWarning('mkdtemp() templates ending with X are not portable. ' +
754757
'For details see: https://nodejs.org/api/fs.html');
755758
nonPortableTemplateWarn = false;

test/parallel/test-fs-mkdtemp.js

+92-18
Original file line numberDiff line numberDiff line change
@@ -8,29 +8,103 @@ const path = require('path');
88
const tmpdir = require('../common/tmpdir');
99
tmpdir.refresh();
1010

11-
const tmpFolder = fs.mkdtempSync(path.join(tmpdir.path, 'foo.'));
12-
13-
assert.strictEqual(path.basename(tmpFolder).length, 'foo.XXXXXX'.length);
14-
assert(fs.existsSync(tmpFolder));
15-
16-
const utf8 = fs.mkdtempSync(path.join(tmpdir.path, '\u0222abc.'));
17-
assert.strictEqual(Buffer.byteLength(path.basename(utf8)),
18-
Buffer.byteLength('\u0222abc.XXXXXX'));
19-
assert(fs.existsSync(utf8));
20-
2111
function handler(err, folder) {
2212
assert.ifError(err);
2313
assert(fs.existsSync(folder));
2414
assert.strictEqual(this, undefined);
2515
}
2616

27-
fs.mkdtemp(path.join(tmpdir.path, 'bar.'), common.mustCall(handler));
17+
// Test with plain string
18+
{
19+
const tmpFolder = fs.mkdtempSync(path.join(tmpdir.path, 'foo.'));
20+
21+
assert.strictEqual(path.basename(tmpFolder).length, 'foo.XXXXXX'.length);
22+
assert(fs.existsSync(tmpFolder));
23+
24+
const utf8 = fs.mkdtempSync(path.join(tmpdir.path, '\u0222abc.'));
25+
assert.strictEqual(Buffer.byteLength(path.basename(utf8)),
26+
Buffer.byteLength('\u0222abc.XXXXXX'));
27+
assert(fs.existsSync(utf8));
28+
29+
fs.mkdtemp(path.join(tmpdir.path, 'bar.'), common.mustCall(handler));
30+
31+
// Same test as above, but making sure that passing an options object doesn't
32+
// affect the way the callback function is handled.
33+
fs.mkdtemp(path.join(tmpdir.path, 'bar.'), {}, common.mustCall(handler));
34+
35+
const warningMsg = 'mkdtemp() templates ending with X are not portable. ' +
36+
'For details see: https://nodejs.org/api/fs.html';
37+
common.expectWarning('Warning', warningMsg);
38+
fs.mkdtemp(path.join(tmpdir.path, 'bar.X'), common.mustCall(handler));
39+
}
40+
41+
// Test with URL object
42+
{
43+
tmpdir.url = new URL(`file://${tmpdir.path}`);
44+
const urljoin = (base, path) => new URL(path, base);
45+
46+
const tmpFolder = fs.mkdtempSync(urljoin(tmpdir.url, 'foo.'));
47+
48+
assert.strictEqual(path.basename(tmpFolder).length, 'foo.XXXXXX'.length);
49+
assert(fs.existsSync(tmpFolder));
50+
51+
const utf8 = fs.mkdtempSync(urljoin(tmpdir.url, '\u0222abc.'));
52+
assert.strictEqual(Buffer.byteLength(path.basename(utf8)),
53+
Buffer.byteLength('\u0222abc.XXXXXX'));
54+
assert(fs.existsSync(utf8));
55+
56+
fs.mkdtemp(urljoin(tmpdir.url, 'bar.'), common.mustCall(handler));
57+
58+
// Same test as above, but making sure that passing an options object doesn't
59+
// affect the way the callback function is handled.
60+
fs.mkdtemp(urljoin(tmpdir.url, 'bar.'), {}, common.mustCall(handler));
61+
62+
// Warning fires only once
63+
fs.mkdtemp(urljoin(tmpdir.url, 'bar.X'), common.mustCall(handler));
64+
}
65+
66+
// Test with Buffer
67+
{
68+
const tmpFolder = fs.mkdtempSync(Buffer.from(path.join(tmpdir.path, 'foo.')));
69+
70+
assert.strictEqual(path.basename(tmpFolder).length, 'foo.XXXXXX'.length);
71+
assert(fs.existsSync(tmpFolder));
2872

29-
// Same test as above, but making sure that passing an options object doesn't
30-
// affect the way the callback function is handled.
31-
fs.mkdtemp(path.join(tmpdir.path, 'bar.'), {}, common.mustCall(handler));
73+
const utf8 = fs.mkdtempSync(Buffer.from(path.join(tmpdir.path, '\u0222abc.')));
74+
assert.strictEqual(Buffer.byteLength(path.basename(utf8)),
75+
Buffer.byteLength('\u0222abc.XXXXXX'));
76+
assert(fs.existsSync(utf8));
77+
78+
fs.mkdtemp(Buffer.from(path.join(tmpdir.path, 'bar.')), common.mustCall(handler));
79+
80+
// Same test as above, but making sure that passing an options object doesn't
81+
// affect the way the callback function is handled.
82+
fs.mkdtemp(Buffer.from(path.join(tmpdir.path, 'bar.')), {}, common.mustCall(handler));
83+
84+
// Warning fires only once
85+
fs.mkdtemp(Buffer.from(path.join(tmpdir.path, 'bar.X')), common.mustCall(handler));
86+
}
3287

33-
const warningMsg = 'mkdtemp() templates ending with X are not portable. ' +
34-
'For details see: https://nodejs.org/api/fs.html';
35-
common.expectWarning('Warning', warningMsg);
36-
fs.mkdtemp(path.join(tmpdir.path, 'bar.X'), common.mustCall(handler));
88+
// Test with Uint8Array
89+
{
90+
const encoder = new TextEncoder();
91+
92+
const tmpFolder = fs.mkdtempSync(encoder.encode(path.join(tmpdir.path, 'foo.')));
93+
94+
assert.strictEqual(path.basename(tmpFolder).length, 'foo.XXXXXX'.length);
95+
assert(fs.existsSync(tmpFolder));
96+
97+
const utf8 = fs.mkdtempSync(encoder.encode(path.join(tmpdir.path, '\u0222abc.')));
98+
assert.strictEqual(Buffer.byteLength(path.basename(utf8)),
99+
Buffer.byteLength('\u0222abc.XXXXXX'));
100+
assert(fs.existsSync(utf8));
101+
102+
fs.mkdtemp(encoder.encode(path.join(tmpdir.path, 'bar.')), common.mustCall(handler));
103+
104+
// Same test as above, but making sure that passing an options object doesn't
105+
// affect the way the callback function is handled.
106+
fs.mkdtemp(encoder.encode(path.join(tmpdir.path, 'bar.')), {}, common.mustCall(handler));
107+
108+
// Warning fires only once
109+
fs.mkdtemp(encoder.encode(path.join(tmpdir.path, 'bar.X')), common.mustCall(handler));
110+
}

0 commit comments

Comments
 (0)