path.js 52 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393
  1. /*---------------------------------------------------------------------------------------------
  2. * Copyright (c) Microsoft Corporation. All rights reserved.
  3. * Licensed under the MIT License. See License.txt in the project root for license information.
  4. *--------------------------------------------------------------------------------------------*/
  5. // NOTE: VSCode's copy of nodejs path library to be usable in common (non-node) namespace
  6. // Copied from: https://github.com/nodejs/node/blob/v16.14.2/lib/path.js
  7. /**
  8. * Copyright Joyent, Inc. and other Node contributors.
  9. *
  10. * Permission is hereby granted, free of charge, to any person obtaining a
  11. * copy of this software and associated documentation files (the
  12. * "Software"), to deal in the Software without restriction, including
  13. * without limitation the rights to use, copy, modify, merge, publish,
  14. * distribute, sublicense, and/or sell copies of the Software, and to permit
  15. * persons to whom the Software is furnished to do so, subject to the
  16. * following conditions:
  17. *
  18. * The above copyright notice and this permission notice shall be included
  19. * in all copies or substantial portions of the Software.
  20. *
  21. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  22. * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  23. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
  24. * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
  25. * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  26. * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
  27. * USE OR OTHER DEALINGS IN THE SOFTWARE.
  28. */
  29. import * as process from './process.js';
  30. const CHAR_UPPERCASE_A = 65; /* A */
  31. const CHAR_LOWERCASE_A = 97; /* a */
  32. const CHAR_UPPERCASE_Z = 90; /* Z */
  33. const CHAR_LOWERCASE_Z = 122; /* z */
  34. const CHAR_DOT = 46; /* . */
  35. const CHAR_FORWARD_SLASH = 47; /* / */
  36. const CHAR_BACKWARD_SLASH = 92; /* \ */
  37. const CHAR_COLON = 58; /* : */
  38. const CHAR_QUESTION_MARK = 63; /* ? */
  39. class ErrorInvalidArgType extends Error {
  40. constructor(name, expected, actual) {
  41. // determiner: 'must be' or 'must not be'
  42. let determiner;
  43. if (typeof expected === 'string' && expected.indexOf('not ') === 0) {
  44. determiner = 'must not be';
  45. expected = expected.replace(/^not /, '');
  46. }
  47. else {
  48. determiner = 'must be';
  49. }
  50. const type = name.indexOf('.') !== -1 ? 'property' : 'argument';
  51. let msg = `The "${name}" ${type} ${determiner} of type ${expected}`;
  52. msg += `. Received type ${typeof actual}`;
  53. super(msg);
  54. this.code = 'ERR_INVALID_ARG_TYPE';
  55. }
  56. }
  57. function validateObject(pathObject, name) {
  58. if (pathObject === null || typeof pathObject !== 'object') {
  59. throw new ErrorInvalidArgType(name, 'Object', pathObject);
  60. }
  61. }
  62. function validateString(value, name) {
  63. if (typeof value !== 'string') {
  64. throw new ErrorInvalidArgType(name, 'string', value);
  65. }
  66. }
  67. const platformIsWin32 = (process.platform === 'win32');
  68. function isPathSeparator(code) {
  69. return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH;
  70. }
  71. function isPosixPathSeparator(code) {
  72. return code === CHAR_FORWARD_SLASH;
  73. }
  74. function isWindowsDeviceRoot(code) {
  75. return (code >= CHAR_UPPERCASE_A && code <= CHAR_UPPERCASE_Z) ||
  76. (code >= CHAR_LOWERCASE_A && code <= CHAR_LOWERCASE_Z);
  77. }
  78. // Resolves . and .. elements in a path with directory names
  79. function normalizeString(path, allowAboveRoot, separator, isPathSeparator) {
  80. let res = '';
  81. let lastSegmentLength = 0;
  82. let lastSlash = -1;
  83. let dots = 0;
  84. let code = 0;
  85. for (let i = 0; i <= path.length; ++i) {
  86. if (i < path.length) {
  87. code = path.charCodeAt(i);
  88. }
  89. else if (isPathSeparator(code)) {
  90. break;
  91. }
  92. else {
  93. code = CHAR_FORWARD_SLASH;
  94. }
  95. if (isPathSeparator(code)) {
  96. if (lastSlash === i - 1 || dots === 1) {
  97. // NOOP
  98. }
  99. else if (dots === 2) {
  100. if (res.length < 2 || lastSegmentLength !== 2 ||
  101. res.charCodeAt(res.length - 1) !== CHAR_DOT ||
  102. res.charCodeAt(res.length - 2) !== CHAR_DOT) {
  103. if (res.length > 2) {
  104. const lastSlashIndex = res.lastIndexOf(separator);
  105. if (lastSlashIndex === -1) {
  106. res = '';
  107. lastSegmentLength = 0;
  108. }
  109. else {
  110. res = res.slice(0, lastSlashIndex);
  111. lastSegmentLength = res.length - 1 - res.lastIndexOf(separator);
  112. }
  113. lastSlash = i;
  114. dots = 0;
  115. continue;
  116. }
  117. else if (res.length !== 0) {
  118. res = '';
  119. lastSegmentLength = 0;
  120. lastSlash = i;
  121. dots = 0;
  122. continue;
  123. }
  124. }
  125. if (allowAboveRoot) {
  126. res += res.length > 0 ? `${separator}..` : '..';
  127. lastSegmentLength = 2;
  128. }
  129. }
  130. else {
  131. if (res.length > 0) {
  132. res += `${separator}${path.slice(lastSlash + 1, i)}`;
  133. }
  134. else {
  135. res = path.slice(lastSlash + 1, i);
  136. }
  137. lastSegmentLength = i - lastSlash - 1;
  138. }
  139. lastSlash = i;
  140. dots = 0;
  141. }
  142. else if (code === CHAR_DOT && dots !== -1) {
  143. ++dots;
  144. }
  145. else {
  146. dots = -1;
  147. }
  148. }
  149. return res;
  150. }
  151. function _format(sep, pathObject) {
  152. validateObject(pathObject, 'pathObject');
  153. const dir = pathObject.dir || pathObject.root;
  154. const base = pathObject.base ||
  155. `${pathObject.name || ''}${pathObject.ext || ''}`;
  156. if (!dir) {
  157. return base;
  158. }
  159. return dir === pathObject.root ? `${dir}${base}` : `${dir}${sep}${base}`;
  160. }
  161. export const win32 = {
  162. // path.resolve([from ...], to)
  163. resolve(...pathSegments) {
  164. let resolvedDevice = '';
  165. let resolvedTail = '';
  166. let resolvedAbsolute = false;
  167. for (let i = pathSegments.length - 1; i >= -1; i--) {
  168. let path;
  169. if (i >= 0) {
  170. path = pathSegments[i];
  171. validateString(path, 'path');
  172. // Skip empty entries
  173. if (path.length === 0) {
  174. continue;
  175. }
  176. }
  177. else if (resolvedDevice.length === 0) {
  178. path = process.cwd();
  179. }
  180. else {
  181. // Windows has the concept of drive-specific current working
  182. // directories. If we've resolved a drive letter but not yet an
  183. // absolute path, get cwd for that drive, or the process cwd if
  184. // the drive cwd is not available. We're sure the device is not
  185. // a UNC path at this points, because UNC paths are always absolute.
  186. path = process.env[`=${resolvedDevice}`] || process.cwd();
  187. // Verify that a cwd was found and that it actually points
  188. // to our drive. If not, default to the drive's root.
  189. if (path === undefined ||
  190. (path.slice(0, 2).toLowerCase() !== resolvedDevice.toLowerCase() &&
  191. path.charCodeAt(2) === CHAR_BACKWARD_SLASH)) {
  192. path = `${resolvedDevice}\\`;
  193. }
  194. }
  195. const len = path.length;
  196. let rootEnd = 0;
  197. let device = '';
  198. let isAbsolute = false;
  199. const code = path.charCodeAt(0);
  200. // Try to match a root
  201. if (len === 1) {
  202. if (isPathSeparator(code)) {
  203. // `path` contains just a path separator
  204. rootEnd = 1;
  205. isAbsolute = true;
  206. }
  207. }
  208. else if (isPathSeparator(code)) {
  209. // Possible UNC root
  210. // If we started with a separator, we know we at least have an
  211. // absolute path of some kind (UNC or otherwise)
  212. isAbsolute = true;
  213. if (isPathSeparator(path.charCodeAt(1))) {
  214. // Matched double path separator at beginning
  215. let j = 2;
  216. let last = j;
  217. // Match 1 or more non-path separators
  218. while (j < len && !isPathSeparator(path.charCodeAt(j))) {
  219. j++;
  220. }
  221. if (j < len && j !== last) {
  222. const firstPart = path.slice(last, j);
  223. // Matched!
  224. last = j;
  225. // Match 1 or more path separators
  226. while (j < len && isPathSeparator(path.charCodeAt(j))) {
  227. j++;
  228. }
  229. if (j < len && j !== last) {
  230. // Matched!
  231. last = j;
  232. // Match 1 or more non-path separators
  233. while (j < len && !isPathSeparator(path.charCodeAt(j))) {
  234. j++;
  235. }
  236. if (j === len || j !== last) {
  237. // We matched a UNC root
  238. device = `\\\\${firstPart}\\${path.slice(last, j)}`;
  239. rootEnd = j;
  240. }
  241. }
  242. }
  243. }
  244. else {
  245. rootEnd = 1;
  246. }
  247. }
  248. else if (isWindowsDeviceRoot(code) &&
  249. path.charCodeAt(1) === CHAR_COLON) {
  250. // Possible device root
  251. device = path.slice(0, 2);
  252. rootEnd = 2;
  253. if (len > 2 && isPathSeparator(path.charCodeAt(2))) {
  254. // Treat separator following drive name as an absolute path
  255. // indicator
  256. isAbsolute = true;
  257. rootEnd = 3;
  258. }
  259. }
  260. if (device.length > 0) {
  261. if (resolvedDevice.length > 0) {
  262. if (device.toLowerCase() !== resolvedDevice.toLowerCase()) {
  263. // This path points to another device so it is not applicable
  264. continue;
  265. }
  266. }
  267. else {
  268. resolvedDevice = device;
  269. }
  270. }
  271. if (resolvedAbsolute) {
  272. if (resolvedDevice.length > 0) {
  273. break;
  274. }
  275. }
  276. else {
  277. resolvedTail = `${path.slice(rootEnd)}\\${resolvedTail}`;
  278. resolvedAbsolute = isAbsolute;
  279. if (isAbsolute && resolvedDevice.length > 0) {
  280. break;
  281. }
  282. }
  283. }
  284. // At this point the path should be resolved to a full absolute path,
  285. // but handle relative paths to be safe (might happen when process.cwd()
  286. // fails)
  287. // Normalize the tail path
  288. resolvedTail = normalizeString(resolvedTail, !resolvedAbsolute, '\\', isPathSeparator);
  289. return resolvedAbsolute ?
  290. `${resolvedDevice}\\${resolvedTail}` :
  291. `${resolvedDevice}${resolvedTail}` || '.';
  292. },
  293. normalize(path) {
  294. validateString(path, 'path');
  295. const len = path.length;
  296. if (len === 0) {
  297. return '.';
  298. }
  299. let rootEnd = 0;
  300. let device;
  301. let isAbsolute = false;
  302. const code = path.charCodeAt(0);
  303. // Try to match a root
  304. if (len === 1) {
  305. // `path` contains just a single char, exit early to avoid
  306. // unnecessary work
  307. return isPosixPathSeparator(code) ? '\\' : path;
  308. }
  309. if (isPathSeparator(code)) {
  310. // Possible UNC root
  311. // If we started with a separator, we know we at least have an absolute
  312. // path of some kind (UNC or otherwise)
  313. isAbsolute = true;
  314. if (isPathSeparator(path.charCodeAt(1))) {
  315. // Matched double path separator at beginning
  316. let j = 2;
  317. let last = j;
  318. // Match 1 or more non-path separators
  319. while (j < len && !isPathSeparator(path.charCodeAt(j))) {
  320. j++;
  321. }
  322. if (j < len && j !== last) {
  323. const firstPart = path.slice(last, j);
  324. // Matched!
  325. last = j;
  326. // Match 1 or more path separators
  327. while (j < len && isPathSeparator(path.charCodeAt(j))) {
  328. j++;
  329. }
  330. if (j < len && j !== last) {
  331. // Matched!
  332. last = j;
  333. // Match 1 or more non-path separators
  334. while (j < len && !isPathSeparator(path.charCodeAt(j))) {
  335. j++;
  336. }
  337. if (j === len) {
  338. // We matched a UNC root only
  339. // Return the normalized version of the UNC root since there
  340. // is nothing left to process
  341. return `\\\\${firstPart}\\${path.slice(last)}\\`;
  342. }
  343. if (j !== last) {
  344. // We matched a UNC root with leftovers
  345. device = `\\\\${firstPart}\\${path.slice(last, j)}`;
  346. rootEnd = j;
  347. }
  348. }
  349. }
  350. }
  351. else {
  352. rootEnd = 1;
  353. }
  354. }
  355. else if (isWindowsDeviceRoot(code) && path.charCodeAt(1) === CHAR_COLON) {
  356. // Possible device root
  357. device = path.slice(0, 2);
  358. rootEnd = 2;
  359. if (len > 2 && isPathSeparator(path.charCodeAt(2))) {
  360. // Treat separator following drive name as an absolute path
  361. // indicator
  362. isAbsolute = true;
  363. rootEnd = 3;
  364. }
  365. }
  366. let tail = rootEnd < len ?
  367. normalizeString(path.slice(rootEnd), !isAbsolute, '\\', isPathSeparator) :
  368. '';
  369. if (tail.length === 0 && !isAbsolute) {
  370. tail = '.';
  371. }
  372. if (tail.length > 0 && isPathSeparator(path.charCodeAt(len - 1))) {
  373. tail += '\\';
  374. }
  375. if (device === undefined) {
  376. return isAbsolute ? `\\${tail}` : tail;
  377. }
  378. return isAbsolute ? `${device}\\${tail}` : `${device}${tail}`;
  379. },
  380. isAbsolute(path) {
  381. validateString(path, 'path');
  382. const len = path.length;
  383. if (len === 0) {
  384. return false;
  385. }
  386. const code = path.charCodeAt(0);
  387. return isPathSeparator(code) ||
  388. // Possible device root
  389. (len > 2 &&
  390. isWindowsDeviceRoot(code) &&
  391. path.charCodeAt(1) === CHAR_COLON &&
  392. isPathSeparator(path.charCodeAt(2)));
  393. },
  394. join(...paths) {
  395. if (paths.length === 0) {
  396. return '.';
  397. }
  398. let joined;
  399. let firstPart;
  400. for (let i = 0; i < paths.length; ++i) {
  401. const arg = paths[i];
  402. validateString(arg, 'path');
  403. if (arg.length > 0) {
  404. if (joined === undefined) {
  405. joined = firstPart = arg;
  406. }
  407. else {
  408. joined += `\\${arg}`;
  409. }
  410. }
  411. }
  412. if (joined === undefined) {
  413. return '.';
  414. }
  415. // Make sure that the joined path doesn't start with two slashes, because
  416. // normalize() will mistake it for a UNC path then.
  417. //
  418. // This step is skipped when it is very clear that the user actually
  419. // intended to point at a UNC path. This is assumed when the first
  420. // non-empty string arguments starts with exactly two slashes followed by
  421. // at least one more non-slash character.
  422. //
  423. // Note that for normalize() to treat a path as a UNC path it needs to
  424. // have at least 2 components, so we don't filter for that here.
  425. // This means that the user can use join to construct UNC paths from
  426. // a server name and a share name; for example:
  427. // path.join('//server', 'share') -> '\\\\server\\share\\')
  428. let needsReplace = true;
  429. let slashCount = 0;
  430. if (typeof firstPart === 'string' && isPathSeparator(firstPart.charCodeAt(0))) {
  431. ++slashCount;
  432. const firstLen = firstPart.length;
  433. if (firstLen > 1 && isPathSeparator(firstPart.charCodeAt(1))) {
  434. ++slashCount;
  435. if (firstLen > 2) {
  436. if (isPathSeparator(firstPart.charCodeAt(2))) {
  437. ++slashCount;
  438. }
  439. else {
  440. // We matched a UNC path in the first part
  441. needsReplace = false;
  442. }
  443. }
  444. }
  445. }
  446. if (needsReplace) {
  447. // Find any more consecutive slashes we need to replace
  448. while (slashCount < joined.length &&
  449. isPathSeparator(joined.charCodeAt(slashCount))) {
  450. slashCount++;
  451. }
  452. // Replace the slashes if needed
  453. if (slashCount >= 2) {
  454. joined = `\\${joined.slice(slashCount)}`;
  455. }
  456. }
  457. return win32.normalize(joined);
  458. },
  459. // It will solve the relative path from `from` to `to`, for instance:
  460. // from = 'C:\\orandea\\test\\aaa'
  461. // to = 'C:\\orandea\\impl\\bbb'
  462. // The output of the function should be: '..\\..\\impl\\bbb'
  463. relative(from, to) {
  464. validateString(from, 'from');
  465. validateString(to, 'to');
  466. if (from === to) {
  467. return '';
  468. }
  469. const fromOrig = win32.resolve(from);
  470. const toOrig = win32.resolve(to);
  471. if (fromOrig === toOrig) {
  472. return '';
  473. }
  474. from = fromOrig.toLowerCase();
  475. to = toOrig.toLowerCase();
  476. if (from === to) {
  477. return '';
  478. }
  479. // Trim any leading backslashes
  480. let fromStart = 0;
  481. while (fromStart < from.length &&
  482. from.charCodeAt(fromStart) === CHAR_BACKWARD_SLASH) {
  483. fromStart++;
  484. }
  485. // Trim trailing backslashes (applicable to UNC paths only)
  486. let fromEnd = from.length;
  487. while (fromEnd - 1 > fromStart &&
  488. from.charCodeAt(fromEnd - 1) === CHAR_BACKWARD_SLASH) {
  489. fromEnd--;
  490. }
  491. const fromLen = fromEnd - fromStart;
  492. // Trim any leading backslashes
  493. let toStart = 0;
  494. while (toStart < to.length &&
  495. to.charCodeAt(toStart) === CHAR_BACKWARD_SLASH) {
  496. toStart++;
  497. }
  498. // Trim trailing backslashes (applicable to UNC paths only)
  499. let toEnd = to.length;
  500. while (toEnd - 1 > toStart &&
  501. to.charCodeAt(toEnd - 1) === CHAR_BACKWARD_SLASH) {
  502. toEnd--;
  503. }
  504. const toLen = toEnd - toStart;
  505. // Compare paths to find the longest common path from root
  506. const length = fromLen < toLen ? fromLen : toLen;
  507. let lastCommonSep = -1;
  508. let i = 0;
  509. for (; i < length; i++) {
  510. const fromCode = from.charCodeAt(fromStart + i);
  511. if (fromCode !== to.charCodeAt(toStart + i)) {
  512. break;
  513. }
  514. else if (fromCode === CHAR_BACKWARD_SLASH) {
  515. lastCommonSep = i;
  516. }
  517. }
  518. // We found a mismatch before the first common path separator was seen, so
  519. // return the original `to`.
  520. if (i !== length) {
  521. if (lastCommonSep === -1) {
  522. return toOrig;
  523. }
  524. }
  525. else {
  526. if (toLen > length) {
  527. if (to.charCodeAt(toStart + i) === CHAR_BACKWARD_SLASH) {
  528. // We get here if `from` is the exact base path for `to`.
  529. // For example: from='C:\\foo\\bar'; to='C:\\foo\\bar\\baz'
  530. return toOrig.slice(toStart + i + 1);
  531. }
  532. if (i === 2) {
  533. // We get here if `from` is the device root.
  534. // For example: from='C:\\'; to='C:\\foo'
  535. return toOrig.slice(toStart + i);
  536. }
  537. }
  538. if (fromLen > length) {
  539. if (from.charCodeAt(fromStart + i) === CHAR_BACKWARD_SLASH) {
  540. // We get here if `to` is the exact base path for `from`.
  541. // For example: from='C:\\foo\\bar'; to='C:\\foo'
  542. lastCommonSep = i;
  543. }
  544. else if (i === 2) {
  545. // We get here if `to` is the device root.
  546. // For example: from='C:\\foo\\bar'; to='C:\\'
  547. lastCommonSep = 3;
  548. }
  549. }
  550. if (lastCommonSep === -1) {
  551. lastCommonSep = 0;
  552. }
  553. }
  554. let out = '';
  555. // Generate the relative path based on the path difference between `to` and
  556. // `from`
  557. for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) {
  558. if (i === fromEnd || from.charCodeAt(i) === CHAR_BACKWARD_SLASH) {
  559. out += out.length === 0 ? '..' : '\\..';
  560. }
  561. }
  562. toStart += lastCommonSep;
  563. // Lastly, append the rest of the destination (`to`) path that comes after
  564. // the common path parts
  565. if (out.length > 0) {
  566. return `${out}${toOrig.slice(toStart, toEnd)}`;
  567. }
  568. if (toOrig.charCodeAt(toStart) === CHAR_BACKWARD_SLASH) {
  569. ++toStart;
  570. }
  571. return toOrig.slice(toStart, toEnd);
  572. },
  573. toNamespacedPath(path) {
  574. // Note: this will *probably* throw somewhere.
  575. if (typeof path !== 'string' || path.length === 0) {
  576. return path;
  577. }
  578. const resolvedPath = win32.resolve(path);
  579. if (resolvedPath.length <= 2) {
  580. return path;
  581. }
  582. if (resolvedPath.charCodeAt(0) === CHAR_BACKWARD_SLASH) {
  583. // Possible UNC root
  584. if (resolvedPath.charCodeAt(1) === CHAR_BACKWARD_SLASH) {
  585. const code = resolvedPath.charCodeAt(2);
  586. if (code !== CHAR_QUESTION_MARK && code !== CHAR_DOT) {
  587. // Matched non-long UNC root, convert the path to a long UNC path
  588. return `\\\\?\\UNC\\${resolvedPath.slice(2)}`;
  589. }
  590. }
  591. }
  592. else if (isWindowsDeviceRoot(resolvedPath.charCodeAt(0)) &&
  593. resolvedPath.charCodeAt(1) === CHAR_COLON &&
  594. resolvedPath.charCodeAt(2) === CHAR_BACKWARD_SLASH) {
  595. // Matched device root, convert the path to a long UNC path
  596. return `\\\\?\\${resolvedPath}`;
  597. }
  598. return path;
  599. },
  600. dirname(path) {
  601. validateString(path, 'path');
  602. const len = path.length;
  603. if (len === 0) {
  604. return '.';
  605. }
  606. let rootEnd = -1;
  607. let offset = 0;
  608. const code = path.charCodeAt(0);
  609. if (len === 1) {
  610. // `path` contains just a path separator, exit early to avoid
  611. // unnecessary work or a dot.
  612. return isPathSeparator(code) ? path : '.';
  613. }
  614. // Try to match a root
  615. if (isPathSeparator(code)) {
  616. // Possible UNC root
  617. rootEnd = offset = 1;
  618. if (isPathSeparator(path.charCodeAt(1))) {
  619. // Matched double path separator at beginning
  620. let j = 2;
  621. let last = j;
  622. // Match 1 or more non-path separators
  623. while (j < len && !isPathSeparator(path.charCodeAt(j))) {
  624. j++;
  625. }
  626. if (j < len && j !== last) {
  627. // Matched!
  628. last = j;
  629. // Match 1 or more path separators
  630. while (j < len && isPathSeparator(path.charCodeAt(j))) {
  631. j++;
  632. }
  633. if (j < len && j !== last) {
  634. // Matched!
  635. last = j;
  636. // Match 1 or more non-path separators
  637. while (j < len && !isPathSeparator(path.charCodeAt(j))) {
  638. j++;
  639. }
  640. if (j === len) {
  641. // We matched a UNC root only
  642. return path;
  643. }
  644. if (j !== last) {
  645. // We matched a UNC root with leftovers
  646. // Offset by 1 to include the separator after the UNC root to
  647. // treat it as a "normal root" on top of a (UNC) root
  648. rootEnd = offset = j + 1;
  649. }
  650. }
  651. }
  652. }
  653. // Possible device root
  654. }
  655. else if (isWindowsDeviceRoot(code) && path.charCodeAt(1) === CHAR_COLON) {
  656. rootEnd = len > 2 && isPathSeparator(path.charCodeAt(2)) ? 3 : 2;
  657. offset = rootEnd;
  658. }
  659. let end = -1;
  660. let matchedSlash = true;
  661. for (let i = len - 1; i >= offset; --i) {
  662. if (isPathSeparator(path.charCodeAt(i))) {
  663. if (!matchedSlash) {
  664. end = i;
  665. break;
  666. }
  667. }
  668. else {
  669. // We saw the first non-path separator
  670. matchedSlash = false;
  671. }
  672. }
  673. if (end === -1) {
  674. if (rootEnd === -1) {
  675. return '.';
  676. }
  677. end = rootEnd;
  678. }
  679. return path.slice(0, end);
  680. },
  681. basename(path, ext) {
  682. if (ext !== undefined) {
  683. validateString(ext, 'ext');
  684. }
  685. validateString(path, 'path');
  686. let start = 0;
  687. let end = -1;
  688. let matchedSlash = true;
  689. let i;
  690. // Check for a drive letter prefix so as not to mistake the following
  691. // path separator as an extra separator at the end of the path that can be
  692. // disregarded
  693. if (path.length >= 2 &&
  694. isWindowsDeviceRoot(path.charCodeAt(0)) &&
  695. path.charCodeAt(1) === CHAR_COLON) {
  696. start = 2;
  697. }
  698. if (ext !== undefined && ext.length > 0 && ext.length <= path.length) {
  699. if (ext === path) {
  700. return '';
  701. }
  702. let extIdx = ext.length - 1;
  703. let firstNonSlashEnd = -1;
  704. for (i = path.length - 1; i >= start; --i) {
  705. const code = path.charCodeAt(i);
  706. if (isPathSeparator(code)) {
  707. // If we reached a path separator that was not part of a set of path
  708. // separators at the end of the string, stop now
  709. if (!matchedSlash) {
  710. start = i + 1;
  711. break;
  712. }
  713. }
  714. else {
  715. if (firstNonSlashEnd === -1) {
  716. // We saw the first non-path separator, remember this index in case
  717. // we need it if the extension ends up not matching
  718. matchedSlash = false;
  719. firstNonSlashEnd = i + 1;
  720. }
  721. if (extIdx >= 0) {
  722. // Try to match the explicit extension
  723. if (code === ext.charCodeAt(extIdx)) {
  724. if (--extIdx === -1) {
  725. // We matched the extension, so mark this as the end of our path
  726. // component
  727. end = i;
  728. }
  729. }
  730. else {
  731. // Extension does not match, so our result is the entire path
  732. // component
  733. extIdx = -1;
  734. end = firstNonSlashEnd;
  735. }
  736. }
  737. }
  738. }
  739. if (start === end) {
  740. end = firstNonSlashEnd;
  741. }
  742. else if (end === -1) {
  743. end = path.length;
  744. }
  745. return path.slice(start, end);
  746. }
  747. for (i = path.length - 1; i >= start; --i) {
  748. if (isPathSeparator(path.charCodeAt(i))) {
  749. // If we reached a path separator that was not part of a set of path
  750. // separators at the end of the string, stop now
  751. if (!matchedSlash) {
  752. start = i + 1;
  753. break;
  754. }
  755. }
  756. else if (end === -1) {
  757. // We saw the first non-path separator, mark this as the end of our
  758. // path component
  759. matchedSlash = false;
  760. end = i + 1;
  761. }
  762. }
  763. if (end === -1) {
  764. return '';
  765. }
  766. return path.slice(start, end);
  767. },
  768. extname(path) {
  769. validateString(path, 'path');
  770. let start = 0;
  771. let startDot = -1;
  772. let startPart = 0;
  773. let end = -1;
  774. let matchedSlash = true;
  775. // Track the state of characters (if any) we see before our first dot and
  776. // after any path separator we find
  777. let preDotState = 0;
  778. // Check for a drive letter prefix so as not to mistake the following
  779. // path separator as an extra separator at the end of the path that can be
  780. // disregarded
  781. if (path.length >= 2 &&
  782. path.charCodeAt(1) === CHAR_COLON &&
  783. isWindowsDeviceRoot(path.charCodeAt(0))) {
  784. start = startPart = 2;
  785. }
  786. for (let i = path.length - 1; i >= start; --i) {
  787. const code = path.charCodeAt(i);
  788. if (isPathSeparator(code)) {
  789. // If we reached a path separator that was not part of a set of path
  790. // separators at the end of the string, stop now
  791. if (!matchedSlash) {
  792. startPart = i + 1;
  793. break;
  794. }
  795. continue;
  796. }
  797. if (end === -1) {
  798. // We saw the first non-path separator, mark this as the end of our
  799. // extension
  800. matchedSlash = false;
  801. end = i + 1;
  802. }
  803. if (code === CHAR_DOT) {
  804. // If this is our first dot, mark it as the start of our extension
  805. if (startDot === -1) {
  806. startDot = i;
  807. }
  808. else if (preDotState !== 1) {
  809. preDotState = 1;
  810. }
  811. }
  812. else if (startDot !== -1) {
  813. // We saw a non-dot and non-path separator before our dot, so we should
  814. // have a good chance at having a non-empty extension
  815. preDotState = -1;
  816. }
  817. }
  818. if (startDot === -1 ||
  819. end === -1 ||
  820. // We saw a non-dot character immediately before the dot
  821. preDotState === 0 ||
  822. // The (right-most) trimmed path component is exactly '..'
  823. (preDotState === 1 &&
  824. startDot === end - 1 &&
  825. startDot === startPart + 1)) {
  826. return '';
  827. }
  828. return path.slice(startDot, end);
  829. },
  830. format: _format.bind(null, '\\'),
  831. parse(path) {
  832. validateString(path, 'path');
  833. const ret = { root: '', dir: '', base: '', ext: '', name: '' };
  834. if (path.length === 0) {
  835. return ret;
  836. }
  837. const len = path.length;
  838. let rootEnd = 0;
  839. let code = path.charCodeAt(0);
  840. if (len === 1) {
  841. if (isPathSeparator(code)) {
  842. // `path` contains just a path separator, exit early to avoid
  843. // unnecessary work
  844. ret.root = ret.dir = path;
  845. return ret;
  846. }
  847. ret.base = ret.name = path;
  848. return ret;
  849. }
  850. // Try to match a root
  851. if (isPathSeparator(code)) {
  852. // Possible UNC root
  853. rootEnd = 1;
  854. if (isPathSeparator(path.charCodeAt(1))) {
  855. // Matched double path separator at beginning
  856. let j = 2;
  857. let last = j;
  858. // Match 1 or more non-path separators
  859. while (j < len && !isPathSeparator(path.charCodeAt(j))) {
  860. j++;
  861. }
  862. if (j < len && j !== last) {
  863. // Matched!
  864. last = j;
  865. // Match 1 or more path separators
  866. while (j < len && isPathSeparator(path.charCodeAt(j))) {
  867. j++;
  868. }
  869. if (j < len && j !== last) {
  870. // Matched!
  871. last = j;
  872. // Match 1 or more non-path separators
  873. while (j < len && !isPathSeparator(path.charCodeAt(j))) {
  874. j++;
  875. }
  876. if (j === len) {
  877. // We matched a UNC root only
  878. rootEnd = j;
  879. }
  880. else if (j !== last) {
  881. // We matched a UNC root with leftovers
  882. rootEnd = j + 1;
  883. }
  884. }
  885. }
  886. }
  887. }
  888. else if (isWindowsDeviceRoot(code) && path.charCodeAt(1) === CHAR_COLON) {
  889. // Possible device root
  890. if (len <= 2) {
  891. // `path` contains just a drive root, exit early to avoid
  892. // unnecessary work
  893. ret.root = ret.dir = path;
  894. return ret;
  895. }
  896. rootEnd = 2;
  897. if (isPathSeparator(path.charCodeAt(2))) {
  898. if (len === 3) {
  899. // `path` contains just a drive root, exit early to avoid
  900. // unnecessary work
  901. ret.root = ret.dir = path;
  902. return ret;
  903. }
  904. rootEnd = 3;
  905. }
  906. }
  907. if (rootEnd > 0) {
  908. ret.root = path.slice(0, rootEnd);
  909. }
  910. let startDot = -1;
  911. let startPart = rootEnd;
  912. let end = -1;
  913. let matchedSlash = true;
  914. let i = path.length - 1;
  915. // Track the state of characters (if any) we see before our first dot and
  916. // after any path separator we find
  917. let preDotState = 0;
  918. // Get non-dir info
  919. for (; i >= rootEnd; --i) {
  920. code = path.charCodeAt(i);
  921. if (isPathSeparator(code)) {
  922. // If we reached a path separator that was not part of a set of path
  923. // separators at the end of the string, stop now
  924. if (!matchedSlash) {
  925. startPart = i + 1;
  926. break;
  927. }
  928. continue;
  929. }
  930. if (end === -1) {
  931. // We saw the first non-path separator, mark this as the end of our
  932. // extension
  933. matchedSlash = false;
  934. end = i + 1;
  935. }
  936. if (code === CHAR_DOT) {
  937. // If this is our first dot, mark it as the start of our extension
  938. if (startDot === -1) {
  939. startDot = i;
  940. }
  941. else if (preDotState !== 1) {
  942. preDotState = 1;
  943. }
  944. }
  945. else if (startDot !== -1) {
  946. // We saw a non-dot and non-path separator before our dot, so we should
  947. // have a good chance at having a non-empty extension
  948. preDotState = -1;
  949. }
  950. }
  951. if (end !== -1) {
  952. if (startDot === -1 ||
  953. // We saw a non-dot character immediately before the dot
  954. preDotState === 0 ||
  955. // The (right-most) trimmed path component is exactly '..'
  956. (preDotState === 1 &&
  957. startDot === end - 1 &&
  958. startDot === startPart + 1)) {
  959. ret.base = ret.name = path.slice(startPart, end);
  960. }
  961. else {
  962. ret.name = path.slice(startPart, startDot);
  963. ret.base = path.slice(startPart, end);
  964. ret.ext = path.slice(startDot, end);
  965. }
  966. }
  967. // If the directory is the root, use the entire root as the `dir` including
  968. // the trailing slash if any (`C:\abc` -> `C:\`). Otherwise, strip out the
  969. // trailing slash (`C:\abc\def` -> `C:\abc`).
  970. if (startPart > 0 && startPart !== rootEnd) {
  971. ret.dir = path.slice(0, startPart - 1);
  972. }
  973. else {
  974. ret.dir = ret.root;
  975. }
  976. return ret;
  977. },
  978. sep: '\\',
  979. delimiter: ';',
  980. win32: null,
  981. posix: null
  982. };
  983. const posixCwd = (() => {
  984. if (platformIsWin32) {
  985. // Converts Windows' backslash path separators to POSIX forward slashes
  986. // and truncates any drive indicator
  987. const regexp = /\\/g;
  988. return () => {
  989. const cwd = process.cwd().replace(regexp, '/');
  990. return cwd.slice(cwd.indexOf('/'));
  991. };
  992. }
  993. // We're already on POSIX, no need for any transformations
  994. return () => process.cwd();
  995. })();
  996. export const posix = {
  997. // path.resolve([from ...], to)
  998. resolve(...pathSegments) {
  999. let resolvedPath = '';
  1000. let resolvedAbsolute = false;
  1001. for (let i = pathSegments.length - 1; i >= -1 && !resolvedAbsolute; i--) {
  1002. const path = i >= 0 ? pathSegments[i] : posixCwd();
  1003. validateString(path, 'path');
  1004. // Skip empty entries
  1005. if (path.length === 0) {
  1006. continue;
  1007. }
  1008. resolvedPath = `${path}/${resolvedPath}`;
  1009. resolvedAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH;
  1010. }
  1011. // At this point the path should be resolved to a full absolute path, but
  1012. // handle relative paths to be safe (might happen when process.cwd() fails)
  1013. // Normalize the path
  1014. resolvedPath = normalizeString(resolvedPath, !resolvedAbsolute, '/', isPosixPathSeparator);
  1015. if (resolvedAbsolute) {
  1016. return `/${resolvedPath}`;
  1017. }
  1018. return resolvedPath.length > 0 ? resolvedPath : '.';
  1019. },
  1020. normalize(path) {
  1021. validateString(path, 'path');
  1022. if (path.length === 0) {
  1023. return '.';
  1024. }
  1025. const isAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH;
  1026. const trailingSeparator = path.charCodeAt(path.length - 1) === CHAR_FORWARD_SLASH;
  1027. // Normalize the path
  1028. path = normalizeString(path, !isAbsolute, '/', isPosixPathSeparator);
  1029. if (path.length === 0) {
  1030. if (isAbsolute) {
  1031. return '/';
  1032. }
  1033. return trailingSeparator ? './' : '.';
  1034. }
  1035. if (trailingSeparator) {
  1036. path += '/';
  1037. }
  1038. return isAbsolute ? `/${path}` : path;
  1039. },
  1040. isAbsolute(path) {
  1041. validateString(path, 'path');
  1042. return path.length > 0 && path.charCodeAt(0) === CHAR_FORWARD_SLASH;
  1043. },
  1044. join(...paths) {
  1045. if (paths.length === 0) {
  1046. return '.';
  1047. }
  1048. let joined;
  1049. for (let i = 0; i < paths.length; ++i) {
  1050. const arg = paths[i];
  1051. validateString(arg, 'path');
  1052. if (arg.length > 0) {
  1053. if (joined === undefined) {
  1054. joined = arg;
  1055. }
  1056. else {
  1057. joined += `/${arg}`;
  1058. }
  1059. }
  1060. }
  1061. if (joined === undefined) {
  1062. return '.';
  1063. }
  1064. return posix.normalize(joined);
  1065. },
  1066. relative(from, to) {
  1067. validateString(from, 'from');
  1068. validateString(to, 'to');
  1069. if (from === to) {
  1070. return '';
  1071. }
  1072. // Trim leading forward slashes.
  1073. from = posix.resolve(from);
  1074. to = posix.resolve(to);
  1075. if (from === to) {
  1076. return '';
  1077. }
  1078. const fromStart = 1;
  1079. const fromEnd = from.length;
  1080. const fromLen = fromEnd - fromStart;
  1081. const toStart = 1;
  1082. const toLen = to.length - toStart;
  1083. // Compare paths to find the longest common path from root
  1084. const length = (fromLen < toLen ? fromLen : toLen);
  1085. let lastCommonSep = -1;
  1086. let i = 0;
  1087. for (; i < length; i++) {
  1088. const fromCode = from.charCodeAt(fromStart + i);
  1089. if (fromCode !== to.charCodeAt(toStart + i)) {
  1090. break;
  1091. }
  1092. else if (fromCode === CHAR_FORWARD_SLASH) {
  1093. lastCommonSep = i;
  1094. }
  1095. }
  1096. if (i === length) {
  1097. if (toLen > length) {
  1098. if (to.charCodeAt(toStart + i) === CHAR_FORWARD_SLASH) {
  1099. // We get here if `from` is the exact base path for `to`.
  1100. // For example: from='/foo/bar'; to='/foo/bar/baz'
  1101. return to.slice(toStart + i + 1);
  1102. }
  1103. if (i === 0) {
  1104. // We get here if `from` is the root
  1105. // For example: from='/'; to='/foo'
  1106. return to.slice(toStart + i);
  1107. }
  1108. }
  1109. else if (fromLen > length) {
  1110. if (from.charCodeAt(fromStart + i) === CHAR_FORWARD_SLASH) {
  1111. // We get here if `to` is the exact base path for `from`.
  1112. // For example: from='/foo/bar/baz'; to='/foo/bar'
  1113. lastCommonSep = i;
  1114. }
  1115. else if (i === 0) {
  1116. // We get here if `to` is the root.
  1117. // For example: from='/foo/bar'; to='/'
  1118. lastCommonSep = 0;
  1119. }
  1120. }
  1121. }
  1122. let out = '';
  1123. // Generate the relative path based on the path difference between `to`
  1124. // and `from`.
  1125. for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) {
  1126. if (i === fromEnd || from.charCodeAt(i) === CHAR_FORWARD_SLASH) {
  1127. out += out.length === 0 ? '..' : '/..';
  1128. }
  1129. }
  1130. // Lastly, append the rest of the destination (`to`) path that comes after
  1131. // the common path parts.
  1132. return `${out}${to.slice(toStart + lastCommonSep)}`;
  1133. },
  1134. toNamespacedPath(path) {
  1135. // Non-op on posix systems
  1136. return path;
  1137. },
  1138. dirname(path) {
  1139. validateString(path, 'path');
  1140. if (path.length === 0) {
  1141. return '.';
  1142. }
  1143. const hasRoot = path.charCodeAt(0) === CHAR_FORWARD_SLASH;
  1144. let end = -1;
  1145. let matchedSlash = true;
  1146. for (let i = path.length - 1; i >= 1; --i) {
  1147. if (path.charCodeAt(i) === CHAR_FORWARD_SLASH) {
  1148. if (!matchedSlash) {
  1149. end = i;
  1150. break;
  1151. }
  1152. }
  1153. else {
  1154. // We saw the first non-path separator
  1155. matchedSlash = false;
  1156. }
  1157. }
  1158. if (end === -1) {
  1159. return hasRoot ? '/' : '.';
  1160. }
  1161. if (hasRoot && end === 1) {
  1162. return '//';
  1163. }
  1164. return path.slice(0, end);
  1165. },
  1166. basename(path, ext) {
  1167. if (ext !== undefined) {
  1168. validateString(ext, 'ext');
  1169. }
  1170. validateString(path, 'path');
  1171. let start = 0;
  1172. let end = -1;
  1173. let matchedSlash = true;
  1174. let i;
  1175. if (ext !== undefined && ext.length > 0 && ext.length <= path.length) {
  1176. if (ext === path) {
  1177. return '';
  1178. }
  1179. let extIdx = ext.length - 1;
  1180. let firstNonSlashEnd = -1;
  1181. for (i = path.length - 1; i >= 0; --i) {
  1182. const code = path.charCodeAt(i);
  1183. if (code === CHAR_FORWARD_SLASH) {
  1184. // If we reached a path separator that was not part of a set of path
  1185. // separators at the end of the string, stop now
  1186. if (!matchedSlash) {
  1187. start = i + 1;
  1188. break;
  1189. }
  1190. }
  1191. else {
  1192. if (firstNonSlashEnd === -1) {
  1193. // We saw the first non-path separator, remember this index in case
  1194. // we need it if the extension ends up not matching
  1195. matchedSlash = false;
  1196. firstNonSlashEnd = i + 1;
  1197. }
  1198. if (extIdx >= 0) {
  1199. // Try to match the explicit extension
  1200. if (code === ext.charCodeAt(extIdx)) {
  1201. if (--extIdx === -1) {
  1202. // We matched the extension, so mark this as the end of our path
  1203. // component
  1204. end = i;
  1205. }
  1206. }
  1207. else {
  1208. // Extension does not match, so our result is the entire path
  1209. // component
  1210. extIdx = -1;
  1211. end = firstNonSlashEnd;
  1212. }
  1213. }
  1214. }
  1215. }
  1216. if (start === end) {
  1217. end = firstNonSlashEnd;
  1218. }
  1219. else if (end === -1) {
  1220. end = path.length;
  1221. }
  1222. return path.slice(start, end);
  1223. }
  1224. for (i = path.length - 1; i >= 0; --i) {
  1225. if (path.charCodeAt(i) === CHAR_FORWARD_SLASH) {
  1226. // If we reached a path separator that was not part of a set of path
  1227. // separators at the end of the string, stop now
  1228. if (!matchedSlash) {
  1229. start = i + 1;
  1230. break;
  1231. }
  1232. }
  1233. else if (end === -1) {
  1234. // We saw the first non-path separator, mark this as the end of our
  1235. // path component
  1236. matchedSlash = false;
  1237. end = i + 1;
  1238. }
  1239. }
  1240. if (end === -1) {
  1241. return '';
  1242. }
  1243. return path.slice(start, end);
  1244. },
  1245. extname(path) {
  1246. validateString(path, 'path');
  1247. let startDot = -1;
  1248. let startPart = 0;
  1249. let end = -1;
  1250. let matchedSlash = true;
  1251. // Track the state of characters (if any) we see before our first dot and
  1252. // after any path separator we find
  1253. let preDotState = 0;
  1254. for (let i = path.length - 1; i >= 0; --i) {
  1255. const code = path.charCodeAt(i);
  1256. if (code === CHAR_FORWARD_SLASH) {
  1257. // If we reached a path separator that was not part of a set of path
  1258. // separators at the end of the string, stop now
  1259. if (!matchedSlash) {
  1260. startPart = i + 1;
  1261. break;
  1262. }
  1263. continue;
  1264. }
  1265. if (end === -1) {
  1266. // We saw the first non-path separator, mark this as the end of our
  1267. // extension
  1268. matchedSlash = false;
  1269. end = i + 1;
  1270. }
  1271. if (code === CHAR_DOT) {
  1272. // If this is our first dot, mark it as the start of our extension
  1273. if (startDot === -1) {
  1274. startDot = i;
  1275. }
  1276. else if (preDotState !== 1) {
  1277. preDotState = 1;
  1278. }
  1279. }
  1280. else if (startDot !== -1) {
  1281. // We saw a non-dot and non-path separator before our dot, so we should
  1282. // have a good chance at having a non-empty extension
  1283. preDotState = -1;
  1284. }
  1285. }
  1286. if (startDot === -1 ||
  1287. end === -1 ||
  1288. // We saw a non-dot character immediately before the dot
  1289. preDotState === 0 ||
  1290. // The (right-most) trimmed path component is exactly '..'
  1291. (preDotState === 1 &&
  1292. startDot === end - 1 &&
  1293. startDot === startPart + 1)) {
  1294. return '';
  1295. }
  1296. return path.slice(startDot, end);
  1297. },
  1298. format: _format.bind(null, '/'),
  1299. parse(path) {
  1300. validateString(path, 'path');
  1301. const ret = { root: '', dir: '', base: '', ext: '', name: '' };
  1302. if (path.length === 0) {
  1303. return ret;
  1304. }
  1305. const isAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH;
  1306. let start;
  1307. if (isAbsolute) {
  1308. ret.root = '/';
  1309. start = 1;
  1310. }
  1311. else {
  1312. start = 0;
  1313. }
  1314. let startDot = -1;
  1315. let startPart = 0;
  1316. let end = -1;
  1317. let matchedSlash = true;
  1318. let i = path.length - 1;
  1319. // Track the state of characters (if any) we see before our first dot and
  1320. // after any path separator we find
  1321. let preDotState = 0;
  1322. // Get non-dir info
  1323. for (; i >= start; --i) {
  1324. const code = path.charCodeAt(i);
  1325. if (code === CHAR_FORWARD_SLASH) {
  1326. // If we reached a path separator that was not part of a set of path
  1327. // separators at the end of the string, stop now
  1328. if (!matchedSlash) {
  1329. startPart = i + 1;
  1330. break;
  1331. }
  1332. continue;
  1333. }
  1334. if (end === -1) {
  1335. // We saw the first non-path separator, mark this as the end of our
  1336. // extension
  1337. matchedSlash = false;
  1338. end = i + 1;
  1339. }
  1340. if (code === CHAR_DOT) {
  1341. // If this is our first dot, mark it as the start of our extension
  1342. if (startDot === -1) {
  1343. startDot = i;
  1344. }
  1345. else if (preDotState !== 1) {
  1346. preDotState = 1;
  1347. }
  1348. }
  1349. else if (startDot !== -1) {
  1350. // We saw a non-dot and non-path separator before our dot, so we should
  1351. // have a good chance at having a non-empty extension
  1352. preDotState = -1;
  1353. }
  1354. }
  1355. if (end !== -1) {
  1356. const start = startPart === 0 && isAbsolute ? 1 : startPart;
  1357. if (startDot === -1 ||
  1358. // We saw a non-dot character immediately before the dot
  1359. preDotState === 0 ||
  1360. // The (right-most) trimmed path component is exactly '..'
  1361. (preDotState === 1 &&
  1362. startDot === end - 1 &&
  1363. startDot === startPart + 1)) {
  1364. ret.base = ret.name = path.slice(start, end);
  1365. }
  1366. else {
  1367. ret.name = path.slice(start, startDot);
  1368. ret.base = path.slice(start, end);
  1369. ret.ext = path.slice(startDot, end);
  1370. }
  1371. }
  1372. if (startPart > 0) {
  1373. ret.dir = path.slice(0, startPart - 1);
  1374. }
  1375. else if (isAbsolute) {
  1376. ret.dir = '/';
  1377. }
  1378. return ret;
  1379. },
  1380. sep: '/',
  1381. delimiter: ':',
  1382. win32: null,
  1383. posix: null
  1384. };
  1385. posix.win32 = win32.win32 = win32;
  1386. posix.posix = win32.posix = posix;
  1387. export const normalize = (platformIsWin32 ? win32.normalize : posix.normalize);
  1388. export const resolve = (platformIsWin32 ? win32.resolve : posix.resolve);
  1389. export const relative = (platformIsWin32 ? win32.relative : posix.relative);
  1390. export const dirname = (platformIsWin32 ? win32.dirname : posix.dirname);
  1391. export const basename = (platformIsWin32 ? win32.basename : posix.basename);
  1392. export const extname = (platformIsWin32 ? win32.extname : posix.extname);
  1393. export const sep = (platformIsWin32 ? win32.sep : posix.sep);