Skip to content

Commit 779fff6

Browse files
committed
path: assert inputs are strings
This commit makes input type checking consistent across all path functions. PR-URL: #5348
1 parent af09a9c commit 779fff6

File tree

3 files changed

+84
-84
lines changed

3 files changed

+84
-84
lines changed

doc/api/path.markdown

+29-22
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,16 @@
33
Stability: 2 - Stable
44

55
This module contains utilities for handling and transforming file
6-
paths. Almost all these methods perform only string transformations.
7-
The file system is not consulted to check whether paths are valid.
6+
paths. The file system is not consulted to check whether paths are valid.
87

98
Use `require('path')` to use this module. The following methods are provided:
109

11-
## path.basename(p[, ext])
10+
## path.basename(path[, ext])
1211

13-
Return the last portion of a path. Similar to the Unix `basename` command.
12+
Return the last portion of a path, similar to the Unix `basename` command.
13+
`path` must be a string. `ext`, if given, must also be a string.
1414

15-
Example:
15+
Examples:
1616

1717
```js
1818
path.basename('/foo/bar/baz/asdf/quux.html')
@@ -46,9 +46,10 @@ process.env.PATH.split(path.delimiter)
4646
// returns ['C:\\Windows\\system32', 'C:\\Windows', 'C:\\Program Files\\node\\']
4747
```
4848

49-
## path.dirname(p)
49+
## path.dirname(path)
5050

51-
Return the directory name of a path. Similar to the Unix `dirname` command.
51+
Return the directory name of a path, similar to the Unix `dirname` command.
52+
`path` must be a string.
5253

5354
Example:
5455

@@ -57,12 +58,14 @@ path.dirname('/foo/bar/baz/asdf/quux')
5758
// returns '/foo/bar/baz/asdf'
5859
```
5960

60-
## path.extname(p)
61+
## path.extname(path)
6162

6263
Return the extension of the path, from the last '.' to end of string
6364
in the last portion of the path. If there is no '.' in the last portion
6465
of the path or the first character of it is '.', then it returns
65-
an empty string. Examples:
66+
an empty string. `path` must be a string.
67+
68+
Examples:
6669

6770
```js
6871
path.extname('index.html')
@@ -100,6 +103,8 @@ string will be the contents of the `base` property.
100103
If the `base` property is not supplied, a concatenation of the `name` property
101104
and the `ext` property will be used as the `base` property.
102105

106+
Examples:
107+
103108
```js
104109
path.format({
105110
root : "/",
@@ -123,9 +128,10 @@ path.format({
123128
## path.isAbsolute(path)
124129

125130
Determines whether `path` is an absolute path. An absolute path will always
126-
resolve to the same location, regardless of the working directory.
131+
resolve to the same location, regardless of the working directory. `path` must
132+
be a string.
127133

128-
Posix examples:
134+
Examples on \*nix:
129135

130136
```js
131137
path.isAbsolute('/foo/bar') // true
@@ -134,7 +140,7 @@ path.isAbsolute('qux/') // false
134140
path.isAbsolute('.') // false
135141
```
136142

137-
Windows examples:
143+
Examples on Windows:
138144

139145
```js
140146
path.isAbsolute('//server') // true
@@ -151,10 +157,10 @@ path.isAbsolute('.') // false
151157

152158
Join all arguments together and normalize the resulting path.
153159

154-
Arguments must be strings. In v0.8, non-string arguments were
160+
All arguments must be strings. In v0.8, non-string arguments were
155161
silently ignored. In v0.10 and up, an exception is thrown.
156162

157-
Example:
163+
Examples:
158164

