├── .github └── FUNDING.yml ├── .npmrc ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── index.js ├── package.json ├── security.md └── test ├── index.js ├── test-path-basename.js ├── test-path-dirname.js ├── test-path-extname.js ├── test-path-isabsolute.js ├── test-path-join.js ├── test-path-parse-format.js ├── test-path-relative.js ├── test-path-resolve.js ├── test-path-zero-length-strings.js └── test-path.js /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: npm/path-browserify 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: false 3 | node_js: 4 | - "10" 5 | - "9" 6 | - "8" 7 | - "6" 8 | - "4" 9 | - "iojs" 10 | - "0.12" 11 | - "0.10" 12 | - "0.8" 13 | before_install: 14 | # Old npm certs are untrusted https://github.com/npm/npm/issues/20191 15 | - 'if [ "${TRAVIS_NODE_VERSION}" = "0.6" ] || [ "${TRAVIS_NODE_VERSION}" = "0.8" ]; then export NPM_CONFIG_STRICT_SSL=false; fi' 16 | - 'nvm install-latest-npm' 17 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # path-browserify change log 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | This project adheres to [Semantic Versioning](http://semver.org/). 6 | 7 | ## 1.0.1 8 | * Fix a duplicate test name. 9 | * Tweak LICENSE text so Github can recognise it. 10 | * Tweak LICENSE text to include the year and author. 11 | * Add security policy file. 12 | 13 | ## 1.0.0 14 | This release updates to the Node v10.3.0 API. **This change is breaking**, 15 | because path methods now throw errors when called with arguments that are not 16 | strings. 17 | 18 | * Add `path.parse` and `path.format`. 19 | * Add `path.posix` as an alias to `path`. 20 | * Port tests from Node.js. 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2013 James Halliday 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # path-browserify [![Build Status](https://travis-ci.org/browserify/path-browserify.png?branch=master)](https://travis-ci.org/browserify/path-browserify) 2 | 3 | > The `path` module from Node.js for browsers 4 | 5 | This implements the Node.js [`path`][path] module for environments that do not have it, like browsers. 6 | 7 | > `path-browserify` currently matches the **Node.js 10.3** API. 8 | 9 | ## Install 10 | 11 | You usually do not have to install `path-browserify` yourself! If your code runs in Node.js, `path` is built in. If your code runs in the browser, bundlers like [browserify](https://github.com/browserify/browserify) or [webpack](https://github.com/webpack/webpack) include the `path-browserify` module by default. 12 | 13 | But if none of those apply, with npm do: 14 | 15 | ``` 16 | npm install path-browserify 17 | ``` 18 | 19 | ## Usage 20 | 21 | ```javascript 22 | var path = require('path') 23 | 24 | var filename = 'logo.png'; 25 | var logo = path.join('./assets/img', filename); 26 | document.querySelector('#logo').src = logo; 27 | ``` 28 | 29 | ## API 30 | 31 | See the [Node.js path docs][path]. `path-browserify` currently matches the Node.js 10.3 API. 32 | `path-browserify` only implements the POSIX functions, not the win32 ones. 33 | 34 | ## Contributing 35 | 36 | PRs are very welcome! The main way to contribute to `path-browserify` is by porting features, bugfixes and tests from Node.js. Ideally, code contributions to this module are copy-pasted from Node.js and transpiled to ES5, rather than reimplemented from scratch. Matching the Node.js code as closely as possible makes maintenance simpler when new changes land in Node.js. 37 | This module intends to provide exactly the same API as Node.js, so features that are not available in the core `path` module will not be accepted. Feature requests should instead be directed at [nodejs/node](https://github.com/nodejs/node) and will be added to this module once they are implemented in Node.js. 38 | 39 | If there is a difference in behaviour between Node.js's `path` module and this module, please open an issue! 40 | 41 | ## License 42 | 43 | [MIT](./LICENSE) 44 | 45 | [path]: https://nodejs.org/docs/v10.3.0/api/path.html 46 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | // 'path' module extracted from Node.js v8.11.1 (only the posix part) 2 | // transplited with Babel 3 | 4 | // Copyright Joyent, Inc. and other Node contributors. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a 7 | // copy of this software and associated documentation files (the 8 | // "Software"), to deal in the Software without restriction, including 9 | // without limitation the rights to use, copy, modify, merge, publish, 10 | // distribute, sublicense, and/or sell copies of the Software, and to permit 11 | // persons to whom the Software is furnished to do so, subject to the 12 | // following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included 15 | // in all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 20 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 21 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 22 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 23 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | 'use strict'; 26 | 27 | function assertPath(path) { 28 | if (typeof path !== 'string') { 29 | throw new TypeError('Path must be a string. Received ' + JSON.stringify(path)); 30 | } 31 | } 32 | 33 | // Resolves . and .. elements in a path with directory names 34 | function normalizeStringPosix(path, allowAboveRoot) { 35 | var res = ''; 36 | var lastSegmentLength = 0; 37 | var lastSlash = -1; 38 | var dots = 0; 39 | var code; 40 | for (var i = 0; i <= path.length; ++i) { 41 | if (i < path.length) 42 | code = path.charCodeAt(i); 43 | else if (code === 47 /*/*/) 44 | break; 45 | else 46 | code = 47 /*/*/; 47 | if (code === 47 /*/*/) { 48 | if (lastSlash === i - 1 || dots === 1) { 49 | // NOOP 50 | } else if (lastSlash !== i - 1 && dots === 2) { 51 | if (res.length < 2 || lastSegmentLength !== 2 || res.charCodeAt(res.length - 1) !== 46 /*.*/ || res.charCodeAt(res.length - 2) !== 46 /*.*/) { 52 | if (res.length > 2) { 53 | var lastSlashIndex = res.lastIndexOf('/'); 54 | if (lastSlashIndex !== res.length - 1) { 55 | if (lastSlashIndex === -1) { 56 | res = ''; 57 | lastSegmentLength = 0; 58 | } else { 59 | res = res.slice(0, lastSlashIndex); 60 | lastSegmentLength = res.length - 1 - res.lastIndexOf('/'); 61 | } 62 | lastSlash = i; 63 | dots = 0; 64 | continue; 65 | } 66 | } else if (res.length === 2 || res.length === 1) { 67 | res = ''; 68 | lastSegmentLength = 0; 69 | lastSlash = i; 70 | dots = 0; 71 | continue; 72 | } 73 | } 74 | if (allowAboveRoot) { 75 | if (res.length > 0) 76 | res += '/..'; 77 | else 78 | res = '..'; 79 | lastSegmentLength = 2; 80 | } 81 | } else { 82 | if (res.length > 0) 83 | res += '/' + path.slice(lastSlash + 1, i); 84 | else 85 | res = path.slice(lastSlash + 1, i); 86 | lastSegmentLength = i - lastSlash - 1; 87 | } 88 | lastSlash = i; 89 | dots = 0; 90 | } else if (code === 46 /*.*/ && dots !== -1) { 91 | ++dots; 92 | } else { 93 | dots = -1; 94 | } 95 | } 96 | return res; 97 | } 98 | 99 | function _format(sep, pathObject) { 100 | var dir = pathObject.dir || pathObject.root; 101 | var base = pathObject.base || (pathObject.name || '') + (pathObject.ext || ''); 102 | if (!dir) { 103 | return base; 104 | } 105 | if (dir === pathObject.root) { 106 | return dir + base; 107 | } 108 | return dir + sep + base; 109 | } 110 | 111 | var posix = { 112 | // path.resolve([from ...], to) 113 | resolve: function resolve() { 114 | var resolvedPath = ''; 115 | var resolvedAbsolute = false; 116 | var cwd; 117 | 118 | for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) { 119 | var path; 120 | if (i >= 0) 121 | path = arguments[i]; 122 | else { 123 | if (cwd === undefined) 124 | cwd = process.cwd(); 125 | path = cwd; 126 | } 127 | 128 | assertPath(path); 129 | 130 | // Skip empty entries 131 | if (path.length === 0) { 132 | continue; 133 | } 134 | 135 | resolvedPath = path + '/' + resolvedPath; 136 | resolvedAbsolute = path.charCodeAt(0) === 47 /*/*/; 137 | } 138 | 139 | // At this point the path should be resolved to a full absolute path, but 140 | // handle relative paths to be safe (might happen when process.cwd() fails) 141 | 142 | // Normalize the path 143 | resolvedPath = normalizeStringPosix(resolvedPath, !resolvedAbsolute); 144 | 145 | if (resolvedAbsolute) { 146 | if (resolvedPath.length > 0) 147 | return '/' + resolvedPath; 148 | else 149 | return '/'; 150 | } else if (resolvedPath.length > 0) { 151 | return resolvedPath; 152 | } else { 153 | return '.'; 154 | } 155 | }, 156 | 157 | normalize: function normalize(path) { 158 | assertPath(path); 159 | 160 | if (path.length === 0) return '.'; 161 | 162 | var isAbsolute = path.charCodeAt(0) === 47 /*/*/; 163 | var trailingSeparator = path.charCodeAt(path.length - 1) === 47 /*/*/; 164 | 165 | // Normalize the path 166 | path = normalizeStringPosix(path, !isAbsolute); 167 | 168 | if (path.length === 0 && !isAbsolute) path = '.'; 169 | if (path.length > 0 && trailingSeparator) path += '/'; 170 | 171 | if (isAbsolute) return '/' + path; 172 | return path; 173 | }, 174 | 175 | isAbsolute: function isAbsolute(path) { 176 | assertPath(path); 177 | return path.length > 0 && path.charCodeAt(0) === 47 /*/*/; 178 | }, 179 | 180 | join: function join() { 181 | if (arguments.length === 0) 182 | return '.'; 183 | var joined; 184 | for (var i = 0; i < arguments.length; ++i) { 185 | var arg = arguments[i]; 186 | assertPath(arg); 187 | if (arg.length > 0) { 188 | if (joined === undefined) 189 | joined = arg; 190 | else 191 | joined += '/' + arg; 192 | } 193 | } 194 | if (joined === undefined) 195 | return '.'; 196 | return posix.normalize(joined); 197 | }, 198 | 199 | relative: function relative(from, to) { 200 | assertPath(from); 201 | assertPath(to); 202 | 203 | if (from === to) return ''; 204 | 205 | from = posix.resolve(from); 206 | to = posix.resolve(to); 207 | 208 | if (from === to) return ''; 209 | 210 | // Trim any leading backslashes 211 | var fromStart = 1; 212 | for (; fromStart < from.length; ++fromStart) { 213 | if (from.charCodeAt(fromStart) !== 47 /*/*/) 214 | break; 215 | } 216 | var fromEnd = from.length; 217 | var fromLen = fromEnd - fromStart; 218 | 219 | // Trim any leading backslashes 220 | var toStart = 1; 221 | for (; toStart < to.length; ++toStart) { 222 | if (to.charCodeAt(toStart) !== 47 /*/*/) 223 | break; 224 | } 225 | var toEnd = to.length; 226 | var toLen = toEnd - toStart; 227 | 228 | // Compare paths to find the longest common path from root 229 | var length = fromLen < toLen ? fromLen : toLen; 230 | var lastCommonSep = -1; 231 | var i = 0; 232 | for (; i <= length; ++i) { 233 | if (i === length) { 234 | if (toLen > length) { 235 | if (to.charCodeAt(toStart + i) === 47 /*/*/) { 236 | // We get here if `from` is the exact base path for `to`. 237 | // For example: from='/foo/bar'; to='/foo/bar/baz' 238 | return to.slice(toStart + i + 1); 239 | } else if (i === 0) { 240 | // We get here if `from` is the root 241 | // For example: from='/'; to='/foo' 242 | return to.slice(toStart + i); 243 | } 244 | } else if (fromLen > length) { 245 | if (from.charCodeAt(fromStart + i) === 47 /*/*/) { 246 | // We get here if `to` is the exact base path for `from`. 247 | // For example: from='/foo/bar/baz'; to='/foo/bar' 248 | lastCommonSep = i; 249 | } else if (i === 0) { 250 | // We get here if `to` is the root. 251 | // For example: from='/foo'; to='/' 252 | lastCommonSep = 0; 253 | } 254 | } 255 | break; 256 | } 257 | var fromCode = from.charCodeAt(fromStart + i); 258 | var toCode = to.charCodeAt(toStart + i); 259 | if (fromCode !== toCode) 260 | break; 261 | else if (fromCode === 47 /*/*/) 262 | lastCommonSep = i; 263 | } 264 | 265 | var out = ''; 266 | // Generate the relative path based on the path difference between `to` 267 | // and `from` 268 | for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) { 269 | if (i === fromEnd || from.charCodeAt(i) === 47 /*/*/) { 270 | if (out.length === 0) 271 | out += '..'; 272 | else 273 | out += '/..'; 274 | } 275 | } 276 | 277 | // Lastly, append the rest of the destination (`to`) path that comes after 278 | // the common path parts 279 | if (out.length > 0) 280 | return out + to.slice(toStart + lastCommonSep); 281 | else { 282 | toStart += lastCommonSep; 283 | if (to.charCodeAt(toStart) === 47 /*/*/) 284 | ++toStart; 285 | return to.slice(toStart); 286 | } 287 | }, 288 | 289 | _makeLong: function _makeLong(path) { 290 | return path; 291 | }, 292 | 293 | dirname: function dirname(path) { 294 | assertPath(path); 295 | if (path.length === 0) return '.'; 296 | var code = path.charCodeAt(0); 297 | var hasRoot = code === 47 /*/*/; 298 | var end = -1; 299 | var matchedSlash = true; 300 | for (var i = path.length - 1; i >= 1; --i) { 301 | code = path.charCodeAt(i); 302 | if (code === 47 /*/*/) { 303 | if (!matchedSlash) { 304 | end = i; 305 | break; 306 | } 307 | } else { 308 | // We saw the first non-path separator 309 | matchedSlash = false; 310 | } 311 | } 312 | 313 | if (end === -1) return hasRoot ? '/' : '.'; 314 | if (hasRoot && end === 1) return '//'; 315 | return path.slice(0, end); 316 | }, 317 | 318 | basename: function basename(path, ext) { 319 | if (ext !== undefined && typeof ext !== 'string') throw new TypeError('"ext" argument must be a string'); 320 | assertPath(path); 321 | 322 | var start = 0; 323 | var end = -1; 324 | var matchedSlash = true; 325 | var i; 326 | 327 | if (ext !== undefined && ext.length > 0 && ext.length <= path.length) { 328 | if (ext.length === path.length && ext === path) return ''; 329 | var extIdx = ext.length - 1; 330 | var firstNonSlashEnd = -1; 331 | for (i = path.length - 1; i >= 0; --i) { 332 | var code = path.charCodeAt(i); 333 | if (code === 47 /*/*/) { 334 | // If we reached a path separator that was not part of a set of path 335 | // separators at the end of the string, stop now 336 | if (!matchedSlash) { 337 | start = i + 1; 338 | break; 339 | } 340 | } else { 341 | if (firstNonSlashEnd === -1) { 342 | // We saw the first non-path separator, remember this index in case 343 | // we need it if the extension ends up not matching 344 | matchedSlash = false; 345 | firstNonSlashEnd = i + 1; 346 | } 347 | if (extIdx >= 0) { 348 | // Try to match the explicit extension 349 | if (code === ext.charCodeAt(extIdx)) { 350 | if (--extIdx === -1) { 351 | // We matched the extension, so mark this as the end of our path 352 | // component 353 | end = i; 354 | } 355 | } else { 356 | // Extension does not match, so our result is the entire path 357 | // component 358 | extIdx = -1; 359 | end = firstNonSlashEnd; 360 | } 361 | } 362 | } 363 | } 364 | 365 | if (start === end) end = firstNonSlashEnd;else if (end === -1) end = path.length; 366 | return path.slice(start, end); 367 | } else { 368 | for (i = path.length - 1; i >= 0; --i) { 369 | if (path.charCodeAt(i) === 47 /*/*/) { 370 | // If we reached a path separator that was not part of a set of path 371 | // separators at the end of the string, stop now 372 | if (!matchedSlash) { 373 | start = i + 1; 374 | break; 375 | } 376 | } else if (end === -1) { 377 | // We saw the first non-path separator, mark this as the end of our 378 | // path component 379 | matchedSlash = false; 380 | end = i + 1; 381 | } 382 | } 383 | 384 | if (end === -1) return ''; 385 | return path.slice(start, end); 386 | } 387 | }, 388 | 389 | extname: function extname(path) { 390 | assertPath(path); 391 | var startDot = -1; 392 | var startPart = 0; 393 | var end = -1; 394 | var matchedSlash = true; 395 | // Track the state of characters (if any) we see before our first dot and 396 | // after any path separator we find 397 | var preDotState = 0; 398 | for (var i = path.length - 1; i >= 0; --i) { 399 | var code = path.charCodeAt(i); 400 | if (code === 47 /*/*/) { 401 | // If we reached a path separator that was not part of a set of path 402 | // separators at the end of the string, stop now 403 | if (!matchedSlash) { 404 | startPart = i + 1; 405 | break; 406 | } 407 | continue; 408 | } 409 | if (end === -1) { 410 | // We saw the first non-path separator, mark this as the end of our 411 | // extension 412 | matchedSlash = false; 413 | end = i + 1; 414 | } 415 | if (code === 46 /*.*/) { 416 | // If this is our first dot, mark it as the start of our extension 417 | if (startDot === -1) 418 | startDot = i; 419 | else if (preDotState !== 1) 420 | preDotState = 1; 421 | } else if (startDot !== -1) { 422 | // We saw a non-dot and non-path separator before our dot, so we should 423 | // have a good chance at having a non-empty extension 424 | preDotState = -1; 425 | } 426 | } 427 | 428 | if (startDot === -1 || end === -1 || 429 | // We saw a non-dot character immediately before the dot 430 | preDotState === 0 || 431 | // The (right-most) trimmed path component is exactly '..' 432 | preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) { 433 | return ''; 434 | } 435 | return path.slice(startDot, end); 436 | }, 437 | 438 | format: function format(pathObject) { 439 | if (pathObject === null || typeof pathObject !== 'object') { 440 | throw new TypeError('The "pathObject" argument must be of type Object. Received type ' + typeof pathObject); 441 | } 442 | return _format('/', pathObject); 443 | }, 444 | 445 | parse: function parse(path) { 446 | assertPath(path); 447 | 448 | var ret = { root: '', dir: '', base: '', ext: '', name: '' }; 449 | if (path.length === 0) return ret; 450 | var code = path.charCodeAt(0); 451 | var isAbsolute = code === 47 /*/*/; 452 | var start; 453 | if (isAbsolute) { 454 | ret.root = '/'; 455 | start = 1; 456 | } else { 457 | start = 0; 458 | } 459 | var startDot = -1; 460 | var startPart = 0; 461 | var end = -1; 462 | var matchedSlash = true; 463 | var i = path.length - 1; 464 | 465 | // Track the state of characters (if any) we see before our first dot and 466 | // after any path separator we find 467 | var preDotState = 0; 468 | 469 | // Get non-dir info 470 | for (; i >= start; --i) { 471 | code = path.charCodeAt(i); 472 | if (code === 47 /*/*/) { 473 | // If we reached a path separator that was not part of a set of path 474 | // separators at the end of the string, stop now 475 | if (!matchedSlash) { 476 | startPart = i + 1; 477 | break; 478 | } 479 | continue; 480 | } 481 | if (end === -1) { 482 | // We saw the first non-path separator, mark this as the end of our 483 | // extension 484 | matchedSlash = false; 485 | end = i + 1; 486 | } 487 | if (code === 46 /*.*/) { 488 | // If this is our first dot, mark it as the start of our extension 489 | if (startDot === -1) startDot = i;else if (preDotState !== 1) preDotState = 1; 490 | } else if (startDot !== -1) { 491 | // We saw a non-dot and non-path separator before our dot, so we should 492 | // have a good chance at having a non-empty extension 493 | preDotState = -1; 494 | } 495 | } 496 | 497 | if (startDot === -1 || end === -1 || 498 | // We saw a non-dot character immediately before the dot 499 | preDotState === 0 || 500 | // The (right-most) trimmed path component is exactly '..' 501 | preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) { 502 | if (end !== -1) { 503 | if (startPart === 0 && isAbsolute) ret.base = ret.name = path.slice(1, end);else ret.base = ret.name = path.slice(startPart, end); 504 | } 505 | } else { 506 | if (startPart === 0 && isAbsolute) { 507 | ret.name = path.slice(1, startDot); 508 | ret.base = path.slice(1, end); 509 | } else { 510 | ret.name = path.slice(startPart, startDot); 511 | ret.base = path.slice(startPart, end); 512 | } 513 | ret.ext = path.slice(startDot, end); 514 | } 515 | 516 | if (startPart > 0) ret.dir = path.slice(0, startPart - 1);else if (isAbsolute) ret.dir = '/'; 517 | 518 | return ret; 519 | }, 520 | 521 | sep: '/', 522 | delimiter: ':', 523 | win32: null, 524 | posix: null 525 | }; 526 | 527 | posix.posix = posix; 528 | 529 | module.exports = posix; 530 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "path-browserify", 3 | "description": "the path module from node core for browsers", 4 | "version": "1.0.1", 5 | "author": { 6 | "name": "James Halliday", 7 | "email": "mail@substack.net", 8 | "url": "http://substack.net" 9 | }, 10 | "bugs": "https://github.com/browserify/path-browserify/issues", 11 | "dependencies": {}, 12 | "devDependencies": { 13 | "tape": "^4.9.0" 14 | }, 15 | "homepage": "https://github.com/browserify/path-browserify", 16 | "keywords": [ 17 | "browser", 18 | "browserify", 19 | "path" 20 | ], 21 | "license": "MIT", 22 | "main": "index.js", 23 | "repository": { 24 | "type": "git", 25 | "url": "git://github.com/browserify/path-browserify.git" 26 | }, 27 | "scripts": { 28 | "test": "node test" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /security.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | Only the latest major version is supported at any given time. 5 | 6 | ## Reporting a Vulnerability 7 | 8 | To report a security vulnerability, please use the 9 | [Tidelift security contact](https://tidelift.com/security). 10 | Tidelift will coordinate the fix and disclosure. 11 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | require('./test-path'); 2 | require('./test-path-basename'); 3 | require('./test-path-dirname'); 4 | require('./test-path-extname'); 5 | require('./test-path-isabsolute'); 6 | require('./test-path-join'); 7 | require('./test-path-relative'); 8 | require('./test-path-resolve'); 9 | require('./test-path-zero-length-strings'); 10 | -------------------------------------------------------------------------------- /test/test-path-basename.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var tape = require('tape'); 3 | var path = require('../'); 4 | 5 | tape('path.basename', function (t) { 6 | t.strictEqual(path.basename(__filename), 'test-path-basename.js'); 7 | t.strictEqual(path.basename(__filename, '.js'), 'test-path-basename'); 8 | t.strictEqual(path.basename('.js', '.js'), ''); 9 | t.strictEqual(path.basename(''), ''); 10 | t.strictEqual(path.basename('/dir/basename.ext'), 'basename.ext'); 11 | t.strictEqual(path.basename('/basename.ext'), 'basename.ext'); 12 | t.strictEqual(path.basename('basename.ext'), 'basename.ext'); 13 | t.strictEqual(path.basename('basename.ext/'), 'basename.ext'); 14 | t.strictEqual(path.basename('basename.ext//'), 'basename.ext'); 15 | t.strictEqual(path.basename('aaa/bbb', '/bbb'), 'bbb'); 16 | t.strictEqual(path.basename('aaa/bbb', 'a/bbb'), 'bbb'); 17 | t.strictEqual(path.basename('aaa/bbb', 'bbb'), 'bbb'); 18 | t.strictEqual(path.basename('aaa/bbb//', 'bbb'), 'bbb'); 19 | t.strictEqual(path.basename('aaa/bbb', 'bb'), 'b'); 20 | t.strictEqual(path.basename('aaa/bbb', 'b'), 'bb'); 21 | t.strictEqual(path.basename('/aaa/bbb', '/bbb'), 'bbb'); 22 | t.strictEqual(path.basename('/aaa/bbb', 'a/bbb'), 'bbb'); 23 | t.strictEqual(path.basename('/aaa/bbb', 'bbb'), 'bbb'); 24 | t.strictEqual(path.basename('/aaa/bbb//', 'bbb'), 'bbb'); 25 | t.strictEqual(path.basename('/aaa/bbb', 'bb'), 'b'); 26 | t.strictEqual(path.basename('/aaa/bbb', 'b'), 'bb'); 27 | t.strictEqual(path.basename('/aaa/bbb'), 'bbb'); 28 | t.strictEqual(path.basename('/aaa/'), 'aaa'); 29 | t.strictEqual(path.basename('/aaa/b'), 'b'); 30 | t.strictEqual(path.basename('/a/b'), 'b'); 31 | t.strictEqual(path.basename('//a'), 'a'); 32 | t.end(); 33 | }) 34 | 35 | tape('path.win32.basename', { skip: true }, function (t) { 36 | // On Windows a backslash acts as a path separator. 37 | t.strictEqual(path.win32.basename('\\dir\\basename.ext'), 'basename.ext'); 38 | t.strictEqual(path.win32.basename('\\basename.ext'), 'basename.ext'); 39 | t.strictEqual(path.win32.basename('basename.ext'), 'basename.ext'); 40 | t.strictEqual(path.win32.basename('basename.ext\\'), 'basename.ext'); 41 | t.strictEqual(path.win32.basename('basename.ext\\\\'), 'basename.ext'); 42 | t.strictEqual(path.win32.basename('foo'), 'foo'); 43 | t.strictEqual(path.win32.basename('aaa\\bbb', '\\bbb'), 'bbb'); 44 | t.strictEqual(path.win32.basename('aaa\\bbb', 'a\\bbb'), 'bbb'); 45 | t.strictEqual(path.win32.basename('aaa\\bbb', 'bbb'), 'bbb'); 46 | t.strictEqual(path.win32.basename('aaa\\bbb\\\\\\\\', 'bbb'), 'bbb'); 47 | t.strictEqual(path.win32.basename('aaa\\bbb', 'bb'), 'b'); 48 | t.strictEqual(path.win32.basename('aaa\\bbb', 'b'), 'bb'); 49 | t.strictEqual(path.win32.basename('C:'), ''); 50 | t.strictEqual(path.win32.basename('C:.'), '.'); 51 | t.strictEqual(path.win32.basename('C:\\'), ''); 52 | t.strictEqual(path.win32.basename('C:\\dir\\base.ext'), 'base.ext'); 53 | t.strictEqual(path.win32.basename('C:\\basename.ext'), 'basename.ext'); 54 | t.strictEqual(path.win32.basename('C:basename.ext'), 'basename.ext'); 55 | t.strictEqual(path.win32.basename('C:basename.ext\\'), 'basename.ext'); 56 | t.strictEqual(path.win32.basename('C:basename.ext\\\\'), 'basename.ext'); 57 | t.strictEqual(path.win32.basename('C:foo'), 'foo'); 58 | t.strictEqual(path.win32.basename('file:stream'), 'file:stream'); 59 | t.end(); 60 | }); 61 | 62 | tape('On unix a backslash is just treated as any other character.', function (t) { 63 | t.strictEqual(path.posix.basename('\\dir\\basename.ext'), 64 | '\\dir\\basename.ext'); 65 | t.strictEqual(path.posix.basename('\\basename.ext'), '\\basename.ext'); 66 | t.strictEqual(path.posix.basename('basename.ext'), 'basename.ext'); 67 | t.strictEqual(path.posix.basename('basename.ext\\'), 'basename.ext\\'); 68 | t.strictEqual(path.posix.basename('basename.ext\\\\'), 'basename.ext\\\\'); 69 | t.strictEqual(path.posix.basename('foo'), 'foo'); 70 | t.end(); 71 | }); 72 | 73 | tape('POSIX filenames may include control characters', function (t) { 74 | // c.f. http://www.dwheeler.com/essays/fixing-unix-linux-filenames.html 75 | var controlCharFilename = "Icon" + (String.fromCharCode(13)); 76 | t.strictEqual(path.posix.basename(("/a/b/" + controlCharFilename)), 77 | controlCharFilename); 78 | t.end(); 79 | }); 80 | -------------------------------------------------------------------------------- /test/test-path-dirname.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var tape = require('tape'); 3 | var path = require('../'); 4 | 5 | tape('path.posix.dirname', function (t) { 6 | t.strictEqual(path.posix.dirname('/a/b/'), '/a'); 7 | t.strictEqual(path.posix.dirname('/a/b'), '/a'); 8 | t.strictEqual(path.posix.dirname('/a'), '/'); 9 | t.strictEqual(path.posix.dirname(''), '.'); 10 | t.strictEqual(path.posix.dirname('/'), '/'); 11 | t.strictEqual(path.posix.dirname('////'), '/'); 12 | t.strictEqual(path.posix.dirname('//a'), '//'); 13 | t.strictEqual(path.posix.dirname('foo'), '.'); 14 | t.end(); 15 | }); 16 | 17 | tape('path.win32.dirname', { skip: true }, function (t) { 18 | t.strictEqual(path.win32.dirname('c:\\'), 'c:\\'); 19 | t.strictEqual(path.win32.dirname('c:\\foo'), 'c:\\'); 20 | t.strictEqual(path.win32.dirname('c:\\foo\\'), 'c:\\'); 21 | t.strictEqual(path.win32.dirname('c:\\foo\\bar'), 'c:\\foo'); 22 | t.strictEqual(path.win32.dirname('c:\\foo\\bar\\'), 'c:\\foo'); 23 | t.strictEqual(path.win32.dirname('c:\\foo\\bar\\baz'), 'c:\\foo\\bar'); 24 | t.strictEqual(path.win32.dirname('\\'), '\\'); 25 | t.strictEqual(path.win32.dirname('\\foo'), '\\'); 26 | t.strictEqual(path.win32.dirname('\\foo\\'), '\\'); 27 | t.strictEqual(path.win32.dirname('\\foo\\bar'), '\\foo'); 28 | t.strictEqual(path.win32.dirname('\\foo\\bar\\'), '\\foo'); 29 | t.strictEqual(path.win32.dirname('\\foo\\bar\\baz'), '\\foo\\bar'); 30 | t.strictEqual(path.win32.dirname('c:'), 'c:'); 31 | t.strictEqual(path.win32.dirname('c:foo'), 'c:'); 32 | t.strictEqual(path.win32.dirname('c:foo\\'), 'c:'); 33 | t.strictEqual(path.win32.dirname('c:foo\\bar'), 'c:foo'); 34 | t.strictEqual(path.win32.dirname('c:foo\\bar\\'), 'c:foo'); 35 | t.strictEqual(path.win32.dirname('c:foo\\bar\\baz'), 'c:foo\\bar'); 36 | t.strictEqual(path.win32.dirname('file:stream'), '.'); 37 | t.strictEqual(path.win32.dirname('dir\\file:stream'), 'dir'); 38 | t.strictEqual(path.win32.dirname('\\\\unc\\share'), 39 | '\\\\unc\\share'); 40 | t.strictEqual(path.win32.dirname('\\\\unc\\share\\foo'), 41 | '\\\\unc\\share\\'); 42 | t.strictEqual(path.win32.dirname('\\\\unc\\share\\foo\\'), 43 | '\\\\unc\\share\\'); 44 | t.strictEqual(path.win32.dirname('\\\\unc\\share\\foo\\bar'), 45 | '\\\\unc\\share\\foo'); 46 | t.strictEqual(path.win32.dirname('\\\\unc\\share\\foo\\bar\\'), 47 | '\\\\unc\\share\\foo'); 48 | t.strictEqual(path.win32.dirname('\\\\unc\\share\\foo\\bar\\baz'), 49 | '\\\\unc\\share\\foo\\bar'); 50 | t.strictEqual(path.win32.dirname('/a/b/'), '/a'); 51 | t.strictEqual(path.win32.dirname('/a/b'), '/a'); 52 | t.strictEqual(path.win32.dirname('/a'), '/'); 53 | t.strictEqual(path.win32.dirname(''), '.'); 54 | t.strictEqual(path.win32.dirname('/'), '/'); 55 | t.strictEqual(path.win32.dirname('////'), '/'); 56 | t.strictEqual(path.win32.dirname('foo'), '.'); 57 | t.end(); 58 | }); 59 | -------------------------------------------------------------------------------- /test/test-path-extname.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var tape = require('tape'); 3 | var path = require('../'); 4 | 5 | var slashRE = /\//g; 6 | 7 | var pairs = [ 8 | [__filename, '.js'], 9 | ['', ''], 10 | ['/path/to/file', ''], 11 | ['/path/to/file.ext', '.ext'], 12 | ['/path.to/file.ext', '.ext'], 13 | ['/path.to/file', ''], 14 | ['/path.to/.file', ''], 15 | ['/path.to/.file.ext', '.ext'], 16 | ['/path/to/f.ext', '.ext'], 17 | ['/path/to/..ext', '.ext'], 18 | ['/path/to/..', ''], 19 | ['file', ''], 20 | ['file.ext', '.ext'], 21 | ['.file', ''], 22 | ['.file.ext', '.ext'], 23 | ['/file', ''], 24 | ['/file.ext', '.ext'], 25 | ['/.file', ''], 26 | ['/.file.ext', '.ext'], 27 | ['.path/file.ext', '.ext'], 28 | ['file.ext.ext', '.ext'], 29 | ['file.', '.'], 30 | ['.', ''], 31 | ['./', ''], 32 | ['.file.ext', '.ext'], 33 | ['.file', ''], 34 | ['.file.', '.'], 35 | ['.file..', '.'], 36 | ['..', ''], 37 | ['../', ''], 38 | ['..file.ext', '.ext'], 39 | ['..file', '.file'], 40 | ['..file.', '.'], 41 | ['..file..', '.'], 42 | ['...', '.'], 43 | ['...ext', '.ext'], 44 | ['....', '.'], 45 | ['file.ext/', '.ext'], 46 | ['file.ext//', '.ext'], 47 | ['file/', ''], 48 | ['file//', ''], 49 | ['file./', '.'], 50 | ['file.//', '.'] ]; 51 | 52 | tape('path.posix.extname', function (t) { 53 | pairs.forEach(function (p) { 54 | var input = p[0]; 55 | var expected = p[1]; 56 | t.strictEqual(expected, path.posix.extname(input)); 57 | }); 58 | t.end(); 59 | }); 60 | 61 | tape('path.win32.extname', { skip: true }, function (t) { 62 | pairs.forEach(function (p) { 63 | var input = p[0].replace(slashRE, '\\'); 64 | var expected = p[1]; 65 | t.strictEqual(expected, path.win32.extname(input)); 66 | t.strictEqual(expected, path.win32.extname("C:" + input)); 67 | }); 68 | t.end(); 69 | }); 70 | 71 | tape('path.win32.extname backslash', { skip: true }, function (t) { 72 | // On Windows, backslash is a path separator. 73 | t.strictEqual(path.win32.extname('.\\'), ''); 74 | t.strictEqual(path.win32.extname('..\\'), ''); 75 | t.strictEqual(path.win32.extname('file.ext\\'), '.ext'); 76 | t.strictEqual(path.win32.extname('file.ext\\\\'), '.ext'); 77 | t.strictEqual(path.win32.extname('file\\'), ''); 78 | t.strictEqual(path.win32.extname('file\\\\'), ''); 79 | t.strictEqual(path.win32.extname('file.\\'), '.'); 80 | t.strictEqual(path.win32.extname('file.\\\\'), '.'); 81 | t.end(); 82 | }); 83 | 84 | tape('path.posix.extname backslash', function (t) { 85 | // On *nix, backslash is a valid name component like any other character. 86 | t.strictEqual(path.posix.extname('.\\'), ''); 87 | t.strictEqual(path.posix.extname('..\\'), '.\\'); 88 | t.strictEqual(path.posix.extname('file.ext\\'), '.ext\\'); 89 | t.strictEqual(path.posix.extname('file.ext\\\\'), '.ext\\\\'); 90 | t.strictEqual(path.posix.extname('file\\'), ''); 91 | t.strictEqual(path.posix.extname('file\\\\'), ''); 92 | t.strictEqual(path.posix.extname('file.\\'), '.\\'); 93 | t.strictEqual(path.posix.extname('file.\\\\'), '.\\\\'); 94 | t.end(); 95 | }); 96 | 97 | -------------------------------------------------------------------------------- /test/test-path-isabsolute.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var tape = require('tape'); 3 | var path = require('../'); 4 | 5 | tape('path.win32.isAbsolute', { skip: true }, function (t) { 6 | t.strictEqual(path.win32.isAbsolute('/'), true); 7 | t.strictEqual(path.win32.isAbsolute('//'), true); 8 | t.strictEqual(path.win32.isAbsolute('//server'), true); 9 | t.strictEqual(path.win32.isAbsolute('//server/file'), true); 10 | t.strictEqual(path.win32.isAbsolute('\\\\server\\file'), true); 11 | t.strictEqual(path.win32.isAbsolute('\\\\server'), true); 12 | t.strictEqual(path.win32.isAbsolute('\\\\'), true); 13 | t.strictEqual(path.win32.isAbsolute('c'), false); 14 | t.strictEqual(path.win32.isAbsolute('c:'), false); 15 | t.strictEqual(path.win32.isAbsolute('c:\\'), true); 16 | t.strictEqual(path.win32.isAbsolute('c:/'), true); 17 | t.strictEqual(path.win32.isAbsolute('c://'), true); 18 | t.strictEqual(path.win32.isAbsolute('C:/Users/'), true); 19 | t.strictEqual(path.win32.isAbsolute('C:\\Users\\'), true); 20 | t.strictEqual(path.win32.isAbsolute('C:cwd/another'), false); 21 | t.strictEqual(path.win32.isAbsolute('C:cwd\\another'), false); 22 | t.strictEqual(path.win32.isAbsolute('directory/directory'), false); 23 | t.strictEqual(path.win32.isAbsolute('directory\\directory'), false); 24 | t.end(); 25 | }); 26 | 27 | tape('path.posix.isAbsolute', function (t) { 28 | t.strictEqual(path.posix.isAbsolute('/home/foo'), true); 29 | t.strictEqual(path.posix.isAbsolute('/home/foo/..'), true); 30 | t.strictEqual(path.posix.isAbsolute('bar/'), false); 31 | t.strictEqual(path.posix.isAbsolute('./baz'), false); 32 | t.end(); 33 | }); 34 | -------------------------------------------------------------------------------- /test/test-path-join.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var tape = require('tape'); 3 | var path = require('../'); 4 | 5 | var backslashRE = /\\/g; 6 | 7 | var joinTests = 8 | // arguments result 9 | [[['.', 'x/b', '..', '/b/c.js'], 'x/b/c.js'], 10 | [[], '.'], 11 | [['/.', 'x/b', '..', '/b/c.js'], '/x/b/c.js'], 12 | [['/foo', '../../../bar'], '/bar'], 13 | [['foo', '../../../bar'], '../../bar'], 14 | [['foo/', '../../../bar'], '../../bar'], 15 | [['foo/x', '../../../bar'], '../bar'], 16 | [['foo/x', './bar'], 'foo/x/bar'], 17 | [['foo/x/', './bar'], 'foo/x/bar'], 18 | [['foo/x/', '.', 'bar'], 'foo/x/bar'], 19 | [['./'], './'], 20 | [['.', './'], './'], 21 | [['.', '.', '.'], '.'], 22 | [['.', './', '.'], '.'], 23 | [['.', '/./', '.'], '.'], 24 | [['.', '/////./', '.'], '.'], 25 | [['.'], '.'], 26 | [['', '.'], '.'], 27 | [['', 'foo'], 'foo'], 28 | [['foo', '/bar'], 'foo/bar'], 29 | [['', '/foo'], '/foo'], 30 | [['', '', '/foo'], '/foo'], 31 | [['', '', 'foo'], 'foo'], 32 | [['foo', ''], 'foo'], 33 | [['foo/', ''], 'foo/'], 34 | [['foo', '', '/bar'], 'foo/bar'], 35 | [['./', '..', '/foo'], '../foo'], 36 | [['./', '..', '..', '/foo'], '../../foo'], 37 | [['.', '..', '..', '/foo'], '../../foo'], 38 | [['', '..', '..', '/foo'], '../../foo'], 39 | [['/'], '/'], 40 | [['/', '.'], '/'], 41 | [['/', '..'], '/'], 42 | [['/', '..', '..'], '/'], 43 | [[''], '.'], 44 | [['', ''], '.'], 45 | [[' /foo'], ' /foo'], 46 | [[' ', 'foo'], ' /foo'], 47 | [[' ', '.'], ' '], 48 | [[' ', '/'], ' /'], 49 | [[' ', ''], ' '], 50 | [['/', 'foo'], '/foo'], 51 | [['/', '/foo'], '/foo'], 52 | [['/', '//foo'], '/foo'], 53 | [['/', '', '/foo'], '/foo'], 54 | [['', '/', 'foo'], '/foo'], 55 | [['', '/', '/foo'], '/foo'] 56 | ]; 57 | 58 | // Windows-specific join tests 59 | var windowsJoinTests = 60 | [// arguments result 61 | // UNC path expected 62 | [['//foo/bar'], '\\\\foo\\bar\\'], 63 | [['\\/foo/bar'], '\\\\foo\\bar\\'], 64 | [['\\\\foo/bar'], '\\\\foo\\bar\\'], 65 | // UNC path expected - server and share separate 66 | [['//foo', 'bar'], '\\\\foo\\bar\\'], 67 | [['//foo/', 'bar'], '\\\\foo\\bar\\'], 68 | [['//foo', '/bar'], '\\\\foo\\bar\\'], 69 | // UNC path expected - questionable 70 | [['//foo', '', 'bar'], '\\\\foo\\bar\\'], 71 | [['//foo/', '', 'bar'], '\\\\foo\\bar\\'], 72 | [['//foo/', '', '/bar'], '\\\\foo\\bar\\'], 73 | // UNC path expected - even more questionable 74 | [['', '//foo', 'bar'], '\\\\foo\\bar\\'], 75 | [['', '//foo/', 'bar'], '\\\\foo\\bar\\'], 76 | [['', '//foo/', '/bar'], '\\\\foo\\bar\\'], 77 | // No UNC path expected (no double slash in first component) 78 | [['\\', 'foo/bar'], '\\foo\\bar'], 79 | [['\\', '/foo/bar'], '\\foo\\bar'], 80 | [['', '/', '/foo/bar'], '\\foo\\bar'], 81 | // No UNC path expected (no non-slashes in first component - 82 | // questionable) 83 | [['//', 'foo/bar'], '\\foo\\bar'], 84 | [['//', '/foo/bar'], '\\foo\\bar'], 85 | [['\\\\', '/', '/foo/bar'], '\\foo\\bar'], 86 | [['//'], '/'], 87 | // No UNC path expected (share name missing - questionable). 88 | [['//foo'], '\\foo'], 89 | [['//foo/'], '\\foo\\'], 90 | [['//foo', '/'], '\\foo\\'], 91 | [['//foo', '', '/'], '\\foo\\'], 92 | // No UNC path expected (too many leading slashes - questionable) 93 | [['///foo/bar'], '\\foo\\bar'], 94 | [['////foo', 'bar'], '\\foo\\bar'], 95 | [['\\\\\\/foo/bar'], '\\foo\\bar'], 96 | // Drive-relative vs drive-absolute paths. This merely describes the 97 | // status quo, rather than being obviously right 98 | [['c:'], 'c:.'], 99 | [['c:.'], 'c:.'], 100 | [['c:', ''], 'c:.'], 101 | [['', 'c:'], 'c:.'], 102 | [['c:.', '/'], 'c:.\\'], 103 | [['c:.', 'file'], 'c:file'], 104 | [['c:', '/'], 'c:\\'], 105 | [['c:', 'file'], 'c:\\file'] 106 | ]; 107 | 108 | tape('path.posix.join', function (t) { 109 | joinTests.forEach(function (p) { 110 | var actual = path.posix.join.apply(null, p[0]); 111 | t.strictEqual(actual, p[1]); 112 | }); 113 | t.end(); 114 | }); 115 | 116 | tape('path.win32.join', { skip: true }, function (t) { 117 | joinTests.forEach(function (p) { 118 | var actual = path.win32.join.apply(null, p[0]).replace(backslashRE, '/'); 119 | t.strictEqual(actual, p[1]); 120 | }); 121 | windowsJoinTests.forEach(function (p) { 122 | var actual = path.win32.join.apply(null, p[0]); 123 | t.strictEqual(actual, p[1]); 124 | }); 125 | t.end(); 126 | }); 127 | -------------------------------------------------------------------------------- /test/test-path-parse-format.js: -------------------------------------------------------------------------------- 1 | // Copyright Joyent, Inc. and other Node contributors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the 5 | // "Software"), to deal in the Software without restriction, including 6 | // without limitation the rights to use, copy, modify, merge, publish, 7 | // distribute, sublicense, and/or sell copies of the Software, and to permit 8 | // persons to whom the Software is furnished to do so, subject to the 9 | // following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included 12 | // in all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | 'use strict'; 23 | var tape = require('tape'); 24 | var path = require('../'); 25 | 26 | var winPaths = [ 27 | // [path, root] 28 | ['C:\\path\\dir\\index.html', 'C:\\'], 29 | ['C:\\another_path\\DIR\\1\\2\\33\\\\index', 'C:\\'], 30 | ['another_path\\DIR with spaces\\1\\2\\33\\index', ''], 31 | ['\\', '\\'], 32 | ['\\foo\\C:', '\\'], 33 | ['file', ''], 34 | ['file:stream', ''], 35 | ['.\\file', ''], 36 | ['C:', 'C:'], 37 | ['C:.', 'C:'], 38 | ['C:..', 'C:'], 39 | ['C:abc', 'C:'], 40 | ['C:\\', 'C:\\'], 41 | ['C:\\abc', 'C:\\' ], 42 | ['', ''], 43 | 44 | // unc 45 | ['\\\\server\\share\\file_path', '\\\\server\\share\\'], 46 | ['\\\\server two\\shared folder\\file path.zip', 47 | '\\\\server two\\shared folder\\'], 48 | ['\\\\teela\\admin$\\system32', '\\\\teela\\admin$\\'], 49 | ['\\\\?\\UNC\\server\\share', '\\\\?\\UNC\\'] 50 | ]; 51 | 52 | var winSpecialCaseParseTests = [ 53 | ['/foo/bar', { root: '/' }], 54 | ]; 55 | 56 | var winSpecialCaseFormatTests = [ 57 | [{ dir: 'some\\dir' }, 'some\\dir\\'], 58 | [{ base: 'index.html' }, 'index.html'], 59 | [{ root: 'C:\\' }, 'C:\\'], 60 | [{ name: 'index', ext: '.html' }, 'index.html'], 61 | [{ dir: 'some\\dir', name: 'index', ext: '.html' }, 'some\\dir\\index.html'], 62 | [{ root: 'C:\\', name: 'index', ext: '.html' }, 'C:\\index.html'], 63 | [{}, ''] 64 | ]; 65 | 66 | var unixPaths = [ 67 | // [path, root] 68 | ['/home/user/dir/file.txt', '/'], 69 | ['/home/user/a dir/another File.zip', '/'], 70 | ['/home/user/a dir//another&File.', '/'], 71 | ['/home/user/a$$$dir//another File.zip', '/'], 72 | ['user/dir/another File.zip', ''], 73 | ['file', ''], 74 | ['.\\file', ''], 75 | ['./file', ''], 76 | ['C:\\foo', ''], 77 | ['/', '/'], 78 | ['', ''], 79 | ['.', ''], 80 | ['..', ''], 81 | ['/foo', '/'], 82 | ['/foo.', '/'], 83 | ['/foo.bar', '/'], 84 | ['/.', '/'], 85 | ['/.foo', '/'], 86 | ['/.foo.bar', '/'], 87 | ['/foo/bar.baz', '/'] 88 | ]; 89 | 90 | var unixSpecialCaseFormatTests = [ 91 | [{ dir: 'some/dir' }, 'some/dir/'], 92 | [{ base: 'index.html' }, 'index.html'], 93 | [{ root: '/' }, '/'], 94 | [{ name: 'index', ext: '.html' }, 'index.html'], 95 | [{ dir: 'some/dir', name: 'index', ext: '.html' }, 'some/dir/index.html'], 96 | [{ root: '/', name: 'index', ext: '.html' }, '/index.html'], 97 | [{}, ''] 98 | ]; 99 | 100 | var errors = [ 101 | { method: 'parse', input: [null], message: TypeError }, 102 | { method: 'parse', input: [{}], message: TypeError }, 103 | { method: 'parse', input: [true], message: TypeError }, 104 | { method: 'parse', input: [1], message: TypeError }, 105 | { method: 'parse', input: [], message: TypeError }, 106 | { method: 'format', input: [null], message: TypeError }, 107 | { method: 'format', input: [''], message: TypeError }, 108 | { method: 'format', input: [true], message: TypeError }, 109 | { method: 'format', input: [1], message: TypeError }, 110 | ]; 111 | 112 | tape('path.win32.parse', { skip: true }, function (t) { 113 | checkParseFormat(t, path.win32, winPaths); 114 | checkSpecialCaseParseFormat(t, path.win32, winSpecialCaseParseTests); 115 | t.end(); 116 | }); 117 | 118 | tape('path.posix.parse', function (t) { 119 | checkParseFormat(t, path.posix, unixPaths); 120 | t.end(); 121 | }); 122 | 123 | tape('path.win32.parse errors', { skip: true }, function (t) { 124 | checkErrors(t, path.win32); 125 | t.end(); 126 | }); 127 | 128 | tape('path.posix.parse errors', function (t) { 129 | checkErrors(t, path.posix); 130 | t.end(); 131 | }); 132 | 133 | tape('path.win32.format', { skip: true }, function (t) { 134 | checkFormat(t, path.win32, winSpecialCaseFormatTests); 135 | t.end(); 136 | }); 137 | 138 | tape('path.posix.format', function (t) { 139 | checkFormat(t, path.posix, unixSpecialCaseFormatTests); 140 | t.end(); 141 | }); 142 | 143 | // Test removal of trailing path separators 144 | var windowsTrailingTests = 145 | [['.\\', { root: '', dir: '', base: '.', ext: '', name: '.' }], 146 | ['\\\\', { root: '\\', dir: '\\', base: '', ext: '', name: '' }], 147 | ['\\\\', { root: '\\', dir: '\\', base: '', ext: '', name: '' }], 148 | ['c:\\foo\\\\\\', 149 | { root: 'c:\\', dir: 'c:\\', base: 'foo', ext: '', name: 'foo' }], 150 | ['D:\\foo\\\\\\bar.baz', 151 | { root: 'D:\\', 152 | dir: 'D:\\foo\\\\', 153 | base: 'bar.baz', 154 | ext: '.baz', 155 | name: 'bar' 156 | } 157 | ] 158 | ]; 159 | var posixTrailingTests = 160 | [['./', { root: '', dir: '', base: '.', ext: '', name: '.' }], 161 | ['//', { root: '/', dir: '/', base: '', ext: '', name: '' }], 162 | ['///', { root: '/', dir: '/', base: '', ext: '', name: '' }], 163 | ['/foo///', { root: '/', dir: '/', base: 'foo', ext: '', name: 'foo' }], 164 | ['/foo///bar.baz', 165 | { root: '/', dir: '/foo//', base: 'bar.baz', ext: '.baz', name: 'bar' } 166 | ] 167 | ]; 168 | 169 | tape('path.win32.parse trailing', { skip: true }, function (t) { 170 | windowsTrailingTests.forEach(function (p) { 171 | var actual = path.win32.parse(p[0]); 172 | var expected = p[1]; 173 | t.deepEqual(actual, expected) 174 | }); 175 | t.end(); 176 | }); 177 | 178 | tape('path.posix.parse trailing', function (t) { 179 | posixTrailingTests.forEach(function (p) { 180 | var actual = path.posix.parse(p[0]); 181 | var expected = p[1]; 182 | t.deepEqual(actual, expected) 183 | }); 184 | t.end(); 185 | }); 186 | 187 | function checkErrors(t, path) { 188 | errors.forEach(function(errorCase) { 189 | t.throws(function () { 190 | path[errorCase.method].apply(path, errorCase.input); 191 | }, errorCase.message); 192 | }); 193 | } 194 | 195 | function checkParseFormat(t, path, paths) { 196 | paths.forEach(function(p) { 197 | var element = p[0]; 198 | var root = p[1]; 199 | var output = path.parse(element); 200 | t.strictEqual(typeof output.root, 'string'); 201 | t.strictEqual(typeof output.dir, 'string'); 202 | t.strictEqual(typeof output.base, 'string'); 203 | t.strictEqual(typeof output.ext, 'string'); 204 | t.strictEqual(typeof output.name, 'string'); 205 | t.strictEqual(path.format(output), element); 206 | t.strictEqual(output.root, root); 207 | t.ok(output.dir.startsWith(output.root)); 208 | t.strictEqual(output.dir, output.dir ? path.dirname(element) : ''); 209 | t.strictEqual(output.base, path.basename(element)); 210 | t.strictEqual(output.ext, path.extname(element)); 211 | }); 212 | } 213 | 214 | function checkSpecialCaseParseFormat(t, path, testCases) { 215 | testCases.forEach(function(testCase) { 216 | var element = testCase[0]; 217 | var expect = testCase[1]; 218 | var output = path.parse(element); 219 | Object.keys(expect).forEach(function(key) { 220 | t.strictEqual(output[key], expect[key]); 221 | }); 222 | }); 223 | } 224 | 225 | function checkFormat(t, path, testCases) { 226 | testCases.forEach(function(testCase) { 227 | t.strictEqual(path.format(testCase[0]), testCase[1]); 228 | }); 229 | 230 | [null, undefined, 1, true, false, 'string'].forEach(function (pathObject) { 231 | t.throws(function() { 232 | path.format(pathObject); 233 | }, /The "pathObject" argument must be of type Object. Received type (\w+)/); 234 | }); 235 | } 236 | -------------------------------------------------------------------------------- /test/test-path-relative.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var tape = require('tape'); 3 | var path = require('../'); 4 | 5 | var relativeTests = { 6 | win32: 7 | // arguments result 8 | [['c:/blah\\blah', 'd:/games', 'd:\\games'], 9 | ['c:/aaaa/bbbb', 'c:/aaaa', '..'], 10 | ['c:/aaaa/bbbb', 'c:/cccc', '..\\..\\cccc'], 11 | ['c:/aaaa/bbbb', 'c:/aaaa/bbbb', ''], 12 | ['c:/aaaa/bbbb', 'c:/aaaa/cccc', '..\\cccc'], 13 | ['c:/aaaa/', 'c:/aaaa/cccc', 'cccc'], 14 | ['c:/', 'c:\\aaaa\\bbbb', 'aaaa\\bbbb'], 15 | ['c:/aaaa/bbbb', 'd:\\', 'd:\\'], 16 | ['c:/AaAa/bbbb', 'c:/aaaa/bbbb', ''], 17 | ['c:/aaaaa/', 'c:/aaaa/cccc', '..\\aaaa\\cccc'], 18 | ['C:\\foo\\bar\\baz\\quux', 'C:\\', '..\\..\\..\\..'], 19 | ['C:\\foo\\test', 'C:\\foo\\test\\bar\\package.json', 'bar\\package.json'], 20 | ['C:\\foo\\bar\\baz-quux', 'C:\\foo\\bar\\baz', '..\\baz'], 21 | ['C:\\foo\\bar\\baz', 'C:\\foo\\bar\\baz-quux', '..\\baz-quux'], 22 | ['\\\\foo\\bar', '\\\\foo\\bar\\baz', 'baz'], 23 | ['\\\\foo\\bar\\baz', '\\\\foo\\bar', '..'], 24 | ['\\\\foo\\bar\\baz-quux', '\\\\foo\\bar\\baz', '..\\baz'], 25 | ['\\\\foo\\bar\\baz', '\\\\foo\\bar\\baz-quux', '..\\baz-quux'], 26 | ['C:\\baz-quux', 'C:\\baz', '..\\baz'], 27 | ['C:\\baz', 'C:\\baz-quux', '..\\baz-quux'], 28 | ['\\\\foo\\baz-quux', '\\\\foo\\baz', '..\\baz'], 29 | ['\\\\foo\\baz', '\\\\foo\\baz-quux', '..\\baz-quux'], 30 | ['C:\\baz', '\\\\foo\\bar\\baz', '\\\\foo\\bar\\baz'], 31 | ['\\\\foo\\bar\\baz', 'C:\\baz', 'C:\\baz'] 32 | ], 33 | posix: 34 | // arguments result 35 | [['/var/lib', '/var', '..'], 36 | ['/var/lib', '/bin', '../../bin'], 37 | ['/var/lib', '/var/lib', ''], 38 | ['/var/lib', '/var/apache', '../apache'], 39 | ['/var/', '/var/lib', 'lib'], 40 | ['/', '/var/lib', 'var/lib'], 41 | ['/foo/test', '/foo/test/bar/package.json', 'bar/package.json'], 42 | ['/Users/a/web/b/test/mails', '/Users/a/web/b', '../..'], 43 | ['/foo/bar/baz-quux', '/foo/bar/baz', '../baz'], 44 | ['/foo/bar/baz', '/foo/bar/baz-quux', '../baz-quux'], 45 | ['/baz-quux', '/baz', '../baz'], 46 | ['/baz', '/baz-quux', '../baz-quux'] 47 | ] 48 | }; 49 | 50 | tape('path.posix.relative', function (t) { 51 | relativeTests.posix.forEach(function (p) { 52 | var expected = p[2]; 53 | var actual = path.posix.relative(p[0], p[1]); 54 | t.strictEqual(actual, expected); 55 | }); 56 | t.end(); 57 | }); 58 | 59 | tape('path.win32.relative', { skip: true }, function (t) { 60 | relativeTests.win32.forEach(function (p) { 61 | var expected = p[2]; 62 | var actual = path.win32.relative(p[0], p[1]); 63 | t.strictEqual(actual, expected); 64 | }); 65 | t.end(); 66 | }); 67 | -------------------------------------------------------------------------------- /test/test-path-resolve.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var tape = require('tape'); 3 | var path = require('../'); 4 | 5 | var windowsTests = 6 | // arguments result 7 | [[['c:/blah\\blah', 'd:/games', 'c:../a'], 'c:\\blah\\a'], 8 | [['c:/ignore', 'd:\\a/b\\c/d', '\\e.exe'], 'd:\\e.exe'], 9 | [['c:/ignore', 'c:/some/file'], 'c:\\some\\file'], 10 | [['d:/ignore', 'd:some/dir//'], 'd:\\ignore\\some\\dir'], 11 | [['.'], process.cwd()], 12 | [['//server/share', '..', 'relative\\'], '\\\\server\\share\\relative'], 13 | [['c:/', '//'], 'c:\\'], 14 | [['c:/', '//dir'], 'c:\\dir'], 15 | [['c:/', '//server/share'], '\\\\server\\share\\'], 16 | [['c:/', '//server//share'], '\\\\server\\share\\'], 17 | [['c:/', '///some//dir'], 'c:\\some\\dir'], 18 | [['C:\\foo\\tmp.3\\', '..\\tmp.3\\cycles\\root.js'], 19 | 'C:\\foo\\tmp.3\\cycles\\root.js'] 20 | ]; 21 | var posixTests = 22 | // arguments result 23 | [[['/var/lib', '../', 'file/'], '/var/file'], 24 | [['/var/lib', '/../', 'file/'], '/file'], 25 | [['a/b/c/', '../../..'], process.cwd()], 26 | [['.'], process.cwd()], 27 | [['/some/dir', '.', '/absolute/'], '/absolute'], 28 | [['/foo/tmp.3/', '../tmp.3/cycles/root.js'], '/foo/tmp.3/cycles/root.js'] 29 | ]; 30 | 31 | tape('path.posix.resolve', function (t) { 32 | posixTests.forEach(function (p) { 33 | var actual = path.posix.resolve.apply(null, p[0]); 34 | t.strictEqual(actual, p[1]); 35 | }); 36 | t.end(); 37 | }); 38 | 39 | tape('path.win32.resolve', { skip: true }, function (t) { 40 | windowsTests.forEach(function (p) { 41 | var actual = path.win32.resolve.apply(null, p[0]); 42 | t.strictEqual(actual, p[1]); 43 | }); 44 | t.end(); 45 | }); 46 | -------------------------------------------------------------------------------- /test/test-path-zero-length-strings.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // These testcases are specific to one uncommon behavior in path module. Few 4 | // of the functions in path module, treat '' strings as current working 5 | // directory. This test makes sure that the behavior is intact between commits. 6 | // See: https://github.com/nodejs/node/pull/2106 7 | 8 | var tape = require('tape'); 9 | var path = require('../'); 10 | var pwd = process.cwd(); 11 | 12 | tape('path.join zero-length', function (t) { 13 | // join will internally ignore all the zero-length strings and it will return 14 | // '.' if the joined string is a zero-length string. 15 | t.strictEqual(path.posix.join(''), '.'); 16 | t.strictEqual(path.posix.join('', ''), '.'); 17 | if (path.win32) t.strictEqual(path.win32.join(''), '.'); 18 | if (path.win32) t.strictEqual(path.win32.join('', ''), '.'); 19 | t.strictEqual(path.join(pwd), pwd); 20 | t.strictEqual(path.join(pwd, ''), pwd); 21 | t.end(); 22 | }); 23 | 24 | tape('path.normalize zero-length', function (t) { 25 | // normalize will return '.' if the input is a zero-length string 26 | t.strictEqual(path.posix.normalize(''), '.'); 27 | if (path.win32) t.strictEqual(path.win32.normalize(''), '.'); 28 | t.strictEqual(path.normalize(pwd), pwd); 29 | t.end(); 30 | }); 31 | 32 | tape('path.isAbsolute zero-length', function (t) { 33 | // Since '' is not a valid path in any of the common environments, return false 34 | t.strictEqual(path.posix.isAbsolute(''), false); 35 | if (path.win32) t.strictEqual(path.win32.isAbsolute(''), false); 36 | t.end(); 37 | }); 38 | 39 | tape('path.resolve zero-length', function (t) { 40 | // resolve, internally ignores all the zero-length strings and returns the 41 | // current working directory 42 | t.strictEqual(path.resolve(''), pwd); 43 | t.strictEqual(path.resolve('', ''), pwd); 44 | t.end(); 45 | }); 46 | 47 | tape('path.relative zero-length', function (t) { 48 | // relative, internally calls resolve. So, '' is actually the current directory 49 | t.strictEqual(path.relative('', pwd), ''); 50 | t.strictEqual(path.relative(pwd, ''), ''); 51 | t.strictEqual(path.relative(pwd, pwd), ''); 52 | t.end(); 53 | }); 54 | -------------------------------------------------------------------------------- /test/test-path.js: -------------------------------------------------------------------------------- 1 | // Copyright Joyent, Inc. and other Node contributors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the 5 | // "Software"), to deal in the Software without restriction, including 6 | // without limitation the rights to use, copy, modify, merge, publish, 7 | // distribute, sublicense, and/or sell copies of the Software, and to permit 8 | // persons to whom the Software is furnished to do so, subject to the 9 | // following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included 12 | // in all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | 'use strict'; 23 | var tape = require('tape'); 24 | var path = require('../'); 25 | 26 | // Test thrown TypeErrors 27 | var typeErrorTests = [true, false, 7, null, {}, undefined, [], NaN]; 28 | 29 | function fail(t, fn) { 30 | var args = [].slice.call(arguments, 1); 31 | 32 | t.throws(function () { 33 | fn.apply(null, args); 34 | }, TypeError); 35 | } 36 | 37 | tape('path.posix TypeErrors', function (t) { 38 | typeErrorTests.forEach(function (test) { 39 | fail(t, path.posix.join, test); 40 | fail(t, path.posix.resolve, test); 41 | fail(t, path.posix.normalize, test); 42 | fail(t, path.posix.isAbsolute, test); 43 | fail(t, path.posix.relative, test, 'foo'); 44 | fail(t, path.posix.relative, 'foo', test); 45 | fail(t, path.posix.parse, test); 46 | fail(t, path.posix.dirname, test); 47 | fail(t, path.posix.basename, test); 48 | fail(t, path.posix.extname, test); 49 | 50 | // undefined is a valid value as the second argument to basename 51 | if (test !== undefined) { 52 | fail(t, path.posix.basename, 'foo', test); 53 | } 54 | }); 55 | t.end(); 56 | }); 57 | 58 | tape('path.win32 TypeErrors', { skip: true }, function (t) { 59 | typeErrorTests.forEach(function (test) { 60 | fail(t, path.win32.join, test); 61 | fail(t, path.win32.resolve, test); 62 | fail(t, path.win32.normalize, test); 63 | fail(t, path.win32.isAbsolute, test); 64 | fail(t, path.win32.relative, test, 'foo'); 65 | fail(t, path.win32.relative, 'foo', test); 66 | fail(t, path.win32.parse, test); 67 | fail(t, path.win32.dirname, test); 68 | fail(t, path.win32.basename, test); 69 | fail(t, path.win32.extname, test); 70 | 71 | // undefined is a valid value as the second argument to basename 72 | if (test !== undefined) { 73 | fail(t, path.win32.basename, 'foo', test); 74 | } 75 | }); 76 | t.end(); 77 | }); 78 | 79 | // path.sep tests 80 | tape('path.win32.sep', { skip: true }, function (t) { 81 | // windows 82 | t.strictEqual(path.win32.sep, '\\'); 83 | t.end(); 84 | }); 85 | tape('path.posix.sep', function (t) { 86 | // posix 87 | t.strictEqual(path.posix.sep, '/'); 88 | t.end(); 89 | }); 90 | 91 | // path.delimiter tests 92 | tape('path.win32.delimiter', { skip: true }, function (t) { 93 | // windows 94 | t.strictEqual(path.win32.delimiter, ';'); 95 | t.end(); 96 | }); 97 | tape('path.posix.delimiter', function (t) { 98 | // posix 99 | t.strictEqual(path.posix.delimiter, ':'); 100 | t.end(); 101 | }); 102 | 103 | tape('path', function (t) { 104 | t.strictEqual(path, path.posix); 105 | t.end(); 106 | }); 107 | 108 | --------------------------------------------------------------------------------