deno.land / std@0.166.0 / node / path / glob.ts

نووسراو ببینە
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
import { isWindows, osType } from "../../_util/os.ts";import { SEP, SEP_PATTERN } from "./separator.ts";import * as _win32 from "./win32.ts";import * as _posix from "./posix.ts";import type { OSType } from "../../_util/os.ts";
const path = isWindows ? _win32 : _posix;const { join, normalize } = path;
export interface GlobOptions { /** Extended glob syntax. * See https://www.linuxjournal.com/content/bash-extended-globbing. Defaults * to true. */ extended?: boolean; /** Globstar syntax. * See https://www.linuxjournal.com/content/globstar-new-bash-globbing-option. * If false, `**` is treated like `*`. Defaults to true. */ globstar?: boolean; /** Whether globstar should be case insensitive. */ caseInsensitive?: boolean; /** Operating system. Defaults to the native OS. */ os?: OSType;}
export type GlobToRegExpOptions = GlobOptions;
const regExpEscapeChars = [ "!", "$", "(", ")", "*", "+", ".", "=", "?", "[", "\\", "^", "{", "|",];const rangeEscapeChars = ["-", "\\", "]"];
/** Convert a glob string to a regular expression. * * Tries to match bash glob expansion as closely as possible. * * Basic glob syntax: * - `*` - Matches everything without leaving the path segment. * - `?` - Matches any single character. * - `{foo,bar}` - Matches `foo` or `bar`. * - `[abcd]` - Matches `a`, `b`, `c` or `d`. * - `[a-d]` - Matches `a`, `b`, `c` or `d`. * - `[!abcd]` - Matches any single character besides `a`, `b`, `c` or `d`. * - `[[:<class>:]]` - Matches any character belonging to `<class>`. * - `[[:alnum:]]` - Matches any digit or letter. * - `[[:digit:]abc]` - Matches any digit, `a`, `b` or `c`. * - See https://facelessuser.github.io/wcmatch/glob/#posix-character-classes * for a complete list of supported character classes. * - `\` - Escapes the next character for an `os` other than `"windows"`. * - \` - Escapes the next character for `os` set to `"windows"`. * - `/` - Path separator. * - `\` - Additional path separator only for `os` set to `"windows"`. * * Extended syntax: * - Requires `{ extended: true }`. * - `?(foo|bar)` - Matches 0 or 1 instance of `{foo,bar}`. * - `@(foo|bar)` - Matches 1 instance of `{foo,bar}`. They behave the same. * - `*(foo|bar)` - Matches _n_ instances of `{foo,bar}`. * - `+(foo|bar)` - Matches _n > 0_ instances of `{foo,bar}`. * - `!(foo|bar)` - Matches anything other than `{foo,bar}`. * - See https://www.linuxjournal.com/content/bash-extended-globbing. * * Globstar syntax: * - Requires `{ globstar: true }`. * - `**` - Matches any number of any path segments. * - Must comprise its entire path segment in the provided glob. * - See https://www.linuxjournal.com/content/globstar-new-bash-globbing-option. * * Note the following properties: * - The generated `RegExp` is anchored at both start and end. * - Repeating and trailing separators are tolerated. Trailing separators in the * provided glob have no meaning and are discarded. * - Absolute globs will only match absolute paths, etc. * - Empty globs will match nothing. * - Any special glob syntax must be contained to one path segment. For example, * `?(foo|bar/baz)` is invalid. The separator will take precedence and the * first segment ends with an unclosed group. * - If a path segment ends with unclosed groups or a dangling escape prefix, a * parse error has occurred. Every character for that segment is taken * literally in this event. * * Limitations: * - A negative group like `!(foo|bar)` will wrongly be converted to a negative * look-ahead followed by a wildcard. This means that `!(foo).js` will wrongly * fail to match `foobar.js`, even though `foobar` is not `foo`. Effectively, * `!(foo|bar)` is treated like `!(@(foo|bar)*)`. This will work correctly if * the group occurs not nested at the end of the segment. */export function globToRegExp( glob: string, { extended = true, globstar: globstarOption = true, os = osType, caseInsensitive = false, }: GlobToRegExpOptions = {},): RegExp { if (glob == "") { return /(?!)/; }
const sep = os == "windows" ? "(?:\\\\|/)+" : "/+"; const sepMaybe = os == "windows" ? "(?:\\\\|/)*" : "/*"; const seps = os == "windows" ? ["\\", "/"] : ["/"]; const globstar = os == "windows" ? "(?:[^\\\\/]*(?:\\\\|/|$)+)*" : "(?:[^/]*(?:/|$)+)*"; const wildcard = os == "windows" ? "[^\\\\/]*" : "[^/]*"; const escapePrefix = os == "windows" ? "`" : "\\";
// Remove trailing separators. let newLength = glob.length; for (; newLength > 1 && seps.includes(glob[newLength - 1]); newLength--); glob = glob.slice(0, newLength);
let regExpString = "";
// Terminates correctly. Trust that `j` is incremented every iteration. for (let j = 0; j < glob.length;) { let segment = ""; const groupStack: string[] = []; let inRange = false; let inEscape = false; let endsWithSep = false; let i = j;
// Terminates with `i` at the non-inclusive end of the current segment. for (; i < glob.length && !seps.includes(glob[i]); i++) { if (inEscape) { inEscape = false; const escapeChars = inRange ? rangeEscapeChars : regExpEscapeChars; segment += escapeChars.includes(glob[i]) ? `\\${glob[i]}` : glob[i]; continue; }
if (glob[i] == escapePrefix) { inEscape = true; continue; }
if (glob[i] == "[") { if (!inRange) { inRange = true; segment += "["; if (glob[i + 1] == "!") { i++; segment += "^"; } else if (glob[i + 1] == "^") { i++; segment += "\\^"; } continue; } else if (glob[i + 1] == ":") { let k = i + 1; let value = ""; while (glob[k + 1] != null && glob[k + 1] != ":") { value += glob[k + 1]; k++; } if (glob[k + 1] == ":" && glob[k + 2] == "]") { i = k + 2; if (value == "alnum") segment += "\\dA-Za-z"; else if (value == "alpha") segment += "A-Za-z"; else if (value == "ascii") segment += "\x00-\x7F"; else if (value == "blank") segment += "\t "; else if (value == "cntrl") segment += "\x00-\x1F\x7F"; else if (value == "digit") segment += "\\d"; else if (value == "graph") segment += "\x21-\x7E"; else if (value == "lower") segment += "a-z"; else if (value == "print") segment += "\x20-\x7E"; else if (value == "punct") { segment += "!\"#$%&'()*+,\\-./:;<=>?@[\\\\\\]^_‘{|}~"; } else if (value == "space") segment += "\\s\v"; else if (value == "upper") segment += "A-Z"; else if (value == "word") segment += "\\w"; else if (value == "xdigit") segment += "\\dA-Fa-f"; continue; } } }
if (glob[i] == "]" && inRange) { inRange = false; segment += "]"; continue; }
if (inRange) { if (glob[i] == "\\") { segment += `\\\\`; } else { segment += glob[i]; } continue; }
if ( glob[i] == ")" && groupStack.length > 0 && groupStack[groupStack.length - 1] != "BRACE" ) { segment += ")"; const type = groupStack.pop()!; if (type == "!") { segment += wildcard; } else if (type != "@") { segment += type; } continue; }
if ( glob[i] == "|" && groupStack.length > 0 && groupStack[groupStack.length - 1] != "BRACE" ) { segment += "|"; continue; }
if (glob[i] == "+" && extended && glob[i + 1] == "(") { i++; groupStack.push("+"); segment += "(?:"; continue; }
if (glob[i] == "@" && extended && glob[i + 1] == "(") { i++; groupStack.push("@"); segment += "(?:"; continue; }
if (glob[i] == "?") { if (extended && glob[i + 1] == "(") { i++; groupStack.push("?"); segment += "(?:"; } else { segment += "."; } continue; }
if (glob[i] == "!" && extended && glob[i + 1] == "(") { i++; groupStack.push("!"); segment += "(?!"; continue; }
if (glob[i] == "{") { groupStack.push("BRACE"); segment += "(?:"; continue; }
if (glob[i] == "}" && groupStack[groupStack.length - 1] == "BRACE") { groupStack.pop(); segment += ")"; continue; }
if (glob[i] == "," && groupStack[groupStack.length - 1] == "BRACE") { segment += "|"; continue; }
if (glob[i] == "*") { if (extended && glob[i + 1] == "(") { i++; groupStack.push("*"); segment += "(?:"; } else { const prevChar = glob[i - 1]; let numStars = 1; while (glob[i + 1] == "*") { i++; numStars++; } const nextChar = glob[i + 1]; if ( globstarOption && numStars == 2 && [...seps, undefined].includes(prevChar) && [...seps, undefined].includes(nextChar) ) { segment += globstar; endsWithSep = true; } else { segment += wildcard; } } continue; }
segment += regExpEscapeChars.includes(glob[i]) ? `\\${glob[i]}` : glob[i]; }
// Check for unclosed groups or a dangling backslash. if (groupStack.length > 0 || inRange || inEscape) { // Parse failure. Take all characters from this segment literally. segment = ""; for (const c of glob.slice(j, i)) { segment += regExpEscapeChars.includes(c) ? `\\${c}` : c; endsWithSep = false; } }
regExpString += segment; if (!endsWithSep) { regExpString += i < glob.length ? sep : sepMaybe; endsWithSep = true; }
// Terminates with `i` at the start of the next segment. while (seps.includes(glob[i])) i++;
// Check that the next value of `j` is indeed higher than the current value. if (!(i > j)) { throw new Error("Assertion failure: i > j (potential infinite loop)"); } j = i; }
regExpString = `^${regExpString}$`; return new RegExp(regExpString, caseInsensitive ? "i" : "");}
/** Test whether the given string is a glob */export function isGlob(str: string): boolean { const chars: Record<string, string> = { "{": "}", "(": ")", "[": "]" }; const regex = /\\(.)|(^!|\*|\?|[\].+)]\?|\[[^\\\]]+\]|\{[^\\}]+\}|\(\?[:!=][^\\)]+\)|\([^|]+\|[^\\)]+\))/;
if (str === "") { return false; }
let match: RegExpExecArray | null;
while ((match = regex.exec(str))) { if (match[2]) return true; let idx = match.index + match[0].length;
// if an open bracket/brace/paren is escaped, // set the index to the next closing character const open = match[1]; const close = open ? chars[open] : null; if (open && close) { const n = str.indexOf(close, idx); if (n !== -1) { idx = n + 1; } }
str = str.slice(idx); }
return false;}
/** Like normalize(), but doesn't collapse "**\/.." when `globstar` is true. */export function normalizeGlob( glob: string, { globstar = false }: GlobOptions = {},): string { if (glob.match(/\0/g)) { throw new Error(`Glob contains invalid characters: "${glob}"`); } if (!globstar) { return normalize(glob); } const s = SEP_PATTERN.source; const badParentPattern = new RegExp( `(?<=(${s}|^)\\*\\*${s})\\.\\.(?=${s}|$)`, "g", ); return normalize(glob.replace(badParentPattern, "\0")).replace(/\0/g, "..");}
/** Like join(), but doesn't collapse "**\/.." when `globstar` is true. */export function joinGlobs( globs: string[], { extended = true, globstar = false }: GlobOptions = {},): string { if (!globstar || globs.length == 0) { return join(...globs); } if (globs.length === 0) return "."; let joined: string | undefined; for (const glob of globs) { const path = glob; if (path.length > 0) { if (!joined) joined = path; else joined += `${SEP}${path}`; } } if (!joined) return "."; return normalizeGlob(joined, { extended, globstar });}
std

Version Info

Tagged at
a year ago