├── .npmignore ├── .travis.yml ├── .gitignore ├── benchmark ├── index.js ├── confused_args.js ├── dirname.js ├── extname.js ├── basename.js ├── resolve.js ├── normalize.js └── join.js ├── .editorconfig ├── Makefile ├── package.json ├── History.md ├── README.md ├── test └── test-path.js └── index.js /.npmignore: -------------------------------------------------------------------------------- 1 | benchmark/ 2 | test/ 3 | cov/ 4 | Makefile 5 | covrage.html 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.8" 4 | - "0.10" 5 | - "0.11" 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.seed 2 | *.log 3 | *.csv 4 | *.dat 5 | *.out 6 | *.pid 7 | *.gz 8 | 9 | coverage.html 10 | coverage/ 11 | cov/ 12 | 13 | node_modules 14 | 15 | dump.rdb 16 | .DS_Store 17 | -------------------------------------------------------------------------------- /benchmark/index.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * fast-path - benchmark/index.js 3 | * Copyright(c) 2014 dead_horse 4 | * MIT Licensed 5 | */ 6 | 7 | 'use strict'; 8 | 9 | /** 10 | * Module dependencies. 11 | */ 12 | 13 | require('./basename'); 14 | require('./dirname'); 15 | require('./extname'); 16 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | 15 | [Makefile] 16 | indent_style = tab 17 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TESTS = test/*.test.js 2 | REPORTER = tap 3 | TIMEOUT = 3000 4 | MOCHA_OPTS = 5 | 6 | install: 7 | @npm install --registry=http://registry.npm.taobao.org 8 | 9 | totoro: 10 | @node_modules/.bin/totoro --runner test/test-path.js -b 'windowsXP/node/0.10,windowsXP/node/0.11' 11 | 12 | autod: install 13 | @node_modules/.bin/autod -w -e example.js --prefix=~ 14 | @$(MAKE) install 15 | 16 | .PHONY: test 17 | -------------------------------------------------------------------------------- /benchmark/confused_args.js: -------------------------------------------------------------------------------- 1 | function test(path) { 2 | return path.split('/').join('/'); 3 | } 4 | 5 | var path1 = 'first'; 6 | var path2 = 'second'; 7 | var longPath = '.././home//Users/name/git/project/lib/abc/def/zzz.js'; 8 | 9 | var plusPath = path1 + '/' + path2 + '/' + longPath; 10 | var constantPath = 'first/second/.././home//Users/name/git/project/lib/abc/def/zzz.js'; 11 | 12 | var length = 1000000; 13 | 14 | console.time('plusPath'); 15 | for (var i = 0; i < length; i++) { 16 | test(plusPath); 17 | } 18 | console.timeEnd('plusPath'); 19 | 20 | console.time('constantPath'); 21 | for (var i = 0; i < length; i++) { 22 | test(constantPath); 23 | } 24 | console.timeEnd('constantPath'); 25 | 26 | // plusPath: 787ms 27 | // constantPath: 177ms 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fast-path", 3 | "version": "1.1.0", 4 | "description": "a fast implementation of node's native path", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "node test/test-path.js" 8 | }, 9 | "keywords": [ 10 | "path" 11 | ], 12 | "author": { 13 | "name": "dead-horse", 14 | "email": "dead_horse@qq.com", 15 | "url": "http://deadhorse.me" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "git@github.com:node-modules/fast-path" 20 | }, 21 | "license": "MIT", 22 | "dependencies": {}, 23 | "devDependencies": { 24 | "autod": "~0.3.2", 25 | "beautify-benchmark": "~0.2.4", 26 | "benchmark": "~1.0.0", 27 | "totoro": "~1.0.2" 28 | }, 29 | "engine": { 30 | "node": ">=0.8" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /History.md: -------------------------------------------------------------------------------- 1 | 2 | 1.1.0 / 2014-09-17 3 | ================== 4 | 5 | * resolve work the same as normalize in windows 6 | 7 | 1.0.0 / 2014-09-11 8 | ================== 9 | 10 | * update api doc 11 | * update readme benchmark 12 | * import all path method from node native, improve normalize 13 | 14 | 0.1.0 / 2014-09-10 15 | ================== 16 | 17 | * fix benchmark, remove exit 18 | * update benchmark 19 | * add replace 20 | * Merge pull request #3 from node-modules/dirname 21 | * update benchmark 22 | * fix dirname in windows 23 | * add dirname 24 | * add basename, close #1 25 | * add more benchmark 26 | * refactor, add comment 27 | * refator extname 28 | 29 | 0.0.1 / 2014-09-09 30 | ================== 31 | 32 | * support windows 33 | * add benchmark result 34 | * extname 35 | -------------------------------------------------------------------------------- /benchmark/dirname.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * fast-path - benchmark/dirname.js 3 | * Copyright(c) 2014 dead_horse 4 | * MIT Licensed 5 | */ 6 | 7 | 'use strict'; 8 | 9 | /** 10 | * Module dependencies. 11 | */ 12 | 13 | var benchmarks = require('beautify-benchmark'); 14 | var Benchmark = require('benchmark'); 15 | var path = require('path'); 16 | var fastPath = require('..'); 17 | 18 | 19 | var suite = new Benchmark.Suite(); 20 | 21 | var shortPath = 'abc/def/zzz.js///'; 22 | var longPath = '/home/Users/name/git/project/lib/abc/def/zzz.js////'; 23 | 24 | suite 25 | .add('path.dirname(shortPath)', function () { 26 | path.dirname(shortPath); 27 | }) 28 | .add('path.dirname(longPath)', function () { 29 | path.dirname(longPath); 30 | }) 31 | .add('fastPath.dirname(shortPath)', function () { 32 | fastPath.dirname(shortPath); 33 | }) 34 | .add('fastPath.dirname(longPath)', function () { 35 | fastPath.dirname(longPath); 36 | }) 37 | 38 | .on('cycle', function(event) { 39 | benchmarks.add(event.target); 40 | }) 41 | .on('start', function(event) { 42 | console.log('\n node version: %s, date: %s\n Starting...', process.version, Date()); 43 | }) 44 | .on('complete', function done() { 45 | benchmarks.log(); 46 | }) 47 | .run({ 'async': false }); 48 | 49 | // node version: v0.11.13, date: Wed Sep 10 2014 12:38:27 GMT+0800 (CST) 50 | 51 | // path.dirname(shortPath) x 1,761,185 ops/sec ±0.70% (96 runs sampled) 52 | // path.dirname(longPath) x 626,764 ops/sec ±1.52% (90 runs sampled) 53 | // fastPath.dirname(shortPath) x 10,489,132 ops/sec ±1.19% (95 runs sampled) 54 | // fastPath.dirname(longPath) x 10,443,216 ops/sec ±2.36% (94 runs sampled) 55 | 56 | // node version: v0.10.31, date: Wed Sep 10 2014 12:42:04 GMT+0800 (CST) 57 | 58 | // path.dirname(shortPath) x 1,849,624 ops/sec ±1.47% (92 runs sampled) 59 | // path.dirname(longPath) x 635,535 ops/sec ±1.39% (94 runs sampled) 60 | // fastPath.dirname(shortPath) x 9,472,159 ops/sec ±1.69% (92 runs sampled) 61 | // fastPath.dirname(longPath) x 9,394,304 ops/sec ±2.75% (91 runs sampled) 62 | -------------------------------------------------------------------------------- /benchmark/extname.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * fast-path - benchmark/extname.js 3 | * Copyright(c) 2014 dead_horse 4 | * MIT Licensed 5 | */ 6 | 7 | 'use strict'; 8 | 9 | /** 10 | * Module dependencies. 11 | */ 12 | 13 | var benchmarks = require('beautify-benchmark'); 14 | var Benchmark = require('benchmark'); 15 | var path = require('path'); 16 | var fastPath = require('..'); 17 | 18 | 19 | var suite = new Benchmark.Suite(); 20 | 21 | var shortPath = 'abc/def/zzz.js///'; 22 | var longPath = '/home/Users/name/git/project/lib/abc/def/zzz.js////'; 23 | 24 | suite 25 | .add('path.extname(shortPath)', function () { 26 | path.extname(shortPath); 27 | }) 28 | .add('path.extname(longPath)', function () { 29 | path.extname(longPath); 30 | }) 31 | .add('fastPath.extname(shortPath)', function () { 32 | fastPath.extname(shortPath); 33 | }) 34 | .add('fastPath.extname(longPath)', function () { 35 | fastPath.extname(longPath); 36 | }) 37 | 38 | .on('cycle', function(event) { 39 | benchmarks.add(event.target); 40 | }) 41 | .on('start', function(event) { 42 | console.log('\n node version: %s, date: %s\n Starting...', process.version, Date()); 43 | }) 44 | .on('complete', function done() { 45 | benchmarks.log(); 46 | }) 47 | .run({ 'async': false }); 48 | 49 | // node version: v0.11.13, date: Wed Sep 10 2014 12:38:27 GMT+0800 (CST) 50 | 51 | // path.extname(shortPath) x 1,854,774 ops/sec ±1.66% (96 runs sampled) 52 | // path.extname(longPath) x 625,796 ops/sec ±1.15% (94 runs sampled) 53 | // fastPath.extname(shortPath) x 9,971,307 ops/sec ±1.39% (88 runs sampled) 54 | // fastPath.extname(longPath) x 9,620,105 ops/sec ±1.17% (92 runs sampled) 55 | 56 | // node version: v0.10.31, date: Wed Sep 10 2014 12:42:04 GMT+0800 (CST) 57 | 58 | // path.extname(shortPath) x 2,005,761 ops/sec ±0.80% (95 runs sampled) 59 | // path.extname(longPath) x 644,765 ops/sec ±1.02% (97 runs sampled) 60 | // fastPath.extname(shortPath) x 9,372,443 ops/sec ±0.62% (94 runs sampled) 61 | // fastPath.extname(longPath) x 9,056,037 ops/sec ±1.00% (94 runs sampled) 62 | -------------------------------------------------------------------------------- /benchmark/basename.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * fast-path - benchmark/basename.js 3 | * Copyright(c) 2014 dead_horse 4 | * MIT Licensed 5 | */ 6 | 7 | 'use strict'; 8 | 9 | /** 10 | * Module dependencies. 11 | */ 12 | 13 | var benchmarks = require('beautify-benchmark'); 14 | var Benchmark = require('benchmark'); 15 | var path = require('path'); 16 | var fastPath = require('..'); 17 | 18 | 19 | var suite = new Benchmark.Suite(); 20 | 21 | var shortPath = 'abc/def/zzz.js///'; 22 | var longPath = '/home/Users/name/git/project/lib/abc/def/zzz.js////'; 23 | 24 | suite 25 | .add('path.basename(shortPath)', function () { 26 | path.basename(shortPath); 27 | }) 28 | .add('path.basename(longPath)', function () { 29 | path.basename(longPath); 30 | }) 31 | .add('fastPath.basename(shortPath)', function () { 32 | fastPath.basename(shortPath); 33 | }) 34 | .add('fastPath.basename(longPath)', function () { 35 | fastPath.basename(longPath); 36 | }) 37 | 38 | .on('cycle', function(event) { 39 | benchmarks.add(event.target); 40 | }) 41 | .on('start', function(event) { 42 | console.log('\n node version: %s, date: %s\n Starting...', process.version, Date()); 43 | }) 44 | .on('complete', function done() { 45 | benchmarks.log(); 46 | }) 47 | .run({ 'async': false }); 48 | 49 | // node version: v0.11.13, date: Wed Sep 10 2014 12:38:27 GMT+0800 (CST) 50 | 51 | // path.basename(shortPath) x 1,769,474 ops/sec ±0.98% (96 runs sampled) 52 | // path.basename(longPath) x 642,424 ops/sec ±0.94% (94 runs sampled) 53 | // fastPath.basename(shortPath) x 10,432,870 ops/sec ±1.41% (90 runs sampled) 54 | // fastPath.basename(longPath) x 10,742,695 ops/sec ±0.78% (96 runs sampled) 55 | 56 | // node version: v0.10.31, date: Wed Sep 10 2014 12:42:04 GMT+0800 (CST) 57 | 58 | // path.basename(shortPath) x 1,930,931 ops/sec ±1.37% (93 runs sampled) 59 | // path.basename(longPath) x 642,663 ops/sec ±1.63% (95 runs sampled) 60 | // fastPath.basename(shortPath) x 10,008,164 ops/sec ±0.76% (94 runs sampled) 61 | // fastPath.basename(longPath) x 9,266,497 ops/sec ±1.42% (95 runs sampled) 62 | -------------------------------------------------------------------------------- /benchmark/resolve.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * fast-path - benchmark/resove.js 3 | * Copyright(c) 2014 dead_horse 4 | * MIT Licensed 5 | */ 6 | 7 | 'use strict'; 8 | 9 | /** 10 | * Module dependencies. 11 | */ 12 | 13 | var benchmarks = require('beautify-benchmark'); 14 | var Benchmark = require('benchmark'); 15 | var path = require('path'); 16 | var fastPath = require('..'); 17 | 18 | 19 | var suite = new Benchmark.Suite(); 20 | 21 | var shortPath = '.././abc//def/zzz.js'; 22 | var longPath = 'first/second/.././home//Users/name/git/project/lib/abc/def/zzz.js'; 23 | 24 | suite 25 | .add('path.resolve(shortPath)', function () { 26 | path.resolve(shortPath); 27 | }) 28 | .add('path.resolve(longPath)', function () { 29 | path.resolve(longPath); 30 | }) 31 | .add('fastPath.resolve(shortPath)', function () { 32 | fastPath.resolve(shortPath); 33 | }) 34 | .add('fastPath.resolve(longPath)', function () { 35 | fastPath.resolve(longPath); 36 | }) 37 | 38 | .on('cycle', function(event) { 39 | benchmarks.add(event.target); 40 | }) 41 | .on('start', function(event) { 42 | console.log('\n node version: %s, date: %s\n Starting...', process.version, Date()); 43 | }) 44 | .on('complete', function done() { 45 | benchmarks.log(); 46 | }) 47 | .run({ 'async': false }); 48 | 49 | // node version: v0.11.13, date: Thu Sep 11 2014 09:59:26 GMT+0800 (CST) 50 | // Starting... 51 | // 4 tests completed. 52 | 53 | // path.resolve(shortPath) x 123,724 ops/sec ±1.29% (94 runs sampled) 54 | // path.resolve(longPath) x 105,019 ops/sec ±1.34% (92 runs sampled) 55 | // fastPath.resolve(shortPath) x 168,584 ops/sec ±1.61% (97 runs sampled) 56 | // fastPath.resolve(longPath) x 150,743 ops/sec ±0.88% (95 runs sampled) 57 | 58 | // node version: v0.10.31, date: Thu Sep 11 2014 10:00:10 GMT+0800 (CST) 59 | // Starting... 60 | // 4 tests completed. 61 | 62 | // path.resolve(shortPath) x 121,911 ops/sec ±1.94% (90 runs sampled) 63 | // path.resolve(longPath) x 110,468 ops/sec ±1.21% (91 runs sampled) 64 | // fastPath.resolve(shortPath) x 174,006 ops/sec ±1.18% (95 runs sampled) 65 | // fastPath.resolve(longPath) x 158,383 ops/sec ±0.53% (98 runs sampled) 66 | -------------------------------------------------------------------------------- /benchmark/normalize.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * fast-path - benchmark/normalize.js 3 | * Copyright(c) 2014 dead_horse 4 | * MIT Licensed 5 | */ 6 | 7 | 'use strict'; 8 | 9 | /** 10 | * Module dependencies. 11 | */ 12 | 13 | var benchmarks = require('beautify-benchmark'); 14 | var Benchmark = require('benchmark'); 15 | var path = require('path'); 16 | var fastPath = require('..'); 17 | 18 | 19 | var suite = new Benchmark.Suite(); 20 | 21 | var shortPath = '.././abc//def/zzz.js'; 22 | var longPath = 'first/second/.././home//Users/name/git/project/lib/abc/def/zzz.js'; 23 | 24 | suite 25 | .add('path.normalize(shortPath)', function () { 26 | path.normalize(shortPath); 27 | }) 28 | .add('path.normalize(longPath)', function () { 29 | path.normalize(longPath); 30 | }) 31 | .add('fastPath.normalize(shortPath)', function () { 32 | fastPath.normalize(shortPath); 33 | }) 34 | .add('fastPath.normalize(longPath)', function () { 35 | fastPath.normalize(longPath); 36 | }) 37 | 38 | .on('cycle', function(event) { 39 | benchmarks.add(event.target); 40 | }) 41 | .on('start', function(event) { 42 | console.log('\n node version: %s, date: %s\n Starting...', process.version, Date()); 43 | }) 44 | .on('complete', function done() { 45 | benchmarks.log(); 46 | }) 47 | .run({ 'async': false }); 48 | 49 | // node version: v0.11.13, date: Thu Sep 11 2014 09:58:20 GMT+0800 (CST) 50 | // Starting... 51 | // 4 tests completed. 52 | 53 | // path.normalize(shortPath) x 1,291,875 ops/sec ±2.17% (91 runs sampled) 54 | // path.normalize(longPath) x 969,211 ops/sec ±2.45% (93 runs sampled) 55 | // fastPath.normalize(shortPath) x 2,037,040 ops/sec ±2.18% (94 runs sampled) 56 | // fastPath.normalize(longPath) x 1,612,871 ops/sec ±0.70% (92 runs sampled) 57 | 58 | // node version: v0.10.31, date: Thu Sep 11 2014 10:01:32 GMT+0800 (CST) 59 | // Starting... 60 | // 4 tests completed. 61 | 62 | // path.normalize(shortPath) x 624,777 ops/sec ±3.60% (79 runs sampled) 63 | // path.normalize(longPath) x 472,721 ops/sec ±2.01% (88 runs sampled) 64 | // fastPath.normalize(shortPath) x 2,379,009 ops/sec ±1.76% (89 runs sampled) 65 | // fastPath.normalize(longPath) x 1,642,025 ops/sec ±1.11% (95 runs sampled) 66 | -------------------------------------------------------------------------------- /benchmark/join.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * fast-path - benchmark/join.js 3 | * Copyright(c) 2014 dead_horse 4 | * MIT Licensed 5 | */ 6 | 7 | 'use strict'; 8 | 9 | /** 10 | * Module dependencies. 11 | */ 12 | 13 | var benchmarks = require('beautify-benchmark'); 14 | var Benchmark = require('benchmark'); 15 | var path = require('path'); 16 | var fastPath = require('..'); 17 | 18 | 19 | var suite = new Benchmark.Suite(); 20 | 21 | var path1 = 'first'; 22 | var path2 = 'second'; 23 | var shortPath = '.././abc//def/zzz.js'; 24 | var longPath = '.././home//Users/name/git/project/lib/abc/def/zzz.js'; 25 | 26 | suite 27 | .add('path.join(path1, path2, shortPath)', function () { 28 | path.join(path1, path2, shortPath); 29 | }) 30 | .add('path.join(path1, path2, longPath)', function () { 31 | path.join(path1, path2, longPath); 32 | }) 33 | .add('fastPath.join(path1, path2, shortPath)', function () { 34 | fastPath.join(path1, path2, shortPath); 35 | }) 36 | .add('fastPath.join(path1, path2, longPath)', function () { 37 | fastPath.join(path1, path2, longPath); 38 | }) 39 | 40 | .on('cycle', function(event) { 41 | benchmarks.add(event.target); 42 | }) 43 | .on('start', function(event) { 44 | console.log('\n node version: %s, date: %s\n Starting...', process.version, Date()); 45 | }) 46 | .on('complete', function done() { 47 | benchmarks.log(); 48 | }) 49 | .run({ 'async': false }); 50 | 51 | // node version: v0.11.13, date: Thu Sep 11 2014 09:59:00 GMT+0800 (CST) 52 | // Starting... 53 | // 4 tests completed. 54 | 55 | // path.join(path1, path2, shortPath) x 629,403 ops/sec ±2.05% (94 runs sampled) 56 | // path.join(path1, path2, longPath) x 500,112 ops/sec ±0.83% (96 runs sampled) 57 | // fastPath.join(path1, path2, shortPath) x 822,540 ops/sec ±1.06% (95 runs sampled) 58 | // fastPath(path1, path2, longPath) x 599,279 ops/sec ±1.13% (93 runs sampled) 59 | 60 | 61 | // node version: v0.10.31, date: Thu Sep 11 2014 10:00:49 GMT+0800 (CST) 62 | // Starting... 63 | // 4 tests completed. 64 | 65 | // path.join(path1, path2, shortPath) x 308,499 ops/sec ±2.18% (85 runs sampled) 66 | // path.join(path1, path2, longPath) x 247,989 ops/sec ±2.17% (87 runs sampled) 67 | // fastPath.join(path1, path2, shortPath) x 825,965 ops/sec ±2.76% (87 runs sampled) 68 | // fastPath(path1, path2, longPath) x 617,719 ops/sec ±0.77% (97 runs sampled) 69 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | fast-path 2 | --------------- 3 | 4 | [![NPM version][npm-image]][npm-url] 5 | [![build status][travis-image]][travis-url] 6 | [![node version][node-image]][node-url] 7 | [![Gittip][gittip-image]][gittip-url] 8 | 9 | [npm-image]: https://img.shields.io/npm/v/fast-path.svg?style=flat-square 10 | [npm-url]: https://npmjs.org/package/fast-path 11 | [travis-image]: https://img.shields.io/travis/node-modules/fast-path.svg?style=flat-square 12 | [travis-url]: https://travis-ci.org/node-modules/fast-path 13 | [node-image]: https://img.shields.io/badge/node.js-%3E=_0.8-green.svg?style=flat-square 14 | [node-url]: http://nodejs.org/download/ 15 | [gittip-image]: https://img.shields.io/gittip/dead-horse.svg?style=flat-square 16 | [gittip-url]: https://www.gittip.com/dead-horse/ 17 | 18 | a fast implementation of node's native path 19 | 20 | **node native path is super slow.** 21 | 22 | ## Installation 23 | 24 | ```bash 25 | $ npm install fast-path 26 | ``` 27 | 28 | ## APIs 29 | 30 | The same as [native path APIs](http://nodejs.org/docs/v0.11.13/api/path.html) 31 | 32 | ## Usage 33 | 34 | If you want to replace these APIs in native path: 35 | 36 | ```js 37 | require('fast-path').replace(); // replace all 38 | require('fast-path').replace('dirname'); // replace `dirname` 39 | require('fast-path').replace(['dirname', 'extname']); // replace `dirname` and `extname` 40 | ``` 41 | 42 | ## Benchmark 43 | 44 | [benchmark code](benchmark) 45 | 46 | ```bash 47 | #node version: v0.11.13 48 | 49 | path.extname(shortPath) x 1,854,774 ops/sec ±1.66% (96 runs sampled) 50 | path.extname(longPath) x 625,796 ops/sec ±1.15% (94 runs sampled) 51 | path.basename(shortPath) x 1,769,474 ops/sec ±0.98% (96 runs sampled) 52 | path.basename(longPath) x 642,424 ops/sec ±0.94% (94 runs sampled) 53 | path.dirname(shortPath) x 1,761,185 ops/sec ±0.70% (96 runs sampled) 54 | path.dirname(longPath) x 626,764 ops/sec ±1.52% (90 runs sampled) 55 | path.join(path1, path2, shortPath) x 629,403 ops/sec ±2.05% (94 runs sampled) 56 | path.join(path1, path2, longPath) x 500,112 ops/sec ±0.83% (96 runs sampled) 57 | path.normalize(shortPath) x 1,291,875 ops/sec ±2.17% (91 runs sampled) 58 | path.normalize(longPath) x 969,211 ops/sec ±2.45% (93 runs sampled) 59 | path.resolve(shortPath) x 123,724 ops/sec ±1.29% (94 runs sampled) 60 | path.resolve(longPath) x 105,019 ops/sec ±1.34% (92 runs sampled) 61 | 62 | fastPath.extname(shortPath) x 9,971,307 ops/sec ±1.39% (88 runs sampled) 63 | fastPath.extname(longPath) x 9,620,105 ops/sec ±1.17% (92 runs sampled) 64 | fastPath.basename(shortPath) x 10,432,870 ops/sec ±1.41% (90 runs sampled) 65 | fastPath.basename(longPath) x 10,742,695 ops/sec ±0.78% (96 runs sampled) 66 | fastPath.dirname(shortPath) x 10,489,132 ops/sec ±1.19% (95 runs sampled) 67 | fastPath.dirname(longPath) x 10,443,216 ops/sec ±2.36% (94 runs sampled) 68 | fastPath.join(path1, path2, shortPath) x 822,540 ops/sec ±1.06% (95 runs sampled) 69 | fastPath.join(path1, path2, longPath) x 599,279 ops/sec ±1.13% (93 runs sampled) 70 | fastPath.normalize(shortPath) x 2,037,040 ops/sec ±2.18% (94 runs sampled) 71 | fastPath.normalize(longPath) x 1,612,871 ops/sec ±0.70% (92 runs sampled) 72 | fastPath.resolve(shortPath) x 168,584 ops/sec ±1.61% (97 runs sampled) 73 | fastPath.resolve(longPath) x 150,743 ops/sec ±0.88% (95 runs sampled) 74 | 75 | #node version: v0.10.31 76 | 77 | path.extname(shortPath) x 2,005,761 ops/sec ±0.80% (95 runs sampled) 78 | path.extname(longPath) x 644,765 ops/sec ±1.02% (97 runs sampled) 79 | path.basename(shortPath) x 1,930,931 ops/sec ±1.37% (93 runs sampled) 80 | path.basename(longPath) x 642,663 ops/sec ±1.63% (95 runs sampled) 81 | path.dirname(shortPath) x 1,849,624 ops/sec ±1.47% (92 runs sampled) 82 | path.dirname(longPath) x 635,535 ops/sec ±1.39% (94 runs sampled) 83 | path.join(path1, path2, shortPath) x 308,499 ops/sec ±2.18% (85 runs sampled) 84 | path.join(path1, path2, longPath) x 247,989 ops/sec ±2.17% (87 runs sampled) 85 | path.normalize(shortPath) x 624,777 ops/sec ±3.60% (79 runs sampled) 86 | path.normalize(longPath) x 472,721 ops/sec ±2.01% (88 runs sampled) 87 | path.resolve(shortPath) x 121,911 ops/sec ±1.94% (90 runs sampled) 88 | path.resolve(longPath) x 110,468 ops/sec ±1.21% (91 runs sampled) 89 | 90 | fastPath.extname(shortPath) x 9,372,443 ops/sec ±0.62% (94 runs sampled) 91 | fastPath.extname(longPath) x 9,056,037 ops/sec ±1.00% (94 runs sampled) 92 | fastPath.basename(shortPath) x 10,008,164 ops/sec ±0.76% (94 runs sampled) 93 | fastPath.basename(longPath) x 9,266,497 ops/sec ±1.42% (95 runs sampled) 94 | fastPath.dirname(shortPath) x 9,472,159 ops/sec ±1.69% (92 runs sampled) 95 | fastPath.dirname(longPath) x 9,394,304 ops/sec ±2.75% (91 runs sampled) 96 | fastPath.join(path1, path2, shortPath) x 825,965 ops/sec ±2.76% (87 runs sampled) 97 | fastPath.join(path1, path2, longPath) x 617,719 ops/sec ±0.77% (97 runs sampled) 98 | fastPath.normalize(shortPath) x 2,379,009 ops/sec ±1.76% (89 runs sampled) 99 | fastPath.normalize(longPath) x 1,642,025 ops/sec ±1.11% (95 runs sampled) 100 | fastPath.resolve(shortPath) x 174,006 ops/sec ±1.18% (95 runs sampled) 101 | fastPath.resolve(longPath) x 158,383 ops/sec ±0.53% (98 runs sampled) 102 | ``` 103 | 104 | ### License 105 | 106 | MIT 107 | -------------------------------------------------------------------------------- /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 | var assert = require('assert'); 23 | 24 | var path = require('..'); 25 | 26 | var isWindows = process.platform === 'win32'; 27 | 28 | var f = __filename; 29 | 30 | assert.equal(path.basename(f), 'test-path.js'); 31 | assert.equal(path.basename(f, '.js'), 'test-path'); 32 | assert.equal(path.basename(''), ''); 33 | assert.equal(path.basename('/dir/basename.ext'), 'basename.ext'); 34 | assert.equal(path.basename('/basename.ext'), 'basename.ext'); 35 | assert.equal(path.basename('basename.ext'), 'basename.ext'); 36 | assert.equal(path.basename('basename.ext/'), 'basename.ext'); 37 | assert.equal(path.basename('basename.ext//'), 'basename.ext'); 38 | 39 | if (isWindows) { 40 | // On Windows a backslash acts as a path separator. 41 | assert.equal(path.basename('\\dir\\basename.ext'), 'basename.ext'); 42 | assert.equal(path.basename('\\basename.ext'), 'basename.ext'); 43 | assert.equal(path.basename('basename.ext'), 'basename.ext'); 44 | assert.equal(path.basename('basename.ext\\'), 'basename.ext'); 45 | assert.equal(path.basename('basename.ext\\\\'), 'basename.ext'); 46 | 47 | } else { 48 | // On unix a backslash is just treated as any other character. 49 | assert.equal(path.basename('\\dir\\basename.ext'), '\\dir\\basename.ext'); 50 | assert.equal(path.basename('\\basename.ext'), '\\basename.ext'); 51 | assert.equal(path.basename('basename.ext'), 'basename.ext'); 52 | assert.equal(path.basename('basename.ext\\'), 'basename.ext\\'); 53 | assert.equal(path.basename('basename.ext\\\\'), 'basename.ext\\\\'); 54 | } 55 | 56 | // POSIX filenames may include control characters 57 | // c.f. http://www.dwheeler.com/essays/fixing-unix-linux-filenames.html 58 | if (!isWindows) { 59 | var controlCharFilename = 'Icon' + String.fromCharCode(13); 60 | assert.equal(path.basename('/a/b/' + controlCharFilename), 61 | controlCharFilename); 62 | } 63 | 64 | assert.equal(path.extname(f), '.js'); 65 | 66 | assert.equal(path.dirname('/a/b/'), '/a'); 67 | assert.equal(path.dirname('/a/b'), '/a'); 68 | assert.equal(path.dirname('/a'), '/'); 69 | assert.equal(path.dirname(''), '.'); 70 | assert.equal(path.dirname('/'), '/'); 71 | assert.equal(path.dirname('////'), '/'); 72 | 73 | if (isWindows) { 74 | assert.equal(path.dirname('c:\\'), 'c:\\'); 75 | assert.equal(path.dirname('c:\\foo'), 'c:\\'); 76 | assert.equal(path.dirname('c:\\foo\\'), 'c:\\'); 77 | assert.equal(path.dirname('c:\\foo\\bar'), 'c:\\foo'); 78 | assert.equal(path.dirname('c:\\foo\\bar\\'), 'c:\\foo'); 79 | assert.equal(path.dirname('c:\\foo\\bar\\baz'), 'c:\\foo\\bar'); 80 | assert.equal(path.dirname('\\'), '\\'); 81 | assert.equal(path.dirname('\\foo'), '\\'); 82 | assert.equal(path.dirname('\\foo\\'), '\\'); 83 | assert.equal(path.dirname('\\foo\\bar'), '\\foo'); 84 | assert.equal(path.dirname('\\foo\\bar\\'), '\\foo'); 85 | assert.equal(path.dirname('\\foo\\bar\\baz'), '\\foo\\bar'); 86 | assert.equal(path.dirname('c:'), 'c:'); 87 | assert.equal(path.dirname('c:foo'), 'c:'); 88 | assert.equal(path.dirname('c:foo\\'), 'c:'); 89 | assert.equal(path.dirname('c:foo\\bar'), 'c:foo'); 90 | assert.equal(path.dirname('c:foo\\bar\\'), 'c:foo'); 91 | assert.equal(path.dirname('c:foo\\bar\\baz'), 'c:foo\\bar'); 92 | assert.equal(path.dirname('\\\\unc\\share'), '\\\\unc\\share'); 93 | assert.equal(path.dirname('\\\\unc\\share\\foo'), '\\\\unc\\share\\'); 94 | assert.equal(path.dirname('\\\\unc\\share\\foo\\'), '\\\\unc\\share\\'); 95 | assert.equal(path.dirname('\\\\unc\\share\\foo\\bar'), 96 | '\\\\unc\\share\\foo'); 97 | assert.equal(path.dirname('\\\\unc\\share\\foo\\bar\\'), 98 | '\\\\unc\\share\\foo'); 99 | assert.equal(path.dirname('\\\\unc\\share\\foo\\bar\\baz'), 100 | '\\\\unc\\share\\foo\\bar'); 101 | } 102 | 103 | 104 | assert.equal(path.extname(''), ''); 105 | assert.equal(path.extname('/path/to/file'), ''); 106 | assert.equal(path.extname('/path/to/file.ext'), '.ext'); 107 | assert.equal(path.extname('/path.to/file.ext'), '.ext'); 108 | assert.equal(path.extname('/path.to/file'), ''); 109 | assert.equal(path.extname('/path.to/.file'), ''); 110 | assert.equal(path.extname('/path.to/.file.ext'), '.ext'); 111 | assert.equal(path.extname('/path/to/f.ext'), '.ext'); 112 | assert.equal(path.extname('/path/to/..ext'), '.ext'); 113 | assert.equal(path.extname('file'), ''); 114 | assert.equal(path.extname('file.ext'), '.ext'); 115 | assert.equal(path.extname('.file'), ''); 116 | assert.equal(path.extname('.file.ext'), '.ext'); 117 | assert.equal(path.extname('/file'), ''); 118 | assert.equal(path.extname('/file.ext'), '.ext'); 119 | assert.equal(path.extname('/.file'), ''); 120 | assert.equal(path.extname('/.file.ext'), '.ext'); 121 | assert.equal(path.extname('.path/file.ext'), '.ext'); 122 | assert.equal(path.extname('file.ext.ext'), '.ext'); 123 | assert.equal(path.extname('file.'), '.'); 124 | assert.equal(path.extname('.'), ''); 125 | assert.equal(path.extname('./'), ''); 126 | assert.equal(path.extname('.file.ext'), '.ext'); 127 | assert.equal(path.extname('.file'), ''); 128 | assert.equal(path.extname('.file.'), '.'); 129 | assert.equal(path.extname('.file..'), '.'); 130 | assert.equal(path.extname('..'), ''); 131 | assert.equal(path.extname('../'), ''); 132 | assert.equal(path.extname('..file.ext'), '.ext'); 133 | assert.equal(path.extname('..file'), '.file'); 134 | assert.equal(path.extname('..file.'), '.'); 135 | assert.equal(path.extname('..file..'), '.'); 136 | assert.equal(path.extname('...'), '.'); 137 | assert.equal(path.extname('...ext'), '.ext'); 138 | assert.equal(path.extname('....'), '.'); 139 | assert.equal(path.extname('file.ext/'), '.ext'); 140 | assert.equal(path.extname('file.ext//'), '.ext'); 141 | assert.equal(path.extname('file/'), ''); 142 | assert.equal(path.extname('file//'), ''); 143 | assert.equal(path.extname('file./'), '.'); 144 | assert.equal(path.extname('file.//'), '.'); 145 | 146 | if (isWindows) { 147 | // On windows, backspace is a path separator. 148 | assert.equal(path.extname('.\\'), ''); 149 | assert.equal(path.extname('..\\'), ''); 150 | assert.equal(path.extname('file.ext\\'), '.ext'); 151 | assert.equal(path.extname('file.ext\\\\'), '.ext'); 152 | assert.equal(path.extname('file\\'), ''); 153 | assert.equal(path.extname('file\\\\'), ''); 154 | assert.equal(path.extname('file.\\'), '.'); 155 | assert.equal(path.extname('file.\\\\'), '.'); 156 | 157 | } else { 158 | // On unix, backspace is a valid name component like any other character. 159 | assert.equal(path.extname('.\\'), ''); 160 | assert.equal(path.extname('..\\'), '.\\'); 161 | assert.equal(path.extname('file.ext\\'), '.ext\\'); 162 | assert.equal(path.extname('file.ext\\\\'), '.ext\\\\'); 163 | assert.equal(path.extname('file\\'), ''); 164 | assert.equal(path.extname('file\\\\'), ''); 165 | assert.equal(path.extname('file.\\'), '.\\'); 166 | assert.equal(path.extname('file.\\\\'), '.\\\\'); 167 | } 168 | 169 | // path.join tests 170 | var failures = []; 171 | var joinTests = 172 | // arguments result 173 | [[['.', 'x/b', '..', '/b/c.js'], 'x/b/c.js'], 174 | [['/.', 'x/b', '..', '/b/c.js'], '/x/b/c.js'], 175 | [['/foo', '../../../bar'], '/bar'], 176 | [['foo', '../../../bar'], '../../bar'], 177 | [['foo/', '../../../bar'], '../../bar'], 178 | [['foo/x', '../../../bar'], '../bar'], 179 | [['foo/x', './bar'], 'foo/x/bar'], 180 | [['foo/x/', './bar'], 'foo/x/bar'], 181 | [['foo/x/', '.', 'bar'], 'foo/x/bar'], 182 | [['./'], './'], 183 | [['.', './'], './'], 184 | [['.', '.', '.'], '.'], 185 | [['.', './', '.'], '.'], 186 | [['.', '/./', '.'], '.'], 187 | [['.', '/////./', '.'], '.'], 188 | [['.'], '.'], 189 | [['', '.'], '.'], 190 | [['', 'foo'], 'foo'], 191 | [['foo', '/bar'], 'foo/bar'], 192 | [['', '/foo'], '/foo'], 193 | [['', '', '/foo'], '/foo'], 194 | [['', '', 'foo'], 'foo'], 195 | [['foo', ''], 'foo'], 196 | [['foo/', ''], 'foo/'], 197 | [['foo', '', '/bar'], 'foo/bar'], 198 | [['./', '..', '/foo'], '../foo'], 199 | [['./', '..', '..', '/foo'], '../../foo'], 200 | [['.', '..', '..', '/foo'], '../../foo'], 201 | [['', '..', '..', '/foo'], '../../foo'], 202 | [['/'], '/'], 203 | [['/', '.'], '/'], 204 | [['/', '..'], '/'], 205 | [['/', '..', '..'], '/'], 206 | [[''], '.'], 207 | [['', ''], '.'], 208 | [[' /foo'], ' /foo'], 209 | [[' ', 'foo'], ' /foo'], 210 | [[' ', '.'], ' '], 211 | [[' ', '/'], ' /'], 212 | [[' ', ''], ' '], 213 | [['/', 'foo'], '/foo'], 214 | [['/', '/foo'], '/foo'], 215 | [['/', '//foo'], '/foo'], 216 | [['/', '', '/foo'], '/foo'], 217 | [['', '/', 'foo'], '/foo'], 218 | [['', '/', '/foo'], '/foo'] 219 | ]; 220 | 221 | // Windows-specific join tests 222 | if (isWindows) { 223 | joinTests = joinTests.concat( 224 | [// UNC path expected 225 | [['//foo/bar'], '//foo/bar/'], 226 | [['\\/foo/bar'], '//foo/bar/'], 227 | [['\\\\foo/bar'], '//foo/bar/'], 228 | // UNC path expected - server and share separate 229 | [['//foo', 'bar'], '//foo/bar/'], 230 | [['//foo/', 'bar'], '//foo/bar/'], 231 | [['//foo', '/bar'], '//foo/bar/'], 232 | // UNC path expected - questionable 233 | [['//foo', '', 'bar'], '//foo/bar/'], 234 | [['//foo/', '', 'bar'], '//foo/bar/'], 235 | [['//foo/', '', '/bar'], '//foo/bar/'], 236 | // UNC path expected - even more questionable 237 | [['', '//foo', 'bar'], '//foo/bar/'], 238 | [['', '//foo/', 'bar'], '//foo/bar/'], 239 | [['', '//foo/', '/bar'], '//foo/bar/'], 240 | // No UNC path expected (no double slash in first component) 241 | [['\\', 'foo/bar'], '/foo/bar'], 242 | [['\\', '/foo/bar'], '/foo/bar'], 243 | [['', '/', '/foo/bar'], '/foo/bar'], 244 | // No UNC path expected (no non-slashes in first component - questionable) 245 | [['//', 'foo/bar'], '/foo/bar'], 246 | [['//', '/foo/bar'], '/foo/bar'], 247 | [['\\\\', '/', '/foo/bar'], '/foo/bar'], 248 | [['//'], '/'], 249 | // No UNC path expected (share name missing - questionable). 250 | [['//foo'], '/foo'], 251 | [['//foo/'], '/foo/'], 252 | [['//foo', '/'], '/foo/'], 253 | [['//foo', '', '/'], '/foo/'], 254 | // No UNC path expected (too many leading slashes - questionable) 255 | [['///foo/bar'], '/foo/bar'], 256 | [['////foo', 'bar'], '/foo/bar'], 257 | [['\\\\\\/foo/bar'], '/foo/bar'], 258 | // Drive-relative vs drive-absolute paths. This merely describes the 259 | // status quo, rather than being obviously right 260 | [['c:'], 'c:.'], 261 | [['c:.'], 'c:.'], 262 | [['c:', ''], 'c:.'], 263 | [['', 'c:'], 'c:.'], 264 | [['c:.', '/'], 'c:./'], 265 | [['c:.', 'file'], 'c:file'], 266 | [['c:', '/'], 'c:/'], 267 | [['c:', 'file'], 'c:/file'] 268 | ]); 269 | } 270 | 271 | // Run the join tests. 272 | joinTests.forEach(function(test) { 273 | var actual = path.join.apply(path, test[0]); 274 | var expected = isWindows ? test[1].replace(/\//g, '\\') : test[1]; 275 | var message = 'path.join(' + test[0].map(JSON.stringify).join(',') + ')' + 276 | '\n expect=' + JSON.stringify(expected) + 277 | '\n actual=' + JSON.stringify(actual); 278 | if (actual !== expected) failures.push('\n' + message); 279 | // assert.equal(actual, expected, message); 280 | }); 281 | assert.equal(failures.length, 0, failures.join('')); 282 | var joinThrowTests = [true, false, 7, null, {}, undefined, [], NaN]; 283 | joinThrowTests.forEach(function(test) { 284 | assert.throws(function() { 285 | path.join(test); 286 | }, TypeError); 287 | assert.throws(function() { 288 | path.resolve(test); 289 | }, TypeError); 290 | }); 291 | 292 | 293 | // path normalize tests 294 | if (isWindows) { 295 | assert.equal(path.normalize('./fixtures///b/../b/c.js'), 296 | 'fixtures\\b\\c.js'); 297 | assert.equal(path.normalize('/foo/../../../bar'), '\\bar'); 298 | assert.equal(path.normalize('a//b//../b'), 'a\\b'); 299 | assert.equal(path.normalize('a//b//./c'), 'a\\b\\c'); 300 | assert.equal(path.normalize('a//b//.'), 'a\\b'); 301 | assert.equal(path.normalize('//server/share/dir/file.ext'), 302 | '\\\\server\\share\\dir\\file.ext'); 303 | } else { 304 | assert.equal(path.normalize('./fixtures///b/../b/c.js'), 305 | 'fixtures/b/c.js'); 306 | assert.equal(path.normalize('/foo/../../../bar'), '/bar'); 307 | assert.equal(path.normalize('a//b//../b'), 'a/b'); 308 | assert.equal(path.normalize('a//b//./c'), 'a/b/c'); 309 | assert.equal(path.normalize('a//b//.'), 'a/b'); 310 | } 311 | 312 | // path.resolve tests 313 | if (isWindows) { 314 | // windows 315 | var cwd = process.cwd(); 316 | cwd = cwd[0].toLowerCase() + cwd.substr(1); 317 | var resolveTests = 318 | // arguments result 319 | [[['c:/blah\\blah', 'd:/games', 'c:../a'], 'c:\\blah\\a'], 320 | [['c:/ignore', 'd:\\a/b\\c/d', '\\e.exe'], 'd:\\e.exe'], 321 | [['c:/ignore', 'c:/some/file'], 'c:\\some\\file'], 322 | [['d:/ignore', 'd:some/dir//'], 'd:\\ignore\\some\\dir'], 323 | [['.'], cwd], 324 | [['//server/share', '..', 'relative\\'], '\\\\server\\share\\relative'], 325 | [['c:/', '//'], 'c:\\'], 326 | [['c:/', '//dir'], 'c:\\dir'], 327 | [['c:/', '//server/share'], '\\\\server\\share\\'], 328 | [['c:/', '//server//share'], '\\\\server\\share\\'], 329 | [['c:/', '///some//dir'], 'c:\\some\\dir'] 330 | ]; 331 | } else { 332 | // Posix 333 | var resolveTests = 334 | // arguments result 335 | [[['/var/lib', '../', 'file/'], '/var/file'], 336 | [['/var/lib', '/../', 'file/'], '/file'], 337 | [['a/b/c/', '../../..'], process.cwd()], 338 | [['.'], process.cwd()], 339 | [['/some/dir', '.', '/absolute/'], '/absolute']]; 340 | } 341 | var failures = []; 342 | resolveTests.forEach(function(test) { 343 | var actual = path.resolve.apply(path, test[0]); 344 | var expected = test[1]; 345 | var message = 'path.resolve(' + test[0].map(JSON.stringify).join(',') + ')' + 346 | '\n expect=' + JSON.stringify(expected) + 347 | '\n actual=' + JSON.stringify(actual); 348 | if (actual !== expected) failures.push('\n' + message); 349 | // assert.equal(actual, expected, message); 350 | }); 351 | assert.equal(failures.length, 0, failures.join('')); 352 | 353 | // path.isAbsolute tests 354 | if (isWindows) { 355 | assert.equal(path.isAbsolute('//server/file'), true); 356 | assert.equal(path.isAbsolute('\\\\server\\file'), true); 357 | assert.equal(path.isAbsolute('C:/Users/'), true); 358 | assert.equal(path.isAbsolute('C:\\Users\\'), true); 359 | assert.equal(path.isAbsolute('C:cwd/another'), false); 360 | assert.equal(path.isAbsolute('C:cwd\\another'), false); 361 | assert.equal(path.isAbsolute('directory/directory'), false); 362 | assert.equal(path.isAbsolute('directory\\directory'), false); 363 | } else { 364 | assert.equal(path.isAbsolute('/home/foo'), true); 365 | assert.equal(path.isAbsolute('/home/foo/..'), true); 366 | assert.equal(path.isAbsolute('bar/'), false); 367 | assert.equal(path.isAbsolute('./baz'), false); 368 | } 369 | 370 | // path.relative tests 371 | if (isWindows) { 372 | // windows 373 | var relativeTests = 374 | // arguments result 375 | [['c:/blah\\blah', 'd:/games', 'd:\\games'], 376 | ['c:/aaaa/bbbb', 'c:/aaaa', '..'], 377 | ['c:/aaaa/bbbb', 'c:/cccc', '..\\..\\cccc'], 378 | ['c:/aaaa/bbbb', 'c:/aaaa/bbbb', ''], 379 | ['c:/aaaa/bbbb', 'c:/aaaa/cccc', '..\\cccc'], 380 | ['c:/aaaa/', 'c:/aaaa/cccc', 'cccc'], 381 | ['c:/', 'c:\\aaaa\\bbbb', 'aaaa\\bbbb'], 382 | ['c:/aaaa/bbbb', 'd:\\', 'd:\\']]; 383 | } else { 384 | // posix 385 | var relativeTests = 386 | // arguments result 387 | [['/var/lib', '/var', '..'], 388 | ['/var/lib', '/bin', '../../bin'], 389 | ['/var/lib', '/var/lib', ''], 390 | ['/var/lib', '/var/apache', '../apache'], 391 | ['/var/', '/var/lib', 'lib'], 392 | ['/', '/var/lib', 'var/lib']]; 393 | } 394 | var failures = []; 395 | relativeTests.forEach(function(test) { 396 | var actual = path.relative(test[0], test[1]); 397 | var expected = test[2]; 398 | var message = 'path.relative(' + 399 | test.slice(0, 2).map(JSON.stringify).join(',') + 400 | ')' + 401 | '\n expect=' + JSON.stringify(expected) + 402 | '\n actual=' + JSON.stringify(actual); 403 | if (actual !== expected) failures.push('\n' + message); 404 | }); 405 | assert.equal(failures.length, 0, failures.join('')); 406 | 407 | // path.sep tests 408 | if (isWindows) { 409 | // windows 410 | assert.equal(path.sep, '\\'); 411 | } else { 412 | // posix 413 | assert.equal(path.sep, '/'); 414 | } 415 | 416 | // path.delimiter tests 417 | if (isWindows) { 418 | // windows 419 | assert.equal(path.delimiter, ';'); 420 | } else { 421 | // posix 422 | assert.equal(path.delimiter, ':'); 423 | } 424 | -------------------------------------------------------------------------------- /index.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 | 23 | /*! 24 | * fast-path - index.js 25 | * Copyright(c) 2014 dead_horse 26 | * MIT Licensed 27 | */ 28 | 29 | 'use strict'; 30 | 31 | /** 32 | * Module dependencies. 33 | */ 34 | 35 | var path = require('path'); 36 | 37 | var all = Object.keys(exports).filter(function (name) { 38 | return name !== 'replace'; 39 | }); 40 | 41 | exports.replace = function (props) { 42 | if (!props) props = all; 43 | if (!Array.isArray(props)) props = [props]; 44 | 45 | props.forEach(function (name) { 46 | if (exports[name]) path[name] = exports[name]; 47 | }); 48 | } 49 | 50 | function isString(arg) { 51 | return typeof arg === 'string'; 52 | } 53 | 54 | var isWindows = process.platform === 'win32'; 55 | var util = require('util'); 56 | 57 | // resolves . and .. elements in a path array with directory names there 58 | // must be no slashes, or device names (c:\) in the array 59 | // (so also no leading and trailing slashes - it does not distinguish 60 | // relative and absolute paths) 61 | function normalizeArray(parts, allowAboveRoot) { 62 | var nonEmptyParts = []; 63 | var nonBack = true; 64 | for (var i = 0; i < parts.length; i++) { 65 | var p = parts[i]; 66 | if (p && p !== '.') { 67 | nonEmptyParts.push(p); 68 | } 69 | if (p === '..') { 70 | nonBack = false; 71 | } 72 | } 73 | 74 | parts = nonEmptyParts; 75 | 76 | // if the path does not contain .. 77 | if (nonBack) { 78 | return parts; 79 | } 80 | 81 | // if the path tries to go ab ove the root, `up` ends up > 0 82 | var up = 0; 83 | var res = []; 84 | for (var i = parts.length - 1; i >= 0; i--) { 85 | if (parts[i] === '..') { 86 | up++; 87 | } else if (up) { 88 | up--; 89 | } else { 90 | res.push(parts[i]); 91 | } 92 | } 93 | 94 | // if the path is allowed to go above the root, restore leading ..s 95 | if (allowAboveRoot) { 96 | for (; up--; up) { 97 | res.push('..'); 98 | } 99 | } 100 | 101 | return res.reverse(); 102 | } 103 | 104 | if (isWindows) { 105 | var normalizeUNCRoot = function(device) { 106 | return '\\\\' + device.replace(/^[\\\/]+/, '').replace(/[\\\/]+/g, '\\'); 107 | }; 108 | 109 | // path.resolve([from ...], to) 110 | // windows version 111 | exports.resolve = function() { 112 | var resolvedDevice = '', 113 | resolvedTail = '', 114 | resolvedAbsolute = false; 115 | 116 | for (var i = arguments.length - 1; i >= -1; i--) { 117 | var path; 118 | if (i >= 0) { 119 | path = arguments[i]; 120 | } else if (!resolvedDevice) { 121 | path = process.cwd(); 122 | } else { 123 | // Windows has the concept of drive-specific current working 124 | // directories. If we've resolved a drive letter but not yet an 125 | // absolute path, get cwd for that drive. We're sure the device is not 126 | // an unc path at this points, because unc paths are always absolute. 127 | path = process.env['=' + resolvedDevice]; 128 | // Verify that a drive-local cwd was found and that it actually points 129 | // to our drive. If not, default to the drive's root. 130 | if (!path || path.substr(0, 3).toLowerCase() !== 131 | resolvedDevice.toLowerCase() + '\\') { 132 | path = resolvedDevice + '\\'; 133 | } 134 | } 135 | 136 | // Skip empty and invalid entries 137 | if (!isString(path)) { 138 | throw new TypeError('Arguments to path.resolve must be strings'); 139 | } else if (!path) { 140 | continue; 141 | } 142 | 143 | var result = splitDeviceRe.exec(path), 144 | device = result[1] || '', 145 | isUnc = device && device.charAt(1) !== ':', 146 | isAbsolute = exports.isAbsolute(path), 147 | tail = result[3]; 148 | 149 | if (device && 150 | resolvedDevice && 151 | device.toLowerCase() !== resolvedDevice.toLowerCase()) { 152 | // This path points to another device so it is not applicable 153 | continue; 154 | } 155 | 156 | if (!resolvedDevice) { 157 | resolvedDevice = device; 158 | } 159 | if (!resolvedAbsolute) { 160 | resolvedTail = tail + '\\' + resolvedTail; 161 | resolvedAbsolute = isAbsolute; 162 | } 163 | 164 | if (resolvedDevice && resolvedAbsolute) { 165 | break; 166 | } 167 | } 168 | 169 | // Convert slashes to backslashes when `resolvedDevice` points to an UNC 170 | // root. Also squash multiple slashes into a single one where appropriate. 171 | if (isUnc) { 172 | resolvedDevice = normalizeUNCRoot(resolvedDevice); 173 | } 174 | 175 | // At this point the path should be resolved to a full absolute path, 176 | // but handle relative paths to be safe (might happen when process.cwd() 177 | // fails) 178 | 179 | // Normalize the tail path 180 | resolvedTail = normalizeArray(resolvedTail.split(/[\\\/]+/), 181 | !resolvedAbsolute).join('\\'); 182 | 183 | // If device is a drive letter, we'll normalize to lower case. 184 | if (resolvedDevice && resolvedDevice.charAt(1) === ':') { 185 | resolvedDevice = resolvedDevice[0].toLowerCase() + resolvedDevice.substr(1); 186 | } 187 | 188 | return (resolvedDevice + (resolvedAbsolute ? '\\' : '') + resolvedTail) || 189 | '.'; 190 | }; 191 | 192 | // windows version 193 | exports.normalize = function(path) { 194 | var result = splitDeviceRe.exec(path), 195 | device = result[1] || '', 196 | isUnc = device && device.charAt(1) !== ':', 197 | isAbsolute = exports.isAbsolute(path), 198 | tail = result[3], 199 | trailingSlash = /[\\\/]$/.test(tail); 200 | 201 | // If device is a drive letter, we'll normalize to lower case. 202 | if (device && device.charAt(1) === ':') { 203 | device = device[0].toLowerCase() + device.substr(1); 204 | } 205 | 206 | // Normalize the tail path 207 | tail = normalizeArray(tail.split(/[\\\/]+/), !isAbsolute).join('\\'); 208 | 209 | if (!tail && !isAbsolute) { 210 | tail = '.'; 211 | } 212 | if (tail && trailingSlash) { 213 | tail += '\\'; 214 | } 215 | 216 | // Convert slashes to backslashes when `device` points to an UNC root. 217 | // Also squash multiple slashes into a single one where appropriate. 218 | if (isUnc) { 219 | device = normalizeUNCRoot(device); 220 | } 221 | 222 | return device + (isAbsolute ? '\\' : '') + tail; 223 | }; 224 | 225 | // windows version 226 | exports.isAbsolute = function(path) { 227 | var result = splitDeviceRe.exec(path), 228 | device = result[1] || '', 229 | isUnc = !!device && device.charAt(1) !== ':'; 230 | // UNC paths are always absolute 231 | return !!result[2] || isUnc; 232 | }; 233 | 234 | // windows version 235 | exports.join = function() { 236 | function f(p) { 237 | if (!isString(p)) { 238 | throw new TypeError('Arguments to path.join must be strings'); 239 | } 240 | return p; 241 | } 242 | 243 | var paths = Array.prototype.filter.call(arguments, f); 244 | var joined = paths.join('\\'); 245 | 246 | // Make sure that the joined path doesn't start with two slashes, because 247 | // normalize() will mistake it for an UNC path then. 248 | // 249 | // This step is skipped when it is very clear that the user actually 250 | // intended to point at an UNC path. This is assumed when the first 251 | // non-empty string arguments starts with exactly two slashes followed by 252 | // at least one more non-slash character. 253 | // 254 | // Note that for normalize() to treat a path as an UNC path it needs to 255 | // have at least 2 components, so we don't filter for that here. 256 | // This means that the user can use join to construct UNC paths from 257 | // a server name and a share name; for example: 258 | // path.join('//server', 'share') -> '\\\\server\\share\') 259 | if (!/^[\\\/]{2}[^\\\/]/.test(paths[0])) { 260 | joined = joined.replace(/^[\\\/]{2,}/, '\\'); 261 | } 262 | 263 | return exports.normalize(joined); 264 | }; 265 | 266 | // path.relative(from, to) 267 | // it will solve the relative path from 'from' to 'to', for instance: 268 | // from = 'C:\\orandea\\test\\aaa' 269 | // to = 'C:\\orandea\\impl\\bbb' 270 | // The output of the function should be: '..\\..\\impl\\bbb' 271 | // windows version 272 | exports.relative = function(from, to) { 273 | from = exports.resolve(from); 274 | to = exports.resolve(to); 275 | 276 | // windows is not case sensitive 277 | var lowerFrom = from.toLowerCase(); 278 | var lowerTo = to.toLowerCase(); 279 | 280 | function trim(arr) { 281 | var start = 0; 282 | for (; start < arr.length; start++) { 283 | if (arr[start] !== '') break; 284 | } 285 | 286 | var end = arr.length - 1; 287 | for (; end >= 0; end--) { 288 | if (arr[end] !== '') break; 289 | } 290 | 291 | if (start > end) return []; 292 | return arr.slice(start, end + 1); 293 | } 294 | 295 | var toParts = trim(to.split('\\')); 296 | 297 | var lowerFromParts = trim(lowerFrom.split('\\')); 298 | var lowerToParts = trim(lowerTo.split('\\')); 299 | 300 | var length = Math.min(lowerFromParts.length, lowerToParts.length); 301 | var samePartsLength = length; 302 | for (var i = 0; i < length; i++) { 303 | if (lowerFromParts[i] !== lowerToParts[i]) { 304 | samePartsLength = i; 305 | break; 306 | } 307 | } 308 | 309 | if (samePartsLength == 0) { 310 | return to; 311 | } 312 | 313 | var outputParts = []; 314 | for (var i = samePartsLength; i < lowerFromParts.length; i++) { 315 | outputParts.push('..'); 316 | } 317 | 318 | outputParts = outputParts.concat(toParts.slice(samePartsLength)); 319 | 320 | return outputParts.join('\\'); 321 | }; 322 | 323 | exports.sep = '\\'; 324 | exports.delimiter = ';'; 325 | 326 | } else /* posix */ { 327 | 328 | // path.resolve([from ...], to) 329 | // posix version 330 | exports.resolve = function() { 331 | var resolvedPath = '', 332 | resolvedAbsolute = false; 333 | 334 | for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) { 335 | var path = (i >= 0) ? arguments[i] : process.cwd(); 336 | 337 | // Skip empty and invalid entries 338 | if (!isString(path)) { 339 | throw new TypeError('Arguments to path.resolve must be strings'); 340 | } else if (!path) { 341 | continue; 342 | } 343 | 344 | resolvedPath = path + '/' + resolvedPath; 345 | resolvedAbsolute = path.charAt(0) === '/'; 346 | } 347 | 348 | // At this point the path should be resolved to a full absolute path, but 349 | // handle relative paths to be safe (might happen when process.cwd() fails) 350 | 351 | // Normalize the path 352 | resolvedPath = normalizeArray(resolvedPath.split('/'), 353 | !resolvedAbsolute).join('/'); 354 | 355 | return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.'; 356 | }; 357 | 358 | // path.normalize(path) 359 | // posix version 360 | exports.normalize = function(path) { 361 | var isAbsolute = exports.isAbsolute(path), 362 | trailingSlash = path[path.length - 1] === '/'; 363 | 364 | // normalize the path 365 | path = normalizeArray(path.split('/'), !isAbsolute).join('/'); 366 | 367 | if (!path && !isAbsolute) { 368 | path = '.'; 369 | } 370 | if (path && trailingSlash) { 371 | path += '/'; 372 | } 373 | 374 | return (isAbsolute ? '/' : '') + path; 375 | }; 376 | 377 | // posix version 378 | exports.isAbsolute = function(path) { 379 | return path.charAt(0) === '/'; 380 | }; 381 | 382 | // posix version 383 | exports.join = function() { 384 | var path = ''; 385 | for (var i = 0; i < arguments.length; i++) { 386 | var segment = arguments[i]; 387 | if (!isString(segment)) { 388 | throw new TypeError('Arguments to path.join must be strings'); 389 | } 390 | if (segment) { 391 | if (!path) { 392 | path += segment; 393 | } else { 394 | path += '/' + segment; 395 | } 396 | } 397 | } 398 | return exports.normalize(path); 399 | }; 400 | 401 | 402 | // path.relative(from, to) 403 | // posix version 404 | exports.relative = function(from, to) { 405 | from = exports.resolve(from).substr(1); 406 | to = exports.resolve(to).substr(1); 407 | 408 | function trim(arr) { 409 | var start = 0; 410 | for (; start < arr.length; start++) { 411 | if (arr[start] !== '') break; 412 | } 413 | 414 | var end = arr.length - 1; 415 | for (; end >= 0; end--) { 416 | if (arr[end] !== '') break; 417 | } 418 | 419 | if (start > end) return []; 420 | return arr.slice(start, end + 1); 421 | } 422 | 423 | var fromParts = trim(from.split('/')); 424 | var toParts = trim(to.split('/')); 425 | 426 | var length = Math.min(fromParts.length, toParts.length); 427 | var samePartsLength = length; 428 | for (var i = 0; i < length; i++) { 429 | if (fromParts[i] !== toParts[i]) { 430 | samePartsLength = i; 431 | break; 432 | } 433 | } 434 | 435 | var outputParts = []; 436 | for (var i = samePartsLength; i < fromParts.length; i++) { 437 | outputParts.push('..'); 438 | } 439 | 440 | outputParts = outputParts.concat(toParts.slice(samePartsLength)); 441 | 442 | return outputParts.join('/'); 443 | }; 444 | 445 | exports.sep = '/'; 446 | exports.delimiter = ':'; 447 | } 448 | 449 | exports.exists = util.deprecate(function(path, callback) { 450 | require('fs').exists(path, callback); 451 | }, 'path.exists is now called `fs.exists`.'); 452 | 453 | 454 | exports.existsSync = util.deprecate(function(path) { 455 | return require('fs').existsSync(path); 456 | }, 'path.existsSync is now called `fs.existsSync`.'); 457 | 458 | 459 | if (isWindows) { 460 | exports._makeLong = function(path) { 461 | // Note: this will *probably* throw somewhere. 462 | if (!isString(path)) 463 | return path; 464 | 465 | if (!path) { 466 | return ''; 467 | } 468 | 469 | var resolvedPath = exports.resolve(path); 470 | 471 | if (/^[a-zA-Z]\:\\/.test(resolvedPath)) { 472 | // path is local filesystem path, which needs to be converted 473 | // to long UNC path. 474 | return '\\\\?\\' + resolvedPath; 475 | } else if (/^\\\\[^?.]/.test(resolvedPath)) { 476 | // path is network UNC path, which needs to be converted 477 | // to long UNC path. 478 | return '\\\\?\\UNC\\' + resolvedPath.substring(2); 479 | } 480 | 481 | return path; 482 | }; 483 | } else { 484 | exports._makeLong = function(path) { 485 | return path; 486 | }; 487 | } 488 | 489 | exports.extname = function (filename) { 490 | if (!filename) return ''; 491 | 492 | // /a.js/// 493 | var end = filename.length; 494 | var c = filename[end - 1]; 495 | while (c === path.sep || c === '/') { 496 | end--; 497 | c = filename[end - 1]; 498 | } 499 | 500 | var lastDot = -1; 501 | var lastSep = -1; 502 | var isWindows = process.platform === 'win32'; 503 | 504 | for (var i = end; i--; ) { 505 | var ch = filename[i]; 506 | if (lastDot === -1 && ch === '.') lastDot = i; 507 | else if (lastSep === -1 && ch === '/') lastSep = i; 508 | else if (isWindows && lastSep === -1 && ch === '\\') lastSep = i; 509 | 510 | // /xxx 511 | if (lastSep !== -1 && lastDot === -1) return ''; 512 | // /*.js 513 | if (lastDot !== -1 && i === lastDot - 2) break; 514 | // /.js 515 | if (lastSep !== -1 && lastDot !== -1) break; 516 | } 517 | 518 | // ./js and /.js 519 | if (lastDot < lastSep + 2) return ''; 520 | 521 | var extname = filename.slice(lastDot, end); 522 | if (extname === '.' && filename[lastDot - 1] === '.') { 523 | // .. 524 | if (lastDot === 1) return ''; 525 | var pre = filename[lastDot - 2]; 526 | // [//\/].. 527 | if (pre === '/' || pre === path.sep) return ''; 528 | } 529 | 530 | return extname; 531 | }; 532 | 533 | exports.basename = function (filename, ext) { 534 | if (!filename) return ''; 535 | 536 | // /a.js/// 537 | var end = filename.length; 538 | var c = filename[end - 1]; 539 | while (c === path.sep || c === '/') { 540 | end--; 541 | c = filename[end - 1]; 542 | } 543 | 544 | var lastSep = -1; 545 | var isWindows = process.platform === 'win32'; 546 | 547 | for (var i = end; i--; ) { 548 | var ch = filename[i]; 549 | if (lastSep === -1 && ch === '/') { 550 | lastSep = i; 551 | break; 552 | } 553 | if (isWindows && lastSep === -1 && ch === '\\') { 554 | lastSep = i; 555 | break; 556 | } 557 | } 558 | 559 | var basename = filename.slice(lastSep + 1, end); 560 | 561 | if (ext) { 562 | var match = basename.lastIndexOf(ext); 563 | if (match === -1 564 | || match !== basename.length - ext.length) { 565 | return basename; 566 | } 567 | return basename.slice(0, basename.length - ext.length); 568 | } 569 | 570 | return basename; 571 | }; 572 | 573 | exports.dirname = function (filename) { 574 | if (!filename) return '.'; 575 | 576 | var isWindows = process.platform === 'win32'; 577 | 578 | var start = 0; 579 | var device = ''; 580 | 581 | if (isWindows) { 582 | // need to get device in windows 583 | device = getDevice(filename); 584 | if (device) start = device.length; 585 | } 586 | 587 | // /a.js/// 588 | var end = filename.length; 589 | var c = filename[end - 1]; 590 | while (end >= start && c === path.sep || c === '/') { 591 | end--; 592 | c = filename[end - 1]; 593 | } 594 | 595 | var lastSep = -1; 596 | for (var i = end; i-- > start; ) { 597 | var ch = filename[i]; 598 | if (lastSep === -1 && ch === '/') { 599 | lastSep = i; 600 | break; 601 | } 602 | if (isWindows && lastSep === -1 && ch === '\\') { 603 | lastSep = i; 604 | break; 605 | } 606 | } 607 | if (lastSep <= start) { 608 | if (device) return device; 609 | if (filename[0] === '/' || filename[0] === path.sep) return filename[0]; 610 | return '.'; 611 | } 612 | 613 | return device + filename.slice(start, lastSep); 614 | }; 615 | 616 | var splitDeviceRe = 617 | /^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)?([\\\/])?([\s\S]*?)$/; 618 | function getDevice(filename) { 619 | var result = splitDeviceRe.exec(filename); 620 | return (result[1] || '') + (result[2] || ''); 621 | } 622 | --------------------------------------------------------------------------------