normalize.js 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. "use strict";
  2. // eslint-disable-next-line complexity
  3. module.exports = function normalize(path) {
  4. var parts = path.split(/(\\+|\/+)/);
  5. if(parts.length === 1)
  6. return path;
  7. var result = [];
  8. var absolutePathStart = 0;
  9. for(var i = 0, sep = false; i < parts.length; i += 1, sep = !sep) {
  10. var part = parts[i];
  11. if(i === 0 && /^([A-Z]:)?$/i.test(part)) {
  12. result.push(part);
  13. absolutePathStart = 2;
  14. } else if(sep) {
  15. // UNC paths on Windows begin with a double backslash.
  16. if (i === 1 && parts[0].length === 0 && part === "\\\\") {
  17. result.push(part);
  18. } else {
  19. result.push(part[0]);
  20. }
  21. } else if(part === "..") {
  22. switch(result.length) {
  23. case 0:
  24. // i. e. ".." => ".."
  25. // i. e. "../a/b/c" => "../a/b/c"
  26. result.push(part);
  27. break;
  28. case 2:
  29. // i. e. "a/.." => ""
  30. // i. e. "/.." => "/"
  31. // i. e. "C:\.." => "C:\"
  32. // i. e. "a/../b/c" => "b/c"
  33. // i. e. "/../b/c" => "/b/c"
  34. // i. e. "C:\..\a\b\c" => "C:\a\b\c"
  35. if (result[0] !== ".") {
  36. i += 1;
  37. sep = !sep;
  38. result.length = absolutePathStart;
  39. } else {
  40. result.length = 0;
  41. result.push(part);
  42. }
  43. break;
  44. case 4:
  45. // i. e. "a/b/.." => "a"
  46. // i. e. "/a/.." => "/"
  47. // i. e. "C:\a\.." => "C:\"
  48. // i. e. "/a/../b/c" => "/b/c"
  49. if(absolutePathStart === 0) {
  50. result.length -= 3;
  51. } else {
  52. i += 1;
  53. sep = !sep;
  54. result.length = 2;
  55. }
  56. break;
  57. default:
  58. // i. e. "/a/b/.." => "/a"
  59. // i. e. "/a/b/../c" => "/a/c"
  60. result.length -= 3;
  61. break;
  62. }
  63. } else if(part === ".") {
  64. switch(result.length) {
  65. case 0:
  66. // i. e. "." => "."
  67. // i. e. "./a/b/c" => "./a/b/c"
  68. result.push(part);
  69. break;
  70. case 2:
  71. // i. e. "a/." => "a"
  72. // i. e. "/." => "/"
  73. // i. e. "C:\." => "C:\"
  74. // i. e. "C:\.\a\b\c" => "C:\a\b\c"
  75. if(absolutePathStart === 0) {
  76. result.length -= 1;
  77. } else {
  78. i += 1;
  79. sep = !sep;
  80. }
  81. break;
  82. default:
  83. // i. e. "a/b/." => "a/b"
  84. // i. e. "/a/." => "/"
  85. // i. e. "C:\a\." => "C:\"
  86. // i. e. "a/./b/c" => "a/b/c"
  87. // i. e. "/a/./b/c" => "/a/b/c"
  88. result.length -= 1;
  89. break;
  90. }
  91. } else if(part) {
  92. result.push(part);
  93. }
  94. }
  95. if(result.length === 1 && /^[A-Za-z]:$/.test(result[0]))
  96. return result[0] + "\\";
  97. return result.join("");
  98. };