Skip to content

Commit a57483b

Browse files
committed
fs: make mkdtemp accept buffers and URL
PR-URL: nodejs/node#48828 Backport-PR-URL: nodejs/node#50669 Reviewed-By: Mohammed Keyvanzadeh <mohammadkeyvanzade94@gmail.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
1 parent 8f3e568 commit a57483b

File tree

5 files changed

+139
-35
lines changed

5 files changed

+139
-35
lines changed

graal-nodejs/doc/api/fs.md

+12-3
Original file line numberDiff line numberDiff line change
@@ -1153,14 +1153,17 @@ makeDirectory().catch(console.error);
11531153
<!-- YAML
11541154
added: v10.0.0
11551155
changes:
1156+
- version: REPLACEME
1157+
pr-url: https://github.com/nodejs/node/pull/48828
1158+
description: The `prefix` parameter now accepts buffers and URL.
11561159
- version:
11571160
- v16.5.0
11581161
- v14.18.0
11591162
pr-url: https://github.com/nodejs/node/pull/39028
11601163
description: The `prefix` parameter now accepts an empty string.
11611164
-->
11621165
1163-
* `prefix` {string}
1166+
* `prefix` {string|Buffer|URL}
11641167
* `options` {string|Object}
11651168
* `encoding` {string} **Default:** `'utf8'`
11661169
* Returns: {Promise} Fulfills with a string containing the file system path
@@ -3225,6 +3228,9 @@ See the POSIX mkdir(2) documentation for more details.
32253228
<!-- YAML
32263229
added: v5.10.0
32273230
changes:
3231+
- version: REPLACEME
3232+
pr-url: https://github.com/nodejs/node/pull/48828
3233+
description: The `prefix` parameter now accepts buffers and URL.
32283234
- version: v18.0.0
32293235
pr-url: https://github.com/nodejs/node/pull/41678
32303236
description: Passing an invalid callback to the `callback` argument
@@ -3248,7 +3254,7 @@ changes:
32483254
description: The `callback` parameter is optional now.
32493255
-->
32503256
3251-
* `prefix` {string}
3257+
* `prefix` {string|Buffer|URL}
32523258
* `options` {string|Object}
32533259
* `encoding` {string} **Default:** `'utf8'`
32543260
* `callback` {Function}
@@ -5478,14 +5484,17 @@ See the POSIX mkdir(2) documentation for more details.
54785484
<!-- YAML
54795485
added: v5.10.0
54805486
changes:
5487+
- version: REPLACEME
5488+
pr-url: https://github.com/nodejs/node/pull/48828
5489+
description: The `prefix` parameter now accepts buffers and URL.
54815490
- version:
54825491
- v16.5.0
54835492
- v14.18.0
54845493
pr-url: https://github.com/nodejs/node/pull/39028
54855494
description: The `prefix` parameter now accepts an empty string.
54865495
-->
54875496
5488-
* `prefix` {string}
5497+
* `prefix` {string|Buffer|URL}
54895498
* `options` {string|Object}
54905499
* `encoding` {string} **Default:** `'utf8'`
54915500
* Returns: {string}

graal-nodejs/lib/fs.js

+21-9
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,6 @@ const {
139139
validateFunction,
140140
validateInteger,
141141
validateObject,
142-
validateString,
143142
} = require('internal/validators');
144143

145144
let truncateWarn = true;
@@ -2884,7 +2883,7 @@ realpath.native = (path, options, callback) => {
28842883

28852884
/**
28862885
* Creates a unique temporary directory.
2887-
* @param {string} prefix
2886+
* @param {string | Buffer | URL} prefix
28882887
* @param {string | { encoding?: string; }} [options]
28892888
* @param {(
28902889
* err?: Error,
@@ -2896,27 +2895,40 @@ function mkdtemp(prefix, options, callback) {
28962895
callback = makeCallback(typeof options === 'function' ? options : callback);
28972896
options = getOptions(options);
28982897

2899-
validateString(prefix, 'prefix');
2900-
nullCheck(prefix, 'prefix');
2898+
prefix = getValidatedPath(prefix, 'prefix');
29012899
warnOnNonPortableTemplate(prefix);
2900+
2901+
let path;
2902+
if (typeof prefix === 'string') {
2903+
path = `${prefix}XXXXXX`;
2904+
} else {
2905+
path = Buffer.concat([prefix, Buffer.from('XXXXXX')]);
2906+
}
2907+
29022908
const req = new FSReqCallback();
29032909
req.oncomplete = callback;
2904-
binding.mkdtemp(`${prefix}XXXXXX`, options.encoding, req);
2910+
binding.mkdtemp(path, options.encoding, req);
29052911
}
29062912

29072913
/**
29082914
* Synchronously creates a unique temporary directory.
2909-
* @param {string} prefix
2915+
* @param {string | Buffer | URL} prefix
29102916
* @param {string | { encoding?: string; }} [options]
29112917
* @returns {string}
29122918
*/
29132919
function mkdtempSync(prefix, options) {
29142920
options = getOptions(options);
29152921

2916-
validateString(prefix, 'prefix');
2917-
nullCheck(prefix, 'prefix');
2922+
prefix = getValidatedPath(prefix, 'prefix');
29182923
warnOnNonPortableTemplate(prefix);
2919-
const path = `${prefix}XXXXXX`;
2924+
2925+
let path;
2926+
if (typeof prefix === 'string') {
2927+
path = `${prefix}XXXXXX`;
2928+
} else {
2929+
path = Buffer.concat([prefix, Buffer.from('XXXXXX')]);
2930+
}
2931+
29202932
const ctx = { path };
29212933
const result = binding.mkdtemp(path, options.encoding,
29222934
undefined, ctx);

graal-nodejs/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,
@@ -976,10 +975,17 @@ async function realpath(path, options) {
976975
async function mkdtemp(prefix, options) {
977976
options = getOptions(options);
978977

979-
validateString(prefix, 'prefix');
980-
nullCheck(prefix);
978+
prefix = getValidatedPath(prefix, 'prefix');
981979
warnOnNonPortableTemplate(prefix);
982-
return binding.mkdtemp(`${prefix}XXXXXX`, options.encoding, kUsePromises);
980+
981+
let path;
982+
if (typeof prefix === 'string') {
983+
path = `${prefix}XXXXXX`;
984+
} else {
985+
path = Buffer.concat([prefix, Buffer.from('XXXXXX')]);
986+
}
987+
988+
return binding.mkdtemp(path, options.encoding, kUsePromises);
983989
}
984990

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

graal-nodejs/lib/internal/fs/utils.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const {
2121
StringPrototypeEndsWith,
2222
StringPrototypeIncludes,
2323
Symbol,
24+
TypedArrayPrototypeAt,
2425
TypedArrayPrototypeIncludes,
2526
} = primordials;
2627

@@ -736,7 +737,9 @@ let nonPortableTemplateWarn = true;
736737
function warnOnNonPortableTemplate(template) {
737738
// Template strings passed to the mkdtemp() family of functions should not
738739
// end with 'X' because they are handled inconsistently across platforms.
739-
if (nonPortableTemplateWarn && StringPrototypeEndsWith(template, 'X')) {
740+
if (nonPortableTemplateWarn &&
741+
((typeof template === 'string' && StringPrototypeEndsWith(template, 'X')) ||
742+
(typeof template !== 'string' && TypedArrayPrototypeAt(template, -1) === 0x58))) {
740743
process.emitWarning('mkdtemp() templates ending with X are not portable. ' +
741744
'For details see: https://nodejs.org/api/fs.html');
742745
nonPortableTemplateWarn = false;

graal-nodejs/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)