From f923d5c4fb629dda881c2d6eb9e7a547d2de2e66 Mon Sep 17 00:00:00 2001 From: sttk Date: Sat, 1 Jul 2023 16:38:08 +0900 Subject: [PATCH 1/2] fix: support windows path with drive or unc volume --- .npmrc | 1 + index.js | 33 +++++++++++++++++++-- test/.gitkeep | 0 test/index.test.js | 71 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 102 insertions(+), 3 deletions(-) create mode 100644 .npmrc delete mode 100644 test/.gitkeep diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..43c97e7 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +package-lock=false diff --git a/index.js b/index.js index 09dde64..8bd6929 100644 --- a/index.js +++ b/index.js @@ -1,7 +1,8 @@ 'use strict'; var isGlob = require('is-glob'); -var pathPosixDirname = require('path').posix.dirname; +var path = require('path'); +var pathPosixDirname = path.posix.dirname; var isWin32 = require('os').platform() === 'win32'; var slash = '/'; @@ -16,6 +17,12 @@ var escaped = /\\([!*?|[\](){}])/g; module.exports = function globParent(str, opts) { var options = Object.assign({ flipBackslashes: true }, opts); + var winDriveOrUncVolume = ''; + if (isWin32) { + winDriveOrUncVolume = getWinDriveOrUncVolume(str); + str = str.slice(winDriveOrUncVolume.length); + } + // flip windows path separators if (options.flipBackslashes && isWin32 && str.indexOf(slash) < 0) { str = str.replace(backslash, slash); @@ -28,14 +35,23 @@ module.exports = function globParent(str, opts) { // preserves full path in case of trailing path separator str += 'a'; - + // remove path parts that are globby do { str = pathPosixDirname(str); } while (isGlobby(str)); // remove escape chars and return result - return str.replace(escaped, '$1'); + str = str.replace(escaped, '$1'); + + // replace continuous slashes to single slash + str = str.replace(/\/+/g, '/'); + + if (isWin32 && winDriveOrUncVolume) { + str = winDriveOrUncVolume + str; + } + + return str; }; function isEnclosure(str) { @@ -73,3 +89,14 @@ function isGlobby(str) { } return isGlob(str); } + +function getWinDriveOrUncVolume(fp) { + if (/^([a-zA-Z]:|\\\\)/.test(fp)) { + var root = path.win32.parse(fp).root; + if (path.win32.isAbsolute(fp)) { + root = root.slice(0, -1); // Strip last path separator + } + return root; + } + return ''; +} diff --git a/test/.gitkeep b/test/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/test/index.test.js b/test/index.test.js index 5889412..be370be 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -10,6 +10,10 @@ describe('glob-parent', function () { expect(gp('.*')).toEqual('.'); expect(gp('/.*')).toEqual('/'); expect(gp('/.*/')).toEqual('/'); + expect(gp('//')).toEqual('/'); + expect(gp('//*')).toEqual('/'); + expect(gp('.//')).toEqual('./'); + expect(gp('.//*')).toEqual('./'); expect(gp('a/.*/b')).toEqual('a'); expect(gp('a*/.*/b')).toEqual('.'); expect(gp('*/a/b/c')).toEqual('.'); @@ -258,4 +262,71 @@ if (isWin32) { done(); }); }); + + describe('windows path with drive or UNC volume', function() { + it('should return parent dirname from absolute path with drive letter', function(done) { + expect(gp('C:/')).toEqual('C:/'); + expect(gp('C:/.')).toEqual('C:/'); + expect(gp('C:/*')).toEqual('C:/'); + expect(gp('C:/./*')).toEqual('C:/.'); + expect(gp('C://')).toEqual('C:/'); + expect(gp('C://*')).toEqual('C:/'); + expect(gp('C:/path/*.js')).toEqual('C:/path'); + + expect(gp('C:\\')).toEqual('C:/'); + expect(gp('C:\\.')).toEqual('C:/'); + expect(gp('C:\\*')).toEqual('C:/'); + expect(gp('C:\\.\\*')).toEqual('C:/.'); + expect(gp('C:\\\\')).toEqual('C:/'); + expect(gp('C:\\\\*')).toEqual('C:/'); + expect(gp('C:\\path\\*.js')).toEqual('C:/path'); + + done(); + }); + + it('should return parent dirname from relative path with drive letter', function(done) { + expect(gp('C:')).toEqual('C:.'); + expect(gp('C:.')).toEqual('C:.'); + expect(gp('C:*')).toEqual('C:.'); + expect(gp('C:./*')).toEqual('C:.'); + expect(gp('C:.//')).toEqual('C:./'); + expect(gp('C:.//*')).toEqual('C:./'); + expect(gp('C:path/*.js')).toEqual('C:path'); + + expect(gp('C:.\\*')).toEqual('C:.'); + expect(gp('C:.\\\\')).toEqual('C:./'); + expect(gp('C:.\\\\*')).toEqual('C:./'); + expect(gp('C:path\\*.js')).toEqual('C:path'); + + done(); + }); + + it('should return parent dirname from UNC path', function(done) { + expect(gp('\\\\System07\\C$/')).toEqual('\\\\System07\\C$/'); + expect(gp('\\\\System07\\C$/.')).toEqual('\\\\System07\\C$/'); + expect(gp('\\\\System07\\C$/*')).toEqual('\\\\System07\\C$/'); + expect(gp('\\\\System07\\C$/./*')).toEqual('\\\\System07\\C$/.'); + expect(gp('\\\\System07\\C$//')).toEqual('\\\\System07\\C$/'); + expect(gp('\\\\System07\\C$//*')).toEqual('\\\\System07\\C$/'); + expect(gp('\\\\System07\\C$/path/*.js')).toEqual('\\\\System07\\C$/path'); + + expect(gp('\\\\System07\\C$/', { flipBackslashes: false })).toEqual('\\\\System07\\C$/'); + expect(gp('\\\\System07\\C$/.', { flipBackslashes: false })).toEqual('\\\\System07\\C$/'); + expect(gp('\\\\System07\\C$/*', { flipBackslashes: false })).toEqual('\\\\System07\\C$/'); + expect(gp('\\\\System07\\C$/./*', { flipBackslashes: false })).toEqual('\\\\System07\\C$/.'); + expect(gp('\\\\System07\\C$//', { flipBackslashes: false })).toEqual('\\\\System07\\C$/'); + expect(gp('\\\\System07\\C$//*', { flipBackslashes: false })).toEqual('\\\\System07\\C$/'); + expect(gp('\\\\System07\\C$/path/*.js')).toEqual('\\\\System07\\C$/path'); + + expect(gp('\\\\System07\\C$\\')).toEqual('\\\\System07\\C$/'); + expect(gp('\\\\System07\\C$\\.')).toEqual('\\\\System07\\C$/'); + expect(gp('\\\\System07\\C$\\*')).toEqual('\\\\System07\\C$/'); + expect(gp('\\\\System07\\C$\\.\\*')).toEqual('\\\\System07\\C$/.'); + expect(gp('\\\\System07\\C$\\\\')).toEqual('\\\\System07\\C$/'); + expect(gp('\\\\System07\\C$\\\\*')).toEqual('\\\\System07\\C$/'); + expect(gp('\\\\System07\\C$\\path\\*.js')).toEqual('\\\\System07\\C$/path'); + + done(); + }); + }); } From bfbf638a650998278ece87209cee3d4f71976d5d Mon Sep 17 00:00:00 2001 From: sttk Date: Sun, 9 Jul 2023 11:08:19 +0900 Subject: [PATCH 2/2] fix: remove last '.' and './' --- index.js | 12 +++++++++ test/index.test.js | 63 +++++++++++++++++++++++++++++++++++----------- 2 files changed, 61 insertions(+), 14 deletions(-) diff --git a/index.js b/index.js index 8bd6929..8811365 100644 --- a/index.js +++ b/index.js @@ -47,7 +47,19 @@ module.exports = function globParent(str, opts) { // replace continuous slashes to single slash str = str.replace(/\/+/g, '/'); + // remove last single dot + if (str.slice(-2) === '/.') { + str = str.slice(0, -1) + } + // remove last './' + while (str.slice(-3) === '/./') { + str = str.slice(0, -2) + } + if (isWin32 && winDriveOrUncVolume) { + if (str === '.' || str === './') { + str = ''; + } str = winDriveOrUncVolume + str; } diff --git a/test/index.test.js b/test/index.test.js index be370be..b42d06d 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -252,6 +252,41 @@ describe('glob2base test patterns', function () { gp('/('.repeat(500000) + ')'); done(); }); + + it('should remove tail \'.\' and \'./\'', function(done) { + expect(gp('foo/./*')).toEqual('foo/'); + expect(gp('foo/./././*')).toEqual('foo/'); + expect(gp('./././*')).toEqual('./'); + expect(gp('/./././*')).toEqual('/'); + + if (isWin32) { + expect(gp('C:/foo/./*')).toEqual('C:/foo/'); + expect(gp('C:/foo/./././*')).toEqual('C:/foo/'); + expect(gp('C:/./././*')).toEqual('C:/'); + + expect(gp('C:\\foo\\.\\*')).toEqual('C:/foo/'); + expect(gp('C:\\foo\\.\\.\\.\\*')).toEqual('C:/foo/'); + expect(gp('C:\\.\\.\\.\\*')).toEqual('C:/'); + + expect(gp('C:foo/./*')).toEqual('C:foo/'); + expect(gp('C:foo/./././*')).toEqual('C:foo/'); + expect(gp('C:./././*')).toEqual('C:'); + + expect(gp('C:foo\\.\\*')).toEqual('C:foo/'); + expect(gp('C:foo\\.\\.\\.\\*')).toEqual('C:foo/'); + expect(gp('C:.\\.\\.\\*')).toEqual('C:'); + + expect(gp('\\\\System07\\C$/foo/./*')).toEqual('\\\\System07\\C$/foo/'); + expect(gp('\\\\System07\\C$/foo/./././*')).toEqual('\\\\System07\\C$/foo/'); + expect(gp('\\\\System07\\C$/./././*')).toEqual('\\\\System07\\C$/'); + + expect(gp('\\\\System07\\C$\\foo\\.\\*')).toEqual('\\\\System07\\C$/foo/'); + expect(gp('\\\\System07\\C$\\foo\\.\\.\\.\\*')).toEqual('\\\\System07\\C$/foo/'); + expect(gp('\\\\System07\\C$\\.\\.\\.\\*')).toEqual('\\\\System07\\C$/'); + } + + done(); + }); }); if (isWin32) { @@ -268,7 +303,7 @@ if (isWin32) { expect(gp('C:/')).toEqual('C:/'); expect(gp('C:/.')).toEqual('C:/'); expect(gp('C:/*')).toEqual('C:/'); - expect(gp('C:/./*')).toEqual('C:/.'); + expect(gp('C:/./*')).toEqual('C:/'); expect(gp('C://')).toEqual('C:/'); expect(gp('C://*')).toEqual('C:/'); expect(gp('C:/path/*.js')).toEqual('C:/path'); @@ -276,7 +311,7 @@ if (isWin32) { expect(gp('C:\\')).toEqual('C:/'); expect(gp('C:\\.')).toEqual('C:/'); expect(gp('C:\\*')).toEqual('C:/'); - expect(gp('C:\\.\\*')).toEqual('C:/.'); + expect(gp('C:\\.\\*')).toEqual('C:/'); expect(gp('C:\\\\')).toEqual('C:/'); expect(gp('C:\\\\*')).toEqual('C:/'); expect(gp('C:\\path\\*.js')).toEqual('C:/path'); @@ -285,17 +320,17 @@ if (isWin32) { }); it('should return parent dirname from relative path with drive letter', function(done) { - expect(gp('C:')).toEqual('C:.'); - expect(gp('C:.')).toEqual('C:.'); - expect(gp('C:*')).toEqual('C:.'); - expect(gp('C:./*')).toEqual('C:.'); - expect(gp('C:.//')).toEqual('C:./'); - expect(gp('C:.//*')).toEqual('C:./'); + expect(gp('C:')).toEqual('C:'); + expect(gp('C:.')).toEqual('C:'); + expect(gp('C:*')).toEqual('C:'); + expect(gp('C:./*')).toEqual('C:'); + expect(gp('C:.//')).toEqual('C:'); + expect(gp('C:.//*')).toEqual('C:'); expect(gp('C:path/*.js')).toEqual('C:path'); - expect(gp('C:.\\*')).toEqual('C:.'); - expect(gp('C:.\\\\')).toEqual('C:./'); - expect(gp('C:.\\\\*')).toEqual('C:./'); + expect(gp('C:.\\*')).toEqual('C:'); + expect(gp('C:.\\\\')).toEqual('C:'); + expect(gp('C:.\\\\*')).toEqual('C:'); expect(gp('C:path\\*.js')).toEqual('C:path'); done(); @@ -305,7 +340,7 @@ if (isWin32) { expect(gp('\\\\System07\\C$/')).toEqual('\\\\System07\\C$/'); expect(gp('\\\\System07\\C$/.')).toEqual('\\\\System07\\C$/'); expect(gp('\\\\System07\\C$/*')).toEqual('\\\\System07\\C$/'); - expect(gp('\\\\System07\\C$/./*')).toEqual('\\\\System07\\C$/.'); + expect(gp('\\\\System07\\C$/./*')).toEqual('\\\\System07\\C$/'); expect(gp('\\\\System07\\C$//')).toEqual('\\\\System07\\C$/'); expect(gp('\\\\System07\\C$//*')).toEqual('\\\\System07\\C$/'); expect(gp('\\\\System07\\C$/path/*.js')).toEqual('\\\\System07\\C$/path'); @@ -313,7 +348,7 @@ if (isWin32) { expect(gp('\\\\System07\\C$/', { flipBackslashes: false })).toEqual('\\\\System07\\C$/'); expect(gp('\\\\System07\\C$/.', { flipBackslashes: false })).toEqual('\\\\System07\\C$/'); expect(gp('\\\\System07\\C$/*', { flipBackslashes: false })).toEqual('\\\\System07\\C$/'); - expect(gp('\\\\System07\\C$/./*', { flipBackslashes: false })).toEqual('\\\\System07\\C$/.'); + expect(gp('\\\\System07\\C$/./*', { flipBackslashes: false })).toEqual('\\\\System07\\C$/'); expect(gp('\\\\System07\\C$//', { flipBackslashes: false })).toEqual('\\\\System07\\C$/'); expect(gp('\\\\System07\\C$//*', { flipBackslashes: false })).toEqual('\\\\System07\\C$/'); expect(gp('\\\\System07\\C$/path/*.js')).toEqual('\\\\System07\\C$/path'); @@ -321,7 +356,7 @@ if (isWin32) { expect(gp('\\\\System07\\C$\\')).toEqual('\\\\System07\\C$/'); expect(gp('\\\\System07\\C$\\.')).toEqual('\\\\System07\\C$/'); expect(gp('\\\\System07\\C$\\*')).toEqual('\\\\System07\\C$/'); - expect(gp('\\\\System07\\C$\\.\\*')).toEqual('\\\\System07\\C$/.'); + expect(gp('\\\\System07\\C$\\.\\*')).toEqual('\\\\System07\\C$/'); expect(gp('\\\\System07\\C$\\\\')).toEqual('\\\\System07\\C$/'); expect(gp('\\\\System07\\C$\\\\*')).toEqual('\\\\System07\\C$/'); expect(gp('\\\\System07\\C$\\path\\*.js')).toEqual('\\\\System07\\C$/path');