From 575afbe527a1214e5ad0917cc050a6951f9b65cf Mon Sep 17 00:00:00 2001 From: "jmordetsky@bloomberg.net" Date: Tue, 14 Jan 2020 09:35:18 -0500 Subject: [PATCH] fix: Make sure glob and minimatch are case insenitive on win32 Supplies `nocase` option to minimatch and glob when on win32. Additionally changes `test-excludes` extension check to be case insenitive. Adds tests to support this behavior. --- .gitignore | 1 + extension-matcher-posix.js | 5 +++++ extension-matcher-win32.js | 5 +++++ extension-matcher.js | 8 ++++++++ index.js | 13 +++++++------ is-outside-dir-posix.js | 9 +++++++-- is-outside-dir-win32.js | 15 ++++++++++----- nyc.config.js | 4 +++- tap-snapshots/test-glob.js-TAP.test.js | 7 +++++++ test/glob.js | 12 ++++++++++++ test/test-exclude.js | 20 ++++++++++++++++++++ 11 files changed, 85 insertions(+), 14 deletions(-) create mode 100644 extension-matcher-posix.js create mode 100644 extension-matcher-win32.js create mode 100644 extension-matcher.js diff --git a/.gitignore b/.gitignore index 633bf1d..6116f20 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ node_modules/* coverage .nyc_output +.idea \ No newline at end of file diff --git a/extension-matcher-posix.js b/extension-matcher-posix.js new file mode 100644 index 0000000..8aed959 --- /dev/null +++ b/extension-matcher-posix.js @@ -0,0 +1,5 @@ +'use-strict' + +module.exports = (filename) => { + return ext => filename.endsWith(ext); +}; diff --git a/extension-matcher-win32.js b/extension-matcher-win32.js new file mode 100644 index 0000000..8501e63 --- /dev/null +++ b/extension-matcher-win32.js @@ -0,0 +1,5 @@ +'use strict' + +module.exports = (filename) => { + return ext => filename.toLowerCase().endsWith(ext.toLowerCase()); +}; diff --git a/extension-matcher.js b/extension-matcher.js new file mode 100644 index 0000000..f2e6f42 --- /dev/null +++ b/extension-matcher.js @@ -0,0 +1,8 @@ +'use strict'; + +if (process.platform === 'win32') { + module.exports = require("./extension-matcher-win32"); +} else { + module.exports = require("./extension-matcher-posix"); +} + diff --git a/index.js b/index.js index aecb72d..4b568b2 100644 --- a/index.js +++ b/index.js @@ -5,7 +5,9 @@ const { promisify } = require('util'); const glob = promisify(require('glob')); const minimatch = require('minimatch'); const { defaults } = require('@istanbuljs/schema'); -const isOutsideDir = require('./is-outside-dir'); +const { isOutsideDir, minimatchOptions } = require('./is-outside-dir'); +const extensionMatcher = require("./extension-matcher"); + class TestExclude { constructor(opts = {}) { @@ -77,7 +79,7 @@ class TestExclude { shouldInstrument(filename, relFile) { if ( this.extension && - !this.extension.some(ext => filename.endsWith(ext)) + !this.extension.some(extensionMatcher(filename)) ) { return false; } @@ -95,8 +97,7 @@ class TestExclude { pathToCheck = relFile.replace(/^\.[\\/]/, ''); // remove leading './' or '.\'. } - const dot = { dot: true }; - const matches = pattern => minimatch(pathToCheck, pattern, dot); + const matches = pattern => minimatch(pathToCheck, pattern, minimatchOptions); return ( (!this.include || this.include.some(matches)) && (!this.exclude.some(matches) || this.excludeNegated.some(matches)) @@ -105,7 +106,7 @@ class TestExclude { globSync(cwd = this.cwd) { const globPatterns = getExtensionPattern(this.extension || []); - const globOptions = { cwd, nodir: true, dot: true }; + const globOptions = Object.assign({ cwd, nodir: true }, minimatchOptions); /* If we don't have any excludeNegated then we can optimize glob by telling * it to not iterate into unwanted directory trees (like node_modules). */ if (this.excludeNegated.length === 0) { @@ -119,7 +120,7 @@ class TestExclude { async glob(cwd = this.cwd) { const globPatterns = getExtensionPattern(this.extension || []); - const globOptions = { cwd, nodir: true, dot: true }; + const globOptions = Object.assign({ cwd, nodir: true }, minimatchOptions); /* If we don't have any excludeNegated then we can optimize glob by telling * it to not iterate into unwanted directory trees (like node_modules). */ if (this.excludeNegated.length === 0) { diff --git a/is-outside-dir-posix.js b/is-outside-dir-posix.js index d6a7275..4e5d66a 100644 --- a/is-outside-dir-posix.js +++ b/is-outside-dir-posix.js @@ -2,6 +2,11 @@ const path = require('path'); -module.exports = function(dir, filename) { - return /^\.\./.test(path.relative(dir, filename)); +module.exports = { + isOutsideDir(dir, filename) { + return /^\.\./.test(path.relative(dir, filename)); + }, + minimatchOptions: { + dot: true + } }; diff --git a/is-outside-dir-win32.js b/is-outside-dir-win32.js index 05e34a9..e28580f 100644 --- a/is-outside-dir-win32.js +++ b/is-outside-dir-win32.js @@ -2,9 +2,14 @@ const path = require('path'); const minimatch = require('minimatch'); - -const dot = { dot: true }; - -module.exports = function(dir, filename) { - return !minimatch(path.resolve(dir, filename), path.join(dir, '**'), dot); +const minimatchOptions = { + dot: true, + nocase: true // win32 should be case insensitive matches }; + +module.exports = { + isOutsideDir(dir, filename) { + return !minimatch(path.resolve(dir, filename), path.join(dir, '**'), minimatchOptions) + }, + minimatchOptions +} diff --git a/nyc.config.js b/nyc.config.js index 6914070..80d0d63 100644 --- a/nyc.config.js +++ b/nyc.config.js @@ -14,6 +14,8 @@ module.exports = { exclude: [ ...defaultExclude, 'is-outside-dir.js', - isWindows ? 'is-outside-dir-posix.js' : 'is-outside-dir-win32.js' + 'extension-matcher.js', + isWindows ? 'is-outside-dir-posix.js' : 'is-outside-dir-win32.js', + isWindows ? 'extension-matcher-posix.js' : 'extension-matcher-win32.js' ] }; diff --git a/tap-snapshots/test-glob.js-TAP.test.js b/tap-snapshots/test-glob.js-TAP.test.js index fe206cc..518a3cf 100644 --- a/tap-snapshots/test-glob.js-TAP.test.js +++ b/tap-snapshots/test-glob.js-TAP.test.js @@ -40,6 +40,13 @@ Array [ ] ` +exports[`test/glob.js TAP handles case insensitive matches on windows > must match snapshot 1`] = ` +Array [ + "file1.js", + "file2.js", +] +` + exports[`test/glob.js TAP should exclude the node_modules folder by default > absolute constructor cwd 1`] = ` Array [ "file1.js", diff --git a/test/glob.js b/test/glob.js index ba7a612..e2ca92b 100644 --- a/test/glob.js +++ b/test/glob.js @@ -95,3 +95,15 @@ t.test('allows negated include patterns', t => } }) ); + +if (process.platform === 'win32') { + t.test('handles case insensitive matches on windows', t => + testHelper(t, { + options: { + cwd, + extension: extension.toUpperCase(), + include: ['file1.js', 'FILE2.js'] + } + }) + ); +} diff --git a/test/test-exclude.js b/test/test-exclude.js index 643741c..3339821 100644 --- a/test/test-exclude.js +++ b/test/test-exclude.js @@ -67,6 +67,26 @@ if (process.platform === 'win32') { no: ['D:\\project\\foo.js'] }) ); + + t.test('should handle case insensitive drives on win32', t => + testHelper(t, { + options: { + cwd: 'C:\\project' + }, + yes: ['c:\\project\\foo.js'] + }) + ); + + t.test('should handle case insensitive paths when on win32', t => + testHelper(t, { + options: { + exclude: ['foo.js'], + include: ['boo.js'] + }, + no: ['FOO.js'], + yes: ['BOO.js'] + }) + ); } t.test('can instrument files outside cwd if relativePath=false', t =>