@bablr/agast-helpers 0.3.1 → 0.4.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/btree.js +1 -0
- package/lib/builders.js +100 -81
- package/lib/path.js +1 -1
- package/lib/print.js +75 -51
- package/lib/shorthand.js +12 -8
- package/lib/stream.js +141 -102
- package/lib/symbols.js +16 -0
- package/lib/template.js +18 -11
- package/lib/tree.js +320 -178
- package/package.json +3 -1
package/lib/stream.js
CHANGED
|
@@ -1,11 +1,25 @@
|
|
|
1
1
|
import { Coroutine } from '@bablr/coroutine';
|
|
2
2
|
import emptyStack from '@iter-tools/imm-stack';
|
|
3
|
-
import { printSelfClosingNodeTag,
|
|
3
|
+
import { printSelfClosingNodeTag, printTag } from './print.js';
|
|
4
4
|
import { buildWriteEffect, buildTokenGroup } from './builders.js';
|
|
5
|
+
import {
|
|
6
|
+
DoctypeTag,
|
|
7
|
+
OpenNodeTag,
|
|
8
|
+
CloseNodeTag,
|
|
9
|
+
ReferenceTag,
|
|
10
|
+
ShiftTag,
|
|
11
|
+
GapTag,
|
|
12
|
+
NullTag,
|
|
13
|
+
ArrayTag,
|
|
14
|
+
LiteralTag,
|
|
15
|
+
EmbeddedExpression,
|
|
16
|
+
TokenGroup,
|
|
17
|
+
} from './symbols.js';
|
|
18
|
+
|
|
5
19
|
export * from './print.js';
|
|
6
20
|
|
|
7
21
|
const getEmbeddedExpression = (obj) => {
|
|
8
|
-
if (obj.type !==
|
|
22
|
+
if (obj.type !== EmbeddedExpression) throw new Error();
|
|
9
23
|
return obj.value;
|
|
10
24
|
};
|
|
11
25
|
|
|
@@ -89,10 +103,8 @@ export class AsyncGenerator {
|
|
|
89
103
|
}
|
|
90
104
|
}
|
|
91
105
|
|
|
92
|
-
export const isIntrinsicToken = (
|
|
93
|
-
return
|
|
94
|
-
terminal.type === 'OpenNodeTag' && terminal.value.flags.intrinsic && terminal.value.flags.token
|
|
95
|
-
);
|
|
106
|
+
export const isIntrinsicToken = (tag) => {
|
|
107
|
+
return tag.type === OpenNodeTag && tag.value.flags.intrinsic && tag.value.flags.token;
|
|
96
108
|
};
|
|
97
109
|
|
|
98
110
|
export class StreamGenerator {
|
|
@@ -149,8 +161,8 @@ export const maybeWait = (maybePromise, callback) => {
|
|
|
149
161
|
}
|
|
150
162
|
};
|
|
151
163
|
|
|
152
|
-
function* __generateStandardOutput(
|
|
153
|
-
const co = new Coroutine(getStreamIterator(
|
|
164
|
+
function* __generateStandardOutput(tags) {
|
|
165
|
+
const co = new Coroutine(getStreamIterator(tags));
|
|
154
166
|
|
|
155
167
|
for (;;) {
|
|
156
168
|
co.advance();
|
|
@@ -160,10 +172,10 @@ function* __generateStandardOutput(terminals) {
|
|
|
160
172
|
}
|
|
161
173
|
if (co.done) break;
|
|
162
174
|
|
|
163
|
-
const
|
|
175
|
+
const tag = co.value;
|
|
164
176
|
|
|
165
|
-
if (
|
|
166
|
-
const effect = getEmbeddedExpression(
|
|
177
|
+
if (tag.type === 'Effect') {
|
|
178
|
+
const effect = getEmbeddedExpression(tag.value);
|
|
167
179
|
if (effect.verb === 'write') {
|
|
168
180
|
const writeEffect = getEmbeddedExpression(effect.value);
|
|
169
181
|
if (writeEffect.stream == null || writeEffect.stream === 1) {
|
|
@@ -174,11 +186,10 @@ function* __generateStandardOutput(terminals) {
|
|
|
174
186
|
}
|
|
175
187
|
}
|
|
176
188
|
|
|
177
|
-
export const generateStandardOutput = (
|
|
178
|
-
new StreamIterable(__generateStandardOutput(terminals));
|
|
189
|
+
export const generateStandardOutput = (tags) => new StreamIterable(__generateStandardOutput(tags));
|
|
179
190
|
|
|
180
|
-
function* __generateAllOutput(
|
|
181
|
-
const co = new Coroutine(getStreamIterator(
|
|
191
|
+
function* __generateAllOutput(tags) {
|
|
192
|
+
const co = new Coroutine(getStreamIterator(tags));
|
|
182
193
|
|
|
183
194
|
let currentStream = null;
|
|
184
195
|
|
|
@@ -190,10 +201,10 @@ function* __generateAllOutput(terminals) {
|
|
|
190
201
|
}
|
|
191
202
|
if (co.done) break;
|
|
192
203
|
|
|
193
|
-
const
|
|
204
|
+
const tag = co.value;
|
|
194
205
|
|
|
195
|
-
if (
|
|
196
|
-
const effect = getEmbeddedExpression(
|
|
206
|
+
if (tag.type === 'Effect') {
|
|
207
|
+
const effect = getEmbeddedExpression(tag.value);
|
|
197
208
|
if (effect.verb === 'write') {
|
|
198
209
|
const writeEffect = getEmbeddedExpression(effect.value);
|
|
199
210
|
const prevStream = currentStream;
|
|
@@ -207,10 +218,10 @@ function* __generateAllOutput(terminals) {
|
|
|
207
218
|
}
|
|
208
219
|
}
|
|
209
220
|
|
|
210
|
-
export const generateAllOutput = (
|
|
221
|
+
export const generateAllOutput = (tags) => new StreamIterable(__generateAllOutput(tags));
|
|
211
222
|
|
|
212
|
-
export const printCSTML = (
|
|
213
|
-
return stringFromStream(generateStandardOutput(generateCSTMLStrategy(
|
|
223
|
+
export const printCSTML = (tags) => {
|
|
224
|
+
return stringFromStream(generateStandardOutput(generateCSTMLStrategy(tags)));
|
|
214
225
|
};
|
|
215
226
|
|
|
216
227
|
function* __emptyStreamIterator() {}
|
|
@@ -230,9 +241,9 @@ export const asyncStringFromStream = async (stream) => {
|
|
|
230
241
|
|
|
231
242
|
if (co.done) break;
|
|
232
243
|
|
|
233
|
-
const
|
|
244
|
+
const tag = co.value;
|
|
234
245
|
|
|
235
|
-
str +=
|
|
246
|
+
str += printTag(tag);
|
|
236
247
|
}
|
|
237
248
|
|
|
238
249
|
return str;
|
|
@@ -255,21 +266,17 @@ export const stringFromStream = (stream) => {
|
|
|
255
266
|
return str;
|
|
256
267
|
};
|
|
257
268
|
|
|
258
|
-
function* __generateCSTMLStrategy(
|
|
269
|
+
function* __generateCSTMLStrategy(tags, options) {
|
|
259
270
|
let { emitEffects = false, inline: inlineOption = true } = options;
|
|
260
271
|
|
|
261
|
-
if (
|
|
262
|
-
throw new Error('You must use generatePrettyCSTML with emitEffects');
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
if (!terminals) {
|
|
272
|
+
if (!tags) {
|
|
266
273
|
yield buildWriteEffect('<//>');
|
|
267
274
|
return;
|
|
268
275
|
}
|
|
269
276
|
|
|
270
|
-
let
|
|
277
|
+
let prevTag = null;
|
|
271
278
|
|
|
272
|
-
const co = new Coroutine(getStreamIterator(prettyGroupTokens(
|
|
279
|
+
const co = new Coroutine(getStreamIterator(prettyGroupTokens(tags)));
|
|
273
280
|
|
|
274
281
|
for (;;) {
|
|
275
282
|
co.advance();
|
|
@@ -279,40 +286,43 @@ function* __generateCSTMLStrategy(terminals, options) {
|
|
|
279
286
|
}
|
|
280
287
|
if (co.done) break;
|
|
281
288
|
|
|
282
|
-
const
|
|
289
|
+
const tag = co.value;
|
|
283
290
|
|
|
284
|
-
if (
|
|
291
|
+
if (tag.type === ReferenceTag && prevTag.type === NullTag) {
|
|
285
292
|
yield buildWriteEffect(' ');
|
|
286
293
|
}
|
|
287
294
|
|
|
288
|
-
if (
|
|
289
|
-
const effect =
|
|
295
|
+
if (tag.type === 'Effect') {
|
|
296
|
+
const effect = tag.value;
|
|
290
297
|
if (emitEffects && effect.verb === 'write') {
|
|
291
298
|
yield buildWriteEffect(effect.value.text, effect.value.options);
|
|
292
299
|
}
|
|
293
300
|
continue;
|
|
294
301
|
}
|
|
295
302
|
|
|
296
|
-
if (
|
|
297
|
-
const intrinsicValue = getCooked(
|
|
298
|
-
yield buildWriteEffect(printSelfClosingNodeTag(
|
|
303
|
+
if (tag.type === TokenGroup) {
|
|
304
|
+
const intrinsicValue = getCooked(tag.value);
|
|
305
|
+
yield buildWriteEffect(printSelfClosingNodeTag(tag.value[0], intrinsicValue));
|
|
299
306
|
} else {
|
|
300
|
-
yield buildWriteEffect(
|
|
307
|
+
yield buildWriteEffect(printTag(tag));
|
|
301
308
|
}
|
|
302
309
|
|
|
303
|
-
|
|
310
|
+
prevTag = tag;
|
|
304
311
|
}
|
|
305
312
|
|
|
306
313
|
yield buildWriteEffect('\n');
|
|
307
314
|
}
|
|
308
315
|
|
|
309
|
-
export const
|
|
316
|
+
export const generateCSTMLStrategy = (tags, options = {}) =>
|
|
317
|
+
new StreamIterable(__generateCSTMLStrategy(tags, options));
|
|
310
318
|
|
|
311
|
-
|
|
319
|
+
export const prettyGroupTokens = (tags) => new StreamIterable(__prettyGroupTokens(tags));
|
|
320
|
+
|
|
321
|
+
function* __prettyGroupTokens(tags) {
|
|
312
322
|
let states = emptyStack.push({ holding: [], broken: false, open: null });
|
|
313
323
|
let state = states.value;
|
|
314
324
|
|
|
315
|
-
const co = new Coroutine(getStreamIterator(
|
|
325
|
+
const co = new Coroutine(getStreamIterator(tags));
|
|
316
326
|
|
|
317
327
|
for (;;) {
|
|
318
328
|
co.advance();
|
|
@@ -323,18 +333,16 @@ function* __prettyGroupTokens(terminals) {
|
|
|
323
333
|
co.current = yield co.current;
|
|
324
334
|
}
|
|
325
335
|
|
|
326
|
-
const
|
|
327
|
-
const isOpenClose =
|
|
328
|
-
terminal.type === 'CloseNodeTag' || (terminal.type === 'OpenNodeTag' && terminal.value.type);
|
|
336
|
+
const tag = co.value;
|
|
337
|
+
const isOpenClose = tag.type === CloseNodeTag || (tag.type === OpenNodeTag && tag.value.type);
|
|
329
338
|
|
|
330
339
|
if (
|
|
331
|
-
(
|
|
332
|
-
[
|
|
333
|
-
(
|
|
340
|
+
(tag.type === 'Effect' && tag.value.verb === 'write') ||
|
|
341
|
+
[ReferenceTag, DoctypeTag, GapTag, NullTag, ArrayTag, ShiftTag].includes(tag.type) ||
|
|
342
|
+
(tag.type === OpenNodeTag && !tag.value.type) ||
|
|
334
343
|
(state.open &&
|
|
335
344
|
!isIntrinsicToken(state.open) &&
|
|
336
|
-
(
|
|
337
|
-
(terminal.type === 'OpenNodeTag' && terminal.value.flags.escape)))
|
|
345
|
+
(tag.type === LiteralTag || (tag.type === OpenNodeTag && tag.value.flags.escape)))
|
|
338
346
|
) {
|
|
339
347
|
state.broken = true;
|
|
340
348
|
|
|
@@ -343,51 +351,48 @@ function* __prettyGroupTokens(terminals) {
|
|
|
343
351
|
state.holding = [];
|
|
344
352
|
}
|
|
345
353
|
|
|
346
|
-
if (!isOpenClose &&
|
|
347
|
-
yield
|
|
354
|
+
if (!isOpenClose && tag.type !== 'Effect') {
|
|
355
|
+
yield tag;
|
|
348
356
|
}
|
|
349
|
-
} else if (!isOpenClose &&
|
|
350
|
-
state.holding.push(
|
|
357
|
+
} else if (!isOpenClose && tag.type !== 'Effect') {
|
|
358
|
+
state.holding.push(tag);
|
|
351
359
|
}
|
|
352
360
|
|
|
353
|
-
if (
|
|
354
|
-
yield
|
|
361
|
+
if (tag.type === 'Effect') {
|
|
362
|
+
yield tag;
|
|
355
363
|
}
|
|
356
364
|
|
|
357
|
-
if (
|
|
365
|
+
if (tag.type === CloseNodeTag) {
|
|
358
366
|
if (!state.broken && (isIntrinsicToken(state.open) || state.holding.length === 1)) {
|
|
359
|
-
state.holding.push(
|
|
367
|
+
state.holding.push(tag);
|
|
360
368
|
yield buildTokenGroup(state.holding);
|
|
361
369
|
} else {
|
|
362
370
|
if (state.holding.length) {
|
|
363
371
|
yield* state.holding;
|
|
364
372
|
}
|
|
365
|
-
yield
|
|
373
|
+
yield tag;
|
|
366
374
|
}
|
|
367
375
|
|
|
368
376
|
states = states.pop();
|
|
369
377
|
state = states.value;
|
|
370
378
|
}
|
|
371
379
|
|
|
372
|
-
if (
|
|
373
|
-
states = states.push({ holding: [
|
|
380
|
+
if (tag.type === OpenNodeTag && tag.value.type) {
|
|
381
|
+
states = states.push({ holding: [tag], broken: false, open: tag });
|
|
374
382
|
state = states.value;
|
|
375
383
|
}
|
|
376
384
|
}
|
|
377
385
|
}
|
|
378
386
|
|
|
379
|
-
|
|
380
|
-
new StreamIterable(__generateCSTMLStrategy(terminals, options));
|
|
381
|
-
|
|
382
|
-
function* __generatePrettyCSTMLStrategy(terminals, options) {
|
|
387
|
+
function* __generatePrettyCSTMLStrategy(tags, options) {
|
|
383
388
|
let { indent = ' ', emitEffects = false, inline: inlineOption = true } = options;
|
|
384
389
|
|
|
385
|
-
if (!
|
|
390
|
+
if (!tags) {
|
|
386
391
|
yield buildWriteEffect('<//>');
|
|
387
392
|
return;
|
|
388
393
|
}
|
|
389
394
|
|
|
390
|
-
const co = new Coroutine(getStreamIterator(prettyGroupTokens(
|
|
395
|
+
const co = new Coroutine(getStreamIterator(prettyGroupTokens(tags)));
|
|
391
396
|
let indentLevel = 0;
|
|
392
397
|
let first = true;
|
|
393
398
|
let inline = false;
|
|
@@ -401,10 +406,10 @@ function* __generatePrettyCSTMLStrategy(terminals, options) {
|
|
|
401
406
|
co.current = yield co.current;
|
|
402
407
|
}
|
|
403
408
|
|
|
404
|
-
const
|
|
409
|
+
const tag = co.value;
|
|
405
410
|
|
|
406
|
-
if (
|
|
407
|
-
const effect = getEmbeddedExpression(
|
|
411
|
+
if (tag.type === 'Effect') {
|
|
412
|
+
const effect = getEmbeddedExpression(tag.value);
|
|
408
413
|
if (emitEffects && effect.verb === 'write') {
|
|
409
414
|
const writeEffect = getEmbeddedExpression(effect.value);
|
|
410
415
|
yield buildWriteEffect(
|
|
@@ -421,13 +426,16 @@ function* __generatePrettyCSTMLStrategy(terminals, options) {
|
|
|
421
426
|
inline =
|
|
422
427
|
inlineOption &&
|
|
423
428
|
inline &&
|
|
424
|
-
(
|
|
429
|
+
(tag.type === NullTag ||
|
|
430
|
+
tag.type === GapTag ||
|
|
431
|
+
tag.type === ArrayTag ||
|
|
432
|
+
tag.type === TokenGroup);
|
|
425
433
|
|
|
426
434
|
if (!first && !inline) {
|
|
427
435
|
yield buildWriteEffect('\n');
|
|
428
436
|
}
|
|
429
437
|
|
|
430
|
-
if (
|
|
438
|
+
if (tag.type === CloseNodeTag) {
|
|
431
439
|
if (indentLevel === 0) {
|
|
432
440
|
throw new Error('imbalanced tag stack');
|
|
433
441
|
}
|
|
@@ -441,18 +449,18 @@ function* __generatePrettyCSTMLStrategy(terminals, options) {
|
|
|
441
449
|
yield buildWriteEffect(' ');
|
|
442
450
|
}
|
|
443
451
|
|
|
444
|
-
if (
|
|
445
|
-
const intrinsicValue = getCooked(
|
|
446
|
-
yield buildWriteEffect(printSelfClosingNodeTag(
|
|
452
|
+
if (tag.type === TokenGroup) {
|
|
453
|
+
const intrinsicValue = tag.value[0].value.flags.token ? getCooked(tag.value) : null;
|
|
454
|
+
yield buildWriteEffect(printSelfClosingNodeTag(tag.value[0], intrinsicValue));
|
|
447
455
|
} else {
|
|
448
|
-
yield buildWriteEffect(
|
|
456
|
+
yield buildWriteEffect(printTag(tag));
|
|
449
457
|
}
|
|
450
458
|
|
|
451
|
-
if (
|
|
459
|
+
if (tag.type === ReferenceTag) {
|
|
452
460
|
inline = true;
|
|
453
461
|
}
|
|
454
462
|
|
|
455
|
-
if (
|
|
463
|
+
if (tag.type === OpenNodeTag) {
|
|
456
464
|
indentLevel++;
|
|
457
465
|
}
|
|
458
466
|
|
|
@@ -462,32 +470,51 @@ function* __generatePrettyCSTMLStrategy(terminals, options) {
|
|
|
462
470
|
yield buildWriteEffect('\n');
|
|
463
471
|
}
|
|
464
472
|
|
|
465
|
-
export const generatePrettyCSTMLStrategy = (
|
|
466
|
-
return new StreamIterable(__generatePrettyCSTMLStrategy(
|
|
473
|
+
export const generatePrettyCSTMLStrategy = (tags, options = {}) => {
|
|
474
|
+
return new StreamIterable(__generatePrettyCSTMLStrategy(tags, options));
|
|
467
475
|
};
|
|
468
476
|
|
|
469
|
-
export const printPrettyCSTML = (
|
|
470
|
-
return stringFromStream(generateStandardOutput(generatePrettyCSTMLStrategy(
|
|
477
|
+
export const printPrettyCSTML = (tags, options = {}) => {
|
|
478
|
+
return stringFromStream(generateStandardOutput(generatePrettyCSTMLStrategy(tags, options)));
|
|
471
479
|
};
|
|
472
480
|
|
|
473
|
-
export const getCooked = (
|
|
481
|
+
export const getCooked = (tags) => {
|
|
474
482
|
let cooked = '';
|
|
475
483
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
484
|
+
let first = true;
|
|
485
|
+
let foundLast = false;
|
|
486
|
+
let depth = 0;
|
|
487
|
+
|
|
488
|
+
for (const tag of tags) {
|
|
489
|
+
if (foundLast) throw new Error();
|
|
490
|
+
|
|
491
|
+
switch (tag.type) {
|
|
492
|
+
case ReferenceTag: {
|
|
493
|
+
if (depth === 1) {
|
|
494
|
+
throw new Error('cookable nodes must not contain other nodes');
|
|
495
|
+
}
|
|
496
|
+
break;
|
|
480
497
|
}
|
|
481
498
|
|
|
482
|
-
case
|
|
483
|
-
const { flags, attributes } =
|
|
499
|
+
case OpenNodeTag: {
|
|
500
|
+
const { flags, attributes } = tag.value;
|
|
501
|
+
|
|
502
|
+
depth++;
|
|
503
|
+
|
|
504
|
+
if (first) {
|
|
505
|
+
if (flags.token) {
|
|
506
|
+
break;
|
|
507
|
+
} else {
|
|
508
|
+
throw new Error(JSON.stringify(flags));
|
|
509
|
+
}
|
|
510
|
+
}
|
|
484
511
|
|
|
485
512
|
if (!(flags.trivia || (flags.escape && attributes.cooked))) {
|
|
486
513
|
throw new Error('cookable nodes must not contain other nodes');
|
|
487
514
|
}
|
|
488
515
|
|
|
489
516
|
if (flags.escape) {
|
|
490
|
-
const { cooked: cookedValue } =
|
|
517
|
+
const { cooked: cookedValue } = tag.value.attributes;
|
|
491
518
|
|
|
492
519
|
if (!cookedValue) throw new Error('cannot cook string: it contains uncooked escapes');
|
|
493
520
|
|
|
@@ -497,8 +524,18 @@ export const getCooked = (terminals) => {
|
|
|
497
524
|
break;
|
|
498
525
|
}
|
|
499
526
|
|
|
500
|
-
case
|
|
501
|
-
|
|
527
|
+
case CloseNodeTag: {
|
|
528
|
+
if (depth === 1) {
|
|
529
|
+
foundLast = true;
|
|
530
|
+
}
|
|
531
|
+
depth--;
|
|
532
|
+
break;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
case LiteralTag: {
|
|
536
|
+
if (depth === 1) {
|
|
537
|
+
cooked += tag.value;
|
|
538
|
+
}
|
|
502
539
|
break;
|
|
503
540
|
}
|
|
504
541
|
|
|
@@ -506,29 +543,31 @@ export const getCooked = (terminals) => {
|
|
|
506
543
|
throw new Error();
|
|
507
544
|
}
|
|
508
545
|
}
|
|
546
|
+
|
|
547
|
+
first = false;
|
|
509
548
|
}
|
|
510
549
|
|
|
511
550
|
return cooked;
|
|
512
551
|
};
|
|
513
552
|
|
|
514
|
-
export const printSource = (
|
|
553
|
+
export const printSource = (tags) => {
|
|
515
554
|
let printed = '';
|
|
516
555
|
|
|
517
|
-
if (!
|
|
556
|
+
if (!tags) return printed;
|
|
518
557
|
|
|
519
|
-
for (const
|
|
520
|
-
if (
|
|
521
|
-
printed +=
|
|
558
|
+
for (const tag of tags) {
|
|
559
|
+
if (tag.type === LiteralTag) {
|
|
560
|
+
printed += tag.value;
|
|
522
561
|
}
|
|
523
562
|
}
|
|
524
563
|
|
|
525
564
|
return printed;
|
|
526
565
|
};
|
|
527
566
|
|
|
528
|
-
export function* generateSourceTextFor(
|
|
529
|
-
for (const
|
|
530
|
-
if (
|
|
531
|
-
yield*
|
|
567
|
+
export function* generateSourceTextFor(tags) {
|
|
568
|
+
for (const tag of tags) {
|
|
569
|
+
if (tag.type === LiteralTag) {
|
|
570
|
+
yield* tag.value;
|
|
532
571
|
}
|
|
533
572
|
}
|
|
534
573
|
}
|
package/lib/symbols.js
CHANGED
|
@@ -6,4 +6,20 @@ export const fragment = Symbol.for('@bablr/fragment');
|
|
|
6
6
|
|
|
7
7
|
export const null_ = Symbol.for('@bablr/null');
|
|
8
8
|
|
|
9
|
+
export const DoctypeTag = Symbol.for('DoctypeTag');
|
|
10
|
+
export const OpenNodeTag = Symbol.for('OpenNodeTag');
|
|
11
|
+
export const CloseNodeTag = Symbol.for('CloseNodeTag');
|
|
12
|
+
export const ReferenceTag = Symbol.for('ReferenceTag');
|
|
13
|
+
export const ShiftTag = Symbol.for('ShiftTag');
|
|
14
|
+
export const GapTag = Symbol.for('GapTag');
|
|
15
|
+
export const NullTag = Symbol.for('NullTag');
|
|
16
|
+
export const ArrayTag = Symbol.for('ArrayTag');
|
|
17
|
+
export const LiteralTag = Symbol.for('LiteralTag');
|
|
18
|
+
|
|
19
|
+
export const EmbeddedNode = Symbol.for('EmbeddedNode');
|
|
20
|
+
export const EmbeddedTag = Symbol.for('EmbeddedTag');
|
|
21
|
+
export const EmbeddedExpression = Symbol.for('EmbeddedExpression');
|
|
22
|
+
|
|
23
|
+
export const TokenGroup = Symbol.for('TokenGroup');
|
|
24
|
+
|
|
9
25
|
export { null_ as null };
|
package/lib/template.js
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import * as t from './builders.js';
|
|
2
|
+
import { LiteralTag, EmbeddedNode } from './symbols.js';
|
|
3
|
+
import * as btree from './btree.js';
|
|
2
4
|
|
|
3
5
|
const { isArray } = Array;
|
|
4
6
|
const { freeze } = Object;
|
|
7
|
+
const { isFinite } = Number;
|
|
5
8
|
|
|
6
9
|
export const interpolateArray = (value) => {
|
|
7
10
|
if (isArray(value)) {
|
|
8
|
-
|
|
11
|
+
if (isFinite(value[0])) {
|
|
12
|
+
return [...btree.traverse(value)];
|
|
13
|
+
} else {
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
9
16
|
} else {
|
|
10
17
|
return [value];
|
|
11
18
|
}
|
|
@@ -27,28 +34,28 @@ export const interpolateArrayChildren = (value, ref, sep) => {
|
|
|
27
34
|
}
|
|
28
35
|
};
|
|
29
36
|
|
|
30
|
-
const
|
|
31
|
-
if (!
|
|
32
|
-
throw new Error('Invalid
|
|
37
|
+
const validateTag = (tag) => {
|
|
38
|
+
if (!tag || (tag.type !== LiteralTag && tag.type !== EmbeddedNode)) {
|
|
39
|
+
throw new Error('Invalid tag');
|
|
33
40
|
}
|
|
34
|
-
if (
|
|
41
|
+
if (tag.type === EmbeddedNode && !tag.value.flags.escape) {
|
|
35
42
|
throw new Error();
|
|
36
43
|
}
|
|
37
44
|
};
|
|
38
45
|
|
|
39
46
|
export const interpolateString = (value) => {
|
|
40
|
-
const
|
|
47
|
+
const tags = [];
|
|
41
48
|
if (isArray(value)) {
|
|
42
49
|
for (const element of value) {
|
|
43
|
-
|
|
50
|
+
validateTag(element);
|
|
44
51
|
|
|
45
|
-
|
|
52
|
+
tags.push(element);
|
|
46
53
|
}
|
|
47
54
|
} else {
|
|
48
55
|
// we can't safely interpolate strings here, though I wish we could
|
|
49
|
-
|
|
50
|
-
|
|
56
|
+
validateTag(value);
|
|
57
|
+
tags.push(value);
|
|
51
58
|
}
|
|
52
59
|
|
|
53
|
-
return t.buildNode('String', 'Content',
|
|
60
|
+
return t.buildNode('String', 'Content', tags);
|
|
54
61
|
};
|