@bablr/agast-helpers 0.8.0 → 0.10.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/lib/builders.js +159 -88
- package/lib/path.js +1571 -419
- package/lib/print.js +78 -41
- package/lib/shorthand.js +17 -13
- package/lib/stream.js +437 -164
- package/lib/symbols.js +5 -2
- package/lib/tags.js +285 -0
- package/lib/template.js +52 -46
- package/lib/tree.js +165 -687
- package/package.json +6 -4
- package/lib/children.js +0 -120
- package/lib/path-facade.js +0 -39
package/lib/symbols.js
CHANGED
|
@@ -6,10 +6,13 @@ export const ShiftTag = Symbol.for('ShiftTag');
|
|
|
6
6
|
export const GapTag = Symbol.for('GapTag');
|
|
7
7
|
export const BindingTag = Symbol.for('BindingTag');
|
|
8
8
|
export const NullTag = Symbol.for('NullTag');
|
|
9
|
-
export const InitializerTag = Symbol.for('InitializerTag');
|
|
10
9
|
export const AttributeDefinition = Symbol.for('AttributeDefinition');
|
|
11
10
|
export const LiteralTag = Symbol.for('LiteralTag');
|
|
12
11
|
|
|
13
|
-
export const
|
|
12
|
+
export const Document = Symbol.for('Document');
|
|
13
|
+
export const TreeNode = Symbol.for('TreeNode');
|
|
14
|
+
export const NullNode = Symbol.for('NullNode');
|
|
15
|
+
export const GapNode = Symbol.for('GapNode');
|
|
16
|
+
export const Node = Symbol.for('Node');
|
|
14
17
|
|
|
15
18
|
export const Property = Symbol.for('Property');
|
package/lib/tags.js
ADDED
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import { buildModule, defaultNodeSize } from '@bablr/btree/enhanceable';
|
|
2
|
+
import {
|
|
3
|
+
AttributeDefinition,
|
|
4
|
+
CloseNodeTag,
|
|
5
|
+
GapNode,
|
|
6
|
+
GapTag,
|
|
7
|
+
LiteralTag,
|
|
8
|
+
OpenNodeTag,
|
|
9
|
+
Property,
|
|
10
|
+
ReferenceTag,
|
|
11
|
+
ShiftTag,
|
|
12
|
+
} from './symbols.js';
|
|
13
|
+
import { isObject } from './object.js';
|
|
14
|
+
import { referenceIsSingular } from './path.js';
|
|
15
|
+
import { buildReference } from './builders.js';
|
|
16
|
+
|
|
17
|
+
const { isArray } = Array;
|
|
18
|
+
const { freeze } = Object;
|
|
19
|
+
|
|
20
|
+
export { defaultNodeSize };
|
|
21
|
+
|
|
22
|
+
const __getAtName = (name, sortedArray, startIdx = 0, endIdx = sortedArray.length - 1) => {
|
|
23
|
+
if (!sortedArray.length || endIdx < startIdx) return null;
|
|
24
|
+
|
|
25
|
+
let idx = startIdx + Math.floor((endIdx - startIdx) / 2 + 0.1);
|
|
26
|
+
let entry = sortedArray[idx];
|
|
27
|
+
let { 0: key, 1: value } = entry;
|
|
28
|
+
|
|
29
|
+
let direction = compareNames(name, key);
|
|
30
|
+
|
|
31
|
+
if (direction === 0) {
|
|
32
|
+
return value;
|
|
33
|
+
} else {
|
|
34
|
+
if (startIdx === endIdx) return null;
|
|
35
|
+
|
|
36
|
+
if (direction > 0) {
|
|
37
|
+
return __getAtName(name, sortedArray, idx + 1, endIdx);
|
|
38
|
+
} else {
|
|
39
|
+
return __getAtName(name, sortedArray, startIdx, idx - 1);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export const getAtName = (name, sortedArray, startIdx = 0, endIdx = sortedArray.length - 1) => {
|
|
45
|
+
return __getAtName(name, sortedArray, startIdx, endIdx);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export const compareNames = (a, b) => (a > b ? 1 : b > a ? -1 : 0);
|
|
49
|
+
|
|
50
|
+
export const sumValues = (values) => {
|
|
51
|
+
let mapStats = values.reduce(
|
|
52
|
+
(acc, val) => {
|
|
53
|
+
const { references, specialTypes } = acc;
|
|
54
|
+
if (isArray(val)) {
|
|
55
|
+
acc.lineBreaks += getSums(val).lineBreaks;
|
|
56
|
+
acc.gaps += getSums(val).gaps;
|
|
57
|
+
|
|
58
|
+
let arrs = [
|
|
59
|
+
[getSums(val).references, references],
|
|
60
|
+
[getSums(val).specialTypes, specialTypes],
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
for (let { 0: arr, 1: map } of arrs) {
|
|
64
|
+
for (const { 0: key, 1: value, 2: reference } of arr) {
|
|
65
|
+
let count = map.get(key)?.[1] ?? 0;
|
|
66
|
+
|
|
67
|
+
if (!reference.flags.array && referenceIsSingular(reference) && count > 0)
|
|
68
|
+
throw new Error();
|
|
69
|
+
|
|
70
|
+
map.set(key, freeze([key, count + value, reference]));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
} else if (val) {
|
|
74
|
+
if (val.type === LiteralTag) {
|
|
75
|
+
let text = val.value;
|
|
76
|
+
let idx = 0;
|
|
77
|
+
while ((idx = text.indexOf('\n', idx + 1)) >= 0) {
|
|
78
|
+
acc.lineBreaks++;
|
|
79
|
+
}
|
|
80
|
+
} else if (val.type === GapTag) {
|
|
81
|
+
acc.gaps++;
|
|
82
|
+
} else if (val.type === Property) {
|
|
83
|
+
let { tags } = val.value;
|
|
84
|
+
let refOrShiftTag = tags[0];
|
|
85
|
+
|
|
86
|
+
if (tags[2]) {
|
|
87
|
+
acc.gaps += tags[2].value.tags[2].gaps;
|
|
88
|
+
}
|
|
89
|
+
if (refOrShiftTag?.type !== ShiftTag) {
|
|
90
|
+
let ref = refOrShiftTag?.value || buildReference();
|
|
91
|
+
const { type, name } = ref;
|
|
92
|
+
|
|
93
|
+
if (name) {
|
|
94
|
+
let count = references.get(name)?.[1] ?? 0;
|
|
95
|
+
|
|
96
|
+
if (!ref.flags.array && referenceIsSingular(ref) && count > 0) throw new Error();
|
|
97
|
+
|
|
98
|
+
references.set(name, freeze([name, count + 1, ref]));
|
|
99
|
+
} else {
|
|
100
|
+
let count = specialTypes.get(type)?.[1] ?? 0;
|
|
101
|
+
|
|
102
|
+
if (!ref.flags.array && referenceIsSingular(ref) && count > 0) throw new Error();
|
|
103
|
+
|
|
104
|
+
specialTypes.set(type, freeze([type, count + 1, ref]));
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return acc;
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
gaps: 0,
|
|
113
|
+
lineBreaks: 0,
|
|
114
|
+
references: new Map(),
|
|
115
|
+
specialTypes: new Map(),
|
|
116
|
+
},
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
let { lineBreaks, gaps } = mapStats;
|
|
120
|
+
let stats = {
|
|
121
|
+
gaps,
|
|
122
|
+
lineBreaks,
|
|
123
|
+
references: [...mapStats.references.values()].sort((a, b) => compareNames(a[0], b[0])),
|
|
124
|
+
specialTypes: [...mapStats.specialTypes.values()].sort((a, b) => compareNames(a[0], b[0])),
|
|
125
|
+
};
|
|
126
|
+
freeze(stats);
|
|
127
|
+
freeze(stats.references);
|
|
128
|
+
freeze(stats.specialTypes);
|
|
129
|
+
return stats;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const module_ = buildModule(defaultNodeSize, sumValues);
|
|
133
|
+
|
|
134
|
+
const {
|
|
135
|
+
from,
|
|
136
|
+
fromValues,
|
|
137
|
+
addAt,
|
|
138
|
+
isValidNode,
|
|
139
|
+
assertValidNode,
|
|
140
|
+
getValues,
|
|
141
|
+
getSums,
|
|
142
|
+
setValues,
|
|
143
|
+
traverse,
|
|
144
|
+
getSize,
|
|
145
|
+
findPath,
|
|
146
|
+
} = module_;
|
|
147
|
+
|
|
148
|
+
const getAt = (idx, tags, propertyIndex) => {
|
|
149
|
+
if (propertyIndex !== undefined) {
|
|
150
|
+
throw new Error('propertyIndex');
|
|
151
|
+
}
|
|
152
|
+
return module_.getAt(idx, tags);
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const push = (tags, tag) => {
|
|
156
|
+
let firstTag = getAt(0, tags);
|
|
157
|
+
|
|
158
|
+
if (
|
|
159
|
+
!Array.isArray(tag) &&
|
|
160
|
+
![OpenNodeTag, Property, CloseNodeTag, AttributeDefinition, LiteralTag, GapTag].includes(
|
|
161
|
+
tag.type,
|
|
162
|
+
)
|
|
163
|
+
)
|
|
164
|
+
throw new Error();
|
|
165
|
+
|
|
166
|
+
if (isArray(tag)) {
|
|
167
|
+
if (!firstTag || firstTag.type !== OpenNodeTag) {
|
|
168
|
+
return module_.concat(tags, tag);
|
|
169
|
+
}
|
|
170
|
+
let children = getValues(tags)[1] ?? module_.fromValues([]);
|
|
171
|
+
|
|
172
|
+
let { 0: open } = getValues(tags);
|
|
173
|
+
|
|
174
|
+
children = module_.concat(children, tag);
|
|
175
|
+
|
|
176
|
+
return module_.fromValues([open, children]);
|
|
177
|
+
} else {
|
|
178
|
+
if (tag.type === OpenNodeTag) {
|
|
179
|
+
if (getSize(tags) !== 0) throw new Error();
|
|
180
|
+
return module_.push(tags, tag);
|
|
181
|
+
} else {
|
|
182
|
+
if (!firstTag || firstTag.type !== OpenNodeTag) {
|
|
183
|
+
return module_.push(tags, tag);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
let children = getValues(tags)[1] ?? [];
|
|
187
|
+
|
|
188
|
+
// sometimes this is the top level
|
|
189
|
+
if (firstTag) {
|
|
190
|
+
if (tag.type === CloseNodeTag) {
|
|
191
|
+
let { 0: open, 1: children = module_.fromValues([]) } = getValues(tags);
|
|
192
|
+
|
|
193
|
+
return module_.fromValues([open, children, tag]);
|
|
194
|
+
} else {
|
|
195
|
+
let { 0: open } = getValues(tags);
|
|
196
|
+
|
|
197
|
+
children = module_.push(children, tag);
|
|
198
|
+
|
|
199
|
+
return module_.fromValues([open, children]);
|
|
200
|
+
}
|
|
201
|
+
} else {
|
|
202
|
+
return module_.push(tags, tag);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
const replaceAt = (idx, tree, value) => {
|
|
209
|
+
let idx_ = idx < 0 ? getSize(tree) + idx : idx;
|
|
210
|
+
|
|
211
|
+
if (value.type !== getAt(idx_, tree).type) throw new Error();
|
|
212
|
+
let firstTag = getAt(0, tree);
|
|
213
|
+
|
|
214
|
+
if (firstTag && firstTag.type === OpenNodeTag) {
|
|
215
|
+
let newTags = [...getValues(tree)];
|
|
216
|
+
if (idx_ === 0) {
|
|
217
|
+
newTags[0] = value;
|
|
218
|
+
} else if (isObject(value) && value.type === CloseNodeTag) {
|
|
219
|
+
if (idx_ !== getSize(tree) - 1) throw new Error();
|
|
220
|
+
newTags[2] = value;
|
|
221
|
+
} else {
|
|
222
|
+
let children = getValues(tree)[1];
|
|
223
|
+
|
|
224
|
+
children = module_.replaceAt(idx_ - 1, children, value);
|
|
225
|
+
|
|
226
|
+
newTags[1] = children;
|
|
227
|
+
}
|
|
228
|
+
return module_.fromValues(newTags);
|
|
229
|
+
} else {
|
|
230
|
+
return module_.replaceAt(idx_, tree, value);
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
const removeAt = (idx, tree) => {
|
|
235
|
+
let idx_ = idx < 0 ? getSize(tree) + idx : idx;
|
|
236
|
+
|
|
237
|
+
let firstTag = getAt(0, tree);
|
|
238
|
+
|
|
239
|
+
if (firstTag && firstTag.type === OpenNodeTag) {
|
|
240
|
+
let newTags = [...getValues(tree)];
|
|
241
|
+
if (idx_ === 0) {
|
|
242
|
+
newTags.splice(0, 1);
|
|
243
|
+
} else if (idx_ === getSize(tree - 1)) {
|
|
244
|
+
newTags.splice(2, 1);
|
|
245
|
+
} else {
|
|
246
|
+
let children = getValues(tree)[1];
|
|
247
|
+
|
|
248
|
+
children = module_.removeAt(idx_ - 1, children);
|
|
249
|
+
|
|
250
|
+
newTags[1] = children;
|
|
251
|
+
}
|
|
252
|
+
return module_.fromValues(newTags);
|
|
253
|
+
} else {
|
|
254
|
+
return module_.removeAt(idx_, tree);
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
function* traverseInner(tree) {
|
|
259
|
+
for (let tag of module_.traverse(tree)) {
|
|
260
|
+
if (tag.type === Property) {
|
|
261
|
+
yield* module_.traverse(tag.value.tags);
|
|
262
|
+
} else {
|
|
263
|
+
yield tag;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
export {
|
|
269
|
+
from,
|
|
270
|
+
fromValues,
|
|
271
|
+
push,
|
|
272
|
+
addAt,
|
|
273
|
+
isValidNode,
|
|
274
|
+
assertValidNode,
|
|
275
|
+
getSize,
|
|
276
|
+
getValues,
|
|
277
|
+
getSums,
|
|
278
|
+
setValues,
|
|
279
|
+
traverse,
|
|
280
|
+
traverseInner,
|
|
281
|
+
findPath,
|
|
282
|
+
getAt,
|
|
283
|
+
replaceAt,
|
|
284
|
+
removeAt,
|
|
285
|
+
};
|
package/lib/template.js
CHANGED
|
@@ -1,15 +1,8 @@
|
|
|
1
1
|
import * as t from './builders.js';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
CloseNodeTag,
|
|
7
|
-
DoctypeTag,
|
|
8
|
-
Property,
|
|
9
|
-
BindingTag,
|
|
10
|
-
} from './symbols.js';
|
|
11
|
-
import * as Children from './children.js';
|
|
12
|
-
import { getOpenTag, get, isFragmentNode } from './tree.js';
|
|
2
|
+
import { getTags, isMultiFragment, Path } from './path.js';
|
|
3
|
+
import { OpenNodeTag, CloseNodeTag, DoctypeTag, Property, GapNode } from './symbols.js';
|
|
4
|
+
import * as Tags from './tags.js';
|
|
5
|
+
import { getOpenTag, isNull } from './tree.js';
|
|
13
6
|
|
|
14
7
|
const { freeze } = Object;
|
|
15
8
|
|
|
@@ -18,63 +11,76 @@ export const buildFilledGapFunction = (expressions) => (value) => {
|
|
|
18
11
|
return t.buildGapTag();
|
|
19
12
|
};
|
|
20
13
|
|
|
21
|
-
export
|
|
14
|
+
export const interpolate = (node, expressions) => {
|
|
15
|
+
let path = Path.from(node);
|
|
16
|
+
|
|
17
|
+
outer: for (let expression of expressions) {
|
|
18
|
+
let i = 0;
|
|
19
|
+
do {
|
|
20
|
+
path = path.atDepth(0);
|
|
21
|
+
let tag;
|
|
22
|
+
while ((tag = Tags.getAt(i, getTags(path.node)))) {
|
|
23
|
+
if (path.node.type === GapNode) {
|
|
24
|
+
path = path.replaceWith(expression);
|
|
25
|
+
continue outer;
|
|
26
|
+
} else if (tag.type === Property && tag.value.tags[2]) {
|
|
27
|
+
let tags = getTags(tag.value.tags[2]);
|
|
28
|
+
if (tags[2].gaps) {
|
|
29
|
+
path = path.push(i);
|
|
30
|
+
i = 0;
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
i++;
|
|
35
|
+
}
|
|
36
|
+
i = path.parent?.tagsIndex + 1;
|
|
37
|
+
path = path.parent;
|
|
38
|
+
} while (path.depth);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return path.atDepth(0).node;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export const interpolateFragment = (node, ref, expressions) => {
|
|
45
|
+
return __interpolateFragment(node, ref, expressions);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
function* __interpolateFragment(node, ref, expressions) {
|
|
49
|
+
if (isNull(node)) return;
|
|
50
|
+
|
|
22
51
|
const open = getOpenTag(node);
|
|
23
52
|
|
|
24
53
|
const gap = buildFilledGapFunction(expressions);
|
|
25
54
|
|
|
26
|
-
if (!open.value.
|
|
27
|
-
let
|
|
28
|
-
let
|
|
29
|
-
let isFragment = isFragmentNode(node);
|
|
30
|
-
for (let tag of Children.traverse(node.children)) {
|
|
55
|
+
if (!open.value.name) {
|
|
56
|
+
let isMultiFragment_ = isMultiFragment(node);
|
|
57
|
+
for (let tag of Tags.traverse(getTags(node))) {
|
|
31
58
|
switch (tag.type) {
|
|
32
59
|
case DoctypeTag: {
|
|
33
60
|
break;
|
|
34
61
|
}
|
|
35
62
|
case OpenNodeTag:
|
|
36
63
|
case CloseNodeTag: {
|
|
37
|
-
if (!
|
|
64
|
+
if (!isMultiFragment_) {
|
|
38
65
|
yield tag;
|
|
39
66
|
}
|
|
40
67
|
break;
|
|
41
68
|
}
|
|
42
69
|
|
|
43
|
-
case ReferenceTag: {
|
|
44
|
-
currentRef = tag;
|
|
45
|
-
break;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
case BindingTag: {
|
|
49
|
-
currentBinding = tag;
|
|
50
|
-
break;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
case InitializerTag: {
|
|
54
|
-
const { type } = currentRef.value;
|
|
55
|
-
if (type === '.') {
|
|
56
|
-
yield freeze(ref);
|
|
57
|
-
} else {
|
|
58
|
-
yield currentRef;
|
|
59
|
-
}
|
|
60
|
-
yield tag;
|
|
61
|
-
break;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
70
|
case Property: {
|
|
65
|
-
let { reference } = tag.value;
|
|
71
|
+
let { reference, node, tags } = tag.value;
|
|
66
72
|
const { type } = reference;
|
|
67
73
|
|
|
68
|
-
if (type === '
|
|
74
|
+
if (type === '_') {
|
|
69
75
|
// TODO check/combine flags
|
|
70
|
-
yield
|
|
71
|
-
yield currentBinding;
|
|
76
|
+
yield ref;
|
|
72
77
|
} else {
|
|
73
|
-
yield
|
|
74
|
-
yield currentBinding;
|
|
78
|
+
yield tags[0];
|
|
75
79
|
}
|
|
76
80
|
|
|
77
|
-
yield
|
|
81
|
+
yield* tags[1];
|
|
82
|
+
|
|
83
|
+
yield gap(node);
|
|
78
84
|
|
|
79
85
|
break;
|
|
80
86
|
}
|