resolve-pathname.js 1.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. 'use strict';
  2. function isAbsolute(pathname) {
  3. return pathname.charAt(0) === '/';
  4. }
  5. // About 1.5x faster than the two-arg version of Array#splice()
  6. function spliceOne(list, index) {
  7. for (var i = index, k = i + 1, n = list.length; k < n; i += 1, k += 1) {
  8. list[i] = list[k];
  9. }
  10. list.pop();
  11. }
  12. // This implementation is based heavily on node's url.parse
  13. function resolvePathname(to, from) {
  14. if (from === undefined) from = '';
  15. var toParts = (to && to.split('/')) || [];
  16. var fromParts = (from && from.split('/')) || [];
  17. var isToAbs = to && isAbsolute(to);
  18. var isFromAbs = from && isAbsolute(from);
  19. var mustEndAbs = isToAbs || isFromAbs;
  20. if (to && isAbsolute(to)) {
  21. // to is absolute
  22. fromParts = toParts;
  23. } else if (toParts.length) {
  24. // to is relative, drop the filename
  25. fromParts.pop();
  26. fromParts = fromParts.concat(toParts);
  27. }
  28. if (!fromParts.length) return '/';
  29. var hasTrailingSlash;
  30. if (fromParts.length) {
  31. var last = fromParts[fromParts.length - 1];
  32. hasTrailingSlash = last === '.' || last === '..' || last === '';
  33. } else {
  34. hasTrailingSlash = false;
  35. }
  36. var up = 0;
  37. for (var i = fromParts.length; i >= 0; i--) {
  38. var part = fromParts[i];
  39. if (part === '.') {
  40. spliceOne(fromParts, i);
  41. } else if (part === '..') {
  42. spliceOne(fromParts, i);
  43. up++;
  44. } else if (up) {
  45. spliceOne(fromParts, i);
  46. up--;
  47. }
  48. }
  49. if (!mustEndAbs) for (; up--; up) fromParts.unshift('..');
  50. if (
  51. mustEndAbs &&
  52. fromParts[0] !== '' &&
  53. (!fromParts[0] || !isAbsolute(fromParts[0]))
  54. )
  55. fromParts.unshift('');
  56. var result = fromParts.join('/');
  57. if (hasTrailingSlash && result.substr(-1) !== '/') result += '/';
  58. return result;
  59. }
  60. module.exports = resolvePathname;