159165
```js
160166
path.join('/foo', 'bar', 'baz/asdf', 'quux', '..')
@@ -170,9 +176,10 @@ TypeError: Arguments to path.join must be strings
170176
zero-length string then `'.'` will be returned, which represents the
171177
current working directory.
172178

173-
## path.normalize(p)
179+
## path.normalize(path)
174180

175-
Normalize a string path, taking care of `'..'` and `'.'` parts.
181+
Normalize a path, taking care of `'..'` and `'.'` parts. `path` must be a
182+
string.
176183

177184
When multiple slashes are found, they're replaced by a single one;
178185
when the path contains a trailing slash, it is preserved.
@@ -188,9 +195,9 @@ path.normalize('/foo/bar//baz/asdf/quux/..')
188195
*Note:* If the path string passed as argument is a zero-length string then `'.'`
189196
will be returned, which represents the current working directory.
190197

191-
## path.parse(pathString)
198+
## path.parse(path)
192199

193-
Returns an object from a path string.
200+
Returns an object from a path. `path` must be a string.
194201

195202
An example on \*nix:
196203

@@ -227,7 +234,7 @@ compatible way.
227234

228235
## path.relative(from, to)
229236

230-
Solve the relative path from `from` to `to`.
237+
Solve the relative path from `from` to `to`. `from` and `to` must be strings.
231238

232239
At times we have two absolute paths, and we need to derive the relative
233240
path from one to the other. This is actually the reverse transform of
@@ -253,13 +260,13 @@ path.relative('/data/orandea/test/aaa', '/data/orandea/impl/bbb')
253260

254261
## path.resolve([from ...], to)
255262

256-
Resolves `to` to an absolute path.
263+
Resolves `to` to an absolute path. All arguments must be strings.
257264

258265
If `to` isn't already absolute `from` arguments are prepended in right to left
259266
order, until an absolute path is found. If after using all `from` paths still
260267
no absolute path is found, the current working directory is used as well. The
261268
resulting path is normalized, and trailing slashes are removed unless the path
262-
gets resolved to the root directory. Non-string `from` arguments are ignored.
269+
gets resolved to the root directory.
263270

264271
Another way to think of it is as a sequence of `cd` commands in a shell.
265272

@@ -320,4 +327,4 @@ An example on Windows:
320327
Provide access to aforementioned `path` methods but always interact in a win32
321328
compatible way.
322329

323-
[`path.parse`]: #path_path_parse_pathstring
330+
[`path.parse`]: #path_path_parse_path

lib/path.js

+6-12
Original file line numberDiff line numberDiff line change
@@ -734,8 +734,7 @@ const win32 = {
734734

735735

736736
dirname: function dirname(path) {
737-
if (typeof path !== 'string')
738-
path = '' + path;
737+
assertPath(path);
739738
const len = path.length;
740739
if (len === 0)
741740
return '.';
@@ -839,8 +838,7 @@ const win32 = {
839838
basename: function basename(path, ext) {
840839
if (ext !== undefined && typeof ext !== 'string')
841840
throw new TypeError('"ext" argument must be a string');
842-
if (typeof path !== 'string')
843-
path = '' + path;
841+
assertPath(path);
844842
var start = 0;
845843
var end = -1;
846844
var matchedSlash = true;
@@ -926,8 +924,7 @@ const win32 = {
926924

927925

928926
extname: function extname(path) {
929-
if (typeof path !== 'string')
930-
path = '' + path;
927+
assertPath(path);
931928
var startDot = -1;
932929
var startPart = 0;
933930
var end = -1;
@@ -1364,8 +1361,7 @@ const posix = {
13641361

13651362

13661363
dirname: function dirname(path) {
1367-
if (typeof path !== 'string')
1368-
path = '' + path;
1364+
assertPath(path);
13691365
if (path.length === 0)
13701366
return '.';
13711367
var code = path.charCodeAt(0);
@@ -1396,8 +1392,7 @@ const posix = {
13961392
basename: function basename(path, ext) {
13971393
if (ext !== undefined && typeof ext !== 'string')
13981394
throw new TypeError('"ext" argument must be a string');
1399-
if (typeof path !== 'string')
1400-
path = '' + path;
1395+
assertPath(path);
14011396

14021397
var start = 0;
14031398
var end = -1;
@@ -1471,8 +1466,7 @@ const posix = {
14711466

14721467

14731468
extname: function extname(path) {
1474-
if (typeof path !== 'string')
1475-
path = '' + path;
1469+
assertPath(path);
14761470
var startDot = -1;
14771471
var startPart = 0;
14781472
var end = -1;

test/parallel/test-path.js

+49-50
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ assert.equal(path.win32.basename('basename.ext'), 'basename.ext');
2323
assert.equal(path.win32.basename('basename.ext\\'), 'basename.ext');
2424
assert.equal(path.win32.basename('basename.ext\\\\'), 'basename.ext');
2525
assert.equal(path.win32.basename('foo'), 'foo');
26-
assert.equal(path.win32.basename(null), 'null');
27-
assert.equal(path.win32.basename(true), 'true');
28-
assert.equal(path.win32.basename(1), '1');
29-
assert.equal(path.win32.basename(), 'undefined');
30-
assert.equal(path.win32.basename({}), '[object Object]');
26+
assert.throws(path.win32.basename.bind(null, null), TypeError);
27+
assert.throws(path.win32.basename.bind(null, true), TypeError);
28+
assert.throws(path.win32.basename.bind(null, 1), TypeError);
29+
assert.throws(path.win32.basename.bind(null), TypeError);
30+
assert.throws(path.win32.basename.bind(null, {}), TypeError);
3131

3232
// On unix a backslash is just treated as any other character.
3333
assert.equal(path.posix.basename('\\dir\\basename.ext'), '\\dir\\basename.ext');
@@ -36,11 +36,11 @@ assert.equal(path.posix.basename('basename.ext'), 'basename.ext');
3636
assert.equal(path.posix.basename('basename.ext\\'), 'basename.ext\\');
3737
assert.equal(path.posix.basename('basename.ext\\\\'), 'basename.ext\\\\');
3838
assert.equal(path.posix.basename('foo'), 'foo');
39-
assert.equal(path.posix.basename(null), 'null');
40-
assert.equal(path.posix.basename(true), 'true');
41-
assert.equal(path.posix.basename(1), '1');
42-
assert.equal(path.posix.basename(), 'undefined');
43-
assert.equal(path.posix.basename({}), '[object Object]');
39+
assert.throws(path.posix.basename.bind(null, null), TypeError);
40+
assert.throws(path.posix.basename.bind(null, true), TypeError);
41+
assert.throws(path.posix.basename.bind(null, 1), TypeError);
42+
assert.throws(path.posix.basename.bind(null), TypeError);
43+
assert.throws(path.posix.basename.bind(null, {}), TypeError);
4444

4545
// POSIX filenames may include control characters
4646
// c.f. http://www.dwheeler.com/essays/fixing-unix-linux-filenames.html
@@ -60,11 +60,11 @@ assert.equal(path.posix.dirname(''), '.');
6060
assert.equal(path.posix.dirname('/'), '/');
6161
assert.equal(path.posix.dirname('////'), '/');
6262
assert.equal(path.posix.dirname('foo'), '.');
63-
assert.equal(path.posix.dirname(null), '.');
64-
assert.equal(path.posix.dirname(true), '.');
65-
assert.equal(path.posix.dirname(1), '.');
66-
assert.equal(path.posix.dirname(), '.');
67-
assert.equal(path.posix.dirname({}), '.');
63+
assert.throws(path.posix.dirname.bind(null, null), TypeError);
64+
assert.throws(path.posix.dirname.bind(null, true), TypeError);
65+
assert.throws(path.posix.dirname.bind(null, 1), TypeError);
66+
assert.throws(path.posix.dirname.bind(null), TypeError);
67+
assert.throws(path.posix.dirname.bind(null, {}), TypeError);
6868

6969
assert.equal(path.win32.dirname('c:\\'), 'c:\\');
7070
assert.equal(path.win32.dirname('c:\\foo'), 'c:\\');
@@ -100,11 +100,11 @@ assert.equal(path.win32.dirname(''), '.');
100100
assert.equal(path.win32.dirname('/'), '/');
101101
assert.equal(path.win32.dirname('////'), '/');
102102
assert.equal(path.win32.dirname('foo'), '.');
103-
assert.equal(path.win32.dirname(null), '.');
104-
assert.equal(path.win32.dirname(true), '.');
105-
assert.equal(path.win32.dirname(1), '.');
106-
assert.equal(path.win32.dirname(), '.');
107-
assert.equal(path.win32.dirname({}), '.');
103+
assert.throws(path.win32.dirname.bind(null, null), TypeError);
104+
assert.throws(path.win32.dirname.bind(null, true), TypeError);
105+
assert.throws(path.win32.dirname.bind(null, 1), TypeError);
106+
assert.throws(path.win32.dirname.bind(null), TypeError);
107+
assert.throws(path.win32.dirname.bind(null, {}), TypeError);
108108

109109

110110
// path.extname tests
@@ -180,11 +180,11 @@ assert.equal(path.win32.extname('file\\'), '');
180180
assert.equal(path.win32.extname('file\\\\'), '');
181181
assert.equal(path.win32.extname('file.\\'), '.');
182182
assert.equal(path.win32.extname('file.\\\\'), '.');
183-
assert.equal(path.win32.extname(null), '');
184-
assert.equal(path.win32.extname(true), '');
185-
assert.equal(path.win32.extname(1), '');
186-
assert.equal(path.win32.extname(), '');
187-
assert.equal(path.win32.extname({}), '');
183+
assert.throws(path.win32.extname.bind(null, null), TypeError);
184+
assert.throws(path.win32.extname.bind(null, true), TypeError);
185+
assert.throws(path.win32.extname.bind(null, 1), TypeError);
186+
assert.throws(path.win32.extname.bind(null), TypeError);
187+
assert.throws(path.win32.extname.bind(null, {}), TypeError);
188188

189189
// On *nix, backslash is a valid name component like any other character.
190190
assert.equal(path.posix.extname('.\\'), '');
@@ -195,11 +195,11 @@ assert.equal(path.posix.extname('file\\'), '');
195195
assert.equal(path.posix.extname('file\\\\'), '');
196196
assert.equal(path.posix.extname('file.\\'), '.\\');
197197
assert.equal(path.posix.extname('file.\\\\'), '.\\\\');
198-
assert.equal(path.posix.extname(null), '');
199-
assert.equal(path.posix.extname(true), '');
200-
assert.equal(path.posix.extname(1), '');
201-
assert.equal(path.posix.extname(), '');
202-
assert.equal(path.posix.extname({}), '');
198+
assert.throws(path.posix.extname.bind(null, null), TypeError);
199+
assert.throws(path.posix.extname.bind(null, true), TypeError);
200+
assert.throws(path.posix.extname.bind(null, 1), TypeError);
201+
assert.throws(path.posix.extname.bind(null), TypeError);
202+
assert.throws(path.posix.extname.bind(null, {}), TypeError);
203203

204204

205205
// path.join tests
@@ -336,35 +336,34 @@ assert.equal(failures.length, 0, failures.join(''));
336336

337337

338338
// Test thrown TypeErrors
339-
var typeErrorTests = [true, false, 7, null, {}, undefined, [], NaN];
339+
const typeErrorTests = [true, false, 7, null, {}, undefined, [], NaN];
340340

341341
function fail(fn) {
342-
var args = Array.prototype.slice.call(arguments, 1);
342+
const args = Array.prototype.slice.call(arguments, 1);
343343

344344
assert.throws(function() {
345345
fn.apply(null, args);
346346
}, TypeError);
347347
}
348348

349349
typeErrorTests.forEach(function(test) {
350-
fail(path.join, test);
351-
fail(path.resolve, test);
352-
fail(path.normalize, test);
353-
fail(path.isAbsolute, test);
354-
fail(path.relative, test, 'foo');
355-
fail(path.relative, 'foo', test);
356-
fail(path.parse, test);
357-
358-
// These methods should throw a TypeError, but do not for backwards
359-
// compatibility. Uncommenting these lines in the future should be a goal.
360-
// fail(path.dirname, test);
361-
// fail(path.basename, test);
362-
// fail(path.extname, test);
363-
364-
// undefined is a valid value as the second argument to basename
365-
if (test !== undefined) {
366-
fail(path.basename, 'foo', test);
367-
}
350+
[path.posix, path.win32].forEach(function(namespace) {
351+
fail(namespace.join, test);
352+
fail(namespace.resolve, test);
353+
fail(namespace.normalize, test);
354+
fail(namespace.isAbsolute, test);
355+
fail(namespace.relative, test, 'foo');
356+
fail(namespace.relative, 'foo', test);
357+
fail(namespace.parse, test);
358+
fail(namespace.dirname, test);
359+
fail(namespace.basename, test);
360+
fail(namespace.extname, test);
361+
362+
// undefined is a valid value as the second argument to basename
363+
if (test !== undefined) {
364+
fail(namespace.basename, 'foo', test);
365+
}
366+
});
368367
});
369368

370369

0 commit comments

Comments
 (0)