@trenskow/parse 0.1.8 → 0.1.10
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/index.js +74 -43
- package/package.json +1 -1
- package/test.js +19 -19
package/index.js
CHANGED
|
@@ -1,20 +1,42 @@
|
|
|
1
1
|
// Created 02/05/22 by Kristian Trenskow
|
|
2
2
|
// See LICENSE for license.
|
|
3
3
|
|
|
4
|
-
export default (
|
|
4
|
+
export default (...args) => {
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
let boundaries;
|
|
7
|
+
let options;
|
|
8
|
+
|
|
9
|
+
if (typeof args[0] === 'string') {
|
|
10
|
+
boundaries = args.slice(0, 2);
|
|
11
|
+
options = args[2];
|
|
12
|
+
} else {
|
|
13
|
+
[ boundaries, options ] = args;
|
|
8
14
|
}
|
|
9
15
|
|
|
10
|
-
if (
|
|
11
|
-
throw new TypeError('
|
|
16
|
+
if (!Array.isArray(boundaries)) {
|
|
17
|
+
throw new TypeError('Boundaries must be an array.');
|
|
12
18
|
}
|
|
13
19
|
|
|
14
|
-
if (
|
|
15
|
-
|
|
20
|
+
if (boundaries.every((boundary) => typeof boundary === 'string')) {
|
|
21
|
+
boundaries = [boundaries];
|
|
16
22
|
}
|
|
17
23
|
|
|
24
|
+
boundaries
|
|
25
|
+
.forEach((boundaries) => {
|
|
26
|
+
if (boundaries.length !== 2) {
|
|
27
|
+
throw new TypeError('Boundaries must be an array containing the opening and closing boundary.');
|
|
28
|
+
}
|
|
29
|
+
if (!boundaries.every((boundary) => typeof boundary === 'string')) {
|
|
30
|
+
throw new TypeError('Boundaries must be strings.');
|
|
31
|
+
}
|
|
32
|
+
if (!boundaries.every((boundary) => boundary.length > 0)) {
|
|
33
|
+
throw new TypeError('Boundaries cannot be zero-length.');
|
|
34
|
+
}
|
|
35
|
+
if (boundaries.every((boundary) => boundary === boundaries[0])) {
|
|
36
|
+
throw new TypeError('Boundary tokens cannot be the same.');
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
|
|
18
40
|
if (typeof options === 'undefined') options = {};
|
|
19
41
|
if (typeof options !== 'object' || options === null) {
|
|
20
42
|
throw new TypeError('Options must be an object.');
|
|
@@ -37,11 +59,9 @@ export default (opening, closing, options) => {
|
|
|
37
59
|
throw new TypeError('Max depth must be a number.');
|
|
38
60
|
}
|
|
39
61
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
if (typeof boundaries === 'undefined') boundaries = 'exclude';
|
|
62
|
+
if (typeof options.boundaries === 'undefined') options.boundaries = 'exclude';
|
|
43
63
|
|
|
44
|
-
if (!['exclude', 'include'].includes(boundaries)) {
|
|
64
|
+
if (!['exclude', 'include'].includes(options.boundaries)) {
|
|
45
65
|
throw new TypeError('Boundaries must be either `\'exclude\'` (default) or `\'include\'`.');
|
|
46
66
|
}
|
|
47
67
|
|
|
@@ -54,18 +74,24 @@ export default (opening, closing, options) => {
|
|
|
54
74
|
return {
|
|
55
75
|
do: (text) => {
|
|
56
76
|
|
|
57
|
-
|
|
77
|
+
let foundBoundaries = [];
|
|
58
78
|
|
|
59
|
-
|
|
79
|
+
const next = (text, offset, depth) => {
|
|
60
80
|
|
|
61
|
-
let
|
|
81
|
+
let result = [''];
|
|
62
82
|
|
|
63
83
|
let idx = offset;
|
|
64
84
|
let ignoredDepths = 0;
|
|
65
85
|
|
|
86
|
+
const appendResult = (text) => {
|
|
87
|
+
result[result.length - 1] += text;
|
|
88
|
+
};
|
|
89
|
+
|
|
66
90
|
for (idx ; idx < text.length ; idx++) {
|
|
67
91
|
|
|
68
|
-
|
|
92
|
+
const nextBoundary = foundBoundaries[foundBoundaries.length - 1];
|
|
93
|
+
|
|
94
|
+
if (text[idx] === '\\') appendResult(text[++idx]);
|
|
69
95
|
else {
|
|
70
96
|
|
|
71
97
|
const matchedIgnore = ignoreInside
|
|
@@ -74,7 +100,7 @@ export default (opening, closing, options) => {
|
|
|
74
100
|
.map(([_, matched]) => matched)[0];
|
|
75
101
|
|
|
76
102
|
if (typeof matchedIgnore !== 'undefined') {
|
|
77
|
-
|
|
103
|
+
appendResult(matchedIgnore);
|
|
78
104
|
if (matchedIgnore === ignoring[ignoring.length - 1]) {
|
|
79
105
|
ignoring.pop();
|
|
80
106
|
} else {
|
|
@@ -82,67 +108,72 @@ export default (opening, closing, options) => {
|
|
|
82
108
|
}
|
|
83
109
|
}
|
|
84
110
|
else if (ignoring.length === 0) {
|
|
85
|
-
if (text.substring(idx, idx + opening.length) === opening) {
|
|
86
111
|
|
|
87
|
-
|
|
112
|
+
const boundary = boundaries.find((boundaries) => text.substring(idx, idx + boundaries[0].length) === boundaries[0]);
|
|
113
|
+
|
|
114
|
+
if (typeof boundary !== 'undefined') {
|
|
88
115
|
|
|
89
|
-
|
|
116
|
+
foundBoundaries.push(boundary);
|
|
117
|
+
|
|
118
|
+
if (maxDepth > depth) {
|
|
90
119
|
|
|
91
120
|
let value;
|
|
92
121
|
|
|
93
|
-
[idx, value] = next(text, idx +
|
|
122
|
+
[idx, value] = next(text, idx + boundary[0].length, depth + 1);
|
|
94
123
|
|
|
95
124
|
result.push(value);
|
|
96
125
|
|
|
97
|
-
|
|
126
|
+
result.push('');
|
|
98
127
|
|
|
99
128
|
} else {
|
|
100
|
-
|
|
129
|
+
appendResult(text[idx]);
|
|
101
130
|
ignoredDepths++;
|
|
102
131
|
}
|
|
103
132
|
|
|
104
|
-
} else if (text.substring(idx, idx +
|
|
133
|
+
} else if (typeof nextBoundary !== 'undefined' && text.substring(idx, idx + nextBoundary[1].length) === nextBoundary[1]) {
|
|
105
134
|
|
|
106
|
-
|
|
135
|
+
foundBoundaries.pop();
|
|
107
136
|
|
|
108
|
-
|
|
137
|
+
if (ignoredDepths === 0) {
|
|
109
138
|
|
|
110
|
-
if (
|
|
111
|
-
result = result[0];
|
|
139
|
+
if (depth > 0 && options.boundaries === 'include') {
|
|
140
|
+
if (Array.isArray(result[0])) result[0] = [nextBoundary[0]].concat(result[0]);
|
|
141
|
+
else result[0] = `${nextBoundary[0]}${result[0]}`;
|
|
142
|
+
if (Array.isArray(result[result.length - 1])) result.push(nextBoundary[1]);
|
|
143
|
+
else result[result.length - 1] = `${result[result.length - 1]}${nextBoundary[1]}`;
|
|
112
144
|
}
|
|
113
145
|
|
|
114
|
-
|
|
115
|
-
if (Array.isArray(result)) {
|
|
116
|
-
if (Array.isArray(result[0])) result[0] = [opening].concat(result[0]);
|
|
117
|
-
else result[0] = `${opening}${result[0]}`;
|
|
118
|
-
if (Array.isArray(result[result.length - 1])) result.push(closing);
|
|
119
|
-
else result[result.length - 1] = `${result[result.length - 1]}${closing}`;
|
|
120
|
-
} else {
|
|
121
|
-
result = `${opening}${result}${closing}`;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
146
|
+
idx += nextBoundary[1].length - 1;
|
|
124
147
|
|
|
125
|
-
|
|
148
|
+
break;
|
|
126
149
|
|
|
127
150
|
} else {
|
|
128
|
-
|
|
151
|
+
appendResult(text[idx]);
|
|
129
152
|
ignoredDepths--;
|
|
130
153
|
}
|
|
131
154
|
|
|
132
155
|
} else {
|
|
133
|
-
|
|
156
|
+
appendResult(text[idx]);
|
|
134
157
|
}
|
|
135
158
|
}
|
|
136
|
-
else
|
|
159
|
+
else appendResult(text[idx]);
|
|
137
160
|
|
|
138
161
|
}
|
|
139
162
|
}
|
|
140
163
|
|
|
141
|
-
|
|
164
|
+
if (Array.isArray(result)) {
|
|
165
|
+
result = result.filter((value) => value.length > 0);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (result.length === 1 && typeof result[0] === 'string') {
|
|
169
|
+
result = result[0];
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return [idx, result];
|
|
142
173
|
|
|
143
174
|
};
|
|
144
175
|
|
|
145
|
-
return next(text
|
|
176
|
+
return next(text, 0, 0)[1];
|
|
146
177
|
|
|
147
178
|
}
|
|
148
179
|
};
|
package/package.json
CHANGED
package/test.js
CHANGED
|
@@ -35,19 +35,19 @@ describe('parser', () => {
|
|
|
35
35
|
]);
|
|
36
36
|
});
|
|
37
37
|
it ('should come back with parsed tree (with escapes and boundaries).', () => {
|
|
38
|
-
expect(parse('[', ']', { boundaries: 'include' }).do('This [is
|
|
39
|
-
'This ',
|
|
38
|
+
expect(parse([['[', ']'], ['{', '}']], { boundaries: 'include' }).do('This [is {my [\\[nested\\]] string}].')).to.eql(
|
|
40
39
|
[
|
|
41
|
-
'
|
|
40
|
+
'This ',
|
|
42
41
|
[
|
|
43
|
-
'[
|
|
44
|
-
|
|
45
|
-
|
|
42
|
+
'[is ',
|
|
43
|
+
[
|
|
44
|
+
'{my ',
|
|
45
|
+
'[[nested]]',
|
|
46
|
+
' string}'
|
|
47
|
+
],
|
|
48
|
+
']'
|
|
46
49
|
],
|
|
47
|
-
']
|
|
48
|
-
],
|
|
49
|
-
'.'
|
|
50
|
-
]);
|
|
50
|
+
'.']);
|
|
51
51
|
});
|
|
52
52
|
it ('should come back with parsed tree (long tokens).', () => {
|
|
53
53
|
expect(parse('hello', 'goodbye').do('This hello is hello my hello nested goodbye string goodbye goodbye.')).to.eql([
|
|
@@ -73,10 +73,10 @@ describe('parser', () => {
|
|
|
73
73
|
]);
|
|
74
74
|
});
|
|
75
75
|
it ('should come back with parsed tree (max depth = 1).', () => {
|
|
76
|
-
expect(parse('[', ']', { maxDepth: 1 }).do('This
|
|
76
|
+
expect(parse([['${', '}'], ['$[', ']']], { boundaries: 'include', maxDepth: 1 }).do('This ${is $[a]} test')).to.eql([
|
|
77
77
|
'This ',
|
|
78
|
-
'is [
|
|
79
|
-
'
|
|
78
|
+
'${is $[a]}',
|
|
79
|
+
' test'
|
|
80
80
|
]);
|
|
81
81
|
});
|
|
82
82
|
it ('should come back with parsed tree (max depth = 0).', () => {
|
|
@@ -86,23 +86,23 @@ describe('parser', () => {
|
|
|
86
86
|
});
|
|
87
87
|
it ('should throw an error if closing token is missing.', () => {
|
|
88
88
|
expect(() => {
|
|
89
|
-
parse('['
|
|
90
|
-
}).to.throw('
|
|
89
|
+
parse('[').do('[this');
|
|
90
|
+
}).to.throw('Boundaries must be an array containing the opening and closing boundary.');
|
|
91
91
|
});
|
|
92
92
|
it ('should throw an error if opening or closing tokens are not a string.', () => {
|
|
93
93
|
expect(() => {
|
|
94
|
-
parse(0, 1).do('[this');
|
|
95
|
-
}).to.throw('
|
|
94
|
+
parse([[0, 1]]).do('[this');
|
|
95
|
+
}).to.throw('Boundaries must be strings.');
|
|
96
96
|
});
|
|
97
97
|
it ('should throw an error if opening or closing tokens are zero-length.', () => {
|
|
98
98
|
expect(() => {
|
|
99
99
|
parse('', '').do('[this');
|
|
100
|
-
}).to.throw('
|
|
100
|
+
}).to.throw('Boundaries cannot be zero-length.');
|
|
101
101
|
});
|
|
102
102
|
it ('should throw an error if opening or closing are the same.', () => {
|
|
103
103
|
expect(() => {
|
|
104
104
|
parse('123', '123').do('[this');
|
|
105
|
-
}).to.throw('
|
|
105
|
+
}).to.throw('Boundary tokens cannot be the same.');
|
|
106
106
|
});
|
|
107
107
|
it ('should throw an error if options is not an object.', () => {
|
|
108
108
|
expect(() => {
|