Skip to content

Commit a728675

Browse files
authoredApr 30, 2020
chore: update globby
BRAKING CHANGE: globby was update to latest version, some pattern can't work on windows, please read documentation
1 parent 64b2e1a commit a728675

10 files changed

+217
-228
lines changed
 

‎README.md

+22-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ Type: `String`
9595
Default: `undefined`
9696

9797
Glob or path from where we сopy files.
98-
Globs accept [minimatch options](https://github.com/isaacs/minimatch).
98+
Globs accept [micromatch options](https://github.com/micromatch/micromatch).
9999

100100
> ⚠️ Don't use directly `\\` in `from` (i.e `path\to\file.ext`) option because on UNIX the backslash is a valid character inside a path component, i.e., it's not a separator.
101101
> On Windows, the forward slash and the backward slash are both separators.
@@ -122,6 +122,27 @@ module.exports = {
122122
};
123123
```
124124

125+
##### `For windows`
126+
127+
If you define `from` as file path or folder path on `Windows`, you can use windows path segment (`\\`)
128+
129+
```
130+
...
131+
from: path.resolve('__dirname', 'file.txt'),
132+
...
133+
```
134+
135+
But you should always use forward-slashes in `glob` expressions
136+
See [fast-glob manual](https://github.com/mrmlnc/fast-glob#how-to-write-patterns-on-windows).
137+
138+
```
139+
...
140+
const FIXTURES_DIR_NORMALIZED = path.resolve(__dirname, 'fixtures').replace(/\\/g, '/');
141+
142+
from: path.posix.join(FIXTURES_DIR_NORMALIZED, 'file.txt'),
143+
...
144+
```
145+
125146
#### `to`
126147

127148
Type: `String`

‎package-lock.json

+68-76
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json

+2-3
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,8 @@
4343
"dependencies": {
4444
"cacache": "^15.0.0",
4545
"find-cache-dir": "^3.3.1",
46-
"glob-parent": "^3.1.0",
47-
"globby": "^7.1.1",
48-
"is-glob": "^4.0.1",
46+
"glob-parent": "^5.1.1",
47+
"globby": "^11.0.0",
4948
"loader-utils": "^2.0.0",
5049
"minimatch": "^3.0.4",
5150
"normalize-path": "^3.0.0",

‎src/preProcessPattern.js

+6-88
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,12 @@
11
import path from 'path';
22

3-
import isGlob from 'is-glob';
4-
import globParent from 'glob-parent';
5-
6-
import normalize from './utils/normalize';
73
import isTemplateLike from './utils/isTemplateLike';
84
import { stat } from './utils/promisify';
95

106
/* eslint-disable no-param-reassign */
117

128
export default function preProcessPattern(globalRef, pattern) {
13-
const {
14-
context,
15-
logger,
16-
inputFileSystem,
17-
fileDependencies,
18-
contextDependencies,
19-
compilation,
20-
} = globalRef;
9+
const { context, logger, inputFileSystem } = globalRef;
2110

2211
pattern =
2312
typeof pattern === 'string'
@@ -31,13 +20,12 @@ export default function preProcessPattern(globalRef, pattern) {
3120
pattern.context = path.join(context, pattern.context);
3221
}
3322

34-
const isFromGlobPatten = pattern.globOptions;
3523
// Todo remove this in next major
3624
const isToDirectory =
3725
path.extname(pattern.to) === '' || pattern.to.slice(-1) === path.sep;
3826

39-
// Normalize paths
40-
pattern.from = isFromGlobPatten ? pattern.from : path.normalize(pattern.from);
27+
pattern.fromOrigin = pattern.from;
28+
pattern.from = path.normalize(pattern.from);
4129
pattern.context = path.normalize(pattern.context);
4230
pattern.to = path.normalize(pattern.to);
4331

@@ -59,102 +47,32 @@ export default function preProcessPattern(globalRef, pattern) {
5947
pattern.toType = 'file';
6048
}
6149

62-
// If we know it's a glob, then bail early
63-
if (isFromGlobPatten) {
64-
logger.debug(`determined '${pattern.absoluteFrom}' is a glob`);
65-
66-
pattern.fromType = 'glob';
67-
68-
pattern.absoluteFrom = path.resolve(pattern.context, pattern.from);
69-
pattern.glob = normalize(pattern.context, pattern.from);
70-
pattern.globOptions = pattern.globOptions || {};
71-
72-
return Promise.resolve(pattern);
73-
}
74-
7550
if (path.isAbsolute(pattern.from)) {
7651
pattern.absoluteFrom = pattern.from;
7752
} else {
7853
pattern.absoluteFrom = path.resolve(pattern.context, pattern.from);
7954
}
8055

81-
logger.debug(
82-
`determined '${pattern.from}' to be read from '${pattern.absoluteFrom}'`
83-
);
84-
85-
const noStatsHandler = () => {
86-
// If from doesn't appear to be a glob, then log a warning
87-
if (isGlob(pattern.from) || pattern.from.includes('*')) {
88-
logger.debug(`determined '${pattern.absoluteFrom}' is a glob`);
89-
90-
pattern.fromType = 'glob';
91-
pattern.glob = normalize(pattern.context, pattern.from);
92-
93-
// We need to add context directory as dependencies to avoid problems when new files added in directories
94-
// when we already in watch mode and this directories are not in context dependencies
95-
// `glob-parent` always return `/` we need normalize path
96-
contextDependencies.add(path.normalize(globParent(pattern.absoluteFrom)));
97-
} else {
98-
const newWarning = new Error(
99-
`unable to locate '${pattern.from}' at '${pattern.absoluteFrom}'`
100-
);
101-
const hasWarning = compilation.warnings.some(
102-
// eslint-disable-next-line no-shadow
103-
(warning) => warning.message === newWarning.message
104-
);
105-
106-
// Only display the same message once
107-
if (!hasWarning) {
108-
logger.warn(newWarning.message);
109-
110-
compilation.warnings.push(newWarning);
111-
}
112-
113-
pattern.fromType = 'nonexistent';
114-
}
115-
};
116-
11756
logger.debug(
11857
`getting stats for '${pattern.absoluteFrom}' to determinate 'fromType'`
11958
);
12059

12160
return stat(inputFileSystem, pattern.absoluteFrom)
122-
.catch(() => noStatsHandler())
12361
.then((stats) => {
12462
if (!stats) {
125-
noStatsHandler();
126-
12763
return pattern;
12864
}
12965

13066
if (stats.isDirectory()) {
131-
logger.debug(`determined '${pattern.absoluteFrom}' is a directory`);
132-
133-
contextDependencies.add(pattern.absoluteFrom);
134-
13567
pattern.fromType = 'dir';
136-
pattern.context = pattern.absoluteFrom;
137-
pattern.glob = normalize(pattern.absoluteFrom, '**/*');
138-
pattern.absoluteFrom = path.join(pattern.absoluteFrom, '**/*');
139-
pattern.globOptions = {
140-
dot: true,
141-
};
14268
} else if (stats.isFile()) {
143-
logger.debug(`determined '${pattern.absoluteFrom}' is a file`);
144-
145-
fileDependencies.add(pattern.absoluteFrom);
146-
147-
pattern.stats = stats;
14869
pattern.fromType = 'file';
149-
pattern.context = path.dirname(pattern.absoluteFrom);
150-
pattern.glob = normalize(pattern.absoluteFrom);
151-
pattern.globOptions = {
152-
dot: true,
153-
};
70+
pattern.stats = stats;
15471
} else if (!pattern.fromType) {
15572
logger.warn(`unrecognized file type for ${pattern.from}`);
15673
}
15774

15875
return pattern;
159-
});
76+
})
77+
.catch(() => pattern);
16078
}

‎src/processPattern.js

+27-18
Original file line numberDiff line numberDiff line change
@@ -5,31 +5,40 @@ import pLimit from 'p-limit';
55
import minimatch from 'minimatch';
66

77
import isObject from './utils/isObject';
8+
import createPatternGlob from './utils/createPatternGlob';
89

9-
export default function processPattern(globalRef, pattern) {
10-
const { logger, output, concurrency } = globalRef;
11-
const globOptions = Object.assign(
12-
{
13-
cwd: pattern.context,
14-
follow: true,
15-
// Todo in next major release
16-
// dot: false
17-
},
18-
pattern.globOptions || {}
19-
);
10+
/* eslint-disable no-param-reassign */
2011

21-
if (pattern.fromType === 'nonexistent') {
22-
return Promise.resolve();
23-
}
12+
export default function processPattern(globalRef, pattern) {
13+
const { logger, output, concurrency, compilation } = globalRef;
14+
createPatternGlob(pattern, globalRef);
2415

2516
const limit = pLimit(concurrency || 100);
2617

2718
logger.log(
2819
`begin globbing '${pattern.glob}' with a context of '${pattern.context}'`
2920
);
3021

31-
return globby(pattern.glob, globOptions).then((paths) =>
32-
Promise.all(
22+
return globby(pattern.glob, pattern.globOptions).then((paths) => {
23+
if (paths.length === 0) {
24+
const newWarning = new Error(
25+
`unable to locate '${pattern.from}' at '${pattern.absoluteFrom}'`
26+
);
27+
const hasWarning = compilation.warnings.some(
28+
// eslint-disable-next-line no-shadow
29+
(warning) => warning.message === newWarning.message
30+
);
31+
32+
// Only display the same message once
33+
if (!hasWarning) {
34+
logger.warn(newWarning.message);
35+
36+
compilation.warnings.push(newWarning);
37+
}
38+
39+
return Promise.resolve();
40+
}
41+
return Promise.all(
3342
paths.map((from) =>
3443
limit(() => {
3544
const file = {
@@ -107,6 +116,6 @@ export default function processPattern(globalRef, pattern) {
107116
return file;
108117
})
109118
)
110-
)
111-
);
119+
);
120+
});
112121
}

‎src/utils/createPatternGlob.js

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import path from 'path';
2+
3+
import normalizePath from 'normalize-path';
4+
import globParent from 'glob-parent';
5+
6+
/* eslint-disable no-param-reassign */
7+
8+
function getAbsoluteContext(context) {
9+
const result = normalizePath(path.resolve(context));
10+
11+
return result.replace(
12+
// eslint-disable-next-line no-useless-escape
13+
/[\*|\?|\!|\||\@|\+|\(|\)|\[|\]|\{|\}]/g,
14+
(substring) => `\\${substring}`
15+
);
16+
}
17+
18+
function createPatternGlob(pattern, globalRef) {
19+
const { logger, fileDependencies, contextDependencies } = globalRef;
20+
21+
pattern.globOptions = Object.assign(
22+
{
23+
cwd: pattern.context,
24+
followSymbolicLinks: true,
25+
},
26+
pattern.globOptions || {}
27+
);
28+
29+
switch (pattern.fromType) {
30+
case 'dir':
31+
logger.debug(`determined '${pattern.absoluteFrom}' is a directory`);
32+
contextDependencies.add(pattern.absoluteFrom);
33+
34+
pattern.context = pattern.absoluteFrom;
35+
pattern.glob = path.posix.join(
36+
getAbsoluteContext(pattern.absoluteFrom),
37+
'**/*'
38+
);
39+
pattern.absoluteFrom = path.join(pattern.absoluteFrom, '**/*');
40+
pattern.globOptions = {
41+
dot: true,
42+
};
43+
break;
44+
45+
case 'file':
46+
logger.debug(`determined '${pattern.absoluteFrom}' is a file`);
47+
fileDependencies.add(pattern.absoluteFrom);
48+
49+
pattern.context = path.dirname(pattern.absoluteFrom);
50+
pattern.glob = getAbsoluteContext(pattern.absoluteFrom);
51+
pattern.globOptions = {
52+
dot: true,
53+
};
54+
break;
55+
56+
default:
57+
logger.debug(`determined '${pattern.absoluteFrom}' is a glob`);
58+
contextDependencies.add(path.normalize(globParent(pattern.absoluteFrom)));
59+
60+
pattern.fromType = 'glob';
61+
pattern.globOptions = pattern.globOptions || {};
62+
pattern.glob = path.isAbsolute(pattern.fromOrigin)
63+
? pattern.fromOrigin
64+
: path.posix.join(
65+
getAbsoluteContext(pattern.context),
66+
pattern.fromOrigin
67+
);
68+
}
69+
70+
return pattern;
71+
}
72+
73+
export default createPatternGlob;

‎src/utils/normalize.js

-32
This file was deleted.

‎test/CopyPlugin.test.js

+6-3
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,8 @@ describe('apply function', () => {
235235
.catch(done);
236236
});
237237

238-
it('should work with windows path segment separation path when "from" is glob', (done) => {
238+
// Windows path segment (\\) can not use as path segment in glob, but can use as dirname at linux
239+
it.skip('should work with windows path segment separation path when "from" is glob', (done) => {
239240
runEmit({
240241
expectedAssetKeys: ['directory/nested/nestedfile.txt'],
241242
patterns: [
@@ -248,7 +249,8 @@ describe('apply function', () => {
248249
.catch(done);
249250
});
250251

251-
it('should work with mixed path segment separation path when "from" is glob', (done) => {
252+
// Windows path segment (\\) can not use as path segment in glob, but can use as dirname at linux
253+
it.skip('should work with mixed path segment separation path when "from" is glob', (done) => {
252254
runEmit({
253255
expectedAssetKeys: ['directory/nested/nestedfile.txt'],
254256
patterns: [
@@ -282,7 +284,8 @@ describe('apply function', () => {
282284
.catch(done);
283285
});
284286

285-
it('should exclude path with windows path segment separators', (done) => {
287+
// Windows path segment (\\) can not use as path segment in glob, but can use as dirname at linux
288+
it.skip('should exclude path with windows path segment separators', (done) => {
286289
runEmit({
287290
expectedAssetKeys: [
288291
'[!]/hello.txt',

‎test/context-option.test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ describe('context option', () => {
151151
expectedAssetKeys: ['(special-*file).txt'],
152152
patterns: [
153153
{
154-
from: '(special-*file).txt',
154+
from: '\\(special-*file\\).txt',
155155
context: '[special?directory]',
156156
},
157157
],

‎test/from-option.test.js

+12-6
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ import path from 'path';
33
import { runEmit } from './helpers/run';
44

55
const FIXTURES_DIR = path.join(__dirname, 'fixtures');
6+
const FIXTURES_DIR_NORMALIZED = path
7+
.join(__dirname, 'fixtures')
8+
.replace(/\\/g, '/');
69

710
describe('from option', () => {
811
describe('is a file', () => {
@@ -24,7 +27,7 @@ describe('from option', () => {
2427
expectedAssetKeys: ['file.txt'],
2528
patterns: [
2629
{
27-
from: path.join(FIXTURES_DIR, 'file.txt'),
30+
from: path.posix.join(FIXTURES_DIR_NORMALIZED, 'file.txt'),
2831
},
2932
],
3033
})
@@ -50,7 +53,10 @@ describe('from option', () => {
5053
expectedAssetKeys: ['directoryfile.txt'],
5154
patterns: [
5255
{
53-
from: path.join(FIXTURES_DIR, 'directory/directoryfile.txt'),
56+
from: path.posix.join(
57+
FIXTURES_DIR_NORMALIZED,
58+
'directory/directoryfile.txt'
59+
),
5460
},
5561
],
5662
})
@@ -216,7 +222,7 @@ describe('from option', () => {
216222
],
217223
patterns: [
218224
{
219-
from: path.join(FIXTURES_DIR, 'directory'),
225+
from: path.posix.join(FIXTURES_DIR_NORMALIZED, 'directory'),
220226
},
221227
],
222228
})
@@ -260,7 +266,7 @@ describe('from option', () => {
260266
expectedAssetKeys: ['deep-nested/deepnested.txt', 'nestedfile.txt'],
261267
patterns: [
262268
{
263-
from: path.join(FIXTURES_DIR, 'directory/nested'),
269+
from: path.posix.join(FIXTURES_DIR_NORMALIZED, 'directory/nested'),
264270
},
265271
],
266272
})
@@ -306,7 +312,7 @@ describe('from option', () => {
306312
expectedAssetKeys: ['file.txt'],
307313
patterns: [
308314
{
309-
from: path.join(FIXTURES_DIR, '*.txt'),
315+
from: path.posix.join(FIXTURES_DIR_NORMALIZED, '*.txt'),
310316
},
311317
],
312318
})
@@ -359,7 +365,7 @@ describe('from option', () => {
359365
],
360366
patterns: [
361367
{
362-
from: path.join(FIXTURES_DIR, '**/*.txt'),
368+
from: path.posix.join(FIXTURES_DIR_NORMALIZED, '**/*.txt'),
363369
},
364370
],
365371
})

0 commit comments

Comments
 (0)
Please sign in to comment.