Skip to content

Commit 07c7606

Browse files
committed
chore: use primordials
1 parent 402b059 commit 07c7606

File tree

8 files changed

+364
-193
lines changed

8 files changed

+364
-193
lines changed

package-lock.json

Lines changed: 13 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@
5555
"node": ">=16 || 14 >=14.17"
5656
},
5757
"dependencies": {
58-
"brace-expansion": "^2.0.1"
58+
"brace-expansion": "^2.0.1",
59+
"node-primordials": "github:MoLow/node-primordials#tmp-build"
5960
},
6061
"devDependencies": {
6162
"@types/brace-expansion": "^1.1.0",

src/ast.ts

Lines changed: 103 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,21 @@
33
import { parseClass } from './brace-expressions.js'
44
import { MinimatchOptions, MMRegExp } from './index.js'
55
import { unescape } from './unescape.js'
6+
import {
7+
ArrayPrototypeFilter,
8+
ArrayPrototypeJoin,
9+
ArrayPrototypeMap,
10+
ArrayPrototypePop,
11+
ArrayPrototypePush,
12+
ArrayPrototypeSlice,
13+
ObjectAssign,
14+
SafeSet,
15+
StringPrototypeCharAt,
16+
StringPrototypeReplace,
17+
StringPrototypeSubstring,
18+
StringPrototypeToLowerCase,
19+
StringPrototypeToUpperCase,
20+
} from './primordials.js'
621

722
// classes [] are handled by the parseClass method
823
// for positive extglobs, we sub-parse the contents, and combine,
@@ -42,7 +57,7 @@ import { unescape } from './unescape.js'
4257
// ['^a(?:i|w(?:(?!(?:x|y).*zb$).*)z|j)b$']
4358

4459
export type ExtglobType = '!' | '?' | '+' | '*' | '@'
45-
const types = new Set<ExtglobType>(['!', '?', '+', '*', '@'])
60+
const types = new SafeSet<ExtglobType>(['!', '?', '+', '*', '@'])
4661
const isExtglobType = (c: string): c is ExtglobType =>
4762
types.has(c as ExtglobType)
4863

@@ -56,12 +71,28 @@ const startNoDot = '(?!\\.)'
5671
// characters that indicate a start of pattern needs the "no dots" bit,
5772
// because a dot *might* be matched. ( is not in the list, because in
5873
// the case of a child extglob, it will handle the prevention itself.
59-
const addPatternStart = new Set(['[', '.'])
74+
const addPatternStart = new SafeSet(['[', '.'])
6075
// cases where traversal is A-OK, no dot prevention needed
61-
const justDots = new Set(['..', '.'])
62-
const reSpecials = new Set('().*{}+?[]^$\\!')
76+
const justDots = new SafeSet(['..', '.'])
77+
const reSpecials = new SafeSet([
78+
"'",
79+
'(',
80+
')',
81+
'.',
82+
'*',
83+
'{',
84+
'}',
85+
'+',
86+
'?',
87+
'[',
88+
']',
89+
'^',
90+
'$',
91+
'\\',
92+
'!',
93+
])
6394
const regExpEscape = (s: string) =>
64-
s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&')
95+
StringPrototypeReplace(s, /[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&')
6596

6697
// any single thing other than /
6798
const qmark = '[^/]'
@@ -95,7 +126,7 @@ export class AST {
95126
constructor(
96127
type: ExtglobType | null,
97128
parent?: AST,
98-
options: MinimatchOptions = {}
129+
options: MinimatchOptions = { __proto__: null } as MinimatchOptions
99130
) {
100131
this.type = type
101132
// extglobs are inherently magical
@@ -104,15 +135,17 @@ export class AST {
104135
this.#root = this.#parent ? this.#parent.#root : this
105136
this.#options = this.#root === this ? options : this.#root.#options
106137
this.#negs = this.#root === this ? [] : this.#root.#negs
107-
if (type === '!' && !this.#root.#filledNegs) this.#negs.push(this)
138+
if (type === '!' && !this.#root.#filledNegs)
139+
ArrayPrototypePush(this.#negs, this)
108140
this.#parentIndex = this.#parent ? this.#parent.#parts.length : 0
109141
}
110142

111143
get hasMagic(): boolean | undefined {
112144
/* c8 ignore start */
113145
if (this.#hasMagic !== undefined) return this.#hasMagic
114146
/* c8 ignore stop */
115-
for (const p of this.#parts) {
147+
for (let i = 0; i < this.#parts.length; i++) {
148+
const p = this.#parts[i]
116149
if (typeof p === 'string') continue
117150
if (p.type || p.hasMagic) return (this.#hasMagic = true)
118151
}
@@ -123,11 +156,12 @@ export class AST {
123156
// reconstructs the pattern
124157
toString(): string {
125158
if (this.#toString !== undefined) return this.#toString
159+
const parts = ArrayPrototypeMap(this.#parts, p => String(p))
126160
if (!this.type) {
127-
return (this.#toString = this.#parts.map(p => String(p)).join(''))
161+
return (this.#toString = ArrayPrototypeJoin(parts, ''))
128162
} else {
129163
return (this.#toString =
130-
this.type + '(' + this.#parts.map(p => String(p)).join('|') + ')')
164+
this.type + '(' + ArrayPrototypeJoin(parts, '|') + ')')
131165
}
132166
}
133167

@@ -141,7 +175,7 @@ export class AST {
141175
this.toString()
142176
this.#filledNegs = true
143177
let n: AST | undefined
144-
while ((n = this.#negs.pop())) {
178+
while ((n = ArrayPrototypePop(this.#negs))) {
145179
if (n.type !== '!') continue
146180
// walk up the tree, appending everthing that comes AFTER parentIndex
147181
let p: AST | undefined = n
@@ -152,7 +186,8 @@ export class AST {
152186
!pp.type && i < pp.#parts.length;
153187
i++
154188
) {
155-
for (const part of n.#parts) {
189+
for (let y = 0; y < n.#parts.length; y++) {
190+
const part = n.#parts[y]
156191
/* c8 ignore start */
157192
if (typeof part === 'string') {
158193
throw new Error('string part in extglob AST??')
@@ -169,7 +204,8 @@ export class AST {
169204
}
170205

171206
push(...parts: (string | AST)[]) {
172-
for (const p of parts) {
207+
for (let i = 0; i < parts.length; i++) {
208+
const p = parts[i]
173209
if (p === '') continue
174210
/* c8 ignore start */
175211
if (typeof p !== 'string' && !(p instanceof AST && p.#parent === this)) {
@@ -183,8 +219,11 @@ export class AST {
183219
toJSON() {
184220
const ret: any[] =
185221
this.type === null
186-
? this.#parts.slice().map(p => (typeof p === 'string' ? p : p.toJSON()))
187-
: [this.type, ...this.#parts.map(p => (p as AST).toJSON())]
222+
? ArrayPrototypeMap(
223+
ArrayPrototypeSlice(this.#parts),
224+
(p: string | AST) => (typeof p === 'string' ? p : p.toJSON())
225+
)
226+
: [this.type, ...ArrayPrototypeMap(this.#parts, (p: AST) => p.toJSON())]
188227
if (this.isStart() && !this.type) ret.unshift([])
189228
if (
190229
this.isEnd() &&
@@ -231,8 +270,8 @@ export class AST {
231270

232271
clone(parent: AST) {
233272
const c = new AST(this.type, parent)
234-
for (const p of this.#parts) {
235-
c.copyIn(p)
273+
for (let i = 0; i < this.#parts.length; i++) {
274+
c.copyIn(this.#parts[i])
236275
}
237276
return c
238277
}
@@ -252,7 +291,7 @@ export class AST {
252291
let i = pos
253292
let acc = ''
254293
while (i < str.length) {
255-
const c = str.charAt(i++)
294+
const c = StringPrototypeCharAt(str, i++)
256295
// still accumulate escapes at this point, but we do ignore
257296
// starts that are escaped
258297
if (escaping || c === '\\') {
@@ -279,7 +318,11 @@ export class AST {
279318
continue
280319
}
281320

282-
if (!opt.noext && isExtglobType(c) && str.charAt(i) === '(') {
321+
if (
322+
!opt.noext &&
323+
isExtglobType(c) &&
324+
StringPrototypeCharAt(str, i) === '('
325+
) {
283326
ast.push(acc)
284327
acc = ''
285328
const ext = new AST(c, ast)
@@ -300,7 +343,7 @@ export class AST {
300343
const parts: AST[] = []
301344
let acc = ''
302345
while (i < str.length) {
303-
const c = str.charAt(i++)
346+
const c = StringPrototypeCharAt(str, i++)
304347
// still accumulate escapes at this point, but we do ignore
305348
// starts that are escaped
306349
if (escaping || c === '\\') {
@@ -327,7 +370,7 @@ export class AST {
327370
continue
328371
}
329372

330-
if (isExtglobType(c) && str.charAt(i) === '(') {
373+
if (isExtglobType(c) && StringPrototypeCharAt(str, i) === '(') {
331374
part.push(acc)
332375
acc = ''
333376
const ext = new AST(c, part)
@@ -338,7 +381,7 @@ export class AST {
338381
if (c === '|') {
339382
part.push(acc)
340383
acc = ''
341-
parts.push(part)
384+
ArrayPrototypePush(parts, part)
342385
part = new AST(null, ast)
343386
continue
344387
}
@@ -359,11 +402,14 @@ export class AST {
359402
// maybe something else in there.
360403
ast.type = null
361404
ast.#hasMagic = undefined
362-
ast.#parts = [str.substring(pos - 1)]
405+
ast.#parts = [StringPrototypeSubstring(str, pos - 1)]
363406
return i
364407
}
365408

366-
static fromGlob(pattern: string, options: MinimatchOptions = {}) {
409+
static fromGlob(
410+
pattern: string,
411+
options: MinimatchOptions = { __proto__: null } as MinimatchOptions
412+
) {
367413
const ast = new AST(null, undefined, options)
368414
AST.#parseAST(pattern, ast, 0, options)
369415
return ast
@@ -386,13 +432,13 @@ export class AST {
386432
this.#hasMagic ||
387433
(this.#options.nocase &&
388434
!this.#options.nocaseMagicOnly &&
389-
glob.toUpperCase() !== glob.toLowerCase())
435+
StringPrototypeToUpperCase(glob) !== StringPrototypeToLowerCase(glob))
390436
if (!anyMagic) {
391437
return body
392438
}
393439

394440
const flags = (this.#options.nocase ? 'i' : '') + (uflag ? 'u' : '')
395-
return Object.assign(new RegExp(`^${re}$`, flags), {
441+
return ObjectAssign(new RegExp(`^${re}$`, flags), {
396442
_src: re,
397443
_glob: glob,
398444
})
@@ -474,17 +520,18 @@ export class AST {
474520
if (this.#root === this) this.#fillNegs()
475521
if (!this.type) {
476522
const noEmpty = this.isStart() && this.isEnd()
477-
const src = this.#parts
478-
.map(p => {
523+
const src = ArrayPrototypeJoin(
524+
ArrayPrototypeMap(this.#parts, (p: string | AST) => {
479525
const [re, _, hasMagic, uflag] =
480526
typeof p === 'string'
481527
? AST.#parseGlob(p, this.#hasMagic, noEmpty)
482528
: p.toRegExpSource(allowDot)
483529
this.#hasMagic = this.#hasMagic || hasMagic
484530
this.#uflag = this.#uflag || uflag
485531
return re
486-
})
487-
.join('')
532+
}),
533+
''
534+
)
488535

489536
let start = ''
490537
if (this.isStart()) {
@@ -502,14 +549,17 @@ export class AST {
502549
// and prevent that.
503550
const needNoTrav =
504551
// dots are allowed, and the pattern starts with [ or .
505-
(dot && aps.has(src.charAt(0))) ||
552+
(dot && aps.has(StringPrototypeCharAt(src, 0))) ||
506553
// the pattern starts with \., and then [ or .
507-
(src.startsWith('\\.') && aps.has(src.charAt(2))) ||
554+
(src.startsWith('\\.') &&
555+
aps.has(StringPrototypeCharAt(src, 2))) ||
508556
// the pattern starts with \.\., and then [ or .
509-
(src.startsWith('\\.\\.') && aps.has(src.charAt(4)))
557+
(src.startsWith('\\.\\.') &&
558+
aps.has(StringPrototypeCharAt(src, 4)))
510559
// no need to prevent dots if it can't match a dot, or if a
511560
// sub-pattern will be preventing it anyway.
512-
const needNoDot = !dot && !allowDot && aps.has(src.charAt(0))
561+
const needNoDot =
562+
!dot && !allowDot && aps.has(StringPrototypeCharAt(src, 0))
513563

514564
start = needNoTrav ? startNoTraversal : needNoDot ? startNoDot : ''
515565
}
@@ -597,21 +647,24 @@ export class AST {
597647
}
598648

599649
#partsToRegExp(dot: boolean) {
600-
return this.#parts
601-
.map(p => {
602-
// extglob ASTs should only contain parent ASTs
603-
/* c8 ignore start */
604-
if (typeof p === 'string') {
605-
throw new Error('string type in extglob ast??')
606-
}
607-
/* c8 ignore stop */
608-
// can ignore hasMagic, because extglobs are already always magic
609-
const [re, _, _hasMagic, uflag] = p.toRegExpSource(dot)
610-
this.#uflag = this.#uflag || uflag
611-
return re
612-
})
613-
.filter(p => !(this.isStart() && this.isEnd()) || !!p)
614-
.join('|')
650+
return ArrayPrototypeJoin(
651+
ArrayPrototypeFilter(
652+
ArrayPrototypeMap(this.#parts, (p: string | AST) => {
653+
// extglob ASTs should only contain parent ASTs
654+
/* c8 ignore start */
655+
if (typeof p === 'string') {
656+
throw new Error('string type in extglob ast??')
657+
}
658+
/* c8 ignore stop */
659+
// can ignore hasMagic, because extglobs are already always magic
660+
const [re, _, _hasMagic, uflag] = p.toRegExpSource(dot)
661+
this.#uflag = this.#uflag || uflag
662+
return re
663+
}),
664+
(p: any) => !(this.isStart() && this.isEnd()) || !!p
665+
),
666+
'|'
667+
)
615668
}
616669

617670
static #parseGlob(
@@ -623,7 +676,7 @@ export class AST {
623676
let re = ''
624677
let uflag = false
625678
for (let i = 0; i < glob.length; i++) {
626-
const c = glob.charAt(i)
679+
const c = StringPrototypeCharAt(glob, i)
627680
if (escaping) {
628681
escaping = false
629682
re += (reSpecials.has(c) ? '\\' : '') + c

0 commit comments

Comments
 (0)