From 89a99643548dfd2db4253d6341316e899c5eafbc Mon Sep 17 00:00:00 2001 From: confused-Techie Date: Mon, 6 Jan 2025 18:04:04 -0800 Subject: [PATCH 01/20] Manual decaf `chunked-executor.js` --- src/chunked-executor.js | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/chunked-executor.js b/src/chunked-executor.js index 3377c09..77f9dcf 100644 --- a/src/chunked-executor.js +++ b/src/chunked-executor.js @@ -1,11 +1,3 @@ -/* - * decaffeinate suggestions: - * DS101: Remove unnecessary use of Array.from - * DS102: Remove unnecessary code created because of implicit returns - * DS202: Simplify dynamic range loops - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md - */ -let ChunkedExecutor; const MAX_CONCURRENT_CHUNK = 20; // Public: {ChunkedExecutor} will execute on an {Array} paths in a pathQueue only @@ -27,7 +19,7 @@ const MAX_CONCURRENT_CHUNK = 20; // executor.push '/path/to/lastone.coffee' // ``` module.exports = -(ChunkedExecutor = class ChunkedExecutor { +class ChunkedExecutor { // Construct a {ChunkedExecutor} // @@ -93,4 +85,4 @@ module.exports = this.executeNextPathIfPossible(); if (this.pathCount === 0) { return this.doneCallback(); } } -}); +} From 441a905e6a331782dfe155ba6f2de7256d758f37 Mon Sep 17 00:00:00 2001 From: confused-Techie Date: Mon, 6 Jan 2025 18:13:38 -0800 Subject: [PATCH 02/20] Manual decaf `chunked-line-reader.js` --- src/chunked-line-reader.js | 40 +++++++++++--------------------------- 1 file changed, 11 insertions(+), 29 deletions(-) diff --git a/src/chunked-line-reader.js b/src/chunked-line-reader.js index 6fe850f..e2f8868 100644 --- a/src/chunked-line-reader.js +++ b/src/chunked-line-reader.js @@ -1,12 +1,3 @@ -/* - * decaffeinate suggestions: - * DS002: Fix invalid constructor - * DS102: Remove unnecessary code created because of implicit returns - * DS206: Consider reworking classes to avoid initClass - * DS207: Consider shorter variations of null checks - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md - */ -let ChunkedLineReader; const fs = require("fs"); const isBinaryFile = require("isbinaryfile"); const {Readable} = require('stream'); @@ -32,24 +23,20 @@ const lastIndexOf = function(buffer, length, char) { // This will collect all the lines in the file, or you can process each line in // the data handler for more efficiency. module.exports = -(ChunkedLineReader = (function() { - ChunkedLineReader = class ChunkedLineReader extends Readable { - static initClass() { +class ChunkedLineReader extends Readable { + constructor(filePath, options) { + super(); + this.encoding = options?.encoding ?? "utf8"; + this.filePath = filePath; this.CHUNK_SIZE = 10240; this.chunkedBuffer = null; this.headerBuffer = new Buffer(256); } - constructor(filePath, options) { - this.encoding = (options != null ? options.encoding : undefined) != null ? (options != null ? options.encoding : undefined) : "utf8"; - super(); - this.filePath = filePath; - } - isBinaryFile() { const fd = fs.openSync(this.filePath, "r"); - const isBin = isBinaryFile(this.constructor.headerBuffer, fs.readSync(fd, this.constructor.headerBuffer, 0, 256)); + const isBin = isBinaryFile(this.headerBuffer, fs.readSync(fd, this.headerBuffer, 0, 256)); fs.closeSync(fd); return isBin; } @@ -61,13 +48,11 @@ module.exports = const line = 0; let offset = 0; let remainder = ''; - const chunkSize = this.constructor.CHUNK_SIZE; - if (isBinaryFile(this.constructor.headerBuffer, fs.readSync(fd, this.constructor.headerBuffer, 0, 256))) { return; } + const chunkSize = this.CHUNK_SIZE; + if (isBinaryFile(this.headerBuffer, fs.readSync(fd, this.headerBuffer, 0, 256))) { return; } - if (this.constructor.chunkedBuffer == null) { this.constructor.chunkedBuffer = new Buffer(chunkSize); } - const { - chunkedBuffer - } = this.constructor; + if (this.chunkedBuffer == null) { this.chunkedBuffer = new Buffer(chunkSize); } + const chunkedBuffer = this.chunkedBuffer; let bytesRead = fs.readSync(fd, chunkedBuffer, 0, chunkSize, 0); const decoder = new StringDecoder(this.encoding); @@ -113,7 +98,4 @@ module.exports = this.push(null); } } - }; - ChunkedLineReader.initClass(); - return ChunkedLineReader; -})()); + } From b3fa2fd88e5721a5da8a9dec2c046568b4de866c Mon Sep 17 00:00:00 2001 From: confused-Techie Date: Mon, 6 Jan 2025 18:14:01 -0800 Subject: [PATCH 03/20] Format `chunked-line-reader.js` --- src/chunked-line-reader.js | 124 ++++++++++++++++++------------------- 1 file changed, 62 insertions(+), 62 deletions(-) diff --git a/src/chunked-line-reader.js b/src/chunked-line-reader.js index e2f8868..8b7c263 100644 --- a/src/chunked-line-reader.js +++ b/src/chunked-line-reader.js @@ -24,78 +24,78 @@ const lastIndexOf = function(buffer, length, char) { // the data handler for more efficiency. module.exports = class ChunkedLineReader extends Readable { - constructor(filePath, options) { - super(); - this.encoding = options?.encoding ?? "utf8"; - this.filePath = filePath; + constructor(filePath, options) { + super(); + this.encoding = options?.encoding ?? "utf8"; + this.filePath = filePath; - this.CHUNK_SIZE = 10240; - this.chunkedBuffer = null; - this.headerBuffer = new Buffer(256); - } - - isBinaryFile() { - const fd = fs.openSync(this.filePath, "r"); - const isBin = isBinaryFile(this.headerBuffer, fs.readSync(fd, this.headerBuffer, 0, 256)); - fs.closeSync(fd); - return isBin; - } - - _read() { - let fd; - try { - fd = fs.openSync(this.filePath, "r"); - const line = 0; - let offset = 0; - let remainder = ''; - const chunkSize = this.CHUNK_SIZE; - if (isBinaryFile(this.headerBuffer, fs.readSync(fd, this.headerBuffer, 0, 256))) { return; } + this.CHUNK_SIZE = 10240; + this.chunkedBuffer = null; + this.headerBuffer = new Buffer(256); + } - if (this.chunkedBuffer == null) { this.chunkedBuffer = new Buffer(chunkSize); } - const chunkedBuffer = this.chunkedBuffer; - let bytesRead = fs.readSync(fd, chunkedBuffer, 0, chunkSize, 0); - const decoder = new StringDecoder(this.encoding); + isBinaryFile() { + const fd = fs.openSync(this.filePath, "r"); + const isBin = isBinaryFile(this.headerBuffer, fs.readSync(fd, this.headerBuffer, 0, 256)); + fs.closeSync(fd); + return isBin; + } - while (bytesRead) { - // Scary looking. Uses very few new objects - var newRemainder, str; - const char = 10; - const index = lastIndexOf(chunkedBuffer, bytesRead, char); + _read() { + let fd; + try { + fd = fs.openSync(this.filePath, "r"); + const line = 0; + let offset = 0; + let remainder = ''; + const chunkSize = this.CHUNK_SIZE; + if (isBinaryFile(this.headerBuffer, fs.readSync(fd, this.headerBuffer, 0, 256))) { return; } - if (index < 0) { - // no newlines here, the whole thing is a remainder - newRemainder = decoder.write(chunkedBuffer.slice(0, bytesRead)); - str = null; - } else if ((index > -1) && (index === (bytesRead - 1))) { - // the last char is a newline - newRemainder = ''; - str = decoder.write(chunkedBuffer.slice(0, bytesRead)); - } else { - str = decoder.write(chunkedBuffer.slice(0, index+1)); - newRemainder = decoder.write(chunkedBuffer.slice(index+1, bytesRead)); - } + if (this.chunkedBuffer == null) { this.chunkedBuffer = new Buffer(chunkSize); } + const chunkedBuffer = this.chunkedBuffer; + let bytesRead = fs.readSync(fd, chunkedBuffer, 0, chunkSize, 0); + const decoder = new StringDecoder(this.encoding); - if (str) { - if (remainder) { str = remainder + str; } - this.push(str); - remainder = newRemainder; - } else { - remainder = remainder + newRemainder; - } + while (bytesRead) { + // Scary looking. Uses very few new objects + var newRemainder, str; + const char = 10; + const index = lastIndexOf(chunkedBuffer, bytesRead, char); - offset += bytesRead; - bytesRead = fs.readSync(fd, chunkedBuffer, 0, chunkSize, offset); + if (index < 0) { + // no newlines here, the whole thing is a remainder + newRemainder = decoder.write(chunkedBuffer.slice(0, bytesRead)); + str = null; + } else if ((index > -1) && (index === (bytesRead - 1))) { + // the last char is a newline + newRemainder = ''; + str = decoder.write(chunkedBuffer.slice(0, bytesRead)); + } else { + str = decoder.write(chunkedBuffer.slice(0, index+1)); + newRemainder = decoder.write(chunkedBuffer.slice(index+1, bytesRead)); } - if (remainder) { return this.push(remainder); } + if (str) { + if (remainder) { str = remainder + str; } + this.push(str); + remainder = newRemainder; + } else { + remainder = remainder + newRemainder; + } - } catch (error) { - return this.emit('error', error); + offset += bytesRead; + bytesRead = fs.readSync(fd, chunkedBuffer, 0, chunkSize, offset); } - finally { - if (fd != null) { fs.closeSync(fd); } - this.push(null); - } + if (remainder) { return this.push(remainder); } + + } catch (error) { + return this.emit('error', error); + } + + finally { + if (fd != null) { fs.closeSync(fd); } + this.push(null); } } +} From 00e18c13f29cd324fa7eddebb443b239444a1d2e Mon Sep 17 00:00:00 2001 From: confused-Techie Date: Mon, 6 Jan 2025 18:14:54 -0800 Subject: [PATCH 04/20] Manual decaf `chunked-scanner.js` --- src/chunked-scanner.js | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/chunked-scanner.js b/src/chunked-scanner.js index 90a0c2f..2404e93 100644 --- a/src/chunked-scanner.js +++ b/src/chunked-scanner.js @@ -1,17 +1,10 @@ -/* - * decaffeinate suggestions: - * DS002: Fix invalid constructor - * DS102: Remove unnecessary code created because of implicit returns - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md - */ -let ChunkedScanner; const ChunkedExecutor = require('./chunked-executor'); module.exports = -(ChunkedScanner = class ChunkedScanner extends ChunkedExecutor { +class ChunkedScanner extends ChunkedExecutor { constructor(scanner, execPathFn) { + super([], execPath); this.finishedScanning = false; - super([], execPathFn); this.onFinishedScanning = this.onFinishedScanning.bind(this); this.scanner = scanner; } @@ -40,4 +33,4 @@ module.exports = return isFinished; } -}); +} From a51077e2c0212e218431f26363b4d6bc570333d8 Mon Sep 17 00:00:00 2001 From: confused-Techie Date: Mon, 6 Jan 2025 18:16:23 -0800 Subject: [PATCH 05/20] Manual decaf `single-process-search.js` --- src/single-process-search.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/single-process-search.js b/src/single-process-search.js index 4691c08..12512e8 100644 --- a/src/single-process-search.js +++ b/src/single-process-search.js @@ -1,10 +1,3 @@ -/* - * decaffeinate suggestions: - * DS101: Remove unnecessary use of Array.from - * DS102: Remove unnecessary code created because of implicit returns - * DS205: Consider reworking code to avoid use of IIFEs - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md - */ const PathSearcher = require('./path-searcher'); const PathScanner = require('./path-scanner'); const PathReplacer = require('./path-replacer'); From 53e739d2e637dab8806706f52d5b264215e1b5d2 Mon Sep 17 00:00:00 2001 From: confused-Techie Date: Mon, 6 Jan 2025 18:17:11 -0800 Subject: [PATCH 06/20] Manual decaf `scandal.js` --- src/scandal.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/scandal.js b/src/scandal.js index 37ca0a5..b967ed6 100644 --- a/src/scandal.js +++ b/src/scandal.js @@ -1,8 +1,3 @@ -/* - * decaffeinate suggestions: - * DS102: Remove unnecessary code created because of implicit returns - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md - */ const {ArgumentParser} = require('argparse'); const PathSearcher = require('./path-searcher'); const PathScanner = require('./path-scanner'); From 3654941a4c01f1c58bdc1881b03a399c493cdd33 Mon Sep 17 00:00:00 2001 From: confused-Techie Date: Mon, 6 Jan 2025 18:21:04 -0800 Subject: [PATCH 07/20] Manual decaf `path-searcher.js` --- src/path-searcher.js | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/path-searcher.js b/src/path-searcher.js index b548c02..c46ca5a 100644 --- a/src/path-searcher.js +++ b/src/path-searcher.js @@ -1,13 +1,3 @@ -/* - * decaffeinate suggestions: - * DS002: Fix invalid constructor - * DS101: Remove unnecessary use of Array.from - * DS102: Remove unnecessary code created because of implicit returns - * DS205: Consider reworking code to avoid use of IIFEs - * DS207: Consider shorter variations of null checks - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md - */ -let PathSearcher; const fs = require("fs"); const os = require("os"); const {EventEmitter} = require("events"); @@ -100,7 +90,7 @@ const TRAILING_LINE_END_REGEX = /\r?\n?$/; // * `error` {Error} object // module.exports = -(PathSearcher = class PathSearcher extends EventEmitter { +class PathSearcher extends EventEmitter { // Public: Construct a {PathSearcher} object. // @@ -116,6 +106,7 @@ module.exports = // * `wordBreakRegex` {RegExp} default `/[ \r\n\t;:?=&\/]/`; // Used to break on a word when finding the context for a match. constructor(param) { + super(); if (param == null) { param = {}; } const {maxLineLength, leadingContextLineCount, trailingContextLineCount, wordBreakRegex} = param; this.maxLineLength = maxLineLength; @@ -243,7 +234,8 @@ module.exports = if (lineMatches != null) { result.push((() => { const result1 = []; - for (match of Array.from(lineMatches)) { result1.push(recentMatches.push(match)); + for (match of Array.from(lineMatches)) { + result1.push(recentMatches.push(match)); } return result1; })()); @@ -330,4 +322,4 @@ module.exports = if (i > maxIndex) { return maxIndex; } return i; } -}); +} From a954d21baba539ac56627233f502717ff24ac06a Mon Sep 17 00:00:00 2001 From: confused-Techie Date: Mon, 6 Jan 2025 18:22:36 -0800 Subject: [PATCH 08/20] Manual decaf `path-scanner.js` --- src/path-scanner.js | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/path-scanner.js b/src/path-scanner.js index 412a095..482d017 100644 --- a/src/path-scanner.js +++ b/src/path-scanner.js @@ -1,11 +1,3 @@ -/* - * decaffeinate suggestions: - * DS002: Fix invalid constructor - * DS102: Remove unnecessary code created because of implicit returns - * DS207: Consider shorter variations of null checks - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md - */ -let PathScanner; const fs = require("fs"); const path = require("path"); const {EventEmitter} = require("events"); @@ -40,7 +32,7 @@ const DIR_SEP = path.sep; // * `finished-scanning` Emit when the scanner is finished // module.exports = -(PathScanner = class PathScanner extends EventEmitter { +class PathScanner extends EventEmitter { // Public: Create a {PathScanner} object. // @@ -53,9 +45,9 @@ module.exports = // directory dirname. // * `exclusions` {Array} of patterns to exclude. Same matcher as inclusions. // * `includeHidden` {Boolean} default false; true includes hidden files - constructor(rootPath, options) { + constructor(rootPath, options = {}) { + super(); this.rootPath = rootPath; - if (options == null) { options = {}; } this.options = options; this.asyncCallsInProgress = 0; this.realPathCache = {}; @@ -150,4 +142,4 @@ module.exports = return this.emit('finished-scanning', this); } } -}); +} From 617e1fb2b224db4bbea9149e5ab940a5b3acf90b Mon Sep 17 00:00:00 2001 From: confused-Techie Date: Mon, 6 Jan 2025 18:23:55 -0800 Subject: [PATCH 09/20] Manual decaf `path-replacer.js` --- src/path-replacer.js | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/src/path-replacer.js b/src/path-replacer.js index ea2892e..654866d 100644 --- a/src/path-replacer.js +++ b/src/path-replacer.js @@ -1,11 +1,3 @@ -/* - * decaffeinate suggestions: - * DS002: Fix invalid constructor - * DS102: Remove unnecessary code created because of implicit returns - * DS207: Consider shorter variations of null checks - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md - */ -let PathReplacer; const fs = require('fs'); const temp = require('temp').track(); const {EventEmitter} = require('events'); @@ -38,11 +30,10 @@ class ReplaceTransformer extends Transform { } module.exports = -(PathReplacer = class PathReplacer extends EventEmitter { - constructor(param) { - if (param == null) { param = {}; } - const {dryReplace} = param; - this.dryReplace = dryReplace; +class PathReplacer extends EventEmitter { + constructor(param = {}) { + super(); + this.dryReplace = param?.dryReplace; } replacePaths(regex, replacementText, paths, doneCallback) { @@ -108,4 +99,4 @@ module.exports = return reader.pipe(replacer).pipe(output); } -}); +} From 3c23d12492620c288631288eff2d80ada9d12f74 Mon Sep 17 00:00:00 2001 From: confused-Techie Date: Mon, 6 Jan 2025 18:26:02 -0800 Subject: [PATCH 10/20] Fix typo in `chunked-scanner.js` --- src/chunked-scanner.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/chunked-scanner.js b/src/chunked-scanner.js index 2404e93..3e41d70 100644 --- a/src/chunked-scanner.js +++ b/src/chunked-scanner.js @@ -3,7 +3,7 @@ const ChunkedExecutor = require('./chunked-executor'); module.exports = class ChunkedScanner extends ChunkedExecutor { constructor(scanner, execPathFn) { - super([], execPath); + super([], execPathFn); this.finishedScanning = false; this.onFinishedScanning = this.onFinishedScanning.bind(this); this.scanner = scanner; From cf6abf1d7cfd10033251f27e52b020dd8b1378d8 Mon Sep 17 00:00:00 2001 From: confused-Techie Date: Mon, 6 Jan 2025 18:28:04 -0800 Subject: [PATCH 11/20] Move placement of `super` in `path-replacer.ReplaceTransformer` --- src/path-replacer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/path-replacer.js b/src/path-replacer.js index 654866d..0ec47b4 100644 --- a/src/path-replacer.js +++ b/src/path-replacer.js @@ -9,8 +9,8 @@ const ChunkedLineReader = require('./chunked-line-reader'); class ReplaceTransformer extends Transform { constructor(regex, replacementText, {dryReplace}) { - this.replacements = 0; super(); + this.replacements = 0; this.regex = regex; this.replacementText = replacementText; this.dryReplace = dryReplace; From 55c713fdb596990a071f0da2b15ba29960af3495 Mon Sep 17 00:00:00 2001 From: confused-Techie Date: Mon, 6 Jan 2025 18:35:16 -0800 Subject: [PATCH 12/20] Manual decaf of `path-filter.js` --- src/path-filter.js | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/path-filter.js b/src/path-filter.js index 91bf706..b824277 100644 --- a/src/path-filter.js +++ b/src/path-filter.js @@ -6,11 +6,10 @@ * DS104: Avoid inline assignments * DS202: Simplify dynamic range loops * DS204: Change includes calls to have a more natural evaluation order - * DS206: Consider reworking classes to avoid initClass * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md */ -let PathFilter; + const {Minimatch} = require('minimatch'); const GitUtils = require('git-utils'); const path = require('path'); @@ -18,11 +17,8 @@ const fs = require('fs'); // Public: {PathFilter} makes testing for path inclusion easy. module.exports = -(PathFilter = (function() { - PathFilter = class PathFilter { - static initClass() { - this.MINIMATCH_OPTIONS = { matchBase: true, dot: true }; - } +class PathFilter { + static MINIMATCH_OPTIONS = { matchBase: true, dot: true }; static escapeRegExp(str) { return str.replace(/([\/'*+?|()\[\]{}.\^$])/g, '\\$1'); @@ -314,10 +310,7 @@ ${'\\'+path.sep}\\**$\ } return false; } - }; - PathFilter.initClass(); - return PathFilter; -})()); + } function __guard__(value, transform) { return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined; From b886af5789374b67a7e6f4277cfaa2b79486edf2 Mon Sep 17 00:00:00 2001 From: confused-Techie Date: Mon, 6 Jan 2025 18:37:14 -0800 Subject: [PATCH 13/20] Format `path-filter.js` --- src/path-filter.js | 440 ++++++++++++++++++++++----------------------- 1 file changed, 220 insertions(+), 220 deletions(-) diff --git a/src/path-filter.js b/src/path-filter.js index b824277..0ec790e 100644 --- a/src/path-filter.js +++ b/src/path-filter.js @@ -18,77 +18,77 @@ const fs = require('fs'); // Public: {PathFilter} makes testing for path inclusion easy. module.exports = class PathFilter { - static MINIMATCH_OPTIONS = { matchBase: true, dot: true }; + static MINIMATCH_OPTIONS = { matchBase: true, dot: true }; - static escapeRegExp(str) { - return str.replace(/([\/'*+?|()\[\]{}.\^$])/g, '\\$1'); - } + static escapeRegExp(str) { + return str.replace(/([\/'*+?|()\[\]{}.\^$])/g, '\\$1'); + } - // Public: Construct a {PathFilter} - // - // * `rootPath` {String} top level directory to scan. eg. `/Users/ben/somedir` - // * `options` {Object} options hash - // * `excludeVcsIgnores` {Boolean}; default false; true to exclude paths - // defined in a .gitignore. Uses git-utils to check ignred files. - // * `inclusions` {Array} of patterns to include. Uses minimatch with a couple - // additions: `['dirname']` and `['dirname/']` will match all paths in - // directory dirname. - // * `exclusions` {Array} of patterns to exclude. Same matcher as inclusions. - // * `globalExclusions` {Array} of patterns to exclude. These patterns can be - // overridden by `inclusions` if the inclusion is a duplicate or a - // subdirectory of the exclusion. Same matcher as inclusions. - // * `includeHidden` {Boolean} default false; true includes hidden files - constructor(rootPath, options) { - this.rootPath = rootPath; - if (options == null) { options = {}; } - const {includeHidden, excludeVcsIgnores} = options; - const {inclusions, exclusions, globalExclusions} = this.sanitizePaths(options); - - this.inclusions = this.createMatchers(inclusions, {deepMatch: true}); - this.exclusions = this.createMatchers(exclusions, {deepMatch: false}); - this.globalExclusions = this.createMatchers(globalExclusions, {deepMatch: false, disallowDuplicatesFrom: this.inclusions}); - - if (excludeVcsIgnores) { this.repo = GitUtils.open(this.rootPath); } - - if (includeHidden !== true) { this.excludeHidden(); } - } + // Public: Construct a {PathFilter} + // + // * `rootPath` {String} top level directory to scan. eg. `/Users/ben/somedir` + // * `options` {Object} options hash + // * `excludeVcsIgnores` {Boolean}; default false; true to exclude paths + // defined in a .gitignore. Uses git-utils to check ignred files. + // * `inclusions` {Array} of patterns to include. Uses minimatch with a couple + // additions: `['dirname']` and `['dirname/']` will match all paths in + // directory dirname. + // * `exclusions` {Array} of patterns to exclude. Same matcher as inclusions. + // * `globalExclusions` {Array} of patterns to exclude. These patterns can be + // overridden by `inclusions` if the inclusion is a duplicate or a + // subdirectory of the exclusion. Same matcher as inclusions. + // * `includeHidden` {Boolean} default false; true includes hidden files + constructor(rootPath, options) { + this.rootPath = rootPath; + if (options == null) { options = {}; } + const {includeHidden, excludeVcsIgnores} = options; + const {inclusions, exclusions, globalExclusions} = this.sanitizePaths(options); + + this.inclusions = this.createMatchers(inclusions, {deepMatch: true}); + this.exclusions = this.createMatchers(exclusions, {deepMatch: false}); + this.globalExclusions = this.createMatchers(globalExclusions, {deepMatch: false, disallowDuplicatesFrom: this.inclusions}); + + if (excludeVcsIgnores) { this.repo = GitUtils.open(this.rootPath); } + + if (includeHidden !== true) { this.excludeHidden(); } + } - /* - Section: Testing For Acceptance - */ - - // Public: Test if the `filepath` is accepted as a file based on the - // constructing options. - // - // * `filepath` {String} path to a file. File should be a file and should exist - // - // Returns {Boolean} true if the file is accepted - isFileAccepted(filepath) { - return this.isDirectoryAccepted(filepath) && - !this.isPathExcluded('file', filepath) && - this.isPathIncluded('file', filepath) && - !this.isPathGloballyExcluded('file', filepath); - } + /* + Section: Testing For Acceptance + */ + + // Public: Test if the `filepath` is accepted as a file based on the + // constructing options. + // + // * `filepath` {String} path to a file. File should be a file and should exist + // + // Returns {Boolean} true if the file is accepted + isFileAccepted(filepath) { + return this.isDirectoryAccepted(filepath) && + !this.isPathExcluded('file', filepath) && + this.isPathIncluded('file', filepath) && + !this.isPathGloballyExcluded('file', filepath); + } - // Public: Test if the `filepath` is accepted as a directory based on the - // constructing options. - // - // * `filepath` {String} path to a directory. File should be a file or directory - // and should exist - // - // Returns {Boolean} true if the directory is accepted - isDirectoryAccepted(filepath) { - if (this.isPathExcluded('directory', filepath) === true) { return false; } - - const matchingInclusions = this.getMatchingItems(this.inclusions['directory'], filepath); - - // Matching global exclusions will be overriden if there is a matching - // inclusion for a subdirectory of the exclusion. - // For example: if node_modules is globally excluded but mode_modules/foo is - // explicitly included, then the global exclusion is overridden for - // node_modules/foo - const matchingGlobalExclusions = this.overrideGlobalExclusions( - this.getMatchingItems(this.globalExclusions['directory'], filepath), matchingInclusions); + // Public: Test if the `filepath` is accepted as a directory based on the + // constructing options. + // + // * `filepath` {String} path to a directory. File should be a file or directory + // and should exist + // + // Returns {Boolean} true if the directory is accepted + isDirectoryAccepted(filepath) { + if (this.isPathExcluded('directory', filepath) === true) { return false; } + + const matchingInclusions = this.getMatchingItems(this.inclusions['directory'], filepath); + + // Matching global exclusions will be overriden if there is a matching + // inclusion for a subdirectory of the exclusion. + // For example: if node_modules is globally excluded but mode_modules/foo is + // explicitly included, then the global exclusion is overridden for + // node_modules/foo + const matchingGlobalExclusions = this.overrideGlobalExclusions( + this.getMatchingItems(this.globalExclusions['directory'], filepath), matchingInclusions); // Don't accept if there's a matching global exclusion if (matchingGlobalExclusions.length) { return false; } @@ -101,183 +101,183 @@ class PathFilter { // Finally, check for Git exclusions return !this.isPathExcludedByGit(filepath); - } + } - /* - Section: Private Methods - */ + /* + Section: Private Methods + */ - isPathIncluded(fileOrDirectory, filepath) { - let stopAfterFirst; - if (!(this.inclusions[fileOrDirectory] != null ? this.inclusions[fileOrDirectory].length : undefined)) { return true; } - return __guard__(this.getMatchingItems(this.inclusions[fileOrDirectory], filepath, - (stopAfterFirst=true)), x => x.length) > 0; - } + isPathIncluded(fileOrDirectory, filepath) { + let stopAfterFirst; + if (!(this.inclusions[fileOrDirectory] != null ? this.inclusions[fileOrDirectory].length : undefined)) { return true; } + return __guard__(this.getMatchingItems(this.inclusions[fileOrDirectory], filepath, + (stopAfterFirst=true)), x => x.length) > 0; + } - isPathExcluded(fileOrDirectory, filepath) { - let stopAfterFirst; - return __guard__(this.getMatchingItems(this.exclusions[fileOrDirectory], filepath, - (stopAfterFirst=true)), x => x.length) > 0; - } + isPathExcluded(fileOrDirectory, filepath) { + let stopAfterFirst; + return __guard__(this.getMatchingItems(this.exclusions[fileOrDirectory], filepath, + (stopAfterFirst=true)), x => x.length) > 0; + } - isPathGloballyExcluded(fileOrDirectory, filepath) { - let stopAfterFirst; - return __guard__(this.getMatchingItems(this.globalExclusions[fileOrDirectory], filepath, - (stopAfterFirst=true)), x => x.length) > 0; - } + isPathGloballyExcluded(fileOrDirectory, filepath) { + let stopAfterFirst; + return __guard__(this.getMatchingItems(this.globalExclusions[fileOrDirectory], filepath, + (stopAfterFirst=true)), x => x.length) > 0; + } - // Given an array of `matchers`, return an array containing only those that - // match `filepath`. - getMatchingItems(matchers, filepath, stopAfterFirst) { - if (stopAfterFirst == null) { stopAfterFirst = false; } - let index = matchers.length; - const result = []; - while (index--) { - if (matchers[index].match(filepath)) { - result.push(matchers[index]); - if (stopAfterFirst) { return result; } - } + // Given an array of `matchers`, return an array containing only those that + // match `filepath`. + getMatchingItems(matchers, filepath, stopAfterFirst) { + if (stopAfterFirst == null) { stopAfterFirst = false; } + let index = matchers.length; + const result = []; + while (index--) { + if (matchers[index].match(filepath)) { + result.push(matchers[index]); + if (stopAfterFirst) { return result; } } - return result; } + return result; + } - isPathExcludedByGit(filepath) { - return (this.repo != null ? this.repo.isIgnored(this.repo.relativize(path.join(this.rootPath, filepath))) : undefined); - } + isPathExcludedByGit(filepath) { + return (this.repo != null ? this.repo.isIgnored(this.repo.relativize(path.join(this.rootPath, filepath))) : undefined); + } - // Given an array of `globalExclusions`, filter out any which have an - // `inclusion` defined for a subdirectory - overrideGlobalExclusions(globalExclusions, inclusions) { - const result = []; - let exclusionIndex = globalExclusions.length; - while (exclusionIndex--) { - let inclusionIndex = inclusions.length; - let requiresOverride = false; - - // Check if an inclusion is specified for a subdirectory of this globalExclusion - while (inclusionIndex--) { - if (this.isSubpathMatcher(globalExclusions[exclusionIndex], inclusions[inclusionIndex])) { - requiresOverride = true; - } + // Given an array of `globalExclusions`, filter out any which have an + // `inclusion` defined for a subdirectory + overrideGlobalExclusions(globalExclusions, inclusions) { + const result = []; + let exclusionIndex = globalExclusions.length; + while (exclusionIndex--) { + let inclusionIndex = inclusions.length; + let requiresOverride = false; + + // Check if an inclusion is specified for a subdirectory of this globalExclusion + while (inclusionIndex--) { + if (this.isSubpathMatcher(globalExclusions[exclusionIndex], inclusions[inclusionIndex])) { + requiresOverride = true; } - - if (!requiresOverride) { result.push(globalExclusions[exclusionIndex]); } } - return result; + + if (!requiresOverride) { result.push(globalExclusions[exclusionIndex]); } } + return result; + } - // Returns true if the `child` matcher is a subdirectory of the `parent` matcher - isSubpathMatcher(parent, child) { - // Strip off trailing wildcards from the parent pattern - let parentPattern = parent.pattern; - const directoryPattern = new RegExp(`\ -${'\\'+path.sep}\\*$|\ -${'\\'+path.sep}\\*\\*$\ -`); + // Returns true if the `child` matcher is a subdirectory of the `parent` matcher + isSubpathMatcher(parent, child) { + // Strip off trailing wildcards from the parent pattern + let parentPattern = parent.pattern; + const directoryPattern = new RegExp(`\ + ${'\\'+path.sep}\\*$|\ + ${'\\'+path.sep}\\*\\*$\ + `); const matchIndex = parentPattern.search(directoryPattern); if (matchIndex > -1) { parentPattern = parentPattern.slice(0, matchIndex); } return child.pattern.substr(0, parentPattern.length) === parentPattern; - } + } - sanitizePaths(options) { - if (!(options.inclusions != null ? options.inclusions.length : undefined)) { return options; } - const inclusions = []; - for (let includedPath of Array.from(options.inclusions)) { - if (includedPath && (includedPath[0] === '!')) { - if (options.exclusions == null) { options.exclusions = []; } - options.exclusions.push(includedPath.slice(1)); - } else if (includedPath) { - inclusions.push(includedPath); - } + sanitizePaths(options) { + if (!(options.inclusions != null ? options.inclusions.length : undefined)) { return options; } + const inclusions = []; + for (let includedPath of Array.from(options.inclusions)) { + if (includedPath && (includedPath[0] === '!')) { + if (options.exclusions == null) { options.exclusions = []; } + options.exclusions.push(includedPath.slice(1)); + } else if (includedPath) { + inclusions.push(includedPath); } - options.inclusions = inclusions; - return options; } + options.inclusions = inclusions; + return options; + } - excludeHidden() { - const matcher = new Minimatch(".*", PathFilter.MINIMATCH_OPTIONS); - this.exclusions.file.push(matcher); - return this.exclusions.directory.push(matcher); - } + excludeHidden() { + const matcher = new Minimatch(".*", PathFilter.MINIMATCH_OPTIONS); + this.exclusions.file.push(matcher); + return this.exclusions.directory.push(matcher); + } - createMatchers(patterns, param) { - if (patterns == null) { patterns = []; } - if (param == null) { param = {}; } - const {deepMatch, disallowDuplicatesFrom} = param; - const addFileMatcher = (matchers, pattern) => { - if ((disallowDuplicatesFrom != null) && this.containsPattern(disallowDuplicatesFrom, 'file', pattern)) { return; } - return matchers.file.push(new Minimatch(pattern, PathFilter.MINIMATCH_OPTIONS)); - }; - - var addDirectoryMatcher = (matchers, pattern, deepMatch) => { - // It is important that we keep two permutations of directory patterns: - // - // * 'directory/anotherdir' - // * 'directory/anotherdir/**' - // - // Minimatch will return false if we were to match 'directory/anotherdir' - // against pattern 'directory/anotherdir/*'. And it will return false - // matching 'directory/anotherdir/file.txt' against pattern - // 'directory/anotherdir'. - - if (pattern[pattern.length - 1] === path.sep) { - pattern += '**'; - } + createMatchers(patterns, param) { + if (patterns == null) { patterns = []; } + if (param == null) { param = {}; } + const {deepMatch, disallowDuplicatesFrom} = param; + const addFileMatcher = (matchers, pattern) => { + if ((disallowDuplicatesFrom != null) && this.containsPattern(disallowDuplicatesFrom, 'file', pattern)) { return; } + return matchers.file.push(new Minimatch(pattern, PathFilter.MINIMATCH_OPTIONS)); + }; + + var addDirectoryMatcher = (matchers, pattern, deepMatch) => { + // It is important that we keep two permutations of directory patterns: + // + // * 'directory/anotherdir' + // * 'directory/anotherdir/**' + // + // Minimatch will return false if we were to match 'directory/anotherdir' + // against pattern 'directory/anotherdir/*'. And it will return false + // matching 'directory/anotherdir/file.txt' against pattern + // 'directory/anotherdir'. + + if (pattern[pattern.length - 1] === path.sep) { + pattern += '**'; + } - // When the user specifies to include a nested directory, we need to - // specify matchers up to the nested directory - // - // * User specifies 'some/directory/anotherdir/**' - // * We need to break it up into multiple matchers - // * 'some' - // * 'some/directory' - // - // Otherwise, we'll hit the 'some' directory, and if there is no matcher, - // it'll fail and have no chance at hitting the - // 'some/directory/anotherdir/**' matcher the user originally specified. - if (deepMatch) { - let needle; - const paths = pattern.split(path.sep); - let lastIndex = paths.length - 2; - if ((needle = paths[paths.length - 1], ['*', '**'].includes(needle))) { lastIndex--; } - - if (lastIndex >= 0) { - let deepPath = ''; - for (let i = 0, end = lastIndex, asc = 0 <= end; asc ? i <= end : i >= end; asc ? i++ : i--) { - deepPath = path.join(deepPath, paths[i]); - addDirectoryMatcher(matchers, deepPath); - } + // When the user specifies to include a nested directory, we need to + // specify matchers up to the nested directory + // + // * User specifies 'some/directory/anotherdir/**' + // * We need to break it up into multiple matchers + // * 'some' + // * 'some/directory' + // + // Otherwise, we'll hit the 'some' directory, and if there is no matcher, + // it'll fail and have no chance at hitting the + // 'some/directory/anotherdir/**' matcher the user originally specified. + if (deepMatch) { + let needle; + const paths = pattern.split(path.sep); + let lastIndex = paths.length - 2; + if ((needle = paths[paths.length - 1], ['*', '**'].includes(needle))) { lastIndex--; } + + if (lastIndex >= 0) { + let deepPath = ''; + for (let i = 0, end = lastIndex, asc = 0 <= end; asc ? i <= end : i >= end; asc ? i++ : i--) { + deepPath = path.join(deepPath, paths[i]); + addDirectoryMatcher(matchers, deepPath); } } + } - const directoryPattern = new RegExp(`\ -${'\\'+path.sep}\\*$|\ -${'\\'+path.sep}\\*\\*$\ -`); + const directoryPattern = new RegExp(`\ + ${'\\'+path.sep}\\*$|\ + ${'\\'+path.sep}\\*\\*$\ + `); const matchIndex = pattern.search(directoryPattern); if (matchIndex > -1) { addDirectoryMatcher(matchers, pattern.slice(0, matchIndex)); } if ((disallowDuplicatesFrom != null) && this.containsPattern(disallowDuplicatesFrom, 'directory', pattern)) { return; } return matchers.directory.push(new Minimatch(pattern, PathFilter.MINIMATCH_OPTIONS)); - }; - - let pattern = null; - const matchers = { - file: [], - directory: [] - }; - - let r = patterns.length; - while (r--) { - pattern = patterns[r].trim(); - if ((pattern.length === 0) || (pattern[0] === '#')) { continue; } - - const endsWithSeparatorOrStar = new RegExp(`\ -${'\\'+path.sep}$|\ -${'\\'+path.sep}\\**$\ -`); + }; + + let pattern = null; + const matchers = { + file: [], + directory: [] + }; + + let r = patterns.length; + while (r--) { + pattern = patterns[r].trim(); + if ((pattern.length === 0) || (pattern[0] === '#')) { continue; } + + const endsWithSeparatorOrStar = new RegExp(`\ + ${'\\'+path.sep}$|\ + ${'\\'+path.sep}\\**$\ + `); if (endsWithSeparatorOrStar.test(pattern)) { // Is a dir if it ends in a '/' or '/*' addDirectoryMatcher(matchers, pattern, deepMatch); @@ -299,18 +299,18 @@ ${'\\'+path.sep}\\**$\ } else { addFileMatcher(matchers, pattern); } - } - - return matchers; } - containsPattern(matchers, fileOrDirectory, pattern) { - for (let matcher of Array.from(matchers[fileOrDirectory])) { - if (matcher.pattern === pattern) { return true; } - } - return false; + return matchers; + } + + containsPattern(matchers, fileOrDirectory, pattern) { + for (let matcher of Array.from(matchers[fileOrDirectory])) { + if (matcher.pattern === pattern) { return true; } } + return false; } +} function __guard__(value, transform) { return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined; From 9d355c843a8cfbede2a87da7dfb907128a5e0f85 Mon Sep 17 00:00:00 2001 From: confused-Techie Date: Mon, 6 Jan 2025 18:37:39 -0800 Subject: [PATCH 14/20] Remove `gruntfile` & it's deps --- Gruntfile.coffee | 38 -------------------------------------- package.json | 11 ++--------- 2 files changed, 2 insertions(+), 47 deletions(-) delete mode 100644 Gruntfile.coffee diff --git a/Gruntfile.coffee b/Gruntfile.coffee deleted file mode 100644 index 2d74697..0000000 --- a/Gruntfile.coffee +++ /dev/null @@ -1,38 +0,0 @@ -module.exports = (grunt) -> - grunt.initConfig - pkg: grunt.file.readJSON('package.json') - - coffee: - glob_to_multiple: - expand: true - cwd: 'src' - src: ['*.coffee'] - dest: 'lib' - ext: '.js' - - coffeelint: - options: - no_empty_param_list: - level: 'error' - max_line_length: - level: 'ignore' - - src: ['src/*.coffee'] - test: ['spec/*.coffee'] - - shell: - test: - command: 'jasmine-focused --captureExceptions --coffee spec/' - options: - stdout: true - stderr: true - failOnError: true - - grunt.loadNpmTasks('grunt-contrib-coffee') - grunt.loadNpmTasks('grunt-shell') - grunt.loadNpmTasks('grunt-coffeelint') - - grunt.registerTask 'clean', -> require('rimraf').sync('lib') - grunt.registerTask('lint', ['coffeelint:src', 'coffeelint:test']) - grunt.registerTask('default', ['lint', 'coffee']) - grunt.registerTask('test', ['default', 'shell:test']) diff --git a/package.json b/package.json index c5d3d8a..43c857d 100644 --- a/package.json +++ b/package.json @@ -2,13 +2,12 @@ "name": "scandal", "version": "3.2.0", "description": "Directory Search and Scan Utilities", - "main": "./lib/scandal.js", + "main": "./src/scandal.js", "bin": { "scandal": "./bin/scandal" }, "scripts": { - "test": "grunt test", - "prepublish": "grunt clean lint coffee" + "test": "jasmine-focused --captureExceptions --coffee spec/" }, "repository": { "type": "git", @@ -28,12 +27,6 @@ "temp": "^0.8.3" }, "devDependencies": { - "coffeelint": "^1.15.7", - "grunt": "^0.4.5", - "grunt-cli": "^0.1.13", - "grunt-coffeelint": "0.0.13", - "grunt-contrib-coffee": "^0.13.0", - "grunt-shell": "^1.1.2", "jasmine-focused": "^1.0.7", "rimraf": "^2.4.2", "wrench": "^1.5.8" From 3b6c572f088ead5b0c02546ff56c70aac1dfad9c Mon Sep 17 00:00:00 2001 From: confused-Techie Date: Mon, 6 Jan 2025 18:47:13 -0800 Subject: [PATCH 15/20] `path-filter` undo possible formatting errors --- src/path-filter.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/path-filter.js b/src/path-filter.js index 0ec790e..392bfd3 100644 --- a/src/path-filter.js +++ b/src/path-filter.js @@ -172,9 +172,9 @@ class PathFilter { // Strip off trailing wildcards from the parent pattern let parentPattern = parent.pattern; const directoryPattern = new RegExp(`\ - ${'\\'+path.sep}\\*$|\ - ${'\\'+path.sep}\\*\\*$\ - `); +${'\\'+path.sep}\\*$|\ +${'\\'+path.sep}\\*\\*$\ +`); const matchIndex = parentPattern.search(directoryPattern); if (matchIndex > -1) { parentPattern = parentPattern.slice(0, matchIndex); } @@ -253,8 +253,8 @@ class PathFilter { } const directoryPattern = new RegExp(`\ - ${'\\'+path.sep}\\*$|\ - ${'\\'+path.sep}\\*\\*$\ +${'\\'+path.sep}\\*$|\ +${'\\'+path.sep}\\*\\*$\ `); const matchIndex = pattern.search(directoryPattern); if (matchIndex > -1) { addDirectoryMatcher(matchers, pattern.slice(0, matchIndex)); } @@ -275,9 +275,9 @@ class PathFilter { if ((pattern.length === 0) || (pattern[0] === '#')) { continue; } const endsWithSeparatorOrStar = new RegExp(`\ - ${'\\'+path.sep}$|\ - ${'\\'+path.sep}\\**$\ - `); +${'\\'+path.sep}$|\ +${'\\'+path.sep}\\**$\ +`); if (endsWithSeparatorOrStar.test(pattern)) { // Is a dir if it ends in a '/' or '/*' addDirectoryMatcher(matchers, pattern, deepMatch); From fca2a274862c511796c91f2ebfc3e97f4c785b4c Mon Sep 17 00:00:00 2001 From: confused-Techie Date: Mon, 6 Jan 2025 18:49:57 -0800 Subject: [PATCH 16/20] Revert "`path-filter` undo possible formatting errors" This reverts commit 3b6c572f088ead5b0c02546ff56c70aac1dfad9c. --- src/path-filter.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/path-filter.js b/src/path-filter.js index 392bfd3..0ec790e 100644 --- a/src/path-filter.js +++ b/src/path-filter.js @@ -172,9 +172,9 @@ class PathFilter { // Strip off trailing wildcards from the parent pattern let parentPattern = parent.pattern; const directoryPattern = new RegExp(`\ -${'\\'+path.sep}\\*$|\ -${'\\'+path.sep}\\*\\*$\ -`); + ${'\\'+path.sep}\\*$|\ + ${'\\'+path.sep}\\*\\*$\ + `); const matchIndex = parentPattern.search(directoryPattern); if (matchIndex > -1) { parentPattern = parentPattern.slice(0, matchIndex); } @@ -253,8 +253,8 @@ ${'\\'+path.sep}\\*\\*$\ } const directoryPattern = new RegExp(`\ -${'\\'+path.sep}\\*$|\ -${'\\'+path.sep}\\*\\*$\ + ${'\\'+path.sep}\\*$|\ + ${'\\'+path.sep}\\*\\*$\ `); const matchIndex = pattern.search(directoryPattern); if (matchIndex > -1) { addDirectoryMatcher(matchers, pattern.slice(0, matchIndex)); } @@ -275,9 +275,9 @@ ${'\\'+path.sep}\\*\\*$\ if ((pattern.length === 0) || (pattern[0] === '#')) { continue; } const endsWithSeparatorOrStar = new RegExp(`\ -${'\\'+path.sep}$|\ -${'\\'+path.sep}\\**$\ -`); + ${'\\'+path.sep}$|\ + ${'\\'+path.sep}\\**$\ + `); if (endsWithSeparatorOrStar.test(pattern)) { // Is a dir if it ends in a '/' or '/*' addDirectoryMatcher(matchers, pattern, deepMatch); From da998d73048eea9d2f666a17996b42cb97c5541c Mon Sep 17 00:00:00 2001 From: confused-Techie Date: Mon, 6 Jan 2025 18:50:06 -0800 Subject: [PATCH 17/20] Revert "Format `path-filter.js`" This reverts commit b886af5789374b67a7e6f4277cfaa2b79486edf2. --- src/path-filter.js | 440 ++++++++++++++++++++++----------------------- 1 file changed, 220 insertions(+), 220 deletions(-) diff --git a/src/path-filter.js b/src/path-filter.js index 0ec790e..b824277 100644 --- a/src/path-filter.js +++ b/src/path-filter.js @@ -18,77 +18,77 @@ const fs = require('fs'); // Public: {PathFilter} makes testing for path inclusion easy. module.exports = class PathFilter { - static MINIMATCH_OPTIONS = { matchBase: true, dot: true }; + static MINIMATCH_OPTIONS = { matchBase: true, dot: true }; - static escapeRegExp(str) { - return str.replace(/([\/'*+?|()\[\]{}.\^$])/g, '\\$1'); - } + static escapeRegExp(str) { + return str.replace(/([\/'*+?|()\[\]{}.\^$])/g, '\\$1'); + } - // Public: Construct a {PathFilter} - // - // * `rootPath` {String} top level directory to scan. eg. `/Users/ben/somedir` - // * `options` {Object} options hash - // * `excludeVcsIgnores` {Boolean}; default false; true to exclude paths - // defined in a .gitignore. Uses git-utils to check ignred files. - // * `inclusions` {Array} of patterns to include. Uses minimatch with a couple - // additions: `['dirname']` and `['dirname/']` will match all paths in - // directory dirname. - // * `exclusions` {Array} of patterns to exclude. Same matcher as inclusions. - // * `globalExclusions` {Array} of patterns to exclude. These patterns can be - // overridden by `inclusions` if the inclusion is a duplicate or a - // subdirectory of the exclusion. Same matcher as inclusions. - // * `includeHidden` {Boolean} default false; true includes hidden files - constructor(rootPath, options) { - this.rootPath = rootPath; - if (options == null) { options = {}; } - const {includeHidden, excludeVcsIgnores} = options; - const {inclusions, exclusions, globalExclusions} = this.sanitizePaths(options); - - this.inclusions = this.createMatchers(inclusions, {deepMatch: true}); - this.exclusions = this.createMatchers(exclusions, {deepMatch: false}); - this.globalExclusions = this.createMatchers(globalExclusions, {deepMatch: false, disallowDuplicatesFrom: this.inclusions}); - - if (excludeVcsIgnores) { this.repo = GitUtils.open(this.rootPath); } - - if (includeHidden !== true) { this.excludeHidden(); } - } + // Public: Construct a {PathFilter} + // + // * `rootPath` {String} top level directory to scan. eg. `/Users/ben/somedir` + // * `options` {Object} options hash + // * `excludeVcsIgnores` {Boolean}; default false; true to exclude paths + // defined in a .gitignore. Uses git-utils to check ignred files. + // * `inclusions` {Array} of patterns to include. Uses minimatch with a couple + // additions: `['dirname']` and `['dirname/']` will match all paths in + // directory dirname. + // * `exclusions` {Array} of patterns to exclude. Same matcher as inclusions. + // * `globalExclusions` {Array} of patterns to exclude. These patterns can be + // overridden by `inclusions` if the inclusion is a duplicate or a + // subdirectory of the exclusion. Same matcher as inclusions. + // * `includeHidden` {Boolean} default false; true includes hidden files + constructor(rootPath, options) { + this.rootPath = rootPath; + if (options == null) { options = {}; } + const {includeHidden, excludeVcsIgnores} = options; + const {inclusions, exclusions, globalExclusions} = this.sanitizePaths(options); + + this.inclusions = this.createMatchers(inclusions, {deepMatch: true}); + this.exclusions = this.createMatchers(exclusions, {deepMatch: false}); + this.globalExclusions = this.createMatchers(globalExclusions, {deepMatch: false, disallowDuplicatesFrom: this.inclusions}); + + if (excludeVcsIgnores) { this.repo = GitUtils.open(this.rootPath); } + + if (includeHidden !== true) { this.excludeHidden(); } + } - /* - Section: Testing For Acceptance - */ - - // Public: Test if the `filepath` is accepted as a file based on the - // constructing options. - // - // * `filepath` {String} path to a file. File should be a file and should exist - // - // Returns {Boolean} true if the file is accepted - isFileAccepted(filepath) { - return this.isDirectoryAccepted(filepath) && - !this.isPathExcluded('file', filepath) && - this.isPathIncluded('file', filepath) && - !this.isPathGloballyExcluded('file', filepath); - } + /* + Section: Testing For Acceptance + */ + + // Public: Test if the `filepath` is accepted as a file based on the + // constructing options. + // + // * `filepath` {String} path to a file. File should be a file and should exist + // + // Returns {Boolean} true if the file is accepted + isFileAccepted(filepath) { + return this.isDirectoryAccepted(filepath) && + !this.isPathExcluded('file', filepath) && + this.isPathIncluded('file', filepath) && + !this.isPathGloballyExcluded('file', filepath); + } - // Public: Test if the `filepath` is accepted as a directory based on the - // constructing options. - // - // * `filepath` {String} path to a directory. File should be a file or directory - // and should exist - // - // Returns {Boolean} true if the directory is accepted - isDirectoryAccepted(filepath) { - if (this.isPathExcluded('directory', filepath) === true) { return false; } - - const matchingInclusions = this.getMatchingItems(this.inclusions['directory'], filepath); - - // Matching global exclusions will be overriden if there is a matching - // inclusion for a subdirectory of the exclusion. - // For example: if node_modules is globally excluded but mode_modules/foo is - // explicitly included, then the global exclusion is overridden for - // node_modules/foo - const matchingGlobalExclusions = this.overrideGlobalExclusions( - this.getMatchingItems(this.globalExclusions['directory'], filepath), matchingInclusions); + // Public: Test if the `filepath` is accepted as a directory based on the + // constructing options. + // + // * `filepath` {String} path to a directory. File should be a file or directory + // and should exist + // + // Returns {Boolean} true if the directory is accepted + isDirectoryAccepted(filepath) { + if (this.isPathExcluded('directory', filepath) === true) { return false; } + + const matchingInclusions = this.getMatchingItems(this.inclusions['directory'], filepath); + + // Matching global exclusions will be overriden if there is a matching + // inclusion for a subdirectory of the exclusion. + // For example: if node_modules is globally excluded but mode_modules/foo is + // explicitly included, then the global exclusion is overridden for + // node_modules/foo + const matchingGlobalExclusions = this.overrideGlobalExclusions( + this.getMatchingItems(this.globalExclusions['directory'], filepath), matchingInclusions); // Don't accept if there's a matching global exclusion if (matchingGlobalExclusions.length) { return false; } @@ -101,183 +101,183 @@ class PathFilter { // Finally, check for Git exclusions return !this.isPathExcludedByGit(filepath); - } + } - /* - Section: Private Methods - */ + /* + Section: Private Methods + */ - isPathIncluded(fileOrDirectory, filepath) { - let stopAfterFirst; - if (!(this.inclusions[fileOrDirectory] != null ? this.inclusions[fileOrDirectory].length : undefined)) { return true; } - return __guard__(this.getMatchingItems(this.inclusions[fileOrDirectory], filepath, - (stopAfterFirst=true)), x => x.length) > 0; - } + isPathIncluded(fileOrDirectory, filepath) { + let stopAfterFirst; + if (!(this.inclusions[fileOrDirectory] != null ? this.inclusions[fileOrDirectory].length : undefined)) { return true; } + return __guard__(this.getMatchingItems(this.inclusions[fileOrDirectory], filepath, + (stopAfterFirst=true)), x => x.length) > 0; + } - isPathExcluded(fileOrDirectory, filepath) { - let stopAfterFirst; - return __guard__(this.getMatchingItems(this.exclusions[fileOrDirectory], filepath, - (stopAfterFirst=true)), x => x.length) > 0; - } + isPathExcluded(fileOrDirectory, filepath) { + let stopAfterFirst; + return __guard__(this.getMatchingItems(this.exclusions[fileOrDirectory], filepath, + (stopAfterFirst=true)), x => x.length) > 0; + } - isPathGloballyExcluded(fileOrDirectory, filepath) { - let stopAfterFirst; - return __guard__(this.getMatchingItems(this.globalExclusions[fileOrDirectory], filepath, - (stopAfterFirst=true)), x => x.length) > 0; - } + isPathGloballyExcluded(fileOrDirectory, filepath) { + let stopAfterFirst; + return __guard__(this.getMatchingItems(this.globalExclusions[fileOrDirectory], filepath, + (stopAfterFirst=true)), x => x.length) > 0; + } - // Given an array of `matchers`, return an array containing only those that - // match `filepath`. - getMatchingItems(matchers, filepath, stopAfterFirst) { - if (stopAfterFirst == null) { stopAfterFirst = false; } - let index = matchers.length; - const result = []; - while (index--) { - if (matchers[index].match(filepath)) { - result.push(matchers[index]); - if (stopAfterFirst) { return result; } + // Given an array of `matchers`, return an array containing only those that + // match `filepath`. + getMatchingItems(matchers, filepath, stopAfterFirst) { + if (stopAfterFirst == null) { stopAfterFirst = false; } + let index = matchers.length; + const result = []; + while (index--) { + if (matchers[index].match(filepath)) { + result.push(matchers[index]); + if (stopAfterFirst) { return result; } + } } + return result; } - return result; - } - isPathExcludedByGit(filepath) { - return (this.repo != null ? this.repo.isIgnored(this.repo.relativize(path.join(this.rootPath, filepath))) : undefined); - } + isPathExcludedByGit(filepath) { + return (this.repo != null ? this.repo.isIgnored(this.repo.relativize(path.join(this.rootPath, filepath))) : undefined); + } - // Given an array of `globalExclusions`, filter out any which have an - // `inclusion` defined for a subdirectory - overrideGlobalExclusions(globalExclusions, inclusions) { - const result = []; - let exclusionIndex = globalExclusions.length; - while (exclusionIndex--) { - let inclusionIndex = inclusions.length; - let requiresOverride = false; - - // Check if an inclusion is specified for a subdirectory of this globalExclusion - while (inclusionIndex--) { - if (this.isSubpathMatcher(globalExclusions[exclusionIndex], inclusions[inclusionIndex])) { - requiresOverride = true; + // Given an array of `globalExclusions`, filter out any which have an + // `inclusion` defined for a subdirectory + overrideGlobalExclusions(globalExclusions, inclusions) { + const result = []; + let exclusionIndex = globalExclusions.length; + while (exclusionIndex--) { + let inclusionIndex = inclusions.length; + let requiresOverride = false; + + // Check if an inclusion is specified for a subdirectory of this globalExclusion + while (inclusionIndex--) { + if (this.isSubpathMatcher(globalExclusions[exclusionIndex], inclusions[inclusionIndex])) { + requiresOverride = true; + } } - } - if (!requiresOverride) { result.push(globalExclusions[exclusionIndex]); } + if (!requiresOverride) { result.push(globalExclusions[exclusionIndex]); } + } + return result; } - return result; - } - // Returns true if the `child` matcher is a subdirectory of the `parent` matcher - isSubpathMatcher(parent, child) { - // Strip off trailing wildcards from the parent pattern - let parentPattern = parent.pattern; - const directoryPattern = new RegExp(`\ - ${'\\'+path.sep}\\*$|\ - ${'\\'+path.sep}\\*\\*$\ - `); + // Returns true if the `child` matcher is a subdirectory of the `parent` matcher + isSubpathMatcher(parent, child) { + // Strip off trailing wildcards from the parent pattern + let parentPattern = parent.pattern; + const directoryPattern = new RegExp(`\ +${'\\'+path.sep}\\*$|\ +${'\\'+path.sep}\\*\\*$\ +`); const matchIndex = parentPattern.search(directoryPattern); if (matchIndex > -1) { parentPattern = parentPattern.slice(0, matchIndex); } return child.pattern.substr(0, parentPattern.length) === parentPattern; - } + } - sanitizePaths(options) { - if (!(options.inclusions != null ? options.inclusions.length : undefined)) { return options; } - const inclusions = []; - for (let includedPath of Array.from(options.inclusions)) { - if (includedPath && (includedPath[0] === '!')) { - if (options.exclusions == null) { options.exclusions = []; } - options.exclusions.push(includedPath.slice(1)); - } else if (includedPath) { - inclusions.push(includedPath); + sanitizePaths(options) { + if (!(options.inclusions != null ? options.inclusions.length : undefined)) { return options; } + const inclusions = []; + for (let includedPath of Array.from(options.inclusions)) { + if (includedPath && (includedPath[0] === '!')) { + if (options.exclusions == null) { options.exclusions = []; } + options.exclusions.push(includedPath.slice(1)); + } else if (includedPath) { + inclusions.push(includedPath); + } } + options.inclusions = inclusions; + return options; } - options.inclusions = inclusions; - return options; - } - excludeHidden() { - const matcher = new Minimatch(".*", PathFilter.MINIMATCH_OPTIONS); - this.exclusions.file.push(matcher); - return this.exclusions.directory.push(matcher); - } + excludeHidden() { + const matcher = new Minimatch(".*", PathFilter.MINIMATCH_OPTIONS); + this.exclusions.file.push(matcher); + return this.exclusions.directory.push(matcher); + } - createMatchers(patterns, param) { - if (patterns == null) { patterns = []; } - if (param == null) { param = {}; } - const {deepMatch, disallowDuplicatesFrom} = param; - const addFileMatcher = (matchers, pattern) => { - if ((disallowDuplicatesFrom != null) && this.containsPattern(disallowDuplicatesFrom, 'file', pattern)) { return; } - return matchers.file.push(new Minimatch(pattern, PathFilter.MINIMATCH_OPTIONS)); - }; - - var addDirectoryMatcher = (matchers, pattern, deepMatch) => { - // It is important that we keep two permutations of directory patterns: - // - // * 'directory/anotherdir' - // * 'directory/anotherdir/**' - // - // Minimatch will return false if we were to match 'directory/anotherdir' - // against pattern 'directory/anotherdir/*'. And it will return false - // matching 'directory/anotherdir/file.txt' against pattern - // 'directory/anotherdir'. - - if (pattern[pattern.length - 1] === path.sep) { - pattern += '**'; - } + createMatchers(patterns, param) { + if (patterns == null) { patterns = []; } + if (param == null) { param = {}; } + const {deepMatch, disallowDuplicatesFrom} = param; + const addFileMatcher = (matchers, pattern) => { + if ((disallowDuplicatesFrom != null) && this.containsPattern(disallowDuplicatesFrom, 'file', pattern)) { return; } + return matchers.file.push(new Minimatch(pattern, PathFilter.MINIMATCH_OPTIONS)); + }; + + var addDirectoryMatcher = (matchers, pattern, deepMatch) => { + // It is important that we keep two permutations of directory patterns: + // + // * 'directory/anotherdir' + // * 'directory/anotherdir/**' + // + // Minimatch will return false if we were to match 'directory/anotherdir' + // against pattern 'directory/anotherdir/*'. And it will return false + // matching 'directory/anotherdir/file.txt' against pattern + // 'directory/anotherdir'. + + if (pattern[pattern.length - 1] === path.sep) { + pattern += '**'; + } - // When the user specifies to include a nested directory, we need to - // specify matchers up to the nested directory - // - // * User specifies 'some/directory/anotherdir/**' - // * We need to break it up into multiple matchers - // * 'some' - // * 'some/directory' - // - // Otherwise, we'll hit the 'some' directory, and if there is no matcher, - // it'll fail and have no chance at hitting the - // 'some/directory/anotherdir/**' matcher the user originally specified. - if (deepMatch) { - let needle; - const paths = pattern.split(path.sep); - let lastIndex = paths.length - 2; - if ((needle = paths[paths.length - 1], ['*', '**'].includes(needle))) { lastIndex--; } - - if (lastIndex >= 0) { - let deepPath = ''; - for (let i = 0, end = lastIndex, asc = 0 <= end; asc ? i <= end : i >= end; asc ? i++ : i--) { - deepPath = path.join(deepPath, paths[i]); - addDirectoryMatcher(matchers, deepPath); + // When the user specifies to include a nested directory, we need to + // specify matchers up to the nested directory + // + // * User specifies 'some/directory/anotherdir/**' + // * We need to break it up into multiple matchers + // * 'some' + // * 'some/directory' + // + // Otherwise, we'll hit the 'some' directory, and if there is no matcher, + // it'll fail and have no chance at hitting the + // 'some/directory/anotherdir/**' matcher the user originally specified. + if (deepMatch) { + let needle; + const paths = pattern.split(path.sep); + let lastIndex = paths.length - 2; + if ((needle = paths[paths.length - 1], ['*', '**'].includes(needle))) { lastIndex--; } + + if (lastIndex >= 0) { + let deepPath = ''; + for (let i = 0, end = lastIndex, asc = 0 <= end; asc ? i <= end : i >= end; asc ? i++ : i--) { + deepPath = path.join(deepPath, paths[i]); + addDirectoryMatcher(matchers, deepPath); + } } } - } - const directoryPattern = new RegExp(`\ - ${'\\'+path.sep}\\*$|\ - ${'\\'+path.sep}\\*\\*$\ - `); + const directoryPattern = new RegExp(`\ +${'\\'+path.sep}\\*$|\ +${'\\'+path.sep}\\*\\*$\ +`); const matchIndex = pattern.search(directoryPattern); if (matchIndex > -1) { addDirectoryMatcher(matchers, pattern.slice(0, matchIndex)); } if ((disallowDuplicatesFrom != null) && this.containsPattern(disallowDuplicatesFrom, 'directory', pattern)) { return; } return matchers.directory.push(new Minimatch(pattern, PathFilter.MINIMATCH_OPTIONS)); - }; - - let pattern = null; - const matchers = { - file: [], - directory: [] - }; - - let r = patterns.length; - while (r--) { - pattern = patterns[r].trim(); - if ((pattern.length === 0) || (pattern[0] === '#')) { continue; } - - const endsWithSeparatorOrStar = new RegExp(`\ - ${'\\'+path.sep}$|\ - ${'\\'+path.sep}\\**$\ - `); + }; + + let pattern = null; + const matchers = { + file: [], + directory: [] + }; + + let r = patterns.length; + while (r--) { + pattern = patterns[r].trim(); + if ((pattern.length === 0) || (pattern[0] === '#')) { continue; } + + const endsWithSeparatorOrStar = new RegExp(`\ +${'\\'+path.sep}$|\ +${'\\'+path.sep}\\**$\ +`); if (endsWithSeparatorOrStar.test(pattern)) { // Is a dir if it ends in a '/' or '/*' addDirectoryMatcher(matchers, pattern, deepMatch); @@ -299,18 +299,18 @@ class PathFilter { } else { addFileMatcher(matchers, pattern); } - } + } - return matchers; - } + return matchers; + } - containsPattern(matchers, fileOrDirectory, pattern) { - for (let matcher of Array.from(matchers[fileOrDirectory])) { - if (matcher.pattern === pattern) { return true; } + containsPattern(matchers, fileOrDirectory, pattern) { + for (let matcher of Array.from(matchers[fileOrDirectory])) { + if (matcher.pattern === pattern) { return true; } + } + return false; } - return false; } -} function __guard__(value, transform) { return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined; From 5b3a0a915929c29f6aeccd84297a01b1a1606a02 Mon Sep 17 00:00:00 2001 From: confused-Techie Date: Mon, 6 Jan 2025 18:56:51 -0800 Subject: [PATCH 18/20] Manual formatting of `path-filter.js` --- src/path-filter.js | 488 ++++++++++++++++++++++----------------------- 1 file changed, 244 insertions(+), 244 deletions(-) diff --git a/src/path-filter.js b/src/path-filter.js index b824277..247f659 100644 --- a/src/path-filter.js +++ b/src/path-filter.js @@ -18,299 +18,299 @@ const fs = require('fs'); // Public: {PathFilter} makes testing for path inclusion easy. module.exports = class PathFilter { - static MINIMATCH_OPTIONS = { matchBase: true, dot: true }; + static MINIMATCH_OPTIONS = { matchBase: true, dot: true }; - static escapeRegExp(str) { - return str.replace(/([\/'*+?|()\[\]{}.\^$])/g, '\\$1'); - } + static escapeRegExp(str) { + return str.replace(/([\/'*+?|()\[\]{}.\^$])/g, '\\$1'); + } - // Public: Construct a {PathFilter} - // - // * `rootPath` {String} top level directory to scan. eg. `/Users/ben/somedir` - // * `options` {Object} options hash - // * `excludeVcsIgnores` {Boolean}; default false; true to exclude paths - // defined in a .gitignore. Uses git-utils to check ignred files. - // * `inclusions` {Array} of patterns to include. Uses minimatch with a couple - // additions: `['dirname']` and `['dirname/']` will match all paths in - // directory dirname. - // * `exclusions` {Array} of patterns to exclude. Same matcher as inclusions. - // * `globalExclusions` {Array} of patterns to exclude. These patterns can be - // overridden by `inclusions` if the inclusion is a duplicate or a - // subdirectory of the exclusion. Same matcher as inclusions. - // * `includeHidden` {Boolean} default false; true includes hidden files - constructor(rootPath, options) { - this.rootPath = rootPath; - if (options == null) { options = {}; } - const {includeHidden, excludeVcsIgnores} = options; - const {inclusions, exclusions, globalExclusions} = this.sanitizePaths(options); - - this.inclusions = this.createMatchers(inclusions, {deepMatch: true}); - this.exclusions = this.createMatchers(exclusions, {deepMatch: false}); - this.globalExclusions = this.createMatchers(globalExclusions, {deepMatch: false, disallowDuplicatesFrom: this.inclusions}); - - if (excludeVcsIgnores) { this.repo = GitUtils.open(this.rootPath); } - - if (includeHidden !== true) { this.excludeHidden(); } - } + // Public: Construct a {PathFilter} + // + // * `rootPath` {String} top level directory to scan. eg. `/Users/ben/somedir` + // * `options` {Object} options hash + // * `excludeVcsIgnores` {Boolean}; default false; true to exclude paths + // defined in a .gitignore. Uses git-utils to check ignred files. + // * `inclusions` {Array} of patterns to include. Uses minimatch with a couple + // additions: `['dirname']` and `['dirname/']` will match all paths in + // directory dirname. + // * `exclusions` {Array} of patterns to exclude. Same matcher as inclusions. + // * `globalExclusions` {Array} of patterns to exclude. These patterns can be + // overridden by `inclusions` if the inclusion is a duplicate or a + // subdirectory of the exclusion. Same matcher as inclusions. + // * `includeHidden` {Boolean} default false; true includes hidden files + constructor(rootPath, options) { + this.rootPath = rootPath; + if (options == null) { options = {}; } + const {includeHidden, excludeVcsIgnores} = options; + const {inclusions, exclusions, globalExclusions} = this.sanitizePaths(options); + + this.inclusions = this.createMatchers(inclusions, {deepMatch: true}); + this.exclusions = this.createMatchers(exclusions, {deepMatch: false}); + this.globalExclusions = this.createMatchers(globalExclusions, {deepMatch: false, disallowDuplicatesFrom: this.inclusions}); + + if (excludeVcsIgnores) { this.repo = GitUtils.open(this.rootPath); } + + if (includeHidden !== true) { this.excludeHidden(); } + } - /* - Section: Testing For Acceptance - */ - - // Public: Test if the `filepath` is accepted as a file based on the - // constructing options. - // - // * `filepath` {String} path to a file. File should be a file and should exist - // - // Returns {Boolean} true if the file is accepted - isFileAccepted(filepath) { - return this.isDirectoryAccepted(filepath) && - !this.isPathExcluded('file', filepath) && - this.isPathIncluded('file', filepath) && - !this.isPathGloballyExcluded('file', filepath); - } + /* + Section: Testing For Acceptance + */ + + // Public: Test if the `filepath` is accepted as a file based on the + // constructing options. + // + // * `filepath` {String} path to a file. File should be a file and should exist + // + // Returns {Boolean} true if the file is accepted + isFileAccepted(filepath) { + return this.isDirectoryAccepted(filepath) && + !this.isPathExcluded('file', filepath) && + this.isPathIncluded('file', filepath) && + !this.isPathGloballyExcluded('file', filepath); + } - // Public: Test if the `filepath` is accepted as a directory based on the - // constructing options. - // - // * `filepath` {String} path to a directory. File should be a file or directory - // and should exist - // - // Returns {Boolean} true if the directory is accepted - isDirectoryAccepted(filepath) { - if (this.isPathExcluded('directory', filepath) === true) { return false; } - - const matchingInclusions = this.getMatchingItems(this.inclusions['directory'], filepath); - - // Matching global exclusions will be overriden if there is a matching - // inclusion for a subdirectory of the exclusion. - // For example: if node_modules is globally excluded but mode_modules/foo is - // explicitly included, then the global exclusion is overridden for - // node_modules/foo - const matchingGlobalExclusions = this.overrideGlobalExclusions( - this.getMatchingItems(this.globalExclusions['directory'], filepath), matchingInclusions); - - // Don't accept if there's a matching global exclusion - if (matchingGlobalExclusions.length) { return false; } - - // A matching explicit local inclusion will override any Git exclusions - if (matchingInclusions.length) { return true; } - - // Don't accept if there Were inclusions specified that didn't match - if (this.inclusions['directory'] != null ? this.inclusions['directory'].length : undefined) { return false; } - - // Finally, check for Git exclusions - return !this.isPathExcludedByGit(filepath); - } + // Public: Test if the `filepath` is accepted as a directory based on the + // constructing options. + // + // * `filepath` {String} path to a directory. File should be a file or directory + // and should exist + // + // Returns {Boolean} true if the directory is accepted + isDirectoryAccepted(filepath) { + if (this.isPathExcluded('directory', filepath) === true) { return false; } + + const matchingInclusions = this.getMatchingItems(this.inclusions['directory'], filepath); + + // Matching global exclusions will be overriden if there is a matching + // inclusion for a subdirectory of the exclusion. + // For example: if node_modules is globally excluded but mode_modules/foo is + // explicitly included, then the global exclusion is overridden for + // node_modules/foo + const matchingGlobalExclusions = this.overrideGlobalExclusions( + this.getMatchingItems(this.globalExclusions['directory'], filepath), matchingInclusions); + + // Don't accept if there's a matching global exclusion + if (matchingGlobalExclusions.length) { return false; } + + // A matching explicit local inclusion will override any Git exclusions + if (matchingInclusions.length) { return true; } + + // Don't accept if there Were inclusions specified that didn't match + if (this.inclusions['directory'] != null ? this.inclusions['directory'].length : undefined) { return false; } + + // Finally, check for Git exclusions + return !this.isPathExcludedByGit(filepath); + } - /* - Section: Private Methods - */ + /* + Section: Private Methods + */ - isPathIncluded(fileOrDirectory, filepath) { - let stopAfterFirst; - if (!(this.inclusions[fileOrDirectory] != null ? this.inclusions[fileOrDirectory].length : undefined)) { return true; } - return __guard__(this.getMatchingItems(this.inclusions[fileOrDirectory], filepath, - (stopAfterFirst=true)), x => x.length) > 0; - } + isPathIncluded(fileOrDirectory, filepath) { + let stopAfterFirst; + if (!(this.inclusions[fileOrDirectory] != null ? this.inclusions[fileOrDirectory].length : undefined)) { return true; } + return __guard__(this.getMatchingItems(this.inclusions[fileOrDirectory], filepath, + (stopAfterFirst=true)), x => x.length) > 0; + } - isPathExcluded(fileOrDirectory, filepath) { - let stopAfterFirst; - return __guard__(this.getMatchingItems(this.exclusions[fileOrDirectory], filepath, - (stopAfterFirst=true)), x => x.length) > 0; - } + isPathExcluded(fileOrDirectory, filepath) { + let stopAfterFirst; + return __guard__(this.getMatchingItems(this.exclusions[fileOrDirectory], filepath, + (stopAfterFirst=true)), x => x.length) > 0; + } - isPathGloballyExcluded(fileOrDirectory, filepath) { - let stopAfterFirst; - return __guard__(this.getMatchingItems(this.globalExclusions[fileOrDirectory], filepath, - (stopAfterFirst=true)), x => x.length) > 0; - } + isPathGloballyExcluded(fileOrDirectory, filepath) { + let stopAfterFirst; + return __guard__(this.getMatchingItems(this.globalExclusions[fileOrDirectory], filepath, + (stopAfterFirst=true)), x => x.length) > 0; + } - // Given an array of `matchers`, return an array containing only those that - // match `filepath`. - getMatchingItems(matchers, filepath, stopAfterFirst) { - if (stopAfterFirst == null) { stopAfterFirst = false; } - let index = matchers.length; - const result = []; - while (index--) { - if (matchers[index].match(filepath)) { - result.push(matchers[index]); - if (stopAfterFirst) { return result; } - } + // Given an array of `matchers`, return an array containing only those that + // match `filepath`. + getMatchingItems(matchers, filepath, stopAfterFirst) { + if (stopAfterFirst == null) { stopAfterFirst = false; } + let index = matchers.length; + const result = []; + while (index--) { + if (matchers[index].match(filepath)) { + result.push(matchers[index]); + if (stopAfterFirst) { return result; } } - return result; } + return result; + } - isPathExcludedByGit(filepath) { - return (this.repo != null ? this.repo.isIgnored(this.repo.relativize(path.join(this.rootPath, filepath))) : undefined); - } + isPathExcludedByGit(filepath) { + return (this.repo != null ? this.repo.isIgnored(this.repo.relativize(path.join(this.rootPath, filepath))) : undefined); + } - // Given an array of `globalExclusions`, filter out any which have an - // `inclusion` defined for a subdirectory - overrideGlobalExclusions(globalExclusions, inclusions) { - const result = []; - let exclusionIndex = globalExclusions.length; - while (exclusionIndex--) { - let inclusionIndex = inclusions.length; - let requiresOverride = false; - - // Check if an inclusion is specified for a subdirectory of this globalExclusion - while (inclusionIndex--) { - if (this.isSubpathMatcher(globalExclusions[exclusionIndex], inclusions[inclusionIndex])) { - requiresOverride = true; - } + // Given an array of `globalExclusions`, filter out any which have an + // `inclusion` defined for a subdirectory + overrideGlobalExclusions(globalExclusions, inclusions) { + const result = []; + let exclusionIndex = globalExclusions.length; + while (exclusionIndex--) { + let inclusionIndex = inclusions.length; + let requiresOverride = false; + + // Check if an inclusion is specified for a subdirectory of this globalExclusion + while (inclusionIndex--) { + if (this.isSubpathMatcher(globalExclusions[exclusionIndex], inclusions[inclusionIndex])) { + requiresOverride = true; } - - if (!requiresOverride) { result.push(globalExclusions[exclusionIndex]); } } - return result; + + if (!requiresOverride) { result.push(globalExclusions[exclusionIndex]); } } + return result; + } - // Returns true if the `child` matcher is a subdirectory of the `parent` matcher - isSubpathMatcher(parent, child) { - // Strip off trailing wildcards from the parent pattern - let parentPattern = parent.pattern; - const directoryPattern = new RegExp(`\ + // Returns true if the `child` matcher is a subdirectory of the `parent` matcher + isSubpathMatcher(parent, child) { + // Strip off trailing wildcards from the parent pattern + let parentPattern = parent.pattern; + const directoryPattern = new RegExp(`\ ${'\\'+path.sep}\\*$|\ ${'\\'+path.sep}\\*\\*$\ `); - const matchIndex = parentPattern.search(directoryPattern); - if (matchIndex > -1) { parentPattern = parentPattern.slice(0, matchIndex); } + const matchIndex = parentPattern.search(directoryPattern); + if (matchIndex > -1) { parentPattern = parentPattern.slice(0, matchIndex); } - return child.pattern.substr(0, parentPattern.length) === parentPattern; - } + return child.pattern.substr(0, parentPattern.length) === parentPattern; + } - sanitizePaths(options) { - if (!(options.inclusions != null ? options.inclusions.length : undefined)) { return options; } - const inclusions = []; - for (let includedPath of Array.from(options.inclusions)) { - if (includedPath && (includedPath[0] === '!')) { - if (options.exclusions == null) { options.exclusions = []; } - options.exclusions.push(includedPath.slice(1)); - } else if (includedPath) { - inclusions.push(includedPath); - } + sanitizePaths(options) { + if (!(options.inclusions != null ? options.inclusions.length : undefined)) { return options; } + const inclusions = []; + for (let includedPath of Array.from(options.inclusions)) { + if (includedPath && (includedPath[0] === '!')) { + if (options.exclusions == null) { options.exclusions = []; } + options.exclusions.push(includedPath.slice(1)); + } else if (includedPath) { + inclusions.push(includedPath); } - options.inclusions = inclusions; - return options; } + options.inclusions = inclusions; + return options; + } - excludeHidden() { - const matcher = new Minimatch(".*", PathFilter.MINIMATCH_OPTIONS); - this.exclusions.file.push(matcher); - return this.exclusions.directory.push(matcher); - } + excludeHidden() { + const matcher = new Minimatch(".*", PathFilter.MINIMATCH_OPTIONS); + this.exclusions.file.push(matcher); + return this.exclusions.directory.push(matcher); + } - createMatchers(patterns, param) { - if (patterns == null) { patterns = []; } - if (param == null) { param = {}; } - const {deepMatch, disallowDuplicatesFrom} = param; - const addFileMatcher = (matchers, pattern) => { - if ((disallowDuplicatesFrom != null) && this.containsPattern(disallowDuplicatesFrom, 'file', pattern)) { return; } - return matchers.file.push(new Minimatch(pattern, PathFilter.MINIMATCH_OPTIONS)); - }; - - var addDirectoryMatcher = (matchers, pattern, deepMatch) => { - // It is important that we keep two permutations of directory patterns: - // - // * 'directory/anotherdir' - // * 'directory/anotherdir/**' - // - // Minimatch will return false if we were to match 'directory/anotherdir' - // against pattern 'directory/anotherdir/*'. And it will return false - // matching 'directory/anotherdir/file.txt' against pattern - // 'directory/anotherdir'. - - if (pattern[pattern.length - 1] === path.sep) { - pattern += '**'; - } + createMatchers(patterns, param) { + if (patterns == null) { patterns = []; } + if (param == null) { param = {}; } + const {deepMatch, disallowDuplicatesFrom} = param; + const addFileMatcher = (matchers, pattern) => { + if ((disallowDuplicatesFrom != null) && this.containsPattern(disallowDuplicatesFrom, 'file', pattern)) { return; } + return matchers.file.push(new Minimatch(pattern, PathFilter.MINIMATCH_OPTIONS)); + }; + + var addDirectoryMatcher = (matchers, pattern, deepMatch) => { + // It is important that we keep two permutations of directory patterns: + // + // * 'directory/anotherdir' + // * 'directory/anotherdir/**' + // + // Minimatch will return false if we were to match 'directory/anotherdir' + // against pattern 'directory/anotherdir/*'. And it will return false + // matching 'directory/anotherdir/file.txt' against pattern + // 'directory/anotherdir'. + + if (pattern[pattern.length - 1] === path.sep) { + pattern += '**'; + } - // When the user specifies to include a nested directory, we need to - // specify matchers up to the nested directory - // - // * User specifies 'some/directory/anotherdir/**' - // * We need to break it up into multiple matchers - // * 'some' - // * 'some/directory' - // - // Otherwise, we'll hit the 'some' directory, and if there is no matcher, - // it'll fail and have no chance at hitting the - // 'some/directory/anotherdir/**' matcher the user originally specified. - if (deepMatch) { - let needle; - const paths = pattern.split(path.sep); - let lastIndex = paths.length - 2; - if ((needle = paths[paths.length - 1], ['*', '**'].includes(needle))) { lastIndex--; } - - if (lastIndex >= 0) { - let deepPath = ''; - for (let i = 0, end = lastIndex, asc = 0 <= end; asc ? i <= end : i >= end; asc ? i++ : i--) { - deepPath = path.join(deepPath, paths[i]); - addDirectoryMatcher(matchers, deepPath); - } + // When the user specifies to include a nested directory, we need to + // specify matchers up to the nested directory + // + // * User specifies 'some/directory/anotherdir/**' + // * We need to break it up into multiple matchers + // * 'some' + // * 'some/directory' + // + // Otherwise, we'll hit the 'some' directory, and if there is no matcher, + // it'll fail and have no chance at hitting the + // 'some/directory/anotherdir/**' matcher the user originally specified. + if (deepMatch) { + let needle; + const paths = pattern.split(path.sep); + let lastIndex = paths.length - 2; + if ((needle = paths[paths.length - 1], ['*', '**'].includes(needle))) { lastIndex--; } + + if (lastIndex >= 0) { + let deepPath = ''; + for (let i = 0, end = lastIndex, asc = 0 <= end; asc ? i <= end : i >= end; asc ? i++ : i--) { + deepPath = path.join(deepPath, paths[i]); + addDirectoryMatcher(matchers, deepPath); } } + } - const directoryPattern = new RegExp(`\ + const directoryPattern = new RegExp(`\ ${'\\'+path.sep}\\*$|\ ${'\\'+path.sep}\\*\\*$\ `); - const matchIndex = pattern.search(directoryPattern); - if (matchIndex > -1) { addDirectoryMatcher(matchers, pattern.slice(0, matchIndex)); } + const matchIndex = pattern.search(directoryPattern); + if (matchIndex > -1) { addDirectoryMatcher(matchers, pattern.slice(0, matchIndex)); } - if ((disallowDuplicatesFrom != null) && this.containsPattern(disallowDuplicatesFrom, 'directory', pattern)) { return; } - return matchers.directory.push(new Minimatch(pattern, PathFilter.MINIMATCH_OPTIONS)); - }; + if ((disallowDuplicatesFrom != null) && this.containsPattern(disallowDuplicatesFrom, 'directory', pattern)) { return; } + return matchers.directory.push(new Minimatch(pattern, PathFilter.MINIMATCH_OPTIONS)); + }; - let pattern = null; - const matchers = { - file: [], - directory: [] - }; + let pattern = null; + const matchers = { + file: [], + directory: [] + }; - let r = patterns.length; - while (r--) { - pattern = patterns[r].trim(); - if ((pattern.length === 0) || (pattern[0] === '#')) { continue; } + let r = patterns.length; + while (r--) { + pattern = patterns[r].trim(); + if ((pattern.length === 0) || (pattern[0] === '#')) { continue; } - const endsWithSeparatorOrStar = new RegExp(`\ + const endsWithSeparatorOrStar = new RegExp(`\ ${'\\'+path.sep}$|\ ${'\\'+path.sep}\\**$\ `); - if (endsWithSeparatorOrStar.test(pattern)) { - // Is a dir if it ends in a '/' or '/*' - addDirectoryMatcher(matchers, pattern, deepMatch); - } else if (pattern.indexOf('*') < 0) { - - var stat; - try { - // Try our best to check if it's a directory - stat = fs.statSync(path.join(this.rootPath, pattern)); - } catch (e) { - stat = null; - } + if (endsWithSeparatorOrStar.test(pattern)) { + // Is a dir if it ends in a '/' or '/*' + addDirectoryMatcher(matchers, pattern, deepMatch); + } else if (pattern.indexOf('*') < 0) { + + var stat; + try { + // Try our best to check if it's a directory + stat = fs.statSync(path.join(this.rootPath, pattern)); + } catch (e) { + stat = null; + } - if ((stat != null ? stat.isFile() : undefined)) { - addFileMatcher(matchers, pattern); - } else { - addDirectoryMatcher(matchers, pattern + path.sep + '**', deepMatch); - } - } else { + if ((stat != null ? stat.isFile() : undefined)) { addFileMatcher(matchers, pattern); + } else { + addDirectoryMatcher(matchers, pattern + path.sep + '**', deepMatch); } + } else { + addFileMatcher(matchers, pattern); } - - return matchers; } - containsPattern(matchers, fileOrDirectory, pattern) { - for (let matcher of Array.from(matchers[fileOrDirectory])) { - if (matcher.pattern === pattern) { return true; } - } - return false; + return matchers; + } + + containsPattern(matchers, fileOrDirectory, pattern) { + for (let matcher of Array.from(matchers[fileOrDirectory])) { + if (matcher.pattern === pattern) { return true; } } + return false; } +} function __guard__(value, transform) { return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined; From e80770abb75364b34c34710c4ca59dd80adc2714 Mon Sep 17 00:00:00 2001 From: confused-Techie Date: Mon, 6 Jan 2025 19:02:52 -0800 Subject: [PATCH 19/20] Update file `bin` points at --- bin/scandal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/scandal b/bin/scandal index 1b13f44..22c3e30 100755 --- a/bin/scandal +++ b/bin/scandal @@ -1,2 +1,2 @@ #!/usr/bin/env node -require("../lib/scandal.js").main(); +require("../src/scandal.js").main(); From d3cdd282dd15682491542a8cb61e3d3b2fa370d9 Mon Sep 17 00:00:00 2001 From: confused-Techie Date: Mon, 6 Jan 2025 19:11:08 -0800 Subject: [PATCH 20/20] Remove `rimraf` a dependency of the `gruntfile` --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 43c857d..08d3414 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,6 @@ }, "devDependencies": { "jasmine-focused": "^1.0.7", - "rimraf": "^2.4.2", "wrench": "^1.5.8" } }