brace-expansion 2.0.1 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +15 -0
- package/README.md +6 -8
- package/bench/bench.js +13 -0
- package/index.js +140 -118
- package/package.json +10 -6
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
on: [push]
|
|
3
|
+
jobs:
|
|
4
|
+
build:
|
|
5
|
+
runs-on: ubuntu-latest
|
|
6
|
+
strategy:
|
|
7
|
+
matrix:
|
|
8
|
+
node: ['18', '20']
|
|
9
|
+
steps:
|
|
10
|
+
- uses: actions/checkout@v2
|
|
11
|
+
- uses: actions/setup-node@v2
|
|
12
|
+
with:
|
|
13
|
+
node-version: ${{ matrix.node }}
|
|
14
|
+
- run: npm ci
|
|
15
|
+
- run: npm test
|
package/README.md
CHANGED
|
@@ -1,18 +1,15 @@
|
|
|
1
1
|
# brace-expansion
|
|
2
2
|
|
|
3
|
-
[Brace expansion](https://www.gnu.org/software/bash/manual/html_node/Brace-Expansion.html),
|
|
3
|
+
[Brace expansion](https://www.gnu.org/software/bash/manual/html_node/Brace-Expansion.html),
|
|
4
4
|
as known from sh/bash, in JavaScript.
|
|
5
5
|
|
|
6
|
-
[](https://github.com/juliangruber/brace-expansion/actions/workflows/ci.yml)
|
|
7
7
|
[](https://www.npmjs.org/package/brace-expansion)
|
|
8
|
-
[](https://greenkeeper.io/)
|
|
9
|
-
|
|
10
|
-
[](https://ci.testling.com/juliangruber/brace-expansion)
|
|
11
8
|
|
|
12
9
|
## Example
|
|
13
10
|
|
|
14
11
|
```js
|
|
15
|
-
|
|
12
|
+
import expand from 'brace-expansion'
|
|
16
13
|
|
|
17
14
|
expand('file-{a,b,c}.jpg')
|
|
18
15
|
// => ['file-a.jpg', 'file-b.jpg', 'file-c.jpg']
|
|
@@ -48,10 +45,10 @@ expand('ppp{,config,oe{,conf}}')
|
|
|
48
45
|
## API
|
|
49
46
|
|
|
50
47
|
```js
|
|
51
|
-
|
|
48
|
+
import expand from 'brace-expansion'
|
|
52
49
|
```
|
|
53
50
|
|
|
54
|
-
###
|
|
51
|
+
### const expanded = expand(str)
|
|
55
52
|
|
|
56
53
|
Return an array of all possible and valid expansions of `str`. If none are
|
|
57
54
|
found, `[str]` is returned.
|
|
@@ -97,6 +94,7 @@ npm install brace-expansion
|
|
|
97
94
|
|
|
98
95
|
- [Julian Gruber](https://github.com/juliangruber)
|
|
99
96
|
- [Isaac Z. Schlueter](https://github.com/isaacs)
|
|
97
|
+
- [Haelwenn Monnier](https://github.com/lanodan)
|
|
100
98
|
|
|
101
99
|
## Sponsors
|
|
102
100
|
|
package/bench/bench.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/* global bench */
|
|
2
|
+
|
|
3
|
+
import expand from '..'
|
|
4
|
+
import fs from 'fs'
|
|
5
|
+
|
|
6
|
+
const resfile = new URL('../test/cases.txt', import.meta.url)
|
|
7
|
+
const cases = fs.readFileSync(resfile, 'utf8').split('\n')
|
|
8
|
+
|
|
9
|
+
bench('Average', function () {
|
|
10
|
+
cases.forEach(function (testcase) {
|
|
11
|
+
expand(testcase)
|
|
12
|
+
})
|
|
13
|
+
})
|
package/index.js
CHANGED
|
@@ -1,69 +1,76 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
1
|
+
import balanced from 'balanced-match'
|
|
2
|
+
|
|
3
|
+
const escSlash = '\0SLASH' + Math.random() + '\0'
|
|
4
|
+
const escOpen = '\0OPEN' + Math.random() + '\0'
|
|
5
|
+
const escClose = '\0CLOSE' + Math.random() + '\0'
|
|
6
|
+
const escComma = '\0COMMA' + Math.random() + '\0'
|
|
7
|
+
const escPeriod = '\0PERIOD' + Math.random() + '\0'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @return {number}
|
|
11
|
+
*/
|
|
12
|
+
function numeric (str) {
|
|
13
|
+
return !isNaN(str)
|
|
13
14
|
? parseInt(str, 10)
|
|
14
|
-
: str.charCodeAt(0)
|
|
15
|
+
: str.charCodeAt(0)
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
/**
|
|
19
|
+
* @param {string} str
|
|
20
|
+
*/
|
|
21
|
+
function escapeBraces (str) {
|
|
18
22
|
return str.split('\\\\').join(escSlash)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
+
.split('\\{').join(escOpen)
|
|
24
|
+
.split('\\}').join(escClose)
|
|
25
|
+
.split('\\,').join(escComma)
|
|
26
|
+
.split('\\.').join(escPeriod)
|
|
23
27
|
}
|
|
24
28
|
|
|
25
|
-
|
|
29
|
+
/**
|
|
30
|
+
* @param {string} str
|
|
31
|
+
*/
|
|
32
|
+
function unescapeBraces (str) {
|
|
26
33
|
return str.split(escSlash).join('\\')
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
34
|
+
.split(escOpen).join('{')
|
|
35
|
+
.split(escClose).join('}')
|
|
36
|
+
.split(escComma).join(',')
|
|
37
|
+
.split(escPeriod).join('.')
|
|
31
38
|
}
|
|
32
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Basically just str.split(","), but handling cases
|
|
42
|
+
* where we have nested braced sections, which should be
|
|
43
|
+
* treated as individual members, like {a,{b,c},d}
|
|
44
|
+
* @param {string} str
|
|
45
|
+
*/
|
|
46
|
+
function parseCommaParts (str) {
|
|
47
|
+
if (!str) { return [''] }
|
|
33
48
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
// treated as individual members, like {a,{b,c},d}
|
|
37
|
-
function parseCommaParts(str) {
|
|
38
|
-
if (!str)
|
|
39
|
-
return [''];
|
|
40
|
-
|
|
41
|
-
var parts = [];
|
|
42
|
-
var m = balanced('{', '}', str);
|
|
49
|
+
const parts = []
|
|
50
|
+
const m = balanced('{', '}', str)
|
|
43
51
|
|
|
44
|
-
if (!m)
|
|
45
|
-
return str.split(',');
|
|
52
|
+
if (!m) { return str.split(',') }
|
|
46
53
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
var post = m.post;
|
|
50
|
-
var p = pre.split(',');
|
|
54
|
+
const { pre, body, post } = m
|
|
55
|
+
const p = pre.split(',')
|
|
51
56
|
|
|
52
|
-
p[p.length-1] += '{' + body + '}'
|
|
53
|
-
|
|
57
|
+
p[p.length - 1] += '{' + body + '}'
|
|
58
|
+
const postParts = parseCommaParts(post)
|
|
54
59
|
if (post.length) {
|
|
55
|
-
p[p.length-1] += postParts.shift()
|
|
56
|
-
p.push.apply(p, postParts)
|
|
60
|
+
p[p.length - 1] += postParts.shift()
|
|
61
|
+
p.push.apply(p, postParts)
|
|
57
62
|
}
|
|
58
63
|
|
|
59
|
-
parts.push.apply(parts, p)
|
|
64
|
+
parts.push.apply(parts, p)
|
|
60
65
|
|
|
61
|
-
return parts
|
|
66
|
+
return parts
|
|
62
67
|
}
|
|
63
68
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
69
|
+
/**
|
|
70
|
+
* @param {string} str
|
|
71
|
+
*/
|
|
72
|
+
export default function expandTop (str) {
|
|
73
|
+
if (!str) { return [] }
|
|
67
74
|
|
|
68
75
|
// I don't know why Bash 4.3 does this, but it does.
|
|
69
76
|
// Anything starting with {} will have the first two bytes preserved
|
|
@@ -71,133 +78,148 @@ function expandTop(str) {
|
|
|
71
78
|
// but a{},b}c will be expanded to [a}c,abc].
|
|
72
79
|
// One could argue that this is a bug in Bash, but since the goal of
|
|
73
80
|
// this module is to match Bash's rules, we escape a leading {}
|
|
74
|
-
if (str.
|
|
75
|
-
str = '\\{\\}' + str.
|
|
81
|
+
if (str.slice(0, 2) === '{}') {
|
|
82
|
+
str = '\\{\\}' + str.slice(2)
|
|
76
83
|
}
|
|
77
84
|
|
|
78
|
-
return expand(escapeBraces(str), true).map(unescapeBraces)
|
|
85
|
+
return expand(escapeBraces(str), true).map(unescapeBraces)
|
|
79
86
|
}
|
|
80
87
|
|
|
81
|
-
|
|
82
|
-
|
|
88
|
+
/**
|
|
89
|
+
* @param {string} str
|
|
90
|
+
*/
|
|
91
|
+
function embrace (str) {
|
|
92
|
+
return '{' + str + '}'
|
|
83
93
|
}
|
|
84
|
-
|
|
85
|
-
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* @param {string} el
|
|
97
|
+
*/
|
|
98
|
+
function isPadded (el) {
|
|
99
|
+
return /^-?0\d/.test(el)
|
|
86
100
|
}
|
|
87
101
|
|
|
88
|
-
|
|
89
|
-
|
|
102
|
+
/**
|
|
103
|
+
* @param {number} i
|
|
104
|
+
* @param {number} y
|
|
105
|
+
*/
|
|
106
|
+
function lte (i, y) {
|
|
107
|
+
return i <= y
|
|
90
108
|
}
|
|
91
|
-
|
|
92
|
-
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* @param {number} i
|
|
112
|
+
* @param {number} y
|
|
113
|
+
*/
|
|
114
|
+
function gte (i, y) {
|
|
115
|
+
return i >= y
|
|
93
116
|
}
|
|
94
117
|
|
|
95
|
-
|
|
96
|
-
|
|
118
|
+
/**
|
|
119
|
+
* @param {string} str
|
|
120
|
+
* @param {boolean} [isTop]
|
|
121
|
+
*/
|
|
122
|
+
function expand (str, isTop) {
|
|
123
|
+
/** @type {string[]} */
|
|
124
|
+
const expansions = []
|
|
97
125
|
|
|
98
|
-
|
|
99
|
-
if (!m) return [str]
|
|
126
|
+
const m = balanced('{', '}', str)
|
|
127
|
+
if (!m) return [str]
|
|
100
128
|
|
|
101
129
|
// no need to expand pre, since it is guaranteed to be free of brace-sets
|
|
102
|
-
|
|
103
|
-
|
|
130
|
+
const pre = m.pre
|
|
131
|
+
const post = m.post.length
|
|
104
132
|
? expand(m.post, false)
|
|
105
|
-
: ['']
|
|
133
|
+
: ['']
|
|
106
134
|
|
|
107
|
-
if (/\$$/.test(m.pre)) {
|
|
108
|
-
for (
|
|
109
|
-
|
|
110
|
-
expansions.push(expansion)
|
|
135
|
+
if (/\$$/.test(m.pre)) {
|
|
136
|
+
for (let k = 0; k < post.length; k++) {
|
|
137
|
+
const expansion = pre + '{' + m.body + '}' + post[k]
|
|
138
|
+
expansions.push(expansion)
|
|
111
139
|
}
|
|
112
140
|
} else {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
141
|
+
const isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body)
|
|
142
|
+
const isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body)
|
|
143
|
+
const isSequence = isNumericSequence || isAlphaSequence
|
|
144
|
+
const isOptions = m.body.indexOf(',') >= 0
|
|
117
145
|
if (!isSequence && !isOptions) {
|
|
118
146
|
// {a},b}
|
|
119
147
|
if (m.post.match(/,.*\}/)) {
|
|
120
|
-
str = m.pre + '{' + m.body + escClose + m.post
|
|
121
|
-
return expand(str)
|
|
148
|
+
str = m.pre + '{' + m.body + escClose + m.post
|
|
149
|
+
return expand(str)
|
|
122
150
|
}
|
|
123
|
-
return [str]
|
|
151
|
+
return [str]
|
|
124
152
|
}
|
|
125
153
|
|
|
126
|
-
|
|
154
|
+
let n
|
|
127
155
|
if (isSequence) {
|
|
128
|
-
n = m.body.split(/\.\./)
|
|
156
|
+
n = m.body.split(/\.\./)
|
|
129
157
|
} else {
|
|
130
|
-
n = parseCommaParts(m.body)
|
|
158
|
+
n = parseCommaParts(m.body)
|
|
131
159
|
if (n.length === 1) {
|
|
132
160
|
// x{{a,b}}y ==> x{a}y x{b}y
|
|
133
|
-
n = expand(n[0], false).map(embrace)
|
|
161
|
+
n = expand(n[0], false).map(embrace)
|
|
134
162
|
if (n.length === 1) {
|
|
135
|
-
return post.map(function(p) {
|
|
136
|
-
return m.pre + n[0] + p
|
|
137
|
-
})
|
|
163
|
+
return post.map(function (p) {
|
|
164
|
+
return m.pre + n[0] + p
|
|
165
|
+
})
|
|
138
166
|
}
|
|
139
167
|
}
|
|
140
168
|
}
|
|
141
169
|
|
|
142
170
|
// at this point, n is the parts, and we know it's not a comma set
|
|
143
171
|
// with a single entry.
|
|
144
|
-
|
|
172
|
+
let N
|
|
145
173
|
|
|
146
174
|
if (isSequence) {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
175
|
+
const x = numeric(n[0])
|
|
176
|
+
const y = numeric(n[1])
|
|
177
|
+
const width = Math.max(n[0].length, n[1].length)
|
|
178
|
+
let incr = n.length === 3
|
|
151
179
|
? Math.abs(numeric(n[2]))
|
|
152
|
-
: 1
|
|
153
|
-
|
|
154
|
-
|
|
180
|
+
: 1
|
|
181
|
+
let test = lte
|
|
182
|
+
const reverse = y < x
|
|
155
183
|
if (reverse) {
|
|
156
|
-
incr *= -1
|
|
157
|
-
test = gte
|
|
184
|
+
incr *= -1
|
|
185
|
+
test = gte
|
|
158
186
|
}
|
|
159
|
-
|
|
187
|
+
const pad = n.some(isPadded)
|
|
160
188
|
|
|
161
|
-
N = []
|
|
189
|
+
N = []
|
|
162
190
|
|
|
163
|
-
for (
|
|
164
|
-
|
|
191
|
+
for (let i = x; test(i, y); i += incr) {
|
|
192
|
+
let c
|
|
165
193
|
if (isAlphaSequence) {
|
|
166
|
-
c = String.fromCharCode(i)
|
|
167
|
-
if (c === '\\')
|
|
168
|
-
c = '';
|
|
194
|
+
c = String.fromCharCode(i)
|
|
195
|
+
if (c === '\\') { c = '' }
|
|
169
196
|
} else {
|
|
170
|
-
c = String(i)
|
|
197
|
+
c = String(i)
|
|
171
198
|
if (pad) {
|
|
172
|
-
|
|
199
|
+
const need = width - c.length
|
|
173
200
|
if (need > 0) {
|
|
174
|
-
|
|
175
|
-
if (i < 0)
|
|
176
|
-
c = '-' + z + c.slice(1);
|
|
177
|
-
else
|
|
178
|
-
c = z + c;
|
|
201
|
+
const z = new Array(need + 1).join('0')
|
|
202
|
+
if (i < 0) { c = '-' + z + c.slice(1) } else { c = z + c }
|
|
179
203
|
}
|
|
180
204
|
}
|
|
181
205
|
}
|
|
182
|
-
N.push(c)
|
|
206
|
+
N.push(c)
|
|
183
207
|
}
|
|
184
208
|
} else {
|
|
185
|
-
N = []
|
|
209
|
+
N = []
|
|
186
210
|
|
|
187
|
-
for (
|
|
188
|
-
N.push.apply(N, expand(n[j], false))
|
|
211
|
+
for (let j = 0; j < n.length; j++) {
|
|
212
|
+
N.push.apply(N, expand(n[j], false))
|
|
189
213
|
}
|
|
190
214
|
}
|
|
191
215
|
|
|
192
|
-
for (
|
|
193
|
-
for (
|
|
194
|
-
|
|
195
|
-
if (!isTop || isSequence || expansion)
|
|
196
|
-
expansions.push(expansion);
|
|
216
|
+
for (let j = 0; j < N.length; j++) {
|
|
217
|
+
for (let k = 0; k < post.length; k++) {
|
|
218
|
+
const expansion = pre + N[j] + post[k]
|
|
219
|
+
if (!isTop || isSequence || expansion) { expansions.push(expansion) }
|
|
197
220
|
}
|
|
198
221
|
}
|
|
199
222
|
}
|
|
200
223
|
|
|
201
|
-
return expansions
|
|
224
|
+
return expansions
|
|
202
225
|
}
|
|
203
|
-
|
package/package.json
CHANGED
|
@@ -1,24 +1,25 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "brace-expansion",
|
|
3
3
|
"description": "Brace expansion as known from sh/bash",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "3.0.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git://github.com/juliangruber/brace-expansion.git"
|
|
8
8
|
},
|
|
9
9
|
"homepage": "https://github.com/juliangruber/brace-expansion",
|
|
10
|
-
"
|
|
10
|
+
"exports": "./index.js",
|
|
11
|
+
"type": "module",
|
|
11
12
|
"scripts": {
|
|
12
|
-
"test": "
|
|
13
|
+
"test": "standard --fix && node --test",
|
|
13
14
|
"gentest": "bash test/generate.sh",
|
|
14
|
-
"bench": "matcha
|
|
15
|
+
"bench": "matcha bench/bench.js"
|
|
15
16
|
},
|
|
16
17
|
"dependencies": {
|
|
17
|
-
"balanced-match": "^
|
|
18
|
+
"balanced-match": "^3.0.0"
|
|
18
19
|
},
|
|
19
20
|
"devDependencies": {
|
|
20
21
|
"@c4312/matcha": "^1.3.1",
|
|
21
|
-
"
|
|
22
|
+
"standard": "^17.1.0"
|
|
22
23
|
},
|
|
23
24
|
"keywords": [],
|
|
24
25
|
"author": {
|
|
@@ -42,5 +43,8 @@
|
|
|
42
43
|
"iphone/6.0..latest",
|
|
43
44
|
"android-browser/4.2..latest"
|
|
44
45
|
]
|
|
46
|
+
},
|
|
47
|
+
"engines": {
|
|
48
|
+
"node": ">= 18"
|
|
45
49
|
}
|
|
46
50
|
}
|