@longform/longform 0.0.22 → 0.0.24
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/dist/longform.cjs +128 -108
- package/dist/longform.cjs.map +1 -1
- package/dist/longform.js +128 -108
- package/dist/longform.js.br +0 -0
- package/dist/longform.js.gz +0 -0
- package/dist/longform.js.map +1 -1
- package/dist/longform.min.js +1 -1
- package/dist/longform.min.js.br +0 -0
- package/dist/longform.min.js.gz +0 -0
- package/dist/longform.min.js.map +1 -1
- package/dist/types.d.ts +7 -8
- package/lib/longform.ts +139 -117
- package/lib/types.ts +12 -8
- package/package.json +1 -1
package/lib/longform.ts
CHANGED
|
@@ -189,15 +189,15 @@ export async function longform(
|
|
|
189
189
|
}
|
|
190
190
|
, promises: Array<Promise<void>> = []
|
|
191
191
|
|
|
192
|
-
let key: string = args?.key
|
|
192
|
+
let key: string | undefined = args?.key;
|
|
193
193
|
|
|
194
|
-
if (
|
|
194
|
+
if (key == null && args?.annotations === 'obscure') {
|
|
195
195
|
const arr = new Uint8Array(10);
|
|
196
196
|
|
|
197
197
|
crypto.getRandomValues(arr)
|
|
198
198
|
|
|
199
199
|
key = arr.toHex().toLowerCase();
|
|
200
|
-
} else if (key == null) {
|
|
200
|
+
} else if (key == null && args?.annotations === 'predictable') {
|
|
201
201
|
key = 'lf';
|
|
202
202
|
}
|
|
203
203
|
|
|
@@ -207,7 +207,11 @@ export async function longform(
|
|
|
207
207
|
if (args?.directives != null) {
|
|
208
208
|
const entries = Object.entries(args.directives);
|
|
209
209
|
|
|
210
|
-
|
|
210
|
+
let e: [string, DirectiveDef];
|
|
211
|
+
|
|
212
|
+
for (let i = 0, l = entries.length; i < l; i++) {
|
|
213
|
+
e = entries[i];
|
|
214
|
+
|
|
211
215
|
if (!directiveValidator.test(e[0])) {
|
|
212
216
|
console.warn(`Invalid custom directive name '$e{[0]}'`);
|
|
213
217
|
|
|
@@ -306,7 +310,7 @@ export async function longform(
|
|
|
306
310
|
doc,
|
|
307
311
|
inlineArgs: directive.inlineArgs,
|
|
308
312
|
blockArgs: verbatimText,
|
|
309
|
-
}) + ' ';
|
|
313
|
+
}) ?? '' + ' ';
|
|
310
314
|
} catch (err) {
|
|
311
315
|
console.error(`Error in calling directive ${directive.name}`)
|
|
312
316
|
console.error(err)
|
|
@@ -451,99 +455,33 @@ export async function longform(
|
|
|
451
455
|
}
|
|
452
456
|
|
|
453
457
|
break;
|
|
454
|
-
case '
|
|
455
|
-
if (
|
|
458
|
+
case '::':
|
|
459
|
+
if (
|
|
460
|
+
element.tag !== undefined ||
|
|
461
|
+
element.indent > indent
|
|
462
|
+
) {
|
|
456
463
|
[element, fragment] = applyIndent(indent, key, element, fragment, doc, parsed, output, args);
|
|
457
464
|
}
|
|
458
465
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
if
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
m3 = idRe.exec(m2[0]);
|
|
471
|
-
|
|
472
|
-
if (m3 != null) fragment.id = m3[3];
|
|
473
|
-
|
|
474
|
-
fragment.html += m2[0];
|
|
475
|
-
} else if (m2[1] == null && indented) {
|
|
476
|
-
sniffTestRe.lastIndex = templateLinesRe.lastIndex - m2[0].length;
|
|
477
|
-
break;
|
|
478
|
-
} else {
|
|
479
|
-
fragment.html += '\n' + m2[0];
|
|
480
|
-
if (m2[1] != null) indented = true;
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
[element, fragment] = applyIndent(0, key, element, fragment, doc, parsed, output, args);
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
continue main;
|
|
488
|
-
case 'doctype':
|
|
489
|
-
fragment.html += `<!doctype ${(inlineArgs.trim() || 'html').trim()}>`;
|
|
490
|
-
break;
|
|
491
|
-
case 'xml':
|
|
492
|
-
fragment.html += `<?xml ${inlineArgs.trim() || 'version="1.0" encoding="UTF-8"'}?>`;
|
|
493
|
-
break;
|
|
494
|
-
case 'mount':
|
|
495
|
-
if (args?.outputMode !== 'mountable') break;
|
|
496
|
-
|
|
497
|
-
if (inlineArgs === '') {
|
|
498
|
-
console.warn('Mount points must have a name');
|
|
499
|
-
} else if (fragment.type !== 'root') {
|
|
500
|
-
console.warn('Mounting is only allowed on a root element');
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
fragment.mountable = true;
|
|
504
|
-
element.mount = inlineArgs.trim();
|
|
505
|
-
break;
|
|
466
|
+
element.indent = indent;
|
|
467
|
+
|
|
468
|
+
if (indent === 0 && fragment.id == null) {
|
|
469
|
+
if (root != null) {
|
|
470
|
+
// skip if root is found and this fragment
|
|
471
|
+
// has no id
|
|
472
|
+
skipping = true;
|
|
473
|
+
} else {
|
|
474
|
+
fragment.type = 'root';
|
|
475
|
+
root = fragment;
|
|
476
|
+
}
|
|
506
477
|
}
|
|
507
|
-
|
|
508
|
-
const def = directives[m1[DIRECTIVE]];
|
|
509
478
|
|
|
510
|
-
//
|
|
511
|
-
//
|
|
512
|
-
|
|
513
|
-
verbatimIndent = indent + 1;
|
|
514
|
-
verbatimType = 4;
|
|
515
|
-
directive = {
|
|
516
|
-
name: m1[DIRECTIVE],
|
|
517
|
-
inlineArgs: m1[DIRECTIVE_INLINE_ARGS],
|
|
518
|
-
def,
|
|
519
|
-
};
|
|
520
|
-
|
|
521
|
-
break;
|
|
522
|
-
case '::':
|
|
523
|
-
if (m1[ELEMENT] !== undefined) {
|
|
479
|
+
// output mode templates parses fragments as though
|
|
480
|
+
// they were templates, including the root fragment
|
|
481
|
+
if (args?.outputMode !== 'templates') {
|
|
524
482
|
let preformattedType: string | undefined;
|
|
525
483
|
let inlineText: string | undefined;
|
|
526
484
|
|
|
527
|
-
if (
|
|
528
|
-
element.tag !== undefined ||
|
|
529
|
-
element.indent > indent
|
|
530
|
-
) {
|
|
531
|
-
[element, fragment] = applyIndent(indent, key, element, fragment, doc, parsed, output, args);
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
element.indent = indent;
|
|
535
|
-
|
|
536
|
-
if (indent === 0 && fragment.id == null) {
|
|
537
|
-
if (root != null) {
|
|
538
|
-
// skip if root is found and this fragment
|
|
539
|
-
// has no id
|
|
540
|
-
skipping = true;
|
|
541
|
-
} else {
|
|
542
|
-
fragment.type = 'root';
|
|
543
|
-
root = fragment;
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
|
|
547
485
|
const parent = element;
|
|
548
486
|
outerRe.lastIndex = 0;
|
|
549
487
|
|
|
@@ -650,6 +588,79 @@ export async function longform(
|
|
|
650
588
|
|
|
651
589
|
break;
|
|
652
590
|
}
|
|
591
|
+
|
|
592
|
+
// handle as directive if output mode is templates
|
|
593
|
+
// the templateLinesRe directive will need to re-process this
|
|
594
|
+
// line so we rewind the sniffTestRe last index for it to copy
|
|
595
|
+
sniffTestRe.lastIndex = sniffTestRe.lastIndex - m1[0].length;
|
|
596
|
+
case '@':
|
|
597
|
+
if (element.tag !== undefined) {
|
|
598
|
+
[element, fragment] = applyIndent(indent, key, element, fragment, doc, parsed, output, args);
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
const inlineArgs = m1[DIRECTIVE_INLINE_ARGS] ?? ''
|
|
602
|
+
|
|
603
|
+
switch (m1[DIRECTIVE] ?? 'template') {
|
|
604
|
+
case 'template':
|
|
605
|
+
if (indent === 0) {
|
|
606
|
+
let indented = false;
|
|
607
|
+
fragment.template = true;
|
|
608
|
+
|
|
609
|
+
templateLinesRe.lastIndex = sniffTestRe.lastIndex;
|
|
610
|
+
while ((m2 = templateLinesRe.exec(input))) {
|
|
611
|
+
if (m2[1] == null && !indented && fragment.id == null) {
|
|
612
|
+
m3 = idRe.exec(m2[0]);
|
|
613
|
+
|
|
614
|
+
if (m3 != null) fragment.id = m3[3];
|
|
615
|
+
|
|
616
|
+
fragment.html += m2[0];
|
|
617
|
+
} else if (m2[1] == null && indented) {
|
|
618
|
+
sniffTestRe.lastIndex = templateLinesRe.lastIndex - m2[0].length;
|
|
619
|
+
break;
|
|
620
|
+
} else {
|
|
621
|
+
fragment.html += '\n' + m2[0];
|
|
622
|
+
if (m2[1] != null) indented = true;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
[element, fragment] = applyIndent(0, key, element, fragment, doc, parsed, output, args);
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
continue main;
|
|
630
|
+
case 'doctype':
|
|
631
|
+
fragment.html += `<!doctype ${(inlineArgs.trim() || 'html').trim()}>`;
|
|
632
|
+
break;
|
|
633
|
+
case 'xml':
|
|
634
|
+
fragment.html += `<?xml ${inlineArgs.trim() || 'version="1.0" encoding="UTF-8"'}?>`;
|
|
635
|
+
break;
|
|
636
|
+
case 'mount':
|
|
637
|
+
if (args?.outputMode !== 'mountable') break;
|
|
638
|
+
|
|
639
|
+
if (inlineArgs === '') {
|
|
640
|
+
console.warn('Mount points must have a name');
|
|
641
|
+
} else if (fragment.type !== 'root') {
|
|
642
|
+
console.warn('Mounting is only allowed on a root element');
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
fragment.mountable = true;
|
|
646
|
+
element.mount = inlineArgs.trim();
|
|
647
|
+
break;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
const def = directives[m1[DIRECTIVE]];
|
|
651
|
+
|
|
652
|
+
// A directive may not be defined but we want to process
|
|
653
|
+
// any block args to keep the output valid. Builtin directives
|
|
654
|
+
// will be ignored unless they require block args.
|
|
655
|
+
verbatimIndent = indent + 1;
|
|
656
|
+
verbatimType = 4;
|
|
657
|
+
directive = {
|
|
658
|
+
name: m1[DIRECTIVE],
|
|
659
|
+
inlineArgs: m1[DIRECTIVE_INLINE_ARGS],
|
|
660
|
+
def,
|
|
661
|
+
};
|
|
662
|
+
|
|
663
|
+
break;
|
|
653
664
|
case '[':
|
|
654
665
|
// TODO: Add attr directive support.
|
|
655
666
|
attributeRe.lastIndex = 0;
|
|
@@ -719,13 +730,18 @@ export async function longform(
|
|
|
719
730
|
}
|
|
720
731
|
|
|
721
732
|
if (root?.html != null) {
|
|
722
|
-
|
|
723
|
-
|
|
733
|
+
if (root.template) {
|
|
734
|
+
output.template = root.html;
|
|
735
|
+
} else {
|
|
736
|
+
output.root = root.html;
|
|
737
|
+
|
|
738
|
+
if (key != null) output.selector = `[data-${key}-root]`;
|
|
739
|
+
}
|
|
724
740
|
}
|
|
725
741
|
|
|
726
742
|
let f: WorkingFragment;
|
|
727
743
|
for (let i = 0, l = arr.length, f = arr[i]; i < l; i++) {
|
|
728
|
-
let selector
|
|
744
|
+
let selector: string = undefined as unknown as string;
|
|
729
745
|
|
|
730
746
|
f = arr[i];
|
|
731
747
|
|
|
@@ -734,14 +750,16 @@ export async function longform(
|
|
|
734
750
|
continue;
|
|
735
751
|
}
|
|
736
752
|
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
753
|
+
if (key != null) {
|
|
754
|
+
switch (f.type) {
|
|
755
|
+
case 'embed':
|
|
756
|
+
if (args?.annotations === 'obscure') {
|
|
757
|
+
selector = `[id=${f.id}]`;
|
|
758
|
+
break;
|
|
759
|
+
}
|
|
760
|
+
case 'bare':
|
|
761
|
+
case 'range': selector = `[data-${key}=${f.id}]`;
|
|
762
|
+
}
|
|
745
763
|
}
|
|
746
764
|
|
|
747
765
|
output.fragments[f.id as string] = {
|
|
@@ -752,7 +770,7 @@ export async function longform(
|
|
|
752
770
|
};
|
|
753
771
|
}
|
|
754
772
|
|
|
755
|
-
output.key = key;
|
|
773
|
+
output.key = key as string;
|
|
756
774
|
output.id = id;
|
|
757
775
|
output.lang = lang;
|
|
758
776
|
output.dir = dir;
|
|
@@ -796,7 +814,7 @@ export async function processTemplate(
|
|
|
796
814
|
*/
|
|
797
815
|
function applyIndent(
|
|
798
816
|
targetIndent: number,
|
|
799
|
-
key: string,
|
|
817
|
+
key: string | undefined,
|
|
800
818
|
element: WorkingElement,
|
|
801
819
|
fragment: WorkingFragment,
|
|
802
820
|
doc: Doc,
|
|
@@ -858,18 +876,20 @@ function applyIndent(
|
|
|
858
876
|
}
|
|
859
877
|
}
|
|
860
878
|
|
|
861
|
-
if (
|
|
862
|
-
if (
|
|
863
|
-
fragment.
|
|
864
|
-
|
|
865
|
-
fragment.
|
|
866
|
-
|
|
867
|
-
fragment.
|
|
879
|
+
if (key != null) {
|
|
880
|
+
if (root) {
|
|
881
|
+
if (fragment.type === 'root') {
|
|
882
|
+
fragment.html += ` data-${key}-root`;
|
|
883
|
+
} else if (fragment.type === 'bare' || fragment.type === 'range') {
|
|
884
|
+
fragment.html += ` data-${key}="${fragment.id}"`;
|
|
885
|
+
} else if (fragment.type === 'embed' && args?.annotations === 'obscure') {
|
|
886
|
+
fragment.html += ` data-${key}="${fragment.id}"`;
|
|
887
|
+
}
|
|
868
888
|
}
|
|
869
|
-
}
|
|
870
889
|
|
|
871
|
-
|
|
872
|
-
|
|
890
|
+
if (element.mount !== undefined) {
|
|
891
|
+
fragment.html += ` data-${key}-mount="${element.mount}"`;
|
|
892
|
+
}
|
|
873
893
|
}
|
|
874
894
|
|
|
875
895
|
fragment.html += '>';
|
|
@@ -934,10 +954,12 @@ function applyIndent(
|
|
|
934
954
|
}
|
|
935
955
|
|
|
936
956
|
if (targetIndent === 0) {
|
|
937
|
-
if (fragment.
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
957
|
+
if (fragment.type !== 'root') {
|
|
958
|
+
if (fragment.template) {
|
|
959
|
+
output.templates[fragment.id] = fragment.html;
|
|
960
|
+
} else {
|
|
961
|
+
parsed.set(fragment.id, fragment);
|
|
962
|
+
}
|
|
941
963
|
}
|
|
942
964
|
|
|
943
965
|
fragment = makeFragment();
|
package/lib/types.ts
CHANGED
|
@@ -151,10 +151,16 @@ export type AppliedDirective = {
|
|
|
151
151
|
element?: (ctx: ElementCtx) => void;
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
+
export type Annotation =
|
|
155
|
+
| 'predictable'
|
|
156
|
+
| 'obscure'
|
|
157
|
+
;
|
|
158
|
+
|
|
154
159
|
export type OutputMode =
|
|
155
160
|
| 'doc'
|
|
156
161
|
| 'head'
|
|
157
162
|
| 'mountable'
|
|
163
|
+
| 'templates'
|
|
158
164
|
;
|
|
159
165
|
|
|
160
166
|
export type LongformArgs = {
|
|
@@ -165,14 +171,6 @@ export type LongformArgs = {
|
|
|
165
171
|
*/
|
|
166
172
|
key?: string;
|
|
167
173
|
|
|
168
|
-
/**
|
|
169
|
-
* If true the parser will default to using the 'lf' key for data attributes
|
|
170
|
-
* and fragments with embedded ids will not used data attributes for selectors.
|
|
171
|
-
*
|
|
172
|
-
* This is less safe in environments where Longform is used for SSR.
|
|
173
|
-
*/
|
|
174
|
-
predictable?: boolean;
|
|
175
|
-
|
|
176
174
|
/**
|
|
177
175
|
* The id of the document. If set the @id is ignored.
|
|
178
176
|
* This can be used when creating partial re-usable documents.
|
|
@@ -204,6 +202,11 @@ export type LongformArgs = {
|
|
|
204
202
|
*/
|
|
205
203
|
meta?: Record<string, unknown>;
|
|
206
204
|
|
|
205
|
+
/**
|
|
206
|
+
* Style in which data attributes are added to output HTML markup.
|
|
207
|
+
*/
|
|
208
|
+
annotations?: Annotation;
|
|
209
|
+
|
|
207
210
|
/**
|
|
208
211
|
* The output mode of the document.
|
|
209
212
|
*/
|
|
@@ -226,6 +229,7 @@ export type ParsedResult = {
|
|
|
226
229
|
data: Record<string, unknown>;
|
|
227
230
|
mountable?: boolean;
|
|
228
231
|
root?: string;
|
|
232
|
+
template?: string;
|
|
229
233
|
selector?: string;
|
|
230
234
|
mountPoints: MountPoint[];
|
|
231
235
|
tail?: string;
|