Skip to content

Commit

Permalink
fix(join): numerous bug fixes to the join algorithm
Browse files Browse the repository at this point in the history
This fix addresses issues with absolute paths and null/empty paths in
the join algorithm. The normalize function was also renamed to
relativeToFile. This is a breaking change.
  • Loading branch information
EisenbergEffect committed Dec 18, 2014
1 parent 0cfaeb1 commit 0114d32
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 39 deletions.
4 changes: 1 addition & 3 deletions ACKNOWLEDGEMENTS.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
# Acknowledgements

The `normalize` function in this library was adapted from the internals of [http://requirejs.org/](http://requirejs.org/). Much thanks goes to the authors who worked out this algorithm. Below is the required notice from the original library.

Also, the `join` function comes from [this gist](https://gist.github.com/creationix/7435851).
The `relativeToFile` function in this library was adapted from the internals of [http://requirejs.org/](http://requirejs.org/). Much thanks goes to the authors who worked out this algorithm. Below is the required notice from the original library.

---

Expand Down
71 changes: 41 additions & 30 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,21 @@ function trimDots(ary) {
}
}

export function normalize(name, baseName){
export function relativeToFile(name, file){
var lastIndex,
normalizedBaseParts,
baseParts = (baseName && baseName.split('/'));
fileParts = (file && file.split('/'));

name = name.trim();
name = name.split('/');

if (name[0].charAt(0) === '.' && baseParts) {
//Convert baseName to array, and lop off the last part,
//so that . matches that 'directory' and not name of the baseName's
//module. For instance, baseName of 'one/two/three', maps to
if (name[0].charAt(0) === '.' && fileParts) {
//Convert file to array, and lop off the last part,
//so that . matches that 'directory' and not name of the file's
//module. For instance, file of 'one/two/three', maps to
//'one/two/three.js', but we want the directory, 'one/two' for
//this normalization.
normalizedBaseParts = baseParts.slice(0, baseParts.length - 1);
normalizedBaseParts = fileParts.slice(0, fileParts.length - 1);
name = normalizedBaseParts.concat(name);
}

Expand All @@ -44,29 +44,40 @@ export function normalize(name, baseName){
return name.join('/');
}

// Joins path segments. Preserves initial "/" and resolves ".." and "."
// Does not support using ".." to go above/outside the root.
// This means that join("foo", "../../bar") will not resolve to "../bar"
export function join(/* path segments */) {
// Split the inputs into a list of path commands.
var parts = [];
for (var i = 0, l = arguments.length; i < l; i++) {
parts = parts.concat(arguments[i].split("/"));
export function join(path1, path2) {
var url1, url2, url3;

if(!path1){
return path2;
}

if(!path2){
return path1;
}

url1 = path1.split('/');
url2 = path2.split('/');
url3 = [];

for (var i = 0, l = url1.length; i < l; i ++) {
if (url1[i] == '..') {
url3.pop();
} else if (url1[i] == '.' || url1[i] == '') {
continue;
} else {
url3.push(url1[i]);
}
}
// Interpret the path commands to get the new resolved path.
var newParts = [];
for (i = 0, l = parts.length; i < l; i++) {
var part = parts[i];
// Remove leading and trailing slashes
// Also remove "." segments
if (!part || part === ".") continue;
// Interpret ".." to pop the last segment
if (part === "..") newParts.pop();
// Push new path segments.
else newParts.push(part);

for (var i = 0, l = url2.length; i < l; i ++) {
if (url2[i] == '..') {
url3.pop();
} else if (url2[i] == '.' || url2[i] == '') {
continue;
} else {
url3.push(url2[i]);
}
}
// Preserve the initial slash if there was one.
if (parts[0] === "") newParts.unshift("");
// Turn back into a single string path.
return newParts.join("/") || (newParts.length ? "/" : ".");

return url3.join('/').replace(/\:\//g, '://');;
}
110 changes: 104 additions & 6 deletions test/path.spec.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,117 @@
import { normalize, join } from '../src/index';
import { relativeToFile, join } from '../src/index';

describe('normalize', () => {
it('can make a path relative to a file', () => {
var baseUrl = 'some/file.html';
describe('relativeToFile', () => {
it('can make a dot path relative to a simple file', () => {
var file = 'some/file.html';
var path = './other/module';

expect(normalize(path, baseUrl)).toBe('some/other/module');
expect(relativeToFile(path, file)).toBe('some/other/module');
});

it('can make a dot path relative to an absolute file', () => {
var file = 'http://durandal.io/some/file.html';
var path = './other/module';

expect(relativeToFile(path, file)).toBe('http://durandal.io/some/other/module');
});

it('can make a double dot path relative to an absolute file', () => {
var file = 'http://durandal.io/some/file.html';
var path = '../other/module';

expect(relativeToFile(path, file)).toBe('http://durandal.io/other/module');
});

it('returns path if null file provided', () => {
var file = null;
var path = 'module';

expect(relativeToFile(path, file)).toBe('module');
});

it('returns path if empty file provided', () => {
var file = '';
var path = 'module';

expect(relativeToFile(path, file)).toBe('module');
});
});

describe('join', () => {
it('can join two paths', () => {
it('can combine two simple paths', () => {
var path1 = 'one';
var path2 = 'two';

expect(join(path1, path2)).toBe('one/two');
});

it('can combine an absolute path and a simple path', () => {
var path1 = 'http://durandal.io';
var path2 = 'two';

expect(join(path1, path2)).toBe('http://durandal.io/two');
});

it('can combine an absolute path and a simple path with slash', () => {
var path1 = 'http://durandal.io';
var path2 = '/two';

expect(join(path1, path2)).toBe('http://durandal.io/two');
});

it('can combine an absolute path and a simple path with a dot', () => {
var path1 = 'http://durandal.io';
var path2 = './two';

expect(join(path1, path2)).toBe('http://durandal.io/two');
});

it('can combine a simple path and a relative path', () => {
var path1 = 'one';
var path2 = '../two';

expect(join(path1, path2)).toBe('two');
});

it('can combine an absolute path and a relative path', () => {
var path1 = 'http://durandal.io/somewhere';
var path2 = '../two';

expect(join(path1, path2)).toBe('http://durandal.io/two');
});

it('can combine a complex path and a relative path', () => {
var path1 = 'one/three';
var path2 = '../two';

expect(join(path1, path2)).toBe('one/two');
});

it('returns path2 if path1 null', () => {
var path1 = null;
var path2 = 'two';

expect(join(path1, path2)).toBe('two');
});

it('returns path2 if path1 empty', () => {
var path1 = '';
var path2 = 'two';

expect(join(path1, path2)).toBe('two');
});

it('returns path1 if path2 null', () => {
var path1 = 'one';
var path2 = null;

expect(join(path1, path2)).toBe('one');
});

it('returns path1 if path2 empty', () => {
var path1 = 'one';
var path2 = '';

expect(join(path1, path2)).toBe('one');
});
});

0 comments on commit 0114d32

Please sign in to comment.