Skip to content

Commit 14ea364

Browse files
Merge pull request #15 from ChALkeR/v0.x-update
backport (dir|base|ext)name from 1.0.0
2 parents 9d4dca5 + 338a800 commit 14ea364

File tree

3 files changed

+529
-27
lines changed

3 files changed

+529
-27
lines changed

index.js

Lines changed: 104 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
// .dirname, .basename, and .extname methods are extracted from Node.js v8.11.1,
2+
// backported and transplited with Babel, with backwards-compat fixes
3+
14
// Copyright Joyent, Inc. and other Node contributors.
25
//
36
// Permission is hereby granted, free of charge, to any person obtaining a
@@ -49,14 +52,6 @@ function normalizeArray(parts, allowAboveRoot) {
4952
return parts;
5053
}
5154

52-
// Split a filename into [root, dir, basename, ext], unix version
53-
// 'root' is just a slash, or nothing.
54-
var splitPathRe =
55-
/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;
56-
var splitPath = function(filename) {
57-
return splitPathRe.exec(filename).slice(1);
58-
};
59-
6055
// path.resolve([from ...], to)
6156
// posix version
6257
exports.resolve = function() {
@@ -172,37 +167,120 @@ exports.relative = function(from, to) {
172167
exports.sep = '/';
173168
exports.delimiter = ':';
174169

175-
exports.dirname = function(path) {
176-
var result = splitPath(path),
177-
root = result[0],
178-
dir = result[1];
179-
180-
if (!root && !dir) {
181-
// No dirname whatsoever
182-
return '.';
170+
exports.dirname = function (path) {
171+
if (typeof path !== 'string') path = path + '';
172+
if (path.length === 0) return '.';
173+
var code = path.charCodeAt(0);
174+
var hasRoot = code === 47 /*/*/;
175+
var end = -1;
176+
var matchedSlash = true;
177+
for (var i = path.length - 1; i >= 1; --i) {
178+
code = path.charCodeAt(i);
179+
if (code === 47 /*/*/) {
180+
if (!matchedSlash) {
181+
end = i;
182+
break;
183+
}
184+
} else {
185+
// We saw the first non-path separator
186+
matchedSlash = false;
187+
}
183188
}
184189

185-
if (dir) {
186-
// It has a dirname, strip trailing slash
187-
dir = dir.substr(0, dir.length - 1);
190+
if (end === -1) return hasRoot ? '/' : '.';
191+
if (hasRoot && end === 1) {
192+
// return '//';
193+
// Backwards-compat fix:
194+
return '/';
188195
}
189-
190-
return root + dir;
196+
return path.slice(0, end);
191197
};
192198

199+
function basename(path) {
200+
if (typeof path !== 'string') path = path + '';
201+
202+
var start = 0;
203+
var end = -1;
204+
var matchedSlash = true;
205+
var i;
206+
207+
for (i = path.length - 1; i >= 0; --i) {
208+
if (path.charCodeAt(i) === 47 /*/*/) {
209+
// If we reached a path separator that was not part of a set of path
210+
// separators at the end of the string, stop now
211+
if (!matchedSlash) {
212+
start = i + 1;
213+
break;
214+
}
215+
} else if (end === -1) {
216+
// We saw the first non-path separator, mark this as the end of our
217+
// path component
218+
matchedSlash = false;
219+
end = i + 1;
220+
}
221+
}
222+
223+
if (end === -1) return '';
224+
return path.slice(start, end);
225+
}
193226

194-
exports.basename = function(path, ext) {
195-
var f = splitPath(path)[2];
196-
// TODO: make this comparison case-insensitive on windows?
227+
// Uses a mixed approach for backwards-compatibility, as ext behavior changed
228+
// in new Node.js versions, so only basename() above is backported here
229+
exports.basename = function (path, ext) {
230+
var f = basename(path);
197231
if (ext && f.substr(-1 * ext.length) === ext) {
198232
f = f.substr(0, f.length - ext.length);
199233
}
200234
return f;
201235
};
202236

237+
exports.extname = function (path) {
238+
if (typeof path !== 'string') path = path + '';
239+
var startDot = -1;
240+
var startPart = 0;
241+
var end = -1;
242+
var matchedSlash = true;
243+
// Track the state of characters (if any) we see before our first dot and
244+
// after any path separator we find
245+
var preDotState = 0;
246+
for (var i = path.length - 1; i >= 0; --i) {
247+
var code = path.charCodeAt(i);
248+
if (code === 47 /*/*/) {
249+
// If we reached a path separator that was not part of a set of path
250+
// separators at the end of the string, stop now
251+
if (!matchedSlash) {
252+
startPart = i + 1;
253+
break;
254+
}
255+
continue;
256+
}
257+
if (end === -1) {
258+
// We saw the first non-path separator, mark this as the end of our
259+
// extension
260+
matchedSlash = false;
261+
end = i + 1;
262+
}
263+
if (code === 46 /*.*/) {
264+
// If this is our first dot, mark it as the start of our extension
265+
if (startDot === -1)
266+
startDot = i;
267+
else if (preDotState !== 1)
268+
preDotState = 1;
269+
} else if (startDot !== -1) {
270+
// We saw a non-dot and non-path separator before our dot, so we should
271+
// have a good chance at having a non-empty extension
272+
preDotState = -1;
273+
}
274+
}
203275

204-
exports.extname = function(path) {
205-
return splitPath(path)[3];
276+
if (startDot === -1 || end === -1 ||
277+
// We saw a non-dot character immediately before the dot
278+
preDotState === 0 ||
279+
// The (right-most) trimmed path component is exactly '..'
280+
preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) {
281+
return '';
282+
}
283+
return path.slice(startDot, end);
206284
};
207285

208286
function filter (xs, f) {

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"tape": "~1.0.4"
99
},
1010
"scripts": {
11-
"test": "tape test/*.js"
11+
"test": "node test/test-path.js"
1212
},
1313
"repository": {
1414
"type": "git",

0 commit comments

Comments
 (0)