提交学习笔记专用
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

349 lines
14 KiB

  1. //#region rolldown:runtime
  2. var __create = Object.create;
  3. var __defProp = Object.defineProperty;
  4. var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
  5. var __getOwnPropNames = Object.getOwnPropertyNames;
  6. var __getProtoOf = Object.getPrototypeOf;
  7. var __hasOwnProp = Object.prototype.hasOwnProperty;
  8. var __copyProps = (to, from, except, desc) => {
  9. if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
  10. key = keys[i];
  11. if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
  12. get: ((k) => from[k]).bind(null, key),
  13. enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
  14. });
  15. }
  16. return to;
  17. };
  18. var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
  19. value: mod,
  20. enumerable: true
  21. }) : target, mod));
  22. //#endregion
  23. let fs = require("fs");
  24. fs = __toESM(fs);
  25. let path = require("path");
  26. path = __toESM(path);
  27. let url = require("url");
  28. url = __toESM(url);
  29. let fdir = require("fdir");
  30. fdir = __toESM(fdir);
  31. let picomatch = require("picomatch");
  32. picomatch = __toESM(picomatch);
  33. //#region src/utils.ts
  34. const isReadonlyArray = Array.isArray;
  35. const isWin = process.platform === "win32";
  36. const ONLY_PARENT_DIRECTORIES = /^(\/?\.\.)+$/;
  37. function getPartialMatcher(patterns, options = {}) {
  38. const patternsCount = patterns.length;
  39. const patternsParts = Array(patternsCount);
  40. const matchers = Array(patternsCount);
  41. const globstarEnabled = !options.noglobstar;
  42. for (let i = 0; i < patternsCount; i++) {
  43. const parts = splitPattern(patterns[i]);
  44. patternsParts[i] = parts;
  45. const partsCount = parts.length;
  46. const partMatchers = Array(partsCount);
  47. for (let j = 0; j < partsCount; j++) partMatchers[j] = (0, picomatch.default)(parts[j], options);
  48. matchers[i] = partMatchers;
  49. }
  50. return (input) => {
  51. const inputParts = input.split("/");
  52. if (inputParts[0] === ".." && ONLY_PARENT_DIRECTORIES.test(input)) return true;
  53. for (let i = 0; i < patterns.length; i++) {
  54. const patternParts = patternsParts[i];
  55. const matcher = matchers[i];
  56. const inputPatternCount = inputParts.length;
  57. const minParts = Math.min(inputPatternCount, patternParts.length);
  58. let j = 0;
  59. while (j < minParts) {
  60. const part = patternParts[j];
  61. if (part.includes("/")) return true;
  62. const match = matcher[j](inputParts[j]);
  63. if (!match) break;
  64. if (globstarEnabled && part === "**") return true;
  65. j++;
  66. }
  67. if (j === inputPatternCount) return true;
  68. }
  69. return false;
  70. };
  71. }
  72. /* node:coverage ignore next 2 */
  73. const WIN32_ROOT_DIR = /^[A-Z]:\/$/i;
  74. const isRoot = isWin ? (p) => WIN32_ROOT_DIR.test(p) : (p) => p === "/";
  75. function buildFormat(cwd, root, absolute) {
  76. if (cwd === root || root.startsWith(`${cwd}/`)) {
  77. if (absolute) {
  78. const start = isRoot(cwd) ? cwd.length : cwd.length + 1;
  79. return (p, isDir) => p.slice(start, isDir ? -1 : void 0) || ".";
  80. }
  81. const prefix = root.slice(cwd.length + 1);
  82. if (prefix) return (p, isDir) => {
  83. if (p === ".") return prefix;
  84. const result = `${prefix}/${p}`;
  85. return isDir ? result.slice(0, -1) : result;
  86. };
  87. return (p, isDir) => isDir && p !== "." ? p.slice(0, -1) : p;
  88. }
  89. if (absolute) return (p) => path.posix.relative(cwd, p) || ".";
  90. return (p) => path.posix.relative(cwd, `${root}/${p}`) || ".";
  91. }
  92. function buildRelative(cwd, root) {
  93. if (root.startsWith(`${cwd}/`)) {
  94. const prefix = root.slice(cwd.length + 1);
  95. return (p) => `${prefix}/${p}`;
  96. }
  97. return (p) => {
  98. const result = path.posix.relative(cwd, `${root}/${p}`);
  99. if (p.endsWith("/") && result !== "") return `${result}/`;
  100. return result || ".";
  101. };
  102. }
  103. const splitPatternOptions = { parts: true };
  104. function splitPattern(path$2) {
  105. var _result$parts;
  106. const result = picomatch.default.scan(path$2, splitPatternOptions);
  107. return ((_result$parts = result.parts) === null || _result$parts === void 0 ? void 0 : _result$parts.length) ? result.parts : [path$2];
  108. }
  109. const ESCAPED_WIN32_BACKSLASHES = /\\(?![()[\]{}!+@])/g;
  110. function convertPosixPathToPattern(path$2) {
  111. return escapePosixPath(path$2);
  112. }
  113. function convertWin32PathToPattern(path$2) {
  114. return escapeWin32Path(path$2).replace(ESCAPED_WIN32_BACKSLASHES, "/");
  115. }
  116. /**
  117. * Converts a path to a pattern depending on the platform.
  118. * Identical to {@link escapePath} on POSIX systems.
  119. * @see {@link https://superchupu.dev/tinyglobby/documentation#convertPathToPattern}
  120. */
  121. /* node:coverage ignore next 3 */
  122. const convertPathToPattern = isWin ? convertWin32PathToPattern : convertPosixPathToPattern;
  123. const POSIX_UNESCAPED_GLOB_SYMBOLS = /(?<!\\)([()[\]{}*?|]|^!|[!+@](?=\()|\\(?![()[\]{}!*+?@|]))/g;
  124. const WIN32_UNESCAPED_GLOB_SYMBOLS = /(?<!\\)([()[\]{}]|^!|[!+@](?=\())/g;
  125. const escapePosixPath = (path$2) => path$2.replace(POSIX_UNESCAPED_GLOB_SYMBOLS, "\\$&");
  126. const escapeWin32Path = (path$2) => path$2.replace(WIN32_UNESCAPED_GLOB_SYMBOLS, "\\$&");
  127. /**
  128. * Escapes a path's special characters depending on the platform.
  129. * @see {@link https://superchupu.dev/tinyglobby/documentation#escapePath}
  130. */
  131. /* node:coverage ignore next */
  132. const escapePath = isWin ? escapeWin32Path : escapePosixPath;
  133. /**
  134. * Checks if a pattern has dynamic parts.
  135. *
  136. * Has a few minor differences with [`fast-glob`](https://github.com/mrmlnc/fast-glob) for better accuracy:
  137. *
  138. * - Doesn't necessarily return `false` on patterns that include `\`.
  139. * - Returns `true` if the pattern includes parentheses, regardless of them representing one single pattern or not.
  140. * - Returns `true` for unfinished glob extensions i.e. `(h`, `+(h`.
  141. * - Returns `true` for unfinished brace expansions as long as they include `,` or `..`.
  142. *
  143. * @see {@link https://superchupu.dev/tinyglobby/documentation#isDynamicPattern}
  144. */
  145. function isDynamicPattern(pattern, options) {
  146. if ((options === null || options === void 0 ? void 0 : options.caseSensitiveMatch) === false) return true;
  147. const scan = picomatch.default.scan(pattern);
  148. return scan.isGlob || scan.negated;
  149. }
  150. function log(...tasks) {
  151. console.log(`[tinyglobby ${(/* @__PURE__ */ new Date()).toLocaleTimeString("es")}]`, ...tasks);
  152. }
  153. //#endregion
  154. //#region src/index.ts
  155. const PARENT_DIRECTORY = /^(\/?\.\.)+/;
  156. const ESCAPING_BACKSLASHES = /\\(?=[()[\]{}!*+?@|])/g;
  157. const BACKSLASHES = /\\/g;
  158. function normalizePattern(pattern, expandDirectories, cwd, props, isIgnore) {
  159. let result = pattern;
  160. if (pattern.endsWith("/")) result = pattern.slice(0, -1);
  161. if (!result.endsWith("*") && expandDirectories) result += "/**";
  162. const escapedCwd = escapePath(cwd);
  163. if (path.default.isAbsolute(result.replace(ESCAPING_BACKSLASHES, ""))) result = path.posix.relative(escapedCwd, result);
  164. else result = path.posix.normalize(result);
  165. const parentDirectoryMatch = PARENT_DIRECTORY.exec(result);
  166. const parts = splitPattern(result);
  167. if (parentDirectoryMatch === null || parentDirectoryMatch === void 0 ? void 0 : parentDirectoryMatch[0]) {
  168. const n = (parentDirectoryMatch[0].length + 1) / 3;
  169. let i = 0;
  170. const cwdParts = escapedCwd.split("/");
  171. while (i < n && parts[i + n] === cwdParts[cwdParts.length + i - n]) {
  172. result = result.slice(0, (n - i - 1) * 3) + result.slice((n - i) * 3 + parts[i + n].length + 1) || ".";
  173. i++;
  174. }
  175. const potentialRoot = path.posix.join(cwd, parentDirectoryMatch[0].slice(i * 3));
  176. if (!potentialRoot.startsWith(".") && props.root.length > potentialRoot.length) {
  177. props.root = potentialRoot;
  178. props.depthOffset = -n + i;
  179. }
  180. }
  181. if (!isIgnore && props.depthOffset >= 0) {
  182. var _props$commonPath;
  183. (_props$commonPath = props.commonPath) !== null && _props$commonPath !== void 0 || (props.commonPath = parts);
  184. const newCommonPath = [];
  185. const length = Math.min(props.commonPath.length, parts.length);
  186. for (let i = 0; i < length; i++) {
  187. const part = parts[i];
  188. if (part === "**" && !parts[i + 1]) {
  189. newCommonPath.pop();
  190. break;
  191. }
  192. if (part !== props.commonPath[i] || isDynamicPattern(part) || i === parts.length - 1) break;
  193. newCommonPath.push(part);
  194. }
  195. props.depthOffset = newCommonPath.length;
  196. props.commonPath = newCommonPath;
  197. props.root = newCommonPath.length > 0 ? path.posix.join(cwd, ...newCommonPath) : cwd;
  198. }
  199. return result;
  200. }
  201. function processPatterns({ patterns = ["**/*"], ignore = [], expandDirectories = true }, cwd, props) {
  202. if (typeof patterns === "string") patterns = [patterns];
  203. if (typeof ignore === "string") ignore = [ignore];
  204. const matchPatterns = [];
  205. const ignorePatterns = [];
  206. for (const pattern of ignore) {
  207. if (!pattern) continue;
  208. if (pattern[0] !== "!" || pattern[1] === "(") ignorePatterns.push(normalizePattern(pattern, expandDirectories, cwd, props, true));
  209. }
  210. for (const pattern of patterns) {
  211. if (!pattern) continue;
  212. if (pattern[0] !== "!" || pattern[1] === "(") matchPatterns.push(normalizePattern(pattern, expandDirectories, cwd, props, false));
  213. else if (pattern[1] !== "!" || pattern[2] === "(") ignorePatterns.push(normalizePattern(pattern.slice(1), expandDirectories, cwd, props, true));
  214. }
  215. return {
  216. match: matchPatterns,
  217. ignore: ignorePatterns
  218. };
  219. }
  220. function formatPaths(paths, relative) {
  221. for (let i = paths.length - 1; i >= 0; i--) {
  222. const path$2 = paths[i];
  223. paths[i] = relative(path$2);
  224. }
  225. return paths;
  226. }
  227. function normalizeCwd(cwd) {
  228. if (!cwd) return process.cwd().replace(BACKSLASHES, "/");
  229. if (cwd instanceof URL) return (0, url.fileURLToPath)(cwd).replace(BACKSLASHES, "/");
  230. return path.default.resolve(cwd).replace(BACKSLASHES, "/");
  231. }
  232. function getCrawler(patterns, inputOptions = {}) {
  233. const options = process.env.TINYGLOBBY_DEBUG ? {
  234. ...inputOptions,
  235. debug: true
  236. } : inputOptions;
  237. const cwd = normalizeCwd(options.cwd);
  238. if (options.debug) log("globbing with:", {
  239. patterns,
  240. options,
  241. cwd
  242. });
  243. if (Array.isArray(patterns) && patterns.length === 0) return [{
  244. sync: () => [],
  245. withPromise: async () => []
  246. }, false];
  247. const props = {
  248. root: cwd,
  249. commonPath: null,
  250. depthOffset: 0
  251. };
  252. const processed = processPatterns({
  253. ...options,
  254. patterns
  255. }, cwd, props);
  256. if (options.debug) log("internal processing patterns:", processed);
  257. const matchOptions = {
  258. dot: options.dot,
  259. nobrace: options.braceExpansion === false,
  260. nocase: options.caseSensitiveMatch === false,
  261. noextglob: options.extglob === false,
  262. noglobstar: options.globstar === false,
  263. posix: true
  264. };
  265. const matcher = (0, picomatch.default)(processed.match, {
  266. ...matchOptions,
  267. ignore: processed.ignore
  268. });
  269. const ignore = (0, picomatch.default)(processed.ignore, matchOptions);
  270. const partialMatcher = getPartialMatcher(processed.match, matchOptions);
  271. const format = buildFormat(cwd, props.root, options.absolute);
  272. const formatExclude = options.absolute ? format : buildFormat(cwd, props.root, true);
  273. const fdirOptions = {
  274. filters: [options.debug ? (p, isDirectory) => {
  275. const path$2 = format(p, isDirectory);
  276. const matches = matcher(path$2);
  277. if (matches) log(`matched ${path$2}`);
  278. return matches;
  279. } : (p, isDirectory) => matcher(format(p, isDirectory))],
  280. exclude: options.debug ? (_, p) => {
  281. const relativePath = formatExclude(p, true);
  282. const skipped = relativePath !== "." && !partialMatcher(relativePath) || ignore(relativePath);
  283. if (skipped) log(`skipped ${p}`);
  284. else log(`crawling ${p}`);
  285. return skipped;
  286. } : (_, p) => {
  287. const relativePath = formatExclude(p, true);
  288. return relativePath !== "." && !partialMatcher(relativePath) || ignore(relativePath);
  289. },
  290. fs: options.fs ? {
  291. readdir: options.fs.readdir || fs.default.readdir,
  292. readdirSync: options.fs.readdirSync || fs.default.readdirSync,
  293. realpath: options.fs.realpath || fs.default.realpath,
  294. realpathSync: options.fs.realpathSync || fs.default.realpathSync,
  295. stat: options.fs.stat || fs.default.stat,
  296. statSync: options.fs.statSync || fs.default.statSync
  297. } : void 0,
  298. pathSeparator: "/",
  299. relativePaths: true,
  300. resolveSymlinks: true,
  301. signal: options.signal
  302. };
  303. if (options.deep !== void 0) fdirOptions.maxDepth = Math.round(options.deep - props.depthOffset);
  304. if (options.absolute) {
  305. fdirOptions.relativePaths = false;
  306. fdirOptions.resolvePaths = true;
  307. fdirOptions.includeBasePath = true;
  308. }
  309. if (options.followSymbolicLinks === false) {
  310. fdirOptions.resolveSymlinks = false;
  311. fdirOptions.excludeSymlinks = true;
  312. }
  313. if (options.onlyDirectories) {
  314. fdirOptions.excludeFiles = true;
  315. fdirOptions.includeDirs = true;
  316. } else if (options.onlyFiles === false) fdirOptions.includeDirs = true;
  317. props.root = props.root.replace(BACKSLASHES, "");
  318. const root = props.root;
  319. if (options.debug) log("internal properties:", props);
  320. const relative = cwd !== root && !options.absolute && buildRelative(cwd, props.root);
  321. return [new fdir.fdir(fdirOptions).crawl(root), relative];
  322. }
  323. async function glob(patternsOrOptions, options) {
  324. if (patternsOrOptions && (options === null || options === void 0 ? void 0 : options.patterns)) throw new Error("Cannot pass patterns as both an argument and an option");
  325. const isModern = isReadonlyArray(patternsOrOptions) || typeof patternsOrOptions === "string";
  326. const opts = isModern ? options : patternsOrOptions;
  327. const patterns = isModern ? patternsOrOptions : patternsOrOptions.patterns;
  328. const [crawler, relative] = getCrawler(patterns, opts);
  329. if (!relative) return crawler.withPromise();
  330. return formatPaths(await crawler.withPromise(), relative);
  331. }
  332. function globSync(patternsOrOptions, options) {
  333. if (patternsOrOptions && (options === null || options === void 0 ? void 0 : options.patterns)) throw new Error("Cannot pass patterns as both an argument and an option");
  334. const isModern = isReadonlyArray(patternsOrOptions) || typeof patternsOrOptions === "string";
  335. const opts = isModern ? options : patternsOrOptions;
  336. const patterns = isModern ? patternsOrOptions : patternsOrOptions.patterns;
  337. const [crawler, relative] = getCrawler(patterns, opts);
  338. if (!relative) return crawler.sync();
  339. return formatPaths(crawler.sync(), relative);
  340. }
  341. //#endregion
  342. exports.convertPathToPattern = convertPathToPattern;
  343. exports.escapePath = escapePath;
  344. exports.glob = glob;
  345. exports.globSync = globSync;
  346. exports.isDynamicPattern = isDynamicPattern;