diff --git a/lib/path.js b/lib/path.js index 641e21e4c4..ccd1598f6d 100644 --- a/lib/path.js +++ b/lib/path.js @@ -165,6 +165,64 @@ if (isWindows) { return exports.normalize(joined); }; + // path.relative(from, to) + // it will solve the relative path from 'from' to 'to', for instance: + // from = 'C:\\orandea\\test\\aaa' + // to = 'C:\\orandea\\impl\\bbb' + // The output of the function should be: '..\\..\\impl\\bbb' + // windows version + exports.relative = function(from, to) { + from = exports.resolve(from); + to = exports.resolve(to); + + // windows is not case sensitive + var lowerFrom = from.toLowerCase(); + var lowerTo = to.toLowerCase(); + + function trim(arr) { + var start = 0; + for(;start < arr.length; start++) { + if (arr[start] !== '') break; + } + + var end = arr.length - 1; + for(;end >= 0; end--) { + if (arr[end] !== '') break; + } + + if (start > end) return []; + return arr.slice(start, end - start + 1); + } + + var fromParts = trim(from.split('\\')); + var toParts = trim(to.split('\\')); + + var lowerFromParts = trim(lowerFrom.split('\\')); + var lowerToParts = trim(lowerTo.split('\\')); + + var length = Math.min(lowerFromParts.length, lowerToParts.length); + var samePartsLength = length; + for(var i = 0; i < length; i++) { + if (lowerFromParts[i] !== lowerToParts[i]) { + samePartsLength = i; + break; + } + } + + if (samePartsLength == 0) { + return to; + } + + var outputParts = []; + for(var i = samePartsLength; i < lowerFromParts.length; i++) { + outputParts.push('..'); + } + + outputParts = outputParts.concat(toParts.slice(samePartsLength)); + + return outputParts.join('\\'); + }; + } else /* posix */ { @@ -232,6 +290,51 @@ if (isWindows) { return p && typeof p === 'string'; }).join('/')); }; + + + // path.relative(from, to) + // posix version + exports.relative = function(from, to) { + from = exports.resolve(from).substr(1); + to = exports.resolve(to).substr(1); + + function trim(arr) { + var start = 0; + for(;start < arr.length; start++) { + if (arr[start] !== '') break; + } + + var end = arr.length - 1; + for(;end >= 0; end--) { + if (arr[end] !== '') break; + } + + if (start > end) return []; + return arr.slice(start, end - start + 1); + } + + var fromParts = trim(from.split('/')); + var toParts = trim(to.split('/')); + + var length = Math.min(fromParts.length, toParts.length); + var samePartsLength = length; + for(var i = 0; i < length; i++) { + if (fromParts[i] !== toParts[i]) { + samePartsLength = i; + break; + } + } + + var outputParts = []; + for(var i = samePartsLength; i < fromParts.length; i++) { + outputParts.push('..'); + } + + outputParts = outputParts.concat(toParts.slice(samePartsLength)); + + return outputParts.join('/'); + }; + } diff --git a/test/simple/test-path.js b/test/simple/test-path.js index c263bbcbfd..92d160ff22 100644 --- a/test/simple/test-path.js +++ b/test/simple/test-path.js @@ -147,3 +147,39 @@ resolveTests.forEach(function(test) { // assert.equal(actual, expected, message); }); assert.equal(failures.length, 0, failures.join('')); + +// path.relative tests +if (isWindows) { + // windows + var relativeTests = + // arguments result + [['c:/blah\\blah', 'd:/games', 'd:\\games'], + ['c:/aaaa/bbbb', 'c:/aaaa', '..'], + ['c:/aaaa/bbbb', 'c:/cccc', '..\\..\\cccc'], + ['c:/aaaa/bbbb', 'c:/aaaa/bbbb',''], + ['c:/aaaa/bbbb', 'c:/aaaa/cccc','..\\cccc'], + ['c:/aaaa/', 'c:/aaaa/cccc', 'cccc'], + ['c:/', 'c:\\aaaa\\bbbb', 'aaaa\\bbbb'], + ['c:/aaaa/bbbb', 'd:\\', 'd:\\']]; +} else { + // posix + var relativeTests = + // arguments result + [['/var/lib', '/var', '..'], + ['/var/lib', '/bin', '../../bin'], + ['/var/lib', '/var/lib', ''], + ['/var/lib', '/var/apache', '../apache'], + ['/var/', '/var/lib', 'lib'], + ['/', '/var/lib', 'var/lib']]; +} +var failures = []; +relativeTests.forEach(function(test) { + var actual = path.relative(test[0], test[1]); + var expected = test[2]; + var message = 'path.relative(' + test.slice(0, 2).map(JSON.stringify).join(',') + ')' + + '\n expect=' + JSON.stringify(expected) + + '\n actual=' + JSON.stringify(actual); + if (actual !== expected) failures.push('\n' + message); +}); +assert.equal(failures.length, 0, failures.join('')); +