| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 97ece4a commit b7fe54f
3 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1074,6 +1074,9 @@ behavior is similar to `cp dir1/ dir2/`. | |||
| 1074 | 1074 | <!-- YAML | |
| 1075 | 1075 | added: v22.0.0 | |
| 1076 | 1076 | changes: | |
| 1077 | + - version: REPLACEME | ||
| 1078 | + pr-url: https://github.com/nodejs/node/pull/56489 | ||
| 1079 | + description: Add support for `exclude` option to accept glob patterns. | ||
| 1077 | 1080 | - version: v22.2.0 | |
| 1078 | 1081 | pr-url: https://github.com/nodejs/node/pull/52837 | |
| 1079 | 1082 | description: Add support for `withFileTypes` as an option. | |
@@ -1084,7 +1087,8 @@ changes: | |||
| 1084 | 1087 | * `pattern` {string|string\[]} | |
| 1085 | 1088 | * `options` {Object} | |
| 1086 | 1089 | * `cwd` {string} current working directory. **Default:** `process.cwd()` | |
| 1087 | - * `exclude` {Function} Function to filter out files/directories. Return | ||
| 1090 | + * `exclude` {Function|string\[]} Function to filter out files/directories or a | ||
| 1091 | + list of glob patterns to be excluded. If a function is provided, return | ||
| 1088 | 1092 | `true` to exclude the item, `false` to include it. **Default:** `undefined`. | |
| 1089 | 1093 | * `withFileTypes` {boolean} `true` if the glob should return paths as Dirents, | |
| 1090 | 1094 | `false` otherwise. **Default:** `false`. | |
@@ -3120,6 +3124,9 @@ descriptor. See [`fs.utimes()`][]. | |||
| 3120 | 3124 | <!-- YAML | |
| 3121 | 3125 | added: v22.0.0 | |
| 3122 | 3126 | changes: | |
| 3127 | + - version: REPLACEME | ||
| 3128 | + pr-url: https://github.com/nodejs/node/pull/56489 | ||
| 3129 | + description: Add support for `exclude` option to accept glob patterns. | ||
| 3123 | 3130 | - version: v22.2.0 | |
| 3124 | 3131 | pr-url: https://github.com/nodejs/node/pull/52837 | |
| 3125 | 3132 | description: Add support for `withFileTypes` as an option. | |
@@ -3131,7 +3138,8 @@ changes: | |||
| 3131 | 3138 | ||
| 3132 | 3139 | * `options` {Object} | |
| 3133 | 3140 | * `cwd` {string} current working directory. **Default:** `process.cwd()` | |
| 3134 | - * `exclude` {Function} Function to filter out files/directories. Return | ||
| 3141 | + * `exclude` {Function|string\[]} Function to filter out files/directories or a | ||
| 3142 | + list of glob patterns to be excluded. If a function is provided, return | ||
| 3135 | 3143 | `true` to exclude the item, `false` to include it. **Default:** `undefined`. | |
| 3136 | 3144 | * `withFileTypes` {boolean} `true` if the glob should return paths as Dirents, | |
| 3137 | 3145 | `false` otherwise. **Default:** `false`. | |
@@ -5656,6 +5664,9 @@ Synchronous version of [`fs.futimes()`][]. Returns `undefined`. | |||
| 5656 | 5664 | <!-- YAML | |
| 5657 | 5665 | added: v22.0.0 | |
| 5658 | 5666 | changes: | |
| 5667 | + - version: REPLACEME | ||
| 5668 | + pr-url: https://github.com/nodejs/node/pull/56489 | ||
| 5669 | + description: Add support for `exclude` option to accept glob patterns. | ||
| 5659 | 5670 | - version: v22.2.0 | |
| 5660 | 5671 | pr-url: https://github.com/nodejs/node/pull/52837 | |
| 5661 | 5672 | description: Add support for `withFileTypes` as an option. | |
@@ -5666,7 +5677,8 @@ changes: | |||
| 5666 | 5677 | * `pattern` {string|string\[]} | |
| 5667 | 5678 | * `options` {Object} | |
| 5668 | 5679 | * `cwd` {string} current working directory. **Default:** `process.cwd()` | |
| 5669 | - * `exclude` {Function} Function to filter out files/directories. Return | ||
| 5680 | + * `exclude` {Function|string\[]} Function to filter out files/directories or a | ||
| 5681 | + list of glob patterns to be excluded. If a function is provided, return | ||
| 5670 | 5682 | `true` to exclude the item, `false` to include it. **Default:** `undefined`. | |
| 5671 | 5683 | * `withFileTypes` {boolean} `true` if the glob should return paths as Dirents, | |
| 5672 | 5684 | `false` otherwise. **Default:** `false`. | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2,6 +2,7 @@ | |||
| 2 | 2 | ||
| 3 | 3 | const { | |
| 4 | 4 | ArrayFrom, | |
| 5 | + ArrayIsArray, | ||
| 5 | 6 | ArrayPrototypeAt, | |
| 6 | 7 | ArrayPrototypeFlatMap, | |
| 7 | 8 | ArrayPrototypeMap, | |
@@ -24,12 +25,18 @@ const { | |||
| 24 | 25 | isMacOS, | |
| 25 | 26 | } = require('internal/util'); | |
| 26 | 27 | const { | |
| 27 | - validateFunction, | ||
| 28 | 28 | validateObject, | |
| 29 | 29 | validateString, | |
| 30 | 30 | validateStringArray, | |
| 31 | 31 | } = require('internal/validators'); | |
| 32 | 32 | const { DirentFromStats } = require('internal/fs/utils'); | |
| 33 | + const { | ||
| 34 | + codes: { | ||
| 35 | + ERR_INVALID_ARG_TYPE, | ||
| 36 | + }, | ||
| 37 | + hideStackFrames, | ||
| 38 | + } = require('internal/errors'); | ||
| 39 | + const assert = require('internal/assert'); | ||
| 33 | 40 | ||
| 34 | 41 | let minimatch; | |
| 35 | 42 | function lazyMinimatch() { | |
@@ -63,6 +70,45 @@ function getDirentSync(path) { | |||
| 63 | 70 | return new DirentFromStats(basename(path), stat, dirname(path)); | |
| 64 | 71 | } | |
| 65 | 72 | ||
| 73 | + /** | ||
| 74 | + * @callback validateStringArrayOrFunction | ||
| 75 | + * @param {*} value | ||
| 76 | + * @param {string} name | ||
| 77 | + */ | ||
| 78 | + const validateStringArrayOrFunction = hideStackFrames((value, name) => { | ||
| 79 | + if (ArrayIsArray(value)) { | ||
| 80 | + for (let i = 0; i < value.length; ++i) { | ||
| 81 | + if (typeof value[i] !== 'string') { | ||
| 82 | + throw new ERR_INVALID_ARG_TYPE(`${name}[${i}]`, 'string', value[i]); | ||
| 83 | + } | ||
| 84 | + } | ||
| 85 | + return; | ||
| 86 | + } | ||
| 87 | + if (typeof value !== 'function') { | ||
| 88 | + throw new ERR_INVALID_ARG_TYPE(name, ['string[]', 'function'], value); | ||
| 89 | + } | ||
| 90 | + }); | ||
| 91 | + | ||
| 92 | + /** | ||
| 93 | + * @param {string} pattern | ||
| 94 | + * @param {options} options | ||
| 95 | + * @returns {Minimatch} | ||
| 96 | + */ | ||
| 97 | + function createMatcher(pattern, options = kEmptyObject) { | ||
| 98 | + const opts = { | ||
| 99 | + __proto__: null, | ||
| 100 | + nocase: isWindows || isMacOS, | ||
| 101 | + windowsPathsNoEscape: true, | ||
| 102 | + nonegate: true, | ||
| 103 | + nocomment: true, | ||
| 104 | + optimizationLevel: 2, | ||
| 105 | + platform: process.platform, | ||
| 106 | + nocaseMagicOnly: true, | ||
| 107 | + ...options, | ||
| 108 | + }; | ||
| 109 | + return new (lazyMinimatch().Minimatch)(pattern, opts); | ||
| 110 | + } | ||
| 111 | + | ||
| 66 | 112 | class Cache { | |
| 67 | 113 | #cache = new SafeMap(); | |
| 68 | 114 | #statsCache = new SafeMap(); | |
@@ -188,24 +234,56 @@ class Pattern { | |||
| 188 | 234 | } | |
| 189 | 235 | } | |
| 190 | 236 | ||
| 237 | + class ResultSet extends SafeSet { | ||
| 238 | + #root = '.'; | ||
| 239 | + #isExcluded = () => false; | ||
| 240 | + constructor(i) { super(i); } // eslint-disable-line no-useless-constructor | ||
| 241 | + | ||
| 242 | + setup(root, isExcludedFn) { | ||
| 243 | + this.#root = root; | ||
| 244 | + this.#isExcluded = isExcludedFn; | ||
| 245 | + } | ||
| 246 | + | ||
| 247 | + add(value) { | ||
| 248 | + if (this.#isExcluded(resolve(this.#root, value))) { | ||
| 249 | + return false; | ||
| 250 | + } | ||
| 251 | + super.add(value); | ||
| 252 | + return true; | ||
| 253 | + } | ||
| 254 | + } | ||
| 255 | + | ||
| 191 | 256 | class Glob { | |
| 192 | 257 | #root; | |
| 193 | 258 | #exclude; | |
| 194 | 259 | #cache = new Cache(); | |
| 195 | - #results = new SafeSet(); | ||
| 260 | + #results = new ResultSet(); | ||
| 196 | 261 | #queue = []; | |
| 197 | 262 | #subpatterns = new SafeMap(); | |
| 198 | 263 | #patterns; | |
| 199 | 264 | #withFileTypes; | |
| 265 | + #isExcluded = () => false; | ||
| 200 | 266 | constructor(pattern, options = kEmptyObject) { | |
| 201 | 267 | validateObject(options, 'options'); | |
| 202 | 268 | const { exclude, cwd, withFileTypes } = options; | |
| 203 | - if (exclude != null) { | ||
| 204 | - validateFunction(exclude, 'options.exclude'); | ||
| 205 | - } | ||
| 206 | 269 | this.#root = cwd ?? '.'; | |
| 207 | - this.#exclude = exclude; | ||
| 208 | 270 | this.#withFileTypes = !!withFileTypes; | |
| 271 | + if (exclude != null) { | ||
| 272 | + validateStringArrayOrFunction(exclude, 'options.exclude'); | ||
| 273 | + if (ArrayIsArray(exclude)) { | ||
| 274 | + assert(typeof this.#root === 'string'); | ||
| 275 | + // Convert the path part of exclude patterns to absolute paths for | ||
| 276 | + // consistent comparison before instantiating matchers. | ||
| 277 | + const matchers = exclude | ||
| 278 | + .map((pattern) => resolve(this.#root, pattern)) | ||
| 279 | + .map((pattern) => createMatcher(pattern)); | ||
| 280 | + this.#isExcluded = (value) => | ||
| 281 | + matchers.some((matcher) => matcher.match(value)); | ||
| 282 | + this.#results.setup(this.#root, this.#isExcluded); | ||
| 283 | + } else { | ||
| 284 | + this.#exclude = exclude; | ||
| 285 | + } | ||
| 286 | + } | ||
| 209 | 287 | let patterns; | |
| 210 | 288 | if (typeof pattern === 'object') { | |
| 211 | 289 | validateStringArray(pattern, 'patterns'); | |
@@ -214,17 +292,7 @@ class Glob { | |||
| 214 | 292 | validateString(pattern, 'patterns'); | |
| 215 | 293 | patterns = [pattern]; | |
| 216 | 294 | } | |
| 217 | - this.matchers = ArrayPrototypeMap(patterns, (pattern) => new (lazyMinimatch().Minimatch)(pattern, { | ||
| 218 | - __proto__: null, | ||
| 219 | - nocase: isWindows || isMacOS, | ||
| 220 | - windowsPathsNoEscape: true, | ||
| 221 | - nonegate: true, | ||
| 222 | - nocomment: true, | ||
| 223 | - optimizationLevel: 2, | ||
| 224 | - platform: process.platform, | ||
| 225 | - nocaseMagicOnly: true, | ||
| 226 | - })); | ||
| 227 | - | ||
| 295 | + this.matchers = ArrayPrototypeMap(patterns, (pattern) => createMatcher(pattern)); | ||
| 228 | 296 | this.#patterns = ArrayPrototypeFlatMap(this.matchers, (matcher) => ArrayPrototypeMap(matcher.set, | |
| 229 | 297 | (pattern, i) => new Pattern( | |
| 230 | 298 | pattern, | |
@@ -255,6 +323,9 @@ class Glob { | |||
| 255 | 323 | ); | |
| 256 | 324 | } | |
| 257 | 325 | #addSubpattern(path, pattern) { | |
| 326 | + if (this.#isExcluded(path)) { | ||
| 327 | + return; | ||
| 328 | + } | ||
| 258 | 329 | if (!this.#subpatterns.has(path)) { | |
| 259 | 330 | this.#subpatterns.set(path, [pattern]); | |
| 260 | 331 | } else { | |
@@ -273,6 +344,9 @@ class Glob { | |||
| 273 | 344 | const isLast = pattern.isLast(isDirectory); | |
| 274 | 345 | const isFirst = pattern.isFirst(); | |
| 275 | 346 | ||
| 347 | + if (this.#isExcluded(fullpath)) { | ||
| 348 | + return; | ||
| 349 | + } | ||
| 276 | 350 | if (isFirst && isWindows && typeof pattern.at(0) === 'string' && StringPrototypeEndsWith(pattern.at(0), ':')) { | |
| 277 | 351 | // Absolute path, go to root | |
| 278 | 352 | this.#addSubpattern(`${pattern.at(0)}\\`, pattern.child(new SafeSet().add(1))); | |
@@ -461,6 +535,9 @@ class Glob { | |||
| 461 | 535 | const isLast = pattern.isLast(isDirectory); | |
| 462 | 536 | const isFirst = pattern.isFirst(); | |
| 463 | 537 | ||
| 538 | + if (this.#isExcluded(fullpath)) { | ||
| 539 | + return; | ||
| 540 | + } | ||
| 464 | 541 | if (isFirst && isWindows && typeof pattern.at(0) === 'string' && StringPrototypeEndsWith(pattern.at(0), ':')) { | |
| 465 | 542 | // Absolute path, go to root | |
| 466 | 543 | this.#addSubpattern(`${pattern.at(0)}\\`, pattern.child(new SafeSet().add(1))); | |
@@ -489,8 +566,9 @@ class Glob { | |||
| 489 | 566 | if (stat && (p || isDirectory)) { | |
| 490 | 567 | const result = join(path, p); | |
| 491 | 568 | if (!this.#results.has(result)) { | |
| 492 | - this.#results.add(result); | ||
| 493 | - yield this.#withFileTypes ? stat : result; | ||
| 569 | + if (this.#results.add(result)) { | ||
| 570 | + yield this.#withFileTypes ? stat : result; | ||
| 571 | + } | ||
| 494 | 572 | } | |
| 495 | 573 | } | |
| 496 | 574 | if (pattern.indexes.size === 1 && pattern.indexes.has(last)) { | |
@@ -501,8 +579,9 @@ class Glob { | |||
| 501 | 579 | // If pattern ends with **, add to results | |
| 502 | 580 | // if path is ".", add it only if pattern starts with "." or pattern is exactly "**" | |
| 503 | 581 | if (!this.#results.has(path)) { | |
| 504 | - this.#results.add(path); | ||
| 505 | - yield this.#withFileTypes ? stat : path; | ||
| 582 | + if (this.#results.add(path)) { | ||
| 583 | + yield this.#withFileTypes ? stat : path; | ||
| 584 | + } | ||
| 506 | 585 | } | |
| 507 | 586 | } | |
| 508 | 587 | ||
@@ -551,8 +630,9 @@ class Glob { | |||
| 551 | 630 | } else if (!fromSymlink && index === last) { | |
| 552 | 631 | // If ** is last, add to results | |
| 553 | 632 | if (!this.#results.has(entryPath)) { | |
| 554 | - this.#results.add(entryPath); | ||
| 555 | - yield this.#withFileTypes ? entry : entryPath; | ||
| 633 | + if (this.#results.add(entryPath)) { | ||
| 634 | + yield this.#withFileTypes ? entry : entryPath; | ||
| 635 | + } | ||
| 556 | 636 | } | |
| 557 | 637 | } | |
| 558 | 638 | ||
@@ -562,8 +642,9 @@ class Glob { | |||
| 562 | 642 | if (nextMatches && nextIndex === last && !isLast) { | |
| 563 | 643 | // If next pattern is the last one, add to results | |
| 564 | 644 | if (!this.#results.has(entryPath)) { | |
| 565 | - this.#results.add(entryPath); | ||
| 566 | - yield this.#withFileTypes ? entry : entryPath; | ||
| 645 | + if (this.#results.add(entryPath)) { | ||
| 646 | + yield this.#withFileTypes ? entry : entryPath; | ||
| 647 | + } | ||
| 567 | 648 | } | |
| 568 | 649 | } else if (nextMatches && entry.isDirectory()) { | |
| 569 | 650 | // Pattern matched, meaning two patterns forward | |
@@ -598,15 +679,17 @@ class Glob { | |||
| 598 | 679 | if (!this.#cache.seen(path, pattern, nextIndex)) { | |
| 599 | 680 | this.#cache.add(path, pattern.child(new SafeSet().add(nextIndex))); | |
| 600 | 681 | if (!this.#results.has(path)) { | |
| 601 | - this.#results.add(path); | ||
| 602 | - yield this.#withFileTypes ? this.#cache.statSync(fullpath) : path; | ||
| 682 | + if (this.#results.add(path)) { | ||
| 683 | + yield this.#withFileTypes ? this.#cache.statSync(fullpath) : path; | ||
| 684 | + } | ||
| 603 | 685 | } | |
| 604 | 686 | } | |
| 605 | 687 | if (!this.#cache.seen(path, pattern, nextIndex) || !this.#cache.seen(parent, pattern, nextIndex)) { | |
| 606 | 688 | this.#cache.add(parent, pattern.child(new SafeSet().add(nextIndex))); | |
| 607 | 689 | if (!this.#results.has(parent)) { | |
| 608 | - this.#results.add(parent); | ||
| 609 | - yield this.#withFileTypes ? this.#cache.statSync(join(this.#root, parent)) : parent; | ||
| 690 | + if (this.#results.add(parent)) { | ||
| 691 | + yield this.#withFileTypes ? this.#cache.statSync(join(this.#root, parent)) : parent; | ||
| 692 | + } | ||
| 610 | 693 | } | |
| 611 | 694 | } | |
| 612 | 695 | } | |
@@ -621,8 +704,9 @@ class Glob { | |||
| 621 | 704 | // If current pattern is ".", proceed to test next pattern | |
| 622 | 705 | if (nextIndex === last) { | |
| 623 | 706 | if (!this.#results.has(entryPath)) { | |
| 624 | - this.#results.add(entryPath); | ||
| 625 | - yield this.#withFileTypes ? entry : entryPath; | ||
| 707 | + if (this.#results.add(entryPath)) { | ||
| 708 | + yield this.#withFileTypes ? entry : entryPath; | ||
| 709 | + } | ||
| 626 | 710 | } | |
| 627 | 711 | } else { | |
| 628 | 712 | subPatterns.add(nextIndex + 1); | |
@@ -634,8 +718,9 @@ class Glob { | |||
| 634 | 718 | // add next pattern to potential patterns, or to results if it's the last pattern | |
| 635 | 719 | if (index === last) { | |
| 636 | 720 | if (!this.#results.has(entryPath)) { | |
| 637 | - this.#results.add(entryPath); | ||
| 638 | - yield this.#withFileTypes ? entry : entryPath; | ||
| 721 | + if (this.#results.add(entryPath)) { | ||
| 722 | + yield this.#withFileTypes ? entry : entryPath; | ||
| 723 | + } | ||
| 639 | 724 | } | |
| 640 | 725 | } else if (entry.isDirectory()) { | |
| 641 | 726 | subPatterns.add(nextIndex); | |
| Back | FazBrowse Home | New Git URL |
0 commit comments