posix.js 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302
  1. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
  2. // Copyright the Browserify authors. MIT License.
  3. function assertPath(path) {
  4. if (typeof path !== "string") {
  5. throw new TypeError(`Path must be a string, received "${JSON.stringify(path)}"`);
  6. }
  7. }
  8. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
  9. // This module is browser compatible.
  10. function stripSuffix(name, suffix) {
  11. if (suffix.length >= name.length) {
  12. return name;
  13. }
  14. const lenDiff = name.length - suffix.length;
  15. for(let i = suffix.length - 1; i >= 0; --i){
  16. if (name.charCodeAt(lenDiff + i) !== suffix.charCodeAt(i)) {
  17. return name;
  18. }
  19. }
  20. return name.slice(0, -suffix.length);
  21. }
  22. function lastPathSegment(path, isSep, start = 0) {
  23. let matchedNonSeparator = false;
  24. let end = path.length;
  25. for(let i = path.length - 1; i >= start; --i){
  26. if (isSep(path.charCodeAt(i))) {
  27. if (matchedNonSeparator) {
  28. start = i + 1;
  29. break;
  30. }
  31. } else if (!matchedNonSeparator) {
  32. matchedNonSeparator = true;
  33. end = i + 1;
  34. }
  35. }
  36. return path.slice(start, end);
  37. }
  38. function assertArgs$1(path, suffix) {
  39. assertPath(path);
  40. if (path.length === 0) return path;
  41. if (typeof suffix !== "string") {
  42. throw new TypeError(`Suffix must be a string, received "${JSON.stringify(suffix)}"`);
  43. }
  44. }
  45. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
  46. // Copyright the Browserify authors. MIT License.
  47. // Ported from https://github.com/browserify/path-browserify/
  48. // This module is browser compatible.
  49. function stripTrailingSeparators(segment, isSep) {
  50. if (segment.length <= 1) {
  51. return segment;
  52. }
  53. let end = segment.length;
  54. for(let i = segment.length - 1; i > 0; i--){
  55. if (isSep(segment.charCodeAt(i))) {
  56. end = i;
  57. } else {
  58. break;
  59. }
  60. }
  61. return segment.slice(0, end);
  62. }
  63. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
  64. // Copyright the Browserify authors. MIT License.
  65. // Ported from https://github.com/browserify/path-browserify/
  66. // This module is browser compatible.
  67. // Alphabet chars.
  68. // Non-alphabetic chars.
  69. const CHAR_DOT = 46; /* . */
  70. const CHAR_FORWARD_SLASH = 47; /* / */
  71. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
  72. // Copyright the Browserify authors. MIT License.
  73. // Ported from https://github.com/browserify/path-browserify/
  74. // This module is browser compatible.
  75. function isPosixPathSeparator(code) {
  76. return code === CHAR_FORWARD_SLASH;
  77. }
  78. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
  79. // This module is browser compatible.
  80. /**
  81. * Return the last portion of a `path`.
  82. * Trailing directory separators are ignored, and optional suffix is removed.
  83. *
  84. * @example Usage
  85. * ```ts
  86. * import { basename } from "@std/path/posix/basename";
  87. * import { assertEquals } from "@std/assert";
  88. *
  89. * assertEquals(basename("/home/user/Documents/"), "Documents");
  90. * assertEquals(basename("/home/user/Documents/image.png"), "image.png");
  91. * assertEquals(basename("/home/user/Documents/image.png", ".png"), "image");
  92. * ```
  93. *
  94. * @example Working with URLs
  95. *
  96. * Note: This function doesn't automatically strip hash and query parts from
  97. * URLs. If your URL contains a hash or query, remove them before passing the
  98. * URL to the function. This can be done by passing the URL to `new URL(url)`,
  99. * and setting the `hash` and `search` properties to empty strings.
  100. *
  101. * ```ts
  102. * import { basename } from "@std/path/posix/basename";
  103. * import { assertEquals } from "@std/assert";
  104. *
  105. * assertEquals(basename("https://deno.land/std/path/mod.ts"), "mod.ts");
  106. * assertEquals(basename("https://deno.land/std/path/mod.ts", ".ts"), "mod");
  107. * assertEquals(basename("https://deno.land/std/path/mod.ts?a=b"), "mod.ts?a=b");
  108. * assertEquals(basename("https://deno.land/std/path/mod.ts#header"), "mod.ts#header");
  109. * ```
  110. *
  111. * Note: If you are working with file URLs,
  112. * use the new version of `basename` from `@std/path/posix/unstable-basename`.
  113. *
  114. * @param path The path to extract the name from.
  115. * @param suffix The suffix to remove from extracted name.
  116. * @returns The extracted name.
  117. */ function basename(path, suffix = "") {
  118. assertArgs$1(path, suffix);
  119. const lastSegment = lastPathSegment(path, isPosixPathSeparator);
  120. const strippedSegment = stripTrailingSeparators(lastSegment, isPosixPathSeparator);
  121. return suffix ? stripSuffix(strippedSegment, suffix) : strippedSegment;
  122. }
  123. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
  124. // This module is browser compatible.
  125. /**
  126. * The character used to separate entries in the PATH environment variable.
  127. */ const DELIMITER = ":";
  128. /**
  129. * The character used to separate components of a file path.
  130. */ const SEPARATOR = "/";
  131. /**
  132. * A regular expression that matches one or more path separators.
  133. */ const SEPARATOR_PATTERN = /\/+/;
  134. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
  135. // This module is browser compatible.
  136. function assertArg$3(path) {
  137. assertPath(path);
  138. if (path.length === 0) return ".";
  139. }
  140. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
  141. // This module is browser compatible.
  142. /**
  143. * Return the directory path of a `path`.
  144. *
  145. * @example Usage
  146. * ```ts
  147. * import { dirname } from "@std/path/posix/dirname";
  148. * import { assertEquals } from "@std/assert";
  149. *
  150. * assertEquals(dirname("/home/user/Documents/"), "/home/user");
  151. * assertEquals(dirname("/home/user/Documents/image.png"), "/home/user/Documents");
  152. * assertEquals(dirname("https://deno.land/std/path/mod.ts"), "https://deno.land/std/path");
  153. * ```
  154. *
  155. * @example Working with URLs
  156. *
  157. * ```ts
  158. * import { dirname } from "@std/path/posix/dirname";
  159. * import { assertEquals } from "@std/assert";
  160. *
  161. * assertEquals(dirname("https://deno.land/std/path/mod.ts"), "https://deno.land/std/path");
  162. * assertEquals(dirname("https://deno.land/std/path/mod.ts?a=b"), "https://deno.land/std/path");
  163. * assertEquals(dirname("https://deno.land/std/path/mod.ts#header"), "https://deno.land/std/path");
  164. * ```
  165. *
  166. * Note: If you are working with file URLs,
  167. * use the new version of `dirname` from `@std/path/posix/unstable-dirname`.
  168. *
  169. * @param path The path to get the directory from.
  170. * @returns The directory path.
  171. */ function dirname(path) {
  172. assertArg$3(path);
  173. let end = -1;
  174. let matchedNonSeparator = false;
  175. for(let i = path.length - 1; i >= 1; --i){
  176. if (isPosixPathSeparator(path.charCodeAt(i))) {
  177. if (matchedNonSeparator) {
  178. end = i;
  179. break;
  180. }
  181. } else {
  182. matchedNonSeparator = true;
  183. }
  184. }
  185. // No matches. Fallback based on provided path:
  186. //
  187. // - leading slashes paths
  188. // "/foo" => "/"
  189. // "///foo" => "/"
  190. // - no slash path
  191. // "foo" => "."
  192. if (end === -1) {
  193. return isPosixPathSeparator(path.charCodeAt(0)) ? "/" : ".";
  194. }
  195. return stripTrailingSeparators(path.slice(0, end), isPosixPathSeparator);
  196. }
  197. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
  198. // This module is browser compatible.
  199. /**
  200. * Return the extension of the `path` with leading period.
  201. *
  202. * @example Usage
  203. * ```ts
  204. * import { extname } from "@std/path/posix/extname";
  205. * import { assertEquals } from "@std/assert";
  206. *
  207. * assertEquals(extname("/home/user/Documents/file.ts"), ".ts");
  208. * assertEquals(extname("/home/user/Documents/"), "");
  209. * assertEquals(extname("/home/user/Documents/image.png"), ".png");
  210. * ```
  211. *
  212. * @example Working with URLs
  213. *
  214. * Note: This function doesn't automatically strip hash and query parts from
  215. * URLs. If your URL contains a hash or query, remove them before passing the
  216. * URL to the function. This can be done by passing the URL to `new URL(url)`,
  217. * and setting the `hash` and `search` properties to empty strings.
  218. *
  219. * ```ts
  220. * import { extname } from "@std/path/posix/extname";
  221. * import { assertEquals } from "@std/assert";
  222. *
  223. * assertEquals(extname("https://deno.land/std/path/mod.ts"), ".ts");
  224. * assertEquals(extname("https://deno.land/std/path/mod.ts?a=b"), ".ts?a=b");
  225. * assertEquals(extname("https://deno.land/std/path/mod.ts#header"), ".ts#header");
  226. * ```
  227. *
  228. * Note: If you are working with file URLs,
  229. * use the new version of `extname` from `@std/path/posix/unstable-extname`.
  230. *
  231. * @param path The path to get the extension from.
  232. * @returns The extension (ex. for `file.ts` returns `.ts`).
  233. */ function extname(path) {
  234. assertPath(path);
  235. let startDot = -1;
  236. let startPart = 0;
  237. let end = -1;
  238. let matchedSlash = true;
  239. // Track the state of characters (if any) we see before our first dot and
  240. // after any path separator we find
  241. let preDotState = 0;
  242. for(let i = path.length - 1; i >= 0; --i){
  243. const code = path.charCodeAt(i);
  244. if (isPosixPathSeparator(code)) {
  245. // If we reached a path separator that was not part of a set of path
  246. // separators at the end of the string, stop now
  247. if (!matchedSlash) {
  248. startPart = i + 1;
  249. break;
  250. }
  251. continue;
  252. }
  253. if (end === -1) {
  254. // We saw the first non-path separator, mark this as the end of our
  255. // extension
  256. matchedSlash = false;
  257. end = i + 1;
  258. }
  259. if (code === CHAR_DOT) {
  260. // If this is our first dot, mark it as the start of our extension
  261. if (startDot === -1) startDot = i;
  262. else if (preDotState !== 1) preDotState = 1;
  263. } else if (startDot !== -1) {
  264. // We saw a non-dot and non-path separator before our dot, so we should
  265. // have a good chance at having a non-empty extension
  266. preDotState = -1;
  267. }
  268. }
  269. if (startDot === -1 || end === -1 || // We saw a non-dot character immediately before the dot
  270. preDotState === 0 || // The (right-most) trimmed path component is exactly '..'
  271. preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) {
  272. return "";
  273. }
  274. return path.slice(startDot, end);
  275. }
  276. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
  277. // This module is browser compatible.
  278. function _format(sep, pathObject) {
  279. const dir = pathObject.dir || pathObject.root;
  280. const base = pathObject.base || (pathObject.name ?? "") + (pathObject.ext ?? "");
  281. if (!dir) return base;
  282. if (base === sep) return dir;
  283. if (dir === pathObject.root) return dir + base;
  284. return dir + sep + base;
  285. }
  286. function assertArg$2(pathObject) {
  287. if (pathObject === null || typeof pathObject !== "object") {
  288. throw new TypeError(`The "pathObject" argument must be of type Object, received type "${typeof pathObject}"`);
  289. }
  290. }
  291. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
  292. // This module is browser compatible.
  293. /**
  294. * Generate a path from `ParsedPath` object.
  295. *
  296. * @example Usage
  297. * ```ts
  298. * import { format } from "@std/path/posix/format";
  299. * import { assertEquals } from "@std/assert";
  300. *
  301. * const path = format({
  302. * root: "/",
  303. * dir: "/path/dir",
  304. * base: "file.txt",
  305. * ext: ".txt",
  306. * name: "file"
  307. * });
  308. * assertEquals(path, "/path/dir/file.txt");
  309. * ```
  310. *
  311. * @param pathObject The path object to format.
  312. * @returns The formatted path.
  313. */ function format(pathObject) {
  314. assertArg$2(pathObject);
  315. return _format("/", pathObject);
  316. }
  317. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
  318. // This module is browser compatible.
  319. function assertArg$1(url) {
  320. url = url instanceof URL ? url : new URL(url);
  321. if (url.protocol !== "file:") {
  322. throw new TypeError(`URL must be a file URL: received "${url.protocol}"`);
  323. }
  324. return url;
  325. }
  326. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
  327. // This module is browser compatible.
  328. /**
  329. * Converts a file URL to a path string.
  330. *
  331. * @example Usage
  332. * ```ts
  333. * import { fromFileUrl } from "@std/path/posix/from-file-url";
  334. * import { assertEquals } from "@std/assert";
  335. *
  336. * assertEquals(fromFileUrl(new URL("file:///home/foo")), "/home/foo");
  337. * ```
  338. *
  339. * @param url The file URL to convert.
  340. * @returns The path string.
  341. */ function fromFileUrl(url) {
  342. url = assertArg$1(url);
  343. return decodeURIComponent(url.pathname.replace(/%(?![0-9A-Fa-f]{2})/g, "%25"));
  344. }
  345. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
  346. // This module is browser compatible.
  347. /**
  348. * Verifies whether provided path is absolute.
  349. *
  350. * @example Usage
  351. * ```ts
  352. * import { isAbsolute } from "@std/path/posix/is-absolute";
  353. * import { assert, assertFalse } from "@std/assert";
  354. *
  355. * assert(isAbsolute("/home/user/Documents/"));
  356. * assertFalse(isAbsolute("home/user/Documents/"));
  357. * ```
  358. *
  359. * @param path The path to verify.
  360. * @returns Whether the path is absolute.
  361. */ function isAbsolute(path) {
  362. assertPath(path);
  363. return path.length > 0 && isPosixPathSeparator(path.charCodeAt(0));
  364. }
  365. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
  366. // This module is browser compatible.
  367. function assertArg(path) {
  368. assertPath(path);
  369. if (path.length === 0) return ".";
  370. }
  371. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
  372. // Copyright the Browserify authors. MIT License.
  373. // Ported from https://github.com/browserify/path-browserify/
  374. // This module is browser compatible.
  375. // Resolves . and .. elements in a path with directory names
  376. function normalizeString(path, allowAboveRoot, separator, isPathSeparator) {
  377. let res = "";
  378. let lastSegmentLength = 0;
  379. let lastSlash = -1;
  380. let dots = 0;
  381. let code;
  382. for(let i = 0; i <= path.length; ++i){
  383. if (i < path.length) code = path.charCodeAt(i);
  384. else if (isPathSeparator(code)) break;
  385. else code = CHAR_FORWARD_SLASH;
  386. if (isPathSeparator(code)) {
  387. if (lastSlash === i - 1 || dots === 1) ; else if (lastSlash !== i - 1 && dots === 2) {
  388. if (res.length < 2 || lastSegmentLength !== 2 || res.charCodeAt(res.length - 1) !== CHAR_DOT || res.charCodeAt(res.length - 2) !== CHAR_DOT) {
  389. if (res.length > 2) {
  390. const lastSlashIndex = res.lastIndexOf(separator);
  391. if (lastSlashIndex === -1) {
  392. res = "";
  393. lastSegmentLength = 0;
  394. } else {
  395. res = res.slice(0, lastSlashIndex);
  396. lastSegmentLength = res.length - 1 - res.lastIndexOf(separator);
  397. }
  398. lastSlash = i;
  399. dots = 0;
  400. continue;
  401. } else if (res.length === 2 || res.length === 1) {
  402. res = "";
  403. lastSegmentLength = 0;
  404. lastSlash = i;
  405. dots = 0;
  406. continue;
  407. }
  408. }
  409. if (allowAboveRoot) {
  410. if (res.length > 0) res += `${separator}..`;
  411. else res = "..";
  412. lastSegmentLength = 2;
  413. }
  414. } else {
  415. if (res.length > 0) res += separator + path.slice(lastSlash + 1, i);
  416. else res = path.slice(lastSlash + 1, i);
  417. lastSegmentLength = i - lastSlash - 1;
  418. }
  419. lastSlash = i;
  420. dots = 0;
  421. } else if (code === CHAR_DOT && dots !== -1) {
  422. ++dots;
  423. } else {
  424. dots = -1;
  425. }
  426. }
  427. return res;
  428. }
  429. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
  430. // This module is browser compatible.
  431. /**
  432. * Normalize the `path`, resolving `'..'` and `'.'` segments.
  433. * Note that resolving these segments does not necessarily mean that all will be eliminated.
  434. * A `'..'` at the top-level will be preserved, and an empty path is canonically `'.'`.
  435. *
  436. * @example Usage
  437. * ```ts
  438. * import { normalize } from "@std/path/posix/normalize";
  439. * import { assertEquals } from "@std/assert";
  440. *
  441. * const path = normalize("/foo/bar//baz/asdf/quux/..");
  442. * assertEquals(path, "/foo/bar/baz/asdf");
  443. * ```
  444. *
  445. * @example Working with URLs
  446. *
  447. * Note: This function will remove the double slashes from a URL's scheme.
  448. * Hence, do not pass a full URL to this function. Instead, pass the pathname of
  449. * the URL.
  450. *
  451. * ```ts
  452. * import { normalize } from "@std/path/posix/normalize";
  453. * import { assertEquals } from "@std/assert";
  454. *
  455. * const url = new URL("https://deno.land");
  456. * url.pathname = normalize("//std//assert//.//mod.ts");
  457. * assertEquals(url.href, "https://deno.land/std/assert/mod.ts");
  458. *
  459. * url.pathname = normalize("std/assert/../async/retry.ts");
  460. * assertEquals(url.href, "https://deno.land/std/async/retry.ts");
  461. * ```
  462. *
  463. * Note: If you are working with file URLs,
  464. * use the new version of `normalize` from `@std/path/posix/unstable-normalize`.
  465. *
  466. * @param path The path to normalize.
  467. * @returns The normalized path.
  468. */ function normalize(path) {
  469. assertArg(path);
  470. const isAbsolute = isPosixPathSeparator(path.charCodeAt(0));
  471. const trailingSeparator = isPosixPathSeparator(path.charCodeAt(path.length - 1));
  472. // Normalize the path
  473. path = normalizeString(path, !isAbsolute, "/", isPosixPathSeparator);
  474. if (path.length === 0 && !isAbsolute) path = ".";
  475. if (path.length > 0 && trailingSeparator) path += "/";
  476. if (isAbsolute) return `/${path}`;
  477. return path;
  478. }
  479. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
  480. // This module is browser compatible.
  481. /**
  482. * Join all given a sequence of `paths`,then normalizes the resulting path.
  483. *
  484. * @example Usage
  485. * ```ts
  486. * import { join } from "@std/path/posix/join";
  487. * import { assertEquals } from "@std/assert";
  488. *
  489. * const path = join("/foo", "bar", "baz/asdf", "quux", "..");
  490. * assertEquals(path, "/foo/bar/baz/asdf");
  491. * ```
  492. *
  493. * @example Working with URLs
  494. * ```ts
  495. * import { join } from "@std/path/posix/join";
  496. * import { assertEquals } from "@std/assert";
  497. *
  498. * const url = new URL("https://deno.land");
  499. * url.pathname = join("std", "path", "mod.ts");
  500. * assertEquals(url.href, "https://deno.land/std/path/mod.ts");
  501. *
  502. * url.pathname = join("//std", "path/", "/mod.ts");
  503. * assertEquals(url.href, "https://deno.land/std/path/mod.ts");
  504. * ```
  505. *
  506. * Note: If you are working with file URLs,
  507. * use the new version of `join` from `@std/path/posix/unstable-join`.
  508. *
  509. * @param paths The paths to join.
  510. * @returns The joined path.
  511. */ function join(...paths) {
  512. if (paths.length === 0) return ".";
  513. paths.forEach((path)=>assertPath(path));
  514. const joined = paths.filter((path)=>path.length > 0).join("/");
  515. return joined === "" ? "." : normalize(joined);
  516. }
  517. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
  518. // This module is browser compatible.
  519. /**
  520. * Return a `ParsedPath` object of the `path`.
  521. *
  522. * @example Usage
  523. * ```ts
  524. * import { parse } from "@std/path/posix/parse";
  525. * import { assertEquals } from "@std/assert";
  526. *
  527. * const path = parse("/home/user/file.txt");
  528. * assertEquals(path, {
  529. * root: "/",
  530. * dir: "/home/user",
  531. * base: "file.txt",
  532. * ext: ".txt",
  533. * name: "file"
  534. * });
  535. * ```
  536. *
  537. * @param path The path to parse.
  538. * @returns The parsed path object.
  539. */ function parse(path) {
  540. assertPath(path);
  541. const ret = {
  542. root: "",
  543. dir: "",
  544. base: "",
  545. ext: "",
  546. name: ""
  547. };
  548. if (path.length === 0) return ret;
  549. const isAbsolute = isPosixPathSeparator(path.charCodeAt(0));
  550. let start;
  551. if (isAbsolute) {
  552. ret.root = "/";
  553. start = 1;
  554. } else {
  555. start = 0;
  556. }
  557. let startDot = -1;
  558. let startPart = 0;
  559. let end = -1;
  560. let matchedSlash = true;
  561. let i = path.length - 1;
  562. // Track the state of characters (if any) we see before our first dot and
  563. // after any path separator we find
  564. let preDotState = 0;
  565. // Get non-dir info
  566. for(; i >= start; --i){
  567. const code = path.charCodeAt(i);
  568. if (isPosixPathSeparator(code)) {
  569. // If we reached a path separator that was not part of a set of path
  570. // separators at the end of the string, stop now
  571. if (!matchedSlash) {
  572. startPart = i + 1;
  573. break;
  574. }
  575. continue;
  576. }
  577. if (end === -1) {
  578. // We saw the first non-path separator, mark this as the end of our
  579. // extension
  580. matchedSlash = false;
  581. end = i + 1;
  582. }
  583. if (code === CHAR_DOT) {
  584. // If this is our first dot, mark it as the start of our extension
  585. if (startDot === -1) startDot = i;
  586. else if (preDotState !== 1) preDotState = 1;
  587. } else if (startDot !== -1) {
  588. // We saw a non-dot and non-path separator before our dot, so we should
  589. // have a good chance at having a non-empty extension
  590. preDotState = -1;
  591. }
  592. }
  593. if (startDot === -1 || end === -1 || // We saw a non-dot character immediately before the dot
  594. preDotState === 0 || // The (right-most) trimmed path component is exactly '..'
  595. preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) {
  596. if (end !== -1) {
  597. if (startPart === 0 && isAbsolute) {
  598. ret.base = ret.name = path.slice(1, end);
  599. } else {
  600. ret.base = ret.name = path.slice(startPart, end);
  601. }
  602. }
  603. // Fallback to '/' in case there is no basename
  604. ret.base = ret.base || "/";
  605. } else {
  606. if (startPart === 0 && isAbsolute) {
  607. ret.name = path.slice(1, startDot);
  608. ret.base = path.slice(1, end);
  609. } else {
  610. ret.name = path.slice(startPart, startDot);
  611. ret.base = path.slice(startPart, end);
  612. }
  613. ret.ext = path.slice(startDot, end);
  614. }
  615. if (startPart > 0) {
  616. ret.dir = stripTrailingSeparators(path.slice(0, startPart - 1), isPosixPathSeparator);
  617. } else if (isAbsolute) ret.dir = "/";
  618. return ret;
  619. }
  620. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
  621. // This module is browser compatible.
  622. /**
  623. * Resolves path segments into a `path`.
  624. *
  625. * @example Usage
  626. * ```ts
  627. * import { resolve } from "@std/path/posix/resolve";
  628. * import { assertEquals } from "@std/assert";
  629. *
  630. * const path = resolve("/foo", "bar", "baz/asdf", "quux", "..");
  631. * assertEquals(path, "/foo/bar/baz/asdf");
  632. * ```
  633. *
  634. * @param pathSegments The path segments to resolve.
  635. * @returns The resolved path.
  636. */ function resolve(...pathSegments) {
  637. let resolvedPath = "";
  638. let resolvedAbsolute = false;
  639. for(let i = pathSegments.length - 1; i >= -1 && !resolvedAbsolute; i--){
  640. let path;
  641. if (i >= 0) path = pathSegments[i];
  642. else {
  643. // deno-lint-ignore no-explicit-any
  644. const { Deno } = globalThis;
  645. if (typeof Deno?.cwd !== "function") {
  646. throw new TypeError("Resolved a relative path without a current working directory (CWD)");
  647. }
  648. path = Deno.cwd();
  649. }
  650. assertPath(path);
  651. // Skip empty entries
  652. if (path.length === 0) {
  653. continue;
  654. }
  655. resolvedPath = `${path}/${resolvedPath}`;
  656. resolvedAbsolute = isPosixPathSeparator(path.charCodeAt(0));
  657. }
  658. // At this point the path should be resolved to a full absolute path, but
  659. // handle relative paths to be safe (might happen when Deno.cwd() fails)
  660. // Normalize the path
  661. resolvedPath = normalizeString(resolvedPath, !resolvedAbsolute, "/", isPosixPathSeparator);
  662. if (resolvedAbsolute) {
  663. if (resolvedPath.length > 0) return `/${resolvedPath}`;
  664. else return "/";
  665. } else if (resolvedPath.length > 0) return resolvedPath;
  666. else return ".";
  667. }
  668. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
  669. // This module is browser compatible.
  670. function assertArgs(from, to) {
  671. assertPath(from);
  672. assertPath(to);
  673. if (from === to) return "";
  674. }
  675. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
  676. // This module is browser compatible.
  677. /**
  678. * Return the relative path from `from` to `to` based on current working directory.
  679. *
  680. * If `from` and `to` are the same, return an empty string.
  681. *
  682. * @example Usage
  683. * ```ts
  684. * import { relative } from "@std/path/posix/relative";
  685. * import { assertEquals } from "@std/assert";
  686. *
  687. * const path = relative("/data/orandea/test/aaa", "/data/orandea/impl/bbb");
  688. * assertEquals(path, "../../impl/bbb");
  689. * ```
  690. *
  691. * @param from The path to start from.
  692. * @param to The path to reach.
  693. * @returns The relative path.
  694. */ function relative(from, to) {
  695. assertArgs(from, to);
  696. from = resolve(from);
  697. to = resolve(to);
  698. if (from === to) return "";
  699. // Trim any leading backslashes
  700. let fromStart = 1;
  701. const fromEnd = from.length;
  702. for(; fromStart < fromEnd; ++fromStart){
  703. if (!isPosixPathSeparator(from.charCodeAt(fromStart))) break;
  704. }
  705. const fromLen = fromEnd - fromStart;
  706. // Trim any leading backslashes
  707. let toStart = 1;
  708. const toEnd = to.length;
  709. for(; toStart < toEnd; ++toStart){
  710. if (!isPosixPathSeparator(to.charCodeAt(toStart))) break;
  711. }
  712. const toLen = toEnd - toStart;
  713. // Compare paths to find the longest common path from root
  714. const length = fromLen < toLen ? fromLen : toLen;
  715. let lastCommonSep = -1;
  716. let i = 0;
  717. for(; i <= length; ++i){
  718. if (i === length) {
  719. if (toLen > length) {
  720. if (isPosixPathSeparator(to.charCodeAt(toStart + i))) {
  721. // We get here if `from` is the exact base path for `to`.
  722. // For example: from='/foo/bar'; to='/foo/bar/baz'
  723. return to.slice(toStart + i + 1);
  724. } else if (i === 0) {
  725. // We get here if `from` is the root
  726. // For example: from='/'; to='/foo'
  727. return to.slice(toStart + i);
  728. }
  729. } else if (fromLen > length) {
  730. if (isPosixPathSeparator(from.charCodeAt(fromStart + i))) {
  731. // We get here if `to` is the exact base path for `from`.
  732. // For example: from='/foo/bar/baz'; to='/foo/bar'
  733. lastCommonSep = i;
  734. } else if (i === 0) {
  735. // We get here if `to` is the root.
  736. // For example: from='/foo'; to='/'
  737. lastCommonSep = 0;
  738. }
  739. }
  740. break;
  741. }
  742. const fromCode = from.charCodeAt(fromStart + i);
  743. const toCode = to.charCodeAt(toStart + i);
  744. if (fromCode !== toCode) break;
  745. else if (isPosixPathSeparator(fromCode)) lastCommonSep = i;
  746. }
  747. let out = "";
  748. // Generate the relative path based on the path difference between `to`
  749. // and `from`
  750. for(i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i){
  751. if (i === fromEnd || isPosixPathSeparator(from.charCodeAt(i))) {
  752. if (out.length === 0) out += "..";
  753. else out += "/..";
  754. }
  755. }
  756. // Lastly, append the rest of the destination (`to`) path that comes after
  757. // the common path parts
  758. if (out.length > 0) return out + to.slice(toStart + lastCommonSep);
  759. else {
  760. toStart += lastCommonSep;
  761. if (isPosixPathSeparator(to.charCodeAt(toStart))) ++toStart;
  762. return to.slice(toStart);
  763. }
  764. }
  765. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
  766. // This module is browser compatible.
  767. const WHITESPACE_ENCODINGS = {
  768. "\u0009": "%09",
  769. "\u000A": "%0A",
  770. "\u000B": "%0B",
  771. "\u000C": "%0C",
  772. "\u000D": "%0D",
  773. "\u0020": "%20"
  774. };
  775. function encodeWhitespace(string) {
  776. return string.replaceAll(/[\s]/g, (c)=>{
  777. return WHITESPACE_ENCODINGS[c] ?? c;
  778. });
  779. }
  780. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
  781. // This module is browser compatible.
  782. /**
  783. * Converts a path string to a file URL.
  784. *
  785. * @example Usage
  786. * ```ts
  787. * import { toFileUrl } from "@std/path/posix/to-file-url";
  788. * import { assertEquals } from "@std/assert";
  789. *
  790. * assertEquals(toFileUrl("/home/foo"), new URL("file:///home/foo"));
  791. * assertEquals(toFileUrl("/home/foo bar"), new URL("file:///home/foo%20bar"));
  792. * ```
  793. *
  794. * @param path The path to convert.
  795. * @returns The file URL.
  796. */ function toFileUrl(path) {
  797. if (!isAbsolute(path)) {
  798. throw new TypeError(`Path must be absolute: received "${path}"`);
  799. }
  800. const url = new URL("file:///");
  801. url.pathname = encodeWhitespace(path.replace(/%/g, "%25").replace(/\\/g, "%5C"));
  802. return url;
  803. }
  804. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
  805. // This module is browser compatible.
  806. /**
  807. * Converts a path to a namespaced path. This function returns the path as is on posix.
  808. *
  809. * @example Usage
  810. * ```ts
  811. * import { toNamespacedPath } from "@std/path/posix/to-namespaced-path";
  812. * import { assertEquals } from "@std/assert";
  813. *
  814. * assertEquals(toNamespacedPath("/home/foo"), "/home/foo");
  815. * ```
  816. *
  817. * @param path The path.
  818. * @returns The namespaced path.
  819. */ function toNamespacedPath(path) {
  820. // Non-op on posix systems
  821. return path;
  822. }
  823. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
  824. // This module is browser compatible.
  825. function common$1(paths, sep) {
  826. const [first = "", ...remaining] = paths;
  827. const parts = first.split(sep);
  828. let endOfPrefix = parts.length;
  829. let append = "";
  830. for (const path of remaining){
  831. const compare = path.split(sep);
  832. if (compare.length <= endOfPrefix) {
  833. endOfPrefix = compare.length;
  834. append = "";
  835. }
  836. for(let i = 0; i < endOfPrefix; i++){
  837. if (compare[i] !== parts[i]) {
  838. endOfPrefix = i;
  839. append = i === 0 ? "" : sep;
  840. break;
  841. }
  842. }
  843. }
  844. return parts.slice(0, endOfPrefix).join(sep) + append;
  845. }
  846. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
  847. // This module is browser compatible.
  848. /** Determines the common path from a set of paths for POSIX systems.
  849. *
  850. * @example Usage
  851. * ```ts
  852. * import { common } from "@std/path/posix/common";
  853. * import { assertEquals } from "@std/assert";
  854. *
  855. * const path = common([
  856. * "./deno/std/path/mod.ts",
  857. * "./deno/std/fs/mod.ts",
  858. * ]);
  859. * assertEquals(path, "./deno/std/");
  860. * ```
  861. *
  862. * @param paths The paths to compare.
  863. * @returns The common path.
  864. */ function common(paths) {
  865. return common$1(paths, SEPARATOR);
  866. }
  867. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
  868. // This module is browser compatible.
  869. /**
  870. * Options for {@linkcode globToRegExp}, {@linkcode joinGlobs},
  871. * {@linkcode normalizeGlob} and {@linkcode expandGlob}.
  872. */ const REG_EXP_ESCAPE_CHARS = [
  873. "!",
  874. "$",
  875. "(",
  876. ")",
  877. "*",
  878. "+",
  879. ".",
  880. "=",
  881. "?",
  882. "[",
  883. "\\",
  884. "^",
  885. "{",
  886. "|"
  887. ];
  888. const RANGE_ESCAPE_CHARS = [
  889. "-",
  890. "\\",
  891. "]"
  892. ];
  893. function _globToRegExp(c, glob, { extended = true, globstar: globstarOption = true, // os = osType,
  894. caseInsensitive = false } = {}) {
  895. if (glob === "") {
  896. return /(?!)/;
  897. }
  898. // Remove trailing separators.
  899. let newLength = glob.length;
  900. for(; newLength > 1 && c.seps.includes(glob[newLength - 1]); newLength--);
  901. glob = glob.slice(0, newLength);
  902. let regExpString = "";
  903. // Terminates correctly. Trust that `j` is incremented every iteration.
  904. for(let j = 0; j < glob.length;){
  905. let segment = "";
  906. const groupStack = [];
  907. let inRange = false;
  908. let inEscape = false;
  909. let endsWithSep = false;
  910. let i = j;
  911. // Terminates with `i` at the non-inclusive end of the current segment.
  912. for(; i < glob.length && !c.seps.includes(glob[i]); i++){
  913. if (inEscape) {
  914. inEscape = false;
  915. const escapeChars = inRange ? RANGE_ESCAPE_CHARS : REG_EXP_ESCAPE_CHARS;
  916. segment += escapeChars.includes(glob[i]) ? `\\${glob[i]}` : glob[i];
  917. continue;
  918. }
  919. if (glob[i] === c.escapePrefix) {
  920. inEscape = true;
  921. continue;
  922. }
  923. if (glob[i] === "[") {
  924. if (!inRange) {
  925. inRange = true;
  926. segment += "[";
  927. if (glob[i + 1] === "!") {
  928. i++;
  929. segment += "^";
  930. } else if (glob[i + 1] === "^") {
  931. i++;
  932. segment += "\\^";
  933. }
  934. continue;
  935. } else if (glob[i + 1] === ":") {
  936. let k = i + 1;
  937. let value = "";
  938. while(glob[k + 1] !== undefined && glob[k + 1] !== ":"){
  939. value += glob[k + 1];
  940. k++;
  941. }
  942. if (glob[k + 1] === ":" && glob[k + 2] === "]") {
  943. i = k + 2;
  944. if (value === "alnum") segment += "\\dA-Za-z";
  945. else if (value === "alpha") segment += "A-Za-z";
  946. else if (value === "ascii") segment += "\x00-\x7F";
  947. else if (value === "blank") segment += "\t ";
  948. else if (value === "cntrl") segment += "\x00-\x1F\x7F";
  949. else if (value === "digit") segment += "\\d";
  950. else if (value === "graph") segment += "\x21-\x7E";
  951. else if (value === "lower") segment += "a-z";
  952. else if (value === "print") segment += "\x20-\x7E";
  953. else if (value === "punct") {
  954. segment += "!\"#$%&'()*+,\\-./:;<=>?@[\\\\\\]^_‘{|}~";
  955. } else if (value === "space") segment += "\\s\v";
  956. else if (value === "upper") segment += "A-Z";
  957. else if (value === "word") segment += "\\w";
  958. else if (value === "xdigit") segment += "\\dA-Fa-f";
  959. continue;
  960. }
  961. }
  962. }
  963. if (glob[i] === "]" && inRange) {
  964. inRange = false;
  965. segment += "]";
  966. continue;
  967. }
  968. if (inRange) {
  969. segment += glob[i];
  970. continue;
  971. }
  972. if (glob[i] === ")" && groupStack.length > 0 && groupStack[groupStack.length - 1] !== "BRACE") {
  973. segment += ")";
  974. const type = groupStack.pop();
  975. if (type === "!") {
  976. segment += c.wildcard;
  977. } else if (type !== "@") {
  978. segment += type;
  979. }
  980. continue;
  981. }
  982. if (glob[i] === "|" && groupStack.length > 0 && groupStack[groupStack.length - 1] !== "BRACE") {
  983. segment += "|";
  984. continue;
  985. }
  986. if (glob[i] === "+" && extended && glob[i + 1] === "(") {
  987. i++;
  988. groupStack.push("+");
  989. segment += "(?:";
  990. continue;
  991. }
  992. if (glob[i] === "@" && extended && glob[i + 1] === "(") {
  993. i++;
  994. groupStack.push("@");
  995. segment += "(?:";
  996. continue;
  997. }
  998. if (glob[i] === "?") {
  999. if (extended && glob[i + 1] === "(") {
  1000. i++;
  1001. groupStack.push("?");
  1002. segment += "(?:";
  1003. } else {
  1004. segment += ".";
  1005. }
  1006. continue;
  1007. }
  1008. if (glob[i] === "!" && extended && glob[i + 1] === "(") {
  1009. i++;
  1010. groupStack.push("!");
  1011. segment += "(?!";
  1012. continue;
  1013. }
  1014. if (glob[i] === "{") {
  1015. groupStack.push("BRACE");
  1016. segment += "(?:";
  1017. continue;
  1018. }
  1019. if (glob[i] === "}" && groupStack[groupStack.length - 1] === "BRACE") {
  1020. groupStack.pop();
  1021. segment += ")";
  1022. continue;
  1023. }
  1024. if (glob[i] === "," && groupStack[groupStack.length - 1] === "BRACE") {
  1025. segment += "|";
  1026. continue;
  1027. }
  1028. if (glob[i] === "*") {
  1029. if (extended && glob[i + 1] === "(") {
  1030. i++;
  1031. groupStack.push("*");
  1032. segment += "(?:";
  1033. } else {
  1034. const prevChar = glob[i - 1];
  1035. let numStars = 1;
  1036. while(glob[i + 1] === "*"){
  1037. i++;
  1038. numStars++;
  1039. }
  1040. const nextChar = glob[i + 1];
  1041. if (globstarOption && numStars === 2 && [
  1042. ...c.seps,
  1043. undefined
  1044. ].includes(prevChar) && [
  1045. ...c.seps,
  1046. undefined
  1047. ].includes(nextChar)) {
  1048. segment += c.globstar;
  1049. endsWithSep = true;
  1050. } else {
  1051. segment += c.wildcard;
  1052. }
  1053. }
  1054. continue;
  1055. }
  1056. segment += REG_EXP_ESCAPE_CHARS.includes(glob[i]) ? `\\${glob[i]}` : glob[i];
  1057. }
  1058. // Check for unclosed groups or a dangling backslash.
  1059. if (groupStack.length > 0 || inRange || inEscape) {
  1060. // Parse failure. Take all characters from this segment literally.
  1061. segment = "";
  1062. for (const c of glob.slice(j, i)){
  1063. segment += REG_EXP_ESCAPE_CHARS.includes(c) ? `\\${c}` : c;
  1064. endsWithSep = false;
  1065. }
  1066. }
  1067. regExpString += segment;
  1068. if (!endsWithSep) {
  1069. regExpString += i < glob.length ? c.sep : c.sepMaybe;
  1070. endsWithSep = true;
  1071. }
  1072. // Terminates with `i` at the start of the next segment.
  1073. while(c.seps.includes(glob[i]))i++;
  1074. j = i;
  1075. }
  1076. regExpString = `^${regExpString}$`;
  1077. return new RegExp(regExpString, caseInsensitive ? "i" : "");
  1078. }
  1079. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
  1080. // This module is browser compatible.
  1081. const constants = {
  1082. sep: "/+",
  1083. sepMaybe: "/*",
  1084. seps: [
  1085. "/"
  1086. ],
  1087. globstar: "(?:[^/]*(?:/|$)+)*",
  1088. wildcard: "[^/]*",
  1089. escapePrefix: "\\"
  1090. };
  1091. /** Convert a glob string to a regular expression.
  1092. *
  1093. * Tries to match bash glob expansion as closely as possible.
  1094. *
  1095. * Basic glob syntax:
  1096. * - `*` - Matches everything without leaving the path segment.
  1097. * - `?` - Matches any single character.
  1098. * - `{foo,bar}` - Matches `foo` or `bar`.
  1099. * - `[abcd]` - Matches `a`, `b`, `c` or `d`.
  1100. * - `[a-d]` - Matches `a`, `b`, `c` or `d`.
  1101. * - `[!abcd]` - Matches any single character besides `a`, `b`, `c` or `d`.
  1102. * - `[[:<class>:]]` - Matches any character belonging to `<class>`.
  1103. * - `[[:alnum:]]` - Matches any digit or letter.
  1104. * - `[[:digit:]abc]` - Matches any digit, `a`, `b` or `c`.
  1105. * - See https://facelessuser.github.io/wcmatch/glob/#posix-character-classes
  1106. * for a complete list of supported character classes.
  1107. * - `\` - Escapes the next character for an `os` other than `"windows"`.
  1108. * - \` - Escapes the next character for `os` set to `"windows"`.
  1109. * - `/` - Path separator.
  1110. * - `\` - Additional path separator only for `os` set to `"windows"`.
  1111. *
  1112. * Extended syntax:
  1113. * - Requires `{ extended: true }`.
  1114. * - `?(foo|bar)` - Matches 0 or 1 instance of `{foo,bar}`.
  1115. * - `@(foo|bar)` - Matches 1 instance of `{foo,bar}`. They behave the same.
  1116. * - `*(foo|bar)` - Matches _n_ instances of `{foo,bar}`.
  1117. * - `+(foo|bar)` - Matches _n > 0_ instances of `{foo,bar}`.
  1118. * - `!(foo|bar)` - Matches anything other than `{foo,bar}`.
  1119. * - See https://www.linuxjournal.com/content/bash-extended-globbing.
  1120. *
  1121. * Globstar syntax:
  1122. * - Requires `{ globstar: true }`.
  1123. * - `**` - Matches any number of any path segments.
  1124. * - Must comprise its entire path segment in the provided glob.
  1125. * - See https://www.linuxjournal.com/content/globstar-new-bash-globbing-option.
  1126. *
  1127. * Note the following properties:
  1128. * - The generated `RegExp` is anchored at both start and end.
  1129. * - Repeating and trailing separators are tolerated. Trailing separators in the
  1130. * provided glob have no meaning and are discarded.
  1131. * - Absolute globs will only match absolute paths, etc.
  1132. * - Empty globs will match nothing.
  1133. * - Any special glob syntax must be contained to one path segment. For example,
  1134. * `?(foo|bar/baz)` is invalid. The separator will take precedence and the
  1135. * first segment ends with an unclosed group.
  1136. * - If a path segment ends with unclosed groups or a dangling escape prefix, a
  1137. * parse error has occurred. Every character for that segment is taken
  1138. * literally in this event.
  1139. *
  1140. * Limitations:
  1141. * - A negative group like `!(foo|bar)` will wrongly be converted to a negative
  1142. * look-ahead followed by a wildcard. This means that `!(foo).js` will wrongly
  1143. * fail to match `foobar.js`, even though `foobar` is not `foo`. Effectively,
  1144. * `!(foo|bar)` is treated like `!(@(foo|bar)*)`. This will work correctly if
  1145. * the group occurs not nested at the end of the segment.
  1146. *
  1147. * @example Usage
  1148. * ```ts
  1149. * import { globToRegExp } from "@std/path/posix/glob-to-regexp";
  1150. * import { assertEquals } from "@std/assert";
  1151. *
  1152. * assertEquals(globToRegExp("*.js"), /^[^/]*\.js\/*$/);
  1153. * ```
  1154. *
  1155. * @param glob Glob string to convert.
  1156. * @param options Conversion options.
  1157. * @returns The regular expression equivalent to the glob.
  1158. */ function globToRegExp(glob, options = {}) {
  1159. return _globToRegExp(constants, glob, options);
  1160. }
  1161. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
  1162. // This module is browser compatible.
  1163. /**
  1164. * Test whether the given string is a glob.
  1165. *
  1166. * @example Usage
  1167. * ```ts
  1168. * import { isGlob } from "@std/path/is-glob";
  1169. * import { assert } from "@std/assert";
  1170. *
  1171. * assert(!isGlob("foo/bar/../baz"));
  1172. * assert(isGlob("foo/*ar/../baz"));
  1173. * ```
  1174. *
  1175. * @param str String to test.
  1176. * @returns `true` if the given string is a glob, otherwise `false`
  1177. */ function isGlob(str) {
  1178. const chars = {
  1179. "{": "}",
  1180. "(": ")",
  1181. "[": "]"
  1182. };
  1183. const regex = /\\(.)|(^!|\*|\?|[\].+)]\?|\[[^\\\]]+\]|\{[^\\}]+\}|\(\?[:!=][^\\)]+\)|\([^|]+\|[^\\)]+\))/;
  1184. if (str === "") {
  1185. return false;
  1186. }
  1187. let match;
  1188. while(match = regex.exec(str)){
  1189. if (match[2]) return true;
  1190. let idx = match.index + match[0].length;
  1191. // if an open bracket/brace/paren is escaped,
  1192. // set the index to the next closing character
  1193. const open = match[1];
  1194. const close = open ? chars[open] : null;
  1195. if (open && close) {
  1196. const n = str.indexOf(close, idx);
  1197. if (n !== -1) {
  1198. idx = n + 1;
  1199. }
  1200. }
  1201. str = str.slice(idx);
  1202. }
  1203. return false;
  1204. }
  1205. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
  1206. // This module is browser compatible.
  1207. /**
  1208. * Like normalize(), but doesn't collapse "**\/.." when `globstar` is true.
  1209. *
  1210. * @example Usage
  1211. * ```ts
  1212. * import { normalizeGlob } from "@std/path/posix/normalize-glob";
  1213. * import { assertEquals } from "@std/assert";
  1214. *
  1215. * const path = normalizeGlob("foo/bar/../*", { globstar: true });
  1216. * assertEquals(path, "foo/*");
  1217. * ```
  1218. *
  1219. * @param glob The glob to normalize.
  1220. * @param options The options to use.
  1221. * @returns The normalized path.
  1222. */ function normalizeGlob(glob, options = {}) {
  1223. const { globstar = false } = options;
  1224. if (glob.match(/\0/g)) {
  1225. throw new Error(`Glob contains invalid characters: "${glob}"`);
  1226. }
  1227. if (!globstar) {
  1228. return normalize(glob);
  1229. }
  1230. const s = SEPARATOR_PATTERN.source;
  1231. const badParentPattern = new RegExp(`(?<=(${s}|^)\\*\\*${s})\\.\\.(?=${s}|$)`, "g");
  1232. return normalize(glob.replace(badParentPattern, "\0")).replace(/\0/g, "..");
  1233. }
  1234. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
  1235. // This module is browser compatible.
  1236. /**
  1237. * Like join(), but doesn't collapse "**\/.." when `globstar` is true.
  1238. *
  1239. * @example Usage
  1240. * ```ts
  1241. * import { joinGlobs } from "@std/path/posix/join-globs";
  1242. * import { assertEquals } from "@std/assert";
  1243. *
  1244. * const path = joinGlobs(["foo", "bar", "**"], { globstar: true });
  1245. * assertEquals(path, "foo/bar/**");
  1246. * ```
  1247. *
  1248. * @param globs The globs to join.
  1249. * @param options The options to use.
  1250. * @returns The joined path.
  1251. */ function joinGlobs(globs, options = {}) {
  1252. const { globstar = false } = options;
  1253. if (!globstar || globs.length === 0) {
  1254. return join(...globs);
  1255. }
  1256. let joined;
  1257. for (const glob of globs){
  1258. const path = glob;
  1259. if (path.length > 0) {
  1260. if (!joined) joined = path;
  1261. else joined += `${SEPARATOR}${path}`;
  1262. }
  1263. }
  1264. if (!joined) return ".";
  1265. return normalizeGlob(joined, {
  1266. globstar
  1267. });
  1268. }
  1269. export { DELIMITER, SEPARATOR, SEPARATOR_PATTERN, basename, common, dirname, extname, format, fromFileUrl, globToRegExp, isAbsolute, isGlob, join, joinGlobs, normalize, normalizeGlob, parse, relative, resolve, toFileUrl, toNamespacedPath };