@longform/longform 0.0.6 → 0.0.8

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.js CHANGED
@@ -1,4 +1,17 @@
1
- const sniffTestRe = /^(?:(?:(--).*)|(?: *(@|#).*)|(?: *[\w\-]+(?::[\w\-]+)?(?:[#.[][^\n]+)?(::).*)|(?: +([\["]).*)|(\ \ .*))$/gmi, element1 = /((?:\ \ )+)? ?([\w\-]+(?::[\w\-]+)?)([#\.\[][^\n]*)?::(?: ({{?|[^\n]+))?/gmi, directive1 = /((?:\ \ )+)? ?@([\w][\w\-]+)(?::: ?([^\n]+)?)?/gmi, attribute1 = /((?:\ \ )+)\[(\w[\w-]*(?::\w[\w-]*)?)(?:=([^\n]+))?\]/, preformattedClose = /[ \t]*}}?[ \t]*/, id1 = /((?:\ \ )+)?#(#)?([\w\-]+)(?: ([\["]))?/gmi, idnt1 = /^(\ \ )+/, text1 = /^((?:\ \ )+)([^ \n][^\n]*)$/i, paramsRe = /(?:(#|\.)([^#.\[\n]+)|(?:\[(\w[\w\-]*(?::\w[\w\-]*)?)(?:=([^\n\]]+))?\]))/g, refRe = /#\[([\w\-]+)\]/g, escapeRe = /([&<>"'#\[\]{}])/g, templateLinesRe = /^(\ \ )?([^\n]+)$/gmi, voids = new Set(['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wrb']);
1
+ const LINE = 0, INDENT = 1, DIRECTIVE_KEY = 2, DIRECTIVE = 3, DIRECTIVE_INLINE_ARGS = 4, ID_TYPE = 5, ID = 6, FRAGMENT_TYPE = 7, ELEMENT = 8, ATTR = 9;
2
+ const sniffTestRe = /^((?: )*)(?:(@)([a-z][a-z\-]*(?::[a-z][a-z\-]*)?)(?:(?::: (.*))| *)?|(##?)([a-z][a-z\-]*)(?: ?(?: +([\["]))? *|(?: *))?|(?:[a-z][a-z\-]*(?::[a-z][a-z\-])?.*(::).*)|(?:(\[)[a-z][a-z\-]?.*(?:=.+)?\]\w*)|(.+))$/gmi
3
+ // captures a single element definition which could be in a chain.
4
+ // id, class and attributes are matched as a single block for a later loop
5
+ // if in chained situation the regexp will need to be looped over for each element
6
+ // we know the element is the last if the final capture group has a positive match
7
+ // or if the line is consumed by the regexp.
8
+ //, outer = /([a-z][\w\-]*(?::[a-z][\w\-]*)?)([^:]+)*::(?: (?:({{?)|(.*)))?/gi
9
+ , outer = /([a-z][\w\-]*(?::[a-z][\w\-]*)?)((?:(?:[^:])|(?::(?!:)))*)::(?: (?:({{?)|(.*)))?/gi
10
+ //, outer = /([a-z][\w\-]*(?::[a-z][\w\-]*)?)(.+?(?=::))?(?: (?:({{?)|(.*)))?/gi
11
+ // captures each id, class and attribute declaration in an element
12
+ , inner = /(?:\.([a-z][\w\-]+)|#([a-z][\w\-]+)|\[([a-z][a-z\-]+(?::[a-z][a-z|\-]*)?)(?:=(?:"([^"]+)"|'([^']+)'|([^\]]+)))?\])/gi, attribute1 = /((?:\ \ )+)\[(\w[\w-]*(?::\w[\w-]*)?)(?:=([^\n]+))?\]/, preformattedClose = /[ \t]*}}?[ \t]*/, id1 = /((?:\ \ )+)?#(#)?([\w\-]+)(?: ([\["]))?/gmi, identRe = /^(\ \ )+/, text1 = /^((?:\ \ )+)([^ \n][^\n]*)$/i, refRe = /#\[([\w\-]+)\]/g, escapeRe = /([&<>"'#\[\]{}])/g, templateLinesRe = /^(\ \ )?([^\n]+)$/gmi
13
+ // TODO: Benchmark v Array.includes()
14
+ , voids = new Set(['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wrb']);
2
15
  let m1, m2, m3;
3
16
  const entities = {
4
17
  '&': '&amp;',
@@ -20,8 +33,17 @@ function escape(value) {
20
33
  function makeElement(indent = 0) {
21
34
  return {
22
35
  indent,
23
- html: '',
36
+ key: undefined,
37
+ id: undefined,
38
+ tag: undefined,
39
+ class: undefined,
40
+ text: undefined,
24
41
  attrs: {},
42
+ html: '',
43
+ mount: undefined,
44
+ serializerConfig: undefined,
45
+ chain: undefined,
46
+ beforeRender: undefined,
25
47
  };
26
48
  }
27
49
  function makeFragment(type = 'bare') {
@@ -35,28 +57,100 @@ function makeFragment(type = 'bare') {
35
57
  mountPoints: [],
36
58
  };
37
59
  }
60
+ class Doc {
61
+ id = undefined;
62
+ lang = undefined;
63
+ meta = {};
64
+ #serializerConfig;
65
+ constructor(id, lang, allowAll, allowedAttributes, allowedElements) {
66
+ this.id = id;
67
+ this.lang = lang;
68
+ this.#serializerConfig = {
69
+ allowAll: allowAll ?? false,
70
+ allowedAttributes: allowedAttributes ?? [],
71
+ allowedElements: {},
72
+ };
73
+ if (allowedElements != null) {
74
+ for (let i = 0, l = allowedElements.length, el = allowedElements[i]; i < l; i++) {
75
+ if (typeof el === 'string') {
76
+ this.#serializerConfig.allowedElements[el] = {
77
+ tag: el,
78
+ attrs: [],
79
+ };
80
+ }
81
+ else {
82
+ this.#serializerConfig.allowedElements[el.tag] = el;
83
+ }
84
+ }
85
+ }
86
+ Object.freeze(this);
87
+ }
88
+ allowAll() {
89
+ }
90
+ allowAttributes() {
91
+ }
92
+ allowElements() {
93
+ }
94
+ }
95
+ const directiveValidator = /^[a-z][a-z\-]*\:[a-z][a-z\-]*$/gi;
38
96
  /**
39
97
  * Parses a longform document into a object containing the root and fragments
40
98
  * in the output format.
41
99
  *
42
- * @param {string} doc - The longform document to parse.
43
- * @returns {ParsedResult}
100
+ * @param input - The Longform document to parse.
101
+ * @param args - Arguments for the Longform parser.
44
102
  */
45
- function longform(doc, debug = () => { }) {
103
+ function longform(input, args) {
46
104
  let textIndent = null, verbatimSerialize = true, verbatimIndent = null, verbatimFirst = false, element = makeElement(), fragment = makeFragment()
47
105
  // the root fragment
48
- , root = null;
106
+ , root = null, id, lang, doc;
49
107
  // ids of claimed fragments
50
108
  const claimed = new Set()
51
109
  // parsed fragments
52
- , parsed = new Map(), output = Object.create(null);
110
+ , parsed = new Map(), output = Object.create(null), directives = {};
53
111
  output.fragments = Object.create(null);
54
112
  output.templates = Object.create(null);
113
+ if (args?.directives != null) {
114
+ const entries = Object.entries(args.directives);
115
+ for (let i = 0, l = entries.length; i < l; i++) {
116
+ if (!directiveValidator.test(entries[i][0])) {
117
+ console.warn(`Invalid custom directive name '${entries[i][0]}'`);
118
+ continue;
119
+ }
120
+ directives[entries[i][0]] = entries[i][1];
121
+ }
122
+ }
55
123
  /**
56
124
  * Closes any current in progress element definition
57
125
  * and creates a new working element.
58
126
  */
59
127
  function applyIndent(targetIndent) {
128
+ if (Array.isArray(element.beforeRender)) {
129
+ const el = {
130
+ id: element.id,
131
+ tag: element.tag,
132
+ class: element.class,
133
+ attrs: element.attrs,
134
+ };
135
+ const chain = [];
136
+ for (let i = 0, l = element.chain.length, el = element.chain[i]; i < l; i++) {
137
+ chain.push({
138
+ id: el.id,
139
+ tag: el.tag,
140
+ class: el.class,
141
+ attrs: el.attrs,
142
+ });
143
+ }
144
+ for (let i = 0, l = element.beforeRender.length, def = element.beforeRender[i]; i < l; i++) {
145
+ def.beforeRender({
146
+ el,
147
+ chain,
148
+ doc,
149
+ inlineArg: def.inlineArg,
150
+ blockArg: def.blockArg,
151
+ });
152
+ }
153
+ }
60
154
  if (element.tag != null) {
61
155
  const root = fragment.type === 'range'
62
156
  ? targetIndent < 2
@@ -70,13 +164,13 @@ function longform(doc, debug = () => { }) {
70
164
  fragment.html += ` data-lf="${fragment.id}"`;
71
165
  }
72
166
  }
73
- if (element.mount != null) {
167
+ if (element.mount !== undefined) {
74
168
  fragment.html += ` data-lf-mount="${element.mount}"`;
75
169
  }
76
- if (element.id != null) {
170
+ if (element.id !== undefined) {
77
171
  fragment.html += ' id="' + element.id + '"';
78
172
  }
79
- if (element.class != null) {
173
+ if (element.class !== undefined) {
80
174
  fragment.html += ' class="' + element.class + '"';
81
175
  }
82
176
  for (const attr of Object.entries(element.attrs)) {
@@ -88,6 +182,28 @@ function longform(doc, debug = () => { }) {
88
182
  }
89
183
  }
90
184
  fragment.html += '>';
185
+ if (Array.isArray(element.chain)) {
186
+ let chained;
187
+ for (let i = 0, l = element.chain.length; i < l; i++) {
188
+ chained = element.chain[i];
189
+ fragment.html += '<' + chained.tag;
190
+ if (chained.id !== undefined) {
191
+ fragment.html += ' id="' + chained.id + '"';
192
+ }
193
+ if (chained.class != undefined) {
194
+ fragment.html += ' class="' + chained.class + '"';
195
+ }
196
+ for (const attr of Object.entries(chained.attrs)) {
197
+ if (attr[1] === undefined) {
198
+ fragment.html += ' ' + attr[0];
199
+ }
200
+ else {
201
+ fragment.html += ` ${attr[0]}="${attr[1]}"`;
202
+ }
203
+ }
204
+ fragment.html += '>';
205
+ }
206
+ }
91
207
  if (!voids.has(element.tag) && element.text != null) {
92
208
  fragment.html += element.text;
93
209
  }
@@ -100,10 +216,14 @@ function longform(doc, debug = () => { }) {
100
216
  while (fragment.els.length !== 0 && (targetIndent == null ||
101
217
  fragment.els[fragment.els.length - 1].indent !== targetIndent - 1)) {
102
218
  const element = fragment.els.pop();
219
+ if (Array.isArray(element.chain)) {
220
+ for (let i = 0, l = element.chain.length; i < l; i++) {
221
+ fragment.html += `</${element.chain[i].tag}>`;
222
+ }
223
+ }
103
224
  fragment.html += `</${element?.tag}>`;
104
225
  }
105
226
  if (targetIndent === 0) {
106
- debug(0, '<', fragment.type, fragment.id);
107
227
  if (fragment.template) {
108
228
  output.templates[fragment.id] = fragment.html;
109
229
  }
@@ -120,7 +240,7 @@ function longform(doc, debug = () => { }) {
120
240
  element = makeElement(targetIndent);
121
241
  }
122
242
  }
123
- while ((m1 = sniffTestRe.exec(doc))) {
243
+ main: while ((m1 = sniffTestRe.exec(input))) {
124
244
  if (m1[1] === '--') {
125
245
  continue;
126
246
  }
@@ -133,14 +253,13 @@ function longform(doc, debug = () => { }) {
133
253
  // by ending the declaration with `:: {`.
134
254
  if (verbatimIndent != null) {
135
255
  // inside a script or preformatted block
136
- idnt1.lastIndex = 0;
137
- m2 = idnt1.exec(m1[0]);
256
+ identRe.lastIndex = 0;
257
+ m2 = identRe.exec(m1[0]);
138
258
  const indent = m2 == null
139
259
  ? null
140
260
  : m2[0].length / 2;
141
261
  if (m2 == null || indent <= verbatimIndent) {
142
262
  fragment.html += '\n';
143
- debug(indent, '}', m2?.[0]);
144
263
  applyIndent(indent);
145
264
  verbatimIndent = null;
146
265
  verbatimFirst = false;
@@ -151,7 +270,6 @@ function longform(doc, debug = () => { }) {
151
270
  }
152
271
  else {
153
272
  const line = m1[0].replace(' '.repeat(verbatimIndent + 1), '');
154
- debug(indent, '{', line);
155
273
  if (element.tag != null) {
156
274
  applyIndent(indent);
157
275
  }
@@ -162,69 +280,148 @@ function longform(doc, debug = () => { }) {
162
280
  fragment.html += '\n';
163
281
  }
164
282
  if (verbatimSerialize) {
165
- fragment.html += escape(line);
283
+ fragment.html += line;
166
284
  }
167
285
  else {
168
- fragment.html += line;
286
+ fragment.html += escape(line);
169
287
  }
170
288
  continue;
171
289
  }
172
290
  }
173
- if (m1[0].trim() === '') {
291
+ if (m1[LINE].trim() === '') {
174
292
  continue;
175
293
  }
176
- switch (m1[2] ?? m1[3] ?? m1[4]) {
177
- // deno-lint-ignore no-fallthrough
178
- case '#': {
294
+ // The id and lang directives should proceed all other directives and
295
+ // fragment declarations.
296
+ if (doc === undefined) {
297
+ const inlineArgs = m1[DIRECTIVE_INLINE_ARGS] ?? '';
298
+ switch (m1[DIRECTIVE]) {
299
+ case 'id': {
300
+ // TODO: Add id validation
301
+ id = inlineArgs.trim();
302
+ continue main;
303
+ }
304
+ case 'lang': {
305
+ lang = inlineArgs.trim();
306
+ continue main;
307
+ }
308
+ }
309
+ doc = new Doc(id, lang, args?.allowAll, args?.allowedAttributes, args?.allowedElements);
310
+ }
311
+ switch (m1[DIRECTIVE_KEY] ?? m1[ID_TYPE] ?? m1[ELEMENT] ?? m1[ATTR]) {
312
+ case '#':
313
+ case '##': {
179
314
  id1.lastIndex = 0;
180
- m2 = id1.exec(m1[0]);
181
- if (m2 != null) {
182
- const indent = (m2[1]?.length ?? 0) / 2;
183
- if (element.tag != null || textIndent != null) {
184
- applyIndent(indent);
185
- textIndent = null;
315
+ const indent = (m1[INDENT].length ?? 0) / 2;
316
+ if (element.tag != null || textIndent != null) {
317
+ applyIndent(indent);
318
+ textIndent = null;
319
+ }
320
+ fragment.id = m1[ID];
321
+ if (indent === 0) {
322
+ if (m1[FRAGMENT_TYPE] == '[') {
323
+ fragment.type = 'range';
324
+ }
325
+ else if (m1[FRAGMENT_TYPE] === '"') {
326
+ fragment.type = 'text';
327
+ }
328
+ else if (m1[ID_TYPE] === '##') {
329
+ fragment.type = 'bare';
330
+ }
331
+ else {
332
+ fragment.type = 'embed';
333
+ element.id = fragment.id;
334
+ }
335
+ }
336
+ else {
337
+ element.id = fragment.id;
338
+ }
339
+ break;
340
+ }
341
+ case '@': {
342
+ const indent = m1[INDENT].length / 2;
343
+ if (element.tag != null || textIndent != null) {
344
+ applyIndent(indent);
345
+ }
346
+ switch (m1[DIRECTIVE]) {
347
+ case 'doctype': {
348
+ const args = m1[DIRECTIVE_INLINE_ARGS] ?? 'html';
349
+ fragment.html += `<!doctype ${args.trim()}>`;
350
+ break;
186
351
  }
187
- debug(indent, 'id', m2[2], m2[3], m2[4]);
188
- fragment.id = m2[3];
189
- if (indent === 0) {
190
- if (m2[4] == '[') {
191
- fragment.type = 'range';
352
+ case 'xml': {
353
+ const args = m1[DIRECTIVE_INLINE_ARGS] ?? 'version="1.0" encoding="UTF-8"';
354
+ fragment.html += `<?xml ${args.trim()}?>`;
355
+ break;
356
+ }
357
+ case 'template': {
358
+ let indented = false;
359
+ fragment.template = indent === 0;
360
+ templateLinesRe.lastIndex = sniffTestRe.lastIndex;
361
+ while ((m2 = templateLinesRe.exec(input))) {
362
+ if (m2[1] == null && !indented && fragment.id == null) {
363
+ id1.lastIndex = 0;
364
+ m3 = id1.exec(m2[0]);
365
+ if (m3 != null)
366
+ fragment.id = m3[3];
367
+ fragment.html += m2[0];
368
+ }
369
+ else if (m2[1] == null && indented) {
370
+ sniffTestRe.lastIndex = templateLinesRe.lastIndex - m2[0].length;
371
+ break;
372
+ }
373
+ else {
374
+ fragment.html += '\n' + m2[0];
375
+ if (m2[1] != null)
376
+ indented = true;
377
+ }
192
378
  }
193
- else if (m2[4] === '"') {
194
- fragment.type = 'text';
379
+ applyIndent(0);
380
+ break;
381
+ }
382
+ case 'mount': {
383
+ if (m1[DIRECTIVE_INLINE_ARGS] == null) {
384
+ throw new Error('Mount points must have a name');
195
385
  }
196
- else if (m2[2] != null) {
197
- fragment.type = 'bare';
386
+ else if (fragment.type !== 'root') {
387
+ throw new Error('Mounting is only allowed on a root element');
198
388
  }
199
- else {
200
- fragment.type = 'embed';
201
- element.id = fragment.id;
389
+ fragment.mountable = true;
390
+ element.mount = m1[DIRECTIVE_INLINE_ARGS].trim();
391
+ break;
392
+ }
393
+ default: {
394
+ const def = directives[m1[DIRECTIVE]];
395
+ if (def == null)
396
+ break;
397
+ if (typeof def.beforeRender === 'function') {
398
+ if (element.id != null) {
399
+ applyIndent(indent);
400
+ }
401
+ if (element.beforeRender == null)
402
+ element.beforeRender = [];
403
+ // TODO: Parse block args
404
+ const applied = {
405
+ ...def,
406
+ inlineArg: m1[DIRECTIVE_INLINE_ARGS],
407
+ };
408
+ element.beforeRender.push(applied);
202
409
  }
203
410
  }
204
- break;
205
411
  }
412
+ break;
206
413
  }
207
- case '@':
208
414
  case '[':
209
- // deno-lint-ignore no-fallthrough
210
415
  case '::': {
211
- element1.lastIndex = 0;
212
- // fall through if m1[3] is a # or @
213
- m2 = m1[2] ?? m1[4] != null
214
- ? null
215
- : element1.exec(m1[0]);
216
- // if null then invalid element selector
217
- // allow the default text case to handle
218
- if (m2 != null) {
219
- const indent = (m2[1]?.length ?? 0) / 2, tg = m2[2], ar = m2[3], pr = m2[4] === '{' || m2[4] === '{{';
220
- const tx = pr ? null : m2[4];
221
- debug(indent, 'e', tg, pr, tx);
222
- if (element.tag != null ||
416
+ if (m1[ELEMENT] !== undefined) {
417
+ const indent = (m1[INDENT]?.length ?? 0) / 2;
418
+ let preformattedType;
419
+ let inlineText;
420
+ if (element.tag !== undefined ||
223
421
  element.indent > indent) {
224
422
  applyIndent(indent);
225
423
  }
226
424
  element.indent = indent;
227
- element.tag = tg;
228
425
  textIndent = null;
229
426
  if (indent === 0 && fragment.id == null) {
230
427
  if (root != null) ;
@@ -233,36 +430,58 @@ function longform(doc, debug = () => { }) {
233
430
  root = fragment;
234
431
  }
235
432
  }
236
- if (ar != null) {
237
- debug(indent, 'a', ar);
238
- while ((m2 = paramsRe.exec(ar))) {
239
- if (m2[1] === '#') {
240
- element.id = m2[2];
433
+ const parent = element;
434
+ outer.lastIndex = 0;
435
+ // Looping through chained element declarations
436
+ // foo#x.y::bar::free::
437
+ while ((m2 = outer.exec(m1[LINE]))) {
438
+ let working;
439
+ preformattedType = m2[3];
440
+ inlineText = m2[4];
441
+ if (element.tag === undefined) {
442
+ element.tag = m2[1];
443
+ working = element;
444
+ }
445
+ else {
446
+ if (parent.chain === undefined)
447
+ parent.chain = [];
448
+ working = makeElement(indent);
449
+ working.tag = m2[1];
450
+ parent.chain.push(working);
451
+ }
452
+ inner.lastIndex = 0;
453
+ // Looping through ids, classes and attrs
454
+ while ((m3 = inner.exec(m2[2]))) {
455
+ if (m3[2] !== undefined) {
456
+ working.id = m3[2];
241
457
  }
242
- else if (m2[1] === '.') {
243
- if (element.class == null) {
244
- element.class = m2[2];
458
+ else if (m3[1] !== undefined) {
459
+ if (working.class == null) {
460
+ working.class = m3[1];
245
461
  }
246
462
  else {
247
- element.class += ' ' + m2[2];
463
+ working.class += ' ' + m3[1];
248
464
  }
249
465
  }
250
466
  else {
251
- if (m2[3] === 'id') {
252
- if (element.id == null) {
253
- element.id = m2[4];
254
- }
255
- }
256
- else if (m2[3] === 'class') {
257
- if (element.class == null) {
258
- element.class = m2[4];
259
- }
260
- else {
261
- element.class += ' ' + m2[4];
262
- }
263
- }
264
- else {
265
- element.attrs[m2[3]] = m2[4];
467
+ // TODO: Preserve quoting style around attribute values
468
+ let value = m3[4] ?? m3[5] ?? m3[6];
469
+ switch (m3[3]) {
470
+ case 'id':
471
+ if (!working.id) {
472
+ working.id = value;
473
+ }
474
+ break;
475
+ case 'class':
476
+ if (!working.class) {
477
+ working.class = value;
478
+ }
479
+ else {
480
+ working.class += ' ' + value;
481
+ }
482
+ break;
483
+ default:
484
+ working.attrs[m3[3]] = value;
266
485
  }
267
486
  }
268
487
  }
@@ -281,22 +500,21 @@ function longform(doc, debug = () => { }) {
281
500
  applyIndent(indent);
282
501
  break;
283
502
  }
284
- if (!pr && tx != null) {
285
- element.text = tx;
286
- }
287
- else if (pr) {
503
+ if (preformattedType !== undefined) {
288
504
  verbatimFirst = true;
289
505
  verbatimIndent = indent;
290
- verbatimSerialize = m2[4] === '{';
506
+ verbatimSerialize = preformattedType === '{{';
507
+ }
508
+ else if (inlineText !== undefined) {
509
+ element.text = inlineText;
291
510
  }
292
511
  break;
293
512
  }
294
513
  attribute1.lastIndex = 0;
295
- m2 = m1[2] != null
296
- ? null
297
- : attribute1.exec(m1[0]);
514
+ m2 = m1[ATTR] !== undefined
515
+ ? attribute1.exec(m1[LINE])
516
+ : undefined;
298
517
  if (m2 != null && element.tag != null) {
299
- debug('a', m2[2], m2[3]);
300
518
  if (m2[2] === 'id') {
301
519
  if (element.id == null) {
302
520
  element.id = m2[3].trim();
@@ -318,64 +536,6 @@ function longform(doc, debug = () => { }) {
318
536
  }
319
537
  break;
320
538
  }
321
- directive1.lastIndex = 0;
322
- m2 = m1[3] != null
323
- ? null
324
- : directive1.exec(m1[0]);
325
- if (m2 != null) {
326
- const indent = (m2[1]?.length ?? 0) / 2;
327
- if (element.tag != null || textIndent != null) {
328
- applyIndent(indent);
329
- }
330
- debug(indent, 'd', m2[2], m2[3]);
331
- switch (m2[2]) {
332
- case 'doctype': {
333
- fragment.html += `<!doctype ${m2[3] ?? 'html'}>`;
334
- break;
335
- }
336
- case 'xml': {
337
- fragment.html += `<?xml ${m2[3] ?? 'version="1.0" encoding="UTF-8"'}?>`;
338
- break;
339
- }
340
- case 'template': {
341
- let indented = false;
342
- fragment.template = indent === 0;
343
- templateLinesRe.lastIndex = sniffTestRe.lastIndex;
344
- while ((m2 = templateLinesRe.exec(doc))) {
345
- if (m2[1] == null && !indented && fragment.id == null) {
346
- id1.lastIndex = 0;
347
- m3 = id1.exec(m2[0]);
348
- if (m3 != null)
349
- fragment.id = m3[3];
350
- fragment.html += m2[0];
351
- }
352
- else if (m2[1] == null && indented) {
353
- sniffTestRe.lastIndex = templateLinesRe.lastIndex - m2[0].length;
354
- break;
355
- }
356
- else {
357
- fragment.html += '\n' + m2[0];
358
- if (m2[1] != null)
359
- indented = true;
360
- }
361
- }
362
- applyIndent(0);
363
- break;
364
- }
365
- case 'mount': {
366
- if (m2[3] == null) {
367
- throw new Error('Mount points must have a name');
368
- }
369
- else if (fragment.type !== 'root') {
370
- throw new Error('Mounting is only allowed on a root element');
371
- }
372
- fragment.mountable = true;
373
- element.mount = m2[3].trim();
374
- break;
375
- }
376
- }
377
- break;
378
- }
379
539
  }
380
540
  default: {
381
541
  m2 = text1.exec(m1[0]);
@@ -384,7 +544,6 @@ function longform(doc, debug = () => { }) {
384
544
  }
385
545
  const indent = m2[1].length / 2;
386
546
  const tx = m2[2].trim();
387
- debug(indent, 't', m2[2]);
388
547
  if (element.tag != null) {
389
548
  applyIndent(indent);
390
549
  fragment.html += tx;
@@ -480,6 +639,8 @@ function longform(doc, debug = () => { }) {
480
639
  html: fragment.html,
481
640
  };
482
641
  }
642
+ output.id = doc?.id;
643
+ output.lang = doc?.lang;
483
644
  return output;
484
645
  }
485
646
  const templateRe = /(?:#{([\w][\w\-_]*)})|(?:#\[([\w][\w\-_]+)\])/g;
Binary file
Binary file