@longform/longform 0.0.18 → 0.0.19
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 +417 -366
- package/dist/longform.cjs.map +1 -1
- package/dist/longform.d.ts +2 -2
- package/dist/longform.js +417 -366
- 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 -735
- 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/mod.d.ts +2 -2
- package/dist/types.d.ts +31 -24
- package/lib/longform.ts +517 -446
- package/lib/mod.ts +17 -2
- package/lib/types.ts +33 -29
- package/package.json +4 -2
package/dist/longform.cjs
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
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
|
|
4
|
-
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
|
+
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, COMMENT = 10, 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
|
|
5
4
|
// captures a single element definition which could be in a chain.
|
|
6
5
|
// id, class and attributes are matched as a single block for a later loop
|
|
7
6
|
// if in chained situation the regexp will need to be looped over for each element
|
|
8
7
|
// we know the element is the last if the final capture group has a positive match
|
|
9
8
|
// or if the line is consumed by the regexp.
|
|
10
|
-
,
|
|
9
|
+
, outerRe = /([a-z][\w\-]*(?::[a-z][\w\-]*)?)((?:(?:[^:])|(?::(?!:)))*)::(?: (?:({{?)|(.*)))?/gi
|
|
11
10
|
// captures each id, class and attribute declaration in an element
|
|
12
|
-
,
|
|
13
|
-
let m1, m2, m3, m4;
|
|
11
|
+
, innerRe = /(?:\.([a-z][\w\-]+)|#([a-z][\w\-]+)|\[([a-z][a-z\-]+(?::[a-z][a-z|\-]*)?)(?:=(?:"([^"]+)"|'([^']+)'|([^\]]+)))?\])/gi, attributeRe = /((?:\ \ )+)\[(\w[\w-]*(?::\w[\w-]*)?)(?:=([^\n]+))?\]/, attributeDirectiveRe = /^@([a-z][\w\-]*(?::[a-z][\w\-]*)?)$/i, idRe = /((?:\ \ )+)?#(#)?([\w\-]+)(?: ([\["]))?/i, refRe = /#\[([\w\-]+)\]/g, escapeRe = /([&<>"'#\[\]{}])/g, templateLinesRe = /^(\ \ )?([^\n]+)$/gm, templateRe = /#(#)?{([a-z][\w\-]*(?::[a-z][\w\-]*)?)}|#\[([a-z][\w\-]*(?::[a-z][\w\-]*)?)\]/g, preformatClose = new Set(['}', '}}']), voids = new Set(['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wrb']);
|
|
12
|
+
let m1 = null, m2 = null, m3 = null, m4 = null;
|
|
14
13
|
const entities = {
|
|
15
14
|
'&': '&',
|
|
16
15
|
'<': '<',
|
|
@@ -46,6 +45,7 @@ function makeElement(indent = 0) {
|
|
|
46
45
|
}
|
|
47
46
|
function makeFragment(type = 'bare') {
|
|
48
47
|
return {
|
|
48
|
+
id: undefined,
|
|
49
49
|
type,
|
|
50
50
|
html: '',
|
|
51
51
|
template: false,
|
|
@@ -101,10 +101,10 @@ const directiveValidator = /^[a-z][a-z\-]*\:[a-z][a-z\-]*$/i;
|
|
|
101
101
|
* @param input - The Longform document to parse.
|
|
102
102
|
* @param args - Arguments for the Longform parser.
|
|
103
103
|
*/
|
|
104
|
-
function longform(input, args) {
|
|
105
|
-
let
|
|
104
|
+
async function longform(input, args) {
|
|
105
|
+
let skipping = false, verbatimText = '', verbatimIndent = 0, verbatimType = 0, element = makeElement(), fragment = makeFragment(), directive
|
|
106
106
|
// the root fragment
|
|
107
|
-
, root = null, id = args?.id, lang = args?.lang, dir = args?.dir, meta = {}, doc;
|
|
107
|
+
, root = null, id = args?.id, lang = args?.lang, dir = args?.dir, meta = {}, data = {}, doc, asyncCount = 0;
|
|
108
108
|
// ids of claimed fragments
|
|
109
109
|
const claimed = new Set()
|
|
110
110
|
// parsed fragments
|
|
@@ -112,7 +112,7 @@ function longform(input, args) {
|
|
|
112
112
|
id: { attr: ctx => ctx.doc.id },
|
|
113
113
|
dir: { attr: ctx => ctx.doc.dir },
|
|
114
114
|
lang: { attr: ctx => ctx.doc.lang },
|
|
115
|
-
};
|
|
115
|
+
}, promises = [];
|
|
116
116
|
let key = args?.key;
|
|
117
117
|
if (!args?.predictable && key == null) {
|
|
118
118
|
const arr = new Uint8Array(10);
|
|
@@ -126,245 +126,200 @@ function longform(input, args) {
|
|
|
126
126
|
output.templates = {};
|
|
127
127
|
if (args?.directives != null) {
|
|
128
128
|
const entries = Object.entries(args.directives);
|
|
129
|
-
for (let i = 0, l = entries.length; i < l; i++) {
|
|
130
|
-
if (!directiveValidator.test(
|
|
131
|
-
console.warn(`Invalid custom directive name '${
|
|
129
|
+
for (let i = 0, l = entries.length, e = entries[i]; i < l; i++) {
|
|
130
|
+
if (!directiveValidator.test(e[0])) {
|
|
131
|
+
console.warn(`Invalid custom directive name '$e{[0]}'`);
|
|
132
132
|
continue;
|
|
133
133
|
}
|
|
134
|
-
directives[
|
|
134
|
+
directives[e[0]] = e[1];
|
|
135
135
|
}
|
|
136
136
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
if (
|
|
143
|
-
|
|
144
|
-
id: element.id,
|
|
145
|
-
tag: element.tag,
|
|
146
|
-
class: element.class,
|
|
147
|
-
attrs: element.attrs,
|
|
148
|
-
};
|
|
149
|
-
const chain = [];
|
|
150
|
-
for (let i = 0, l = element.chain.length, el = element.chain[i]; i < l; i++) {
|
|
151
|
-
chain.push({
|
|
152
|
-
id: el.id,
|
|
153
|
-
tag: el.tag,
|
|
154
|
-
class: el.class,
|
|
155
|
-
attrs: el.attrs,
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
for (let i = 0, l = element.beforeRender.length, def = element.beforeRender[i]; i < l; i++) {
|
|
159
|
-
def.beforeRender({
|
|
160
|
-
el,
|
|
161
|
-
chain,
|
|
162
|
-
doc: doc,
|
|
163
|
-
inlineArg: def.inlineArg,
|
|
164
|
-
blockArg: def.blockArg,
|
|
165
|
-
});
|
|
166
|
-
}
|
|
137
|
+
// This is a hack to allow open verbatim blocks to detect
|
|
138
|
+
// when they close via an extra step in the loop.
|
|
139
|
+
input += '\n ';
|
|
140
|
+
sniffTestRe.lastIndex = 0;
|
|
141
|
+
main: while ((m1 = sniffTestRe.exec(input))) {
|
|
142
|
+
if (m1[COMMENT] === '--') {
|
|
143
|
+
continue;
|
|
167
144
|
}
|
|
168
|
-
if (
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
145
|
+
else if (fragment.template) {
|
|
146
|
+
fragment.html += m1[0];
|
|
147
|
+
}
|
|
148
|
+
const indent = m1[INDENT].length / 2;
|
|
149
|
+
// only one root fragment is allowed. Skip until beginning of next fragment / directive.
|
|
150
|
+
if (skipping && indent !== 0)
|
|
151
|
+
continue;
|
|
152
|
+
if (skipping)
|
|
153
|
+
skipping = false;
|
|
154
|
+
// verbatim blocks collect the string as is and do processing on it
|
|
155
|
+
// once the full block has been collected into one string
|
|
156
|
+
if (verbatimType !== 0) {
|
|
157
|
+
if (indent >= verbatimIndent && (verbatimType !== 1 || // text verbatim type should exit when other symbols are parsed
|
|
158
|
+
(m1[DIRECTIVE_KEY] ?? m1[ID_TYPE] ?? m1[ELEMENT] ?? m1[ATTR]) === undefined)) {
|
|
159
|
+
// still in verbatim block
|
|
160
|
+
if (verbatimType === 1) {
|
|
161
|
+
verbatimText += ' ' + m1[0].trim();
|
|
184
162
|
}
|
|
185
163
|
else {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
if (root) {
|
|
190
|
-
if (fragment.type === 'root') {
|
|
191
|
-
fragment.html += ` data-${key}-root`;
|
|
192
|
-
}
|
|
193
|
-
else if (fragment.type === 'bare' || fragment.type === 'range') {
|
|
194
|
-
fragment.html += ` data-${key}="${fragment.id}"`;
|
|
195
|
-
}
|
|
196
|
-
else if (fragment.type === 'embed' && !args?.predictable) {
|
|
197
|
-
fragment.html += ` data-${key}="${fragment.id}"`;
|
|
164
|
+
if (verbatimText !== '')
|
|
165
|
+
verbatimText += '\n';
|
|
166
|
+
verbatimText += m1[0].replace(m1[INDENT], '');
|
|
198
167
|
}
|
|
168
|
+
continue;
|
|
199
169
|
}
|
|
200
|
-
if (
|
|
201
|
-
|
|
170
|
+
else if (m1[0].trim() === '' &&
|
|
171
|
+
input.length !== m1.index + m1[0].length) {
|
|
172
|
+
// blank line in verbatim
|
|
173
|
+
if (verbatimType !== 1)
|
|
174
|
+
verbatimText += '\n';
|
|
175
|
+
continue;
|
|
202
176
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
177
|
+
else {
|
|
178
|
+
// verbatim block is finished
|
|
179
|
+
switch (verbatimType) {
|
|
180
|
+
case 1:
|
|
181
|
+
// text
|
|
182
|
+
fragment.html += verbatimText;
|
|
183
|
+
// locate reference points in text
|
|
184
|
+
while ((m2 = refRe.exec(verbatimText))) {
|
|
185
|
+
const start = fragment.html.length + m2.index - verbatimText.length;
|
|
186
|
+
fragment.refs.push({
|
|
187
|
+
id: m2[1],
|
|
188
|
+
start,
|
|
189
|
+
end: start + m2[0].length,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
applyIndent(indent, key, element, fragment, doc, parsed, output, args);
|
|
193
|
+
break;
|
|
194
|
+
case 2:
|
|
195
|
+
// escaped preformatted text
|
|
196
|
+
fragment.html += escape(verbatimText);
|
|
197
|
+
break;
|
|
198
|
+
case 3:
|
|
199
|
+
// preformatted
|
|
200
|
+
fragment.html += verbatimText + '\n';
|
|
201
|
+
break;
|
|
202
|
+
case 4:
|
|
203
|
+
// directive block args
|
|
204
|
+
if (directive.def == null)
|
|
205
|
+
break;
|
|
206
|
+
if (doc == null) {
|
|
207
|
+
if (typeof directive.def.meta === 'function') {
|
|
208
|
+
meta[m1[DIRECTIVE]] = directive.def.meta({
|
|
209
|
+
inlineArgs: directive.inlineArgs,
|
|
210
|
+
blockArgs: verbatimText,
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
else if (typeof directive.def.render === 'function') {
|
|
215
|
+
try {
|
|
216
|
+
element.html += directive.def.render({
|
|
217
|
+
doc,
|
|
218
|
+
inlineArgs: directive.inlineArgs,
|
|
219
|
+
blockArgs: verbatimText,
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
catch (err) {
|
|
223
|
+
console.error(`Error in calling directive ${directive.name}`);
|
|
224
|
+
console.error(err);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
else if (typeof directive.def.asyncRender === 'function') {
|
|
228
|
+
asyncCount++;
|
|
229
|
+
// async rendering uses the #[ref] feature to insert the
|
|
230
|
+
// eventual response.
|
|
231
|
+
const directiveFragment = makeFragment('embed');
|
|
232
|
+
directiveFragment.id = `@${asyncCount}`;
|
|
233
|
+
parsed.set(directiveFragment.id, directiveFragment);
|
|
234
|
+
fragment.refs.push({
|
|
235
|
+
id: directiveFragment.id,
|
|
236
|
+
start: fragment.html.length,
|
|
237
|
+
end: fragment.html.length,
|
|
238
|
+
});
|
|
239
|
+
promises.push(directive.def.asyncRender({
|
|
240
|
+
doc,
|
|
241
|
+
inlineArgs: directive.inlineArgs,
|
|
242
|
+
blockArgs: verbatimText,
|
|
243
|
+
}).then(res => {
|
|
244
|
+
directiveFragment.html = res ?? '';
|
|
245
|
+
}).catch(err => {
|
|
246
|
+
console.error(`Error in calling directive ${directive.name}`);
|
|
247
|
+
console.error(err);
|
|
248
|
+
}));
|
|
249
|
+
}
|
|
250
|
+
else if (typeof directive.def.element === 'function') {
|
|
251
|
+
if (element.beforeRender == null)
|
|
252
|
+
element.beforeRender = [];
|
|
253
|
+
element.beforeRender.push({
|
|
254
|
+
blockArgs: verbatimText,
|
|
255
|
+
inlineArgs: directive.inlineArgs,
|
|
256
|
+
element: directive.def.element,
|
|
257
|
+
});
|
|
218
258
|
}
|
|
219
259
|
else {
|
|
220
|
-
|
|
260
|
+
console.warn(`Directive used in incorrect context ${directive.name}`);
|
|
221
261
|
}
|
|
222
|
-
}
|
|
223
|
-
fragment.html += '>';
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
if (!voids.has(element.tag) && element.text != null) {
|
|
227
|
-
fragment.html += element.text;
|
|
228
|
-
}
|
|
229
|
-
if (!voids.has(element.tag)) {
|
|
230
|
-
fragment.els.push(element);
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
if (targetIndent <= element.indent) {
|
|
234
|
-
element = makeElement(targetIndent);
|
|
235
|
-
while (fragment.els.length !== 0 && (targetIndent == null ||
|
|
236
|
-
fragment.els[fragment.els.length - 1].indent !== targetIndent - 1)) {
|
|
237
|
-
const element = fragment.els.pop();
|
|
238
|
-
if (Array.isArray(element.chain)) {
|
|
239
|
-
for (let i = 0, l = element.chain.length; i < l; i++) {
|
|
240
|
-
fragment.html += `</${element.chain[i].tag}>`;
|
|
241
|
-
}
|
|
242
262
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
if (fragment.template) {
|
|
247
|
-
output.templates[fragment.id] = fragment.html;
|
|
248
|
-
}
|
|
249
|
-
else if (fragment.type === 'root') {
|
|
250
|
-
root = fragment;
|
|
251
|
-
}
|
|
252
|
-
else {
|
|
253
|
-
parsed.set(fragment.id, fragment);
|
|
254
|
-
}
|
|
255
|
-
fragment = makeFragment();
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
else {
|
|
259
|
-
element = makeElement(targetIndent);
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
main: while ((m1 = sniffTestRe.exec(input))) {
|
|
263
|
-
if (m1[1] === '--') {
|
|
264
|
-
continue;
|
|
265
|
-
}
|
|
266
|
-
else if (fragment.template) {
|
|
267
|
-
fragment.html += m1[0];
|
|
268
|
-
}
|
|
269
|
-
// If this is a script tag or preformatted block
|
|
270
|
-
// we want to retain the intended formatting less
|
|
271
|
-
// the indent. Pre-formatting can apply to any element
|
|
272
|
-
// by ending the declaration with `:: {`.
|
|
273
|
-
if (verbatimIndent != null) {
|
|
274
|
-
// inside a script or preformatted block
|
|
275
|
-
identRe.lastIndex = 0;
|
|
276
|
-
m2 = identRe.exec(m1[0]);
|
|
277
|
-
const indent = m2 == null
|
|
278
|
-
? null
|
|
279
|
-
: m2[0].length / 2;
|
|
280
|
-
if (m2 == null || indent <= verbatimIndent) {
|
|
281
|
-
fragment.html += '\n';
|
|
282
|
-
applyIndent(indent);
|
|
283
|
-
verbatimIndent = null;
|
|
284
|
-
verbatimFirst = false;
|
|
285
|
-
textIndent = indent;
|
|
286
|
-
if (preformattedClose.test(m1[0])) {
|
|
263
|
+
verbatimType = 0;
|
|
264
|
+
verbatimText = '';
|
|
265
|
+
if (preformatClose.has(m1[0].trim()))
|
|
287
266
|
continue;
|
|
288
|
-
}
|
|
289
267
|
}
|
|
290
|
-
|
|
291
|
-
const line = m1[0].replace(' '.repeat(verbatimIndent + 1), '');
|
|
292
|
-
if (element.tag != null) {
|
|
293
|
-
applyIndent(indent);
|
|
294
|
-
}
|
|
295
|
-
if (verbatimFirst) {
|
|
296
|
-
verbatimFirst = false;
|
|
297
|
-
}
|
|
298
|
-
else {
|
|
299
|
-
fragment.html += '\n';
|
|
300
|
-
}
|
|
301
|
-
if (verbatimSerialize) {
|
|
302
|
-
fragment.html += line;
|
|
303
|
-
}
|
|
304
|
-
else {
|
|
305
|
-
fragment.html += escape(line);
|
|
306
|
-
}
|
|
307
|
-
continue;
|
|
308
|
-
}
|
|
309
|
-
}
|
|
268
|
+
} // end verbatim
|
|
310
269
|
if (m1[LINE].trim() === '') {
|
|
270
|
+
// empty lines have no effect from here on
|
|
311
271
|
continue;
|
|
312
272
|
}
|
|
313
|
-
// The id and lang directives should proceed all other directives and
|
|
314
|
-
// fragment declarations.
|
|
315
273
|
if (doc === undefined) {
|
|
274
|
+
// The meta directives get special treatment.
|
|
275
|
+
let parseBlock = false;
|
|
316
276
|
const inlineArgs = m1[DIRECTIVE_INLINE_ARGS] ?? '';
|
|
317
277
|
switch (m1[DIRECTIVE]) {
|
|
318
|
-
case 'id':
|
|
278
|
+
case 'id':
|
|
319
279
|
const url = inlineArgs.trim();
|
|
280
|
+
parseBlock = true;
|
|
320
281
|
try {
|
|
321
282
|
id = id ?? new URL(url, args?.base).toString();
|
|
322
283
|
}
|
|
323
284
|
catch (err) {
|
|
324
|
-
console.warn(`Invalid URL given to @id directive: ${url} base=${args
|
|
285
|
+
console.warn(`Invalid URL given to @id directive: ${url} base=${args?.base}`);
|
|
325
286
|
}
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
287
|
+
break;
|
|
288
|
+
case 'lang':
|
|
289
|
+
parseBlock = true;
|
|
329
290
|
lang = lang ?? inlineArgs.trim();
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
291
|
+
break;
|
|
292
|
+
case 'dir':
|
|
293
|
+
parseBlock = true;
|
|
333
294
|
dir = dir ?? inlineArgs.trim();
|
|
334
|
-
continue main;
|
|
335
|
-
}
|
|
336
|
-
default: {
|
|
337
|
-
const def = directives[m1[DIRECTIVE]];
|
|
338
|
-
if (typeof def?.meta === 'function') {
|
|
339
|
-
if (Object.keys(def).length > 1) {
|
|
340
|
-
throw new Error(`A custom directive performing the meta role cannot be used for other purposes. ` +
|
|
341
|
-
`See @${m1[DIRECTIVE]}`);
|
|
342
|
-
}
|
|
343
|
-
meta[m1[DIRECTIVE]] = def.meta({ inlineArgs });
|
|
344
|
-
continue main;
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
295
|
}
|
|
348
|
-
|
|
349
|
-
if (
|
|
296
|
+
const def = directives[m1[DIRECTIVE]];
|
|
297
|
+
if (typeof def?.meta === 'function' || parseBlock) {
|
|
298
|
+
verbatimIndent = indent + 1;
|
|
299
|
+
verbatimType = 4;
|
|
300
|
+
directive = {
|
|
301
|
+
name: m1[DIRECTIVE],
|
|
302
|
+
inlineArgs,
|
|
303
|
+
def,
|
|
304
|
+
};
|
|
305
|
+
continue main;
|
|
306
|
+
}
|
|
307
|
+
if (args?.outputMode === 'head') {
|
|
350
308
|
return {
|
|
351
309
|
key,
|
|
352
310
|
id,
|
|
353
311
|
lang,
|
|
354
312
|
dir,
|
|
355
|
-
meta
|
|
313
|
+
meta,
|
|
314
|
+
data,
|
|
356
315
|
};
|
|
357
316
|
}
|
|
317
|
+
doc = new Doc(id, lang, dir, meta, args?.allowAll, args?.allowedAttributes, args?.allowedElements);
|
|
358
318
|
}
|
|
359
319
|
switch (m1[DIRECTIVE_KEY] ?? m1[ID_TYPE] ?? m1[ELEMENT] ?? m1[ATTR]) {
|
|
360
320
|
case '#':
|
|
361
|
-
case '##':
|
|
362
|
-
|
|
363
|
-
const indent = (m1[INDENT].length ?? 0) / 2;
|
|
364
|
-
if (element.tag != null || textIndent != null) {
|
|
365
|
-
applyIndent(indent);
|
|
366
|
-
textIndent = null;
|
|
367
|
-
}
|
|
321
|
+
case '##':
|
|
322
|
+
[element, fragment] = applyIndent(indent, key, element, fragment, doc, parsed, output, args);
|
|
368
323
|
fragment.id = m1[ID];
|
|
369
324
|
if (indent === 0) {
|
|
370
325
|
if (m1[FRAGMENT_TYPE] == '[') {
|
|
@@ -385,104 +340,93 @@ function longform(input, args) {
|
|
|
385
340
|
element.id = fragment.id;
|
|
386
341
|
}
|
|
387
342
|
break;
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
if (element.tag != null || textIndent != null) {
|
|
392
|
-
applyIndent(indent);
|
|
343
|
+
case '@':
|
|
344
|
+
if (element.tag !== undefined) {
|
|
345
|
+
[element, fragment] = applyIndent(indent, key, element, fragment, doc, parsed, output, args);
|
|
393
346
|
}
|
|
347
|
+
const inlineArgs = m1[DIRECTIVE_INLINE_ARGS] ?? '';
|
|
394
348
|
switch (m1[DIRECTIVE]) {
|
|
395
|
-
case '
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
}
|
|
417
|
-
else if (m2[1] == null && indented) {
|
|
418
|
-
sniffTestRe.lastIndex = templateLinesRe.lastIndex - m2[0].length;
|
|
419
|
-
break;
|
|
420
|
-
}
|
|
421
|
-
else {
|
|
422
|
-
fragment.html += '\n' + m2[0];
|
|
423
|
-
if (m2[1] != null)
|
|
424
|
-
indented = true;
|
|
349
|
+
case 'template':
|
|
350
|
+
if (indent === 0) {
|
|
351
|
+
let indented = false;
|
|
352
|
+
fragment.template = true;
|
|
353
|
+
templateLinesRe.lastIndex = sniffTestRe.lastIndex;
|
|
354
|
+
while ((m2 = templateLinesRe.exec(input))) {
|
|
355
|
+
if (m2[1] == null && !indented && fragment.id == null) {
|
|
356
|
+
m3 = idRe.exec(m2[0]);
|
|
357
|
+
if (m3 != null)
|
|
358
|
+
fragment.id = m3[3];
|
|
359
|
+
fragment.html += m2[0];
|
|
360
|
+
}
|
|
361
|
+
else if (m2[1] == null && indented) {
|
|
362
|
+
sniffTestRe.lastIndex = templateLinesRe.lastIndex - m2[0].length;
|
|
363
|
+
break;
|
|
364
|
+
}
|
|
365
|
+
else {
|
|
366
|
+
fragment.html += '\n' + m2[0];
|
|
367
|
+
if (m2[1] != null)
|
|
368
|
+
indented = true;
|
|
369
|
+
}
|
|
425
370
|
}
|
|
371
|
+
[element, fragment] = applyIndent(0, key, element, fragment, doc, parsed, output, args);
|
|
426
372
|
}
|
|
427
|
-
|
|
373
|
+
continue main;
|
|
374
|
+
case 'doctype':
|
|
375
|
+
fragment.html += `<!doctype ${(inlineArgs.trim() || 'html').trim()}>`;
|
|
428
376
|
break;
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
377
|
+
case 'xml':
|
|
378
|
+
fragment.html += `<?xml ${inlineArgs.trim() || 'version="1.0" encoding="UTF-8"'}?>`;
|
|
379
|
+
break;
|
|
380
|
+
case 'mount':
|
|
381
|
+
if (args?.outputMode !== 'mountable')
|
|
382
|
+
break;
|
|
383
|
+
if (inlineArgs === '') {
|
|
384
|
+
console.warn('Mount points must have a name');
|
|
433
385
|
}
|
|
434
386
|
else if (fragment.type !== 'root') {
|
|
435
|
-
|
|
387
|
+
console.warn('Mounting is only allowed on a root element');
|
|
436
388
|
}
|
|
437
389
|
fragment.mountable = true;
|
|
438
|
-
element.mount =
|
|
390
|
+
element.mount = inlineArgs.trim();
|
|
439
391
|
break;
|
|
440
|
-
}
|
|
441
|
-
default: {
|
|
442
|
-
const def = directives[m1[DIRECTIVE]];
|
|
443
|
-
if (def == null)
|
|
444
|
-
break;
|
|
445
|
-
if (typeof def.beforeRender === 'function') {
|
|
446
|
-
if (element.id != null) {
|
|
447
|
-
applyIndent(indent);
|
|
448
|
-
}
|
|
449
|
-
if (element.beforeRender == null)
|
|
450
|
-
element.beforeRender = [];
|
|
451
|
-
// TODO: Parse block args
|
|
452
|
-
const applied = {
|
|
453
|
-
...def,
|
|
454
|
-
inlineArg: m1[DIRECTIVE_INLINE_ARGS],
|
|
455
|
-
};
|
|
456
|
-
element.beforeRender.push(applied);
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
392
|
}
|
|
393
|
+
const def = directives[m1[DIRECTIVE]];
|
|
394
|
+
// A directive may not be defined but we want to process
|
|
395
|
+
// any block args to keep the output valid. Builtin directives
|
|
396
|
+
// will be ignored unless they require block args.
|
|
397
|
+
verbatimIndent = indent + 1;
|
|
398
|
+
verbatimType = 4;
|
|
399
|
+
directive = {
|
|
400
|
+
name: m1[DIRECTIVE],
|
|
401
|
+
inlineArgs: m1[DIRECTIVE_INLINE_ARGS],
|
|
402
|
+
def,
|
|
403
|
+
};
|
|
460
404
|
break;
|
|
461
|
-
|
|
462
|
-
case '[':
|
|
463
|
-
case '::': {
|
|
405
|
+
case '::':
|
|
464
406
|
if (m1[ELEMENT] !== undefined) {
|
|
465
|
-
const indent = (m1[INDENT]?.length ?? 0) / 2;
|
|
466
407
|
let preformattedType;
|
|
467
408
|
let inlineText;
|
|
468
409
|
if (element.tag !== undefined ||
|
|
469
410
|
element.indent > indent) {
|
|
470
|
-
applyIndent(indent);
|
|
411
|
+
[element, fragment] = applyIndent(indent, key, element, fragment, doc, parsed, output, args);
|
|
471
412
|
}
|
|
472
413
|
element.indent = indent;
|
|
473
|
-
textIndent = null;
|
|
474
414
|
if (indent === 0 && fragment.id == null) {
|
|
475
|
-
if (root != null)
|
|
415
|
+
if (root != null) {
|
|
416
|
+
// skip if root is found and this fragment
|
|
417
|
+
// has no id
|
|
418
|
+
skipping = true;
|
|
419
|
+
}
|
|
476
420
|
else {
|
|
477
421
|
fragment.type = 'root';
|
|
478
422
|
root = fragment;
|
|
479
423
|
}
|
|
480
424
|
}
|
|
481
425
|
const parent = element;
|
|
482
|
-
|
|
426
|
+
outerRe.lastIndex = 0;
|
|
483
427
|
// Looping through chained element declarations
|
|
484
428
|
// foo#x.y::bar::free::
|
|
485
|
-
while ((m2 =
|
|
429
|
+
while ((m2 = outerRe.exec(m1[LINE]))) {
|
|
486
430
|
let working;
|
|
487
431
|
preformattedType = m2[3];
|
|
488
432
|
inlineText = m2[4];
|
|
@@ -497,9 +441,9 @@ function longform(input, args) {
|
|
|
497
441
|
working.tag = m2[1];
|
|
498
442
|
parent.chain.push(working);
|
|
499
443
|
}
|
|
500
|
-
|
|
444
|
+
innerRe.lastIndex = 0;
|
|
501
445
|
// Looping through ids, classes and attrs
|
|
502
|
-
while ((m3 =
|
|
446
|
+
while ((m3 = innerRe.exec(m2[2]))) {
|
|
503
447
|
if (m3[2] !== undefined) {
|
|
504
448
|
working.id = m3[2];
|
|
505
449
|
}
|
|
@@ -516,8 +460,8 @@ function longform(input, args) {
|
|
|
516
460
|
let value = m3[4] ?? m3[5] ?? m3[6];
|
|
517
461
|
// attribute directives
|
|
518
462
|
if (value[0] === '@') {
|
|
519
|
-
|
|
520
|
-
m4 =
|
|
463
|
+
attributeDirectiveRe.lastIndex = 0;
|
|
464
|
+
m4 = attributeDirectiveRe.exec(value);
|
|
521
465
|
if (m4 != null) {
|
|
522
466
|
const def = directives[m4[1]];
|
|
523
467
|
if (def != null && typeof def.attr === 'function') {
|
|
@@ -532,20 +476,21 @@ function longform(input, args) {
|
|
|
532
476
|
}
|
|
533
477
|
switch (m3[3]) {
|
|
534
478
|
case 'id':
|
|
535
|
-
if (!working.id) {
|
|
479
|
+
if (!working.id && typeof value === 'string') {
|
|
536
480
|
working.id = value;
|
|
537
481
|
}
|
|
538
482
|
break;
|
|
539
483
|
case 'class':
|
|
540
|
-
if (!working.class) {
|
|
484
|
+
if (!working.class && typeof value === 'string') {
|
|
541
485
|
working.class = value;
|
|
542
486
|
}
|
|
543
|
-
else {
|
|
487
|
+
else if (typeof value === 'string') {
|
|
544
488
|
working.class += ' ' + value;
|
|
545
489
|
}
|
|
546
490
|
break;
|
|
547
491
|
default:
|
|
548
|
-
|
|
492
|
+
if (value !== false)
|
|
493
|
+
working.attrs[m3[3]] = value;
|
|
549
494
|
}
|
|
550
495
|
}
|
|
551
496
|
}
|
|
@@ -555,29 +500,33 @@ function longform(input, args) {
|
|
|
555
500
|
// server specific process.
|
|
556
501
|
if (element.mount != null) {
|
|
557
502
|
const id = element.mount;
|
|
558
|
-
applyIndent(indent + 1);
|
|
503
|
+
[element, fragment] = applyIndent(indent + 1, key, element, fragment, doc, parsed, output, args);
|
|
504
|
+
if (fragment.mountPoints == null)
|
|
505
|
+
fragment.mountPoints = [];
|
|
559
506
|
fragment.mountPoints.push({
|
|
560
507
|
id,
|
|
561
508
|
part: fragment.html,
|
|
562
509
|
});
|
|
563
510
|
fragment.html = '';
|
|
564
|
-
applyIndent(indent);
|
|
511
|
+
[element, fragment] = applyIndent(indent, key, element, fragment, doc, parsed, output, args);
|
|
565
512
|
break;
|
|
566
513
|
}
|
|
567
514
|
if (preformattedType !== undefined) {
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
515
|
+
if (element.tag !== undefined)
|
|
516
|
+
[element, fragment] = applyIndent(indent + 1, key, element, fragment, doc, parsed, output, args);
|
|
517
|
+
verbatimIndent = indent + 1;
|
|
518
|
+
verbatimType = preformattedType === '{{' ? 3 : 2;
|
|
571
519
|
}
|
|
572
520
|
else if (inlineText !== undefined) {
|
|
573
521
|
element.text = inlineText;
|
|
574
522
|
}
|
|
575
523
|
break;
|
|
576
524
|
}
|
|
577
|
-
|
|
525
|
+
case '[':
|
|
526
|
+
attributeRe.lastIndex = 0;
|
|
578
527
|
m2 = m1[ATTR] !== undefined
|
|
579
|
-
?
|
|
580
|
-
:
|
|
528
|
+
? attributeRe.exec(m1[LINE])
|
|
529
|
+
: null;
|
|
581
530
|
if (m2 != null && element.tag != null) {
|
|
582
531
|
if (m2[2] === 'id') {
|
|
583
532
|
if (element.id == null) {
|
|
@@ -600,69 +549,24 @@ function longform(input, args) {
|
|
|
600
549
|
}
|
|
601
550
|
break;
|
|
602
551
|
}
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
const indent = m2[1].length / 2;
|
|
610
|
-
const tx = m2[2].trim();
|
|
611
|
-
if (element.tag != null) {
|
|
612
|
-
applyIndent(indent);
|
|
613
|
-
fragment.html += tx;
|
|
614
|
-
}
|
|
615
|
-
else if (fragment.type === 'text' && fragment.html === '') {
|
|
616
|
-
fragment.html += tx;
|
|
617
|
-
}
|
|
618
|
-
else {
|
|
619
|
-
fragment.html += ' ' + tx;
|
|
620
|
-
}
|
|
621
|
-
textIndent = indent;
|
|
622
|
-
while ((m2 = refRe.exec(tx))) {
|
|
623
|
-
const start = fragment.html.length + m2.index - tx.length;
|
|
624
|
-
fragment.refs.push({
|
|
625
|
-
id: m2[1],
|
|
626
|
-
start,
|
|
627
|
-
end: start + m2[0].length,
|
|
628
|
-
});
|
|
629
|
-
}
|
|
630
|
-
break;
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
applyIndent(0);
|
|
635
|
-
const arr = Array.from(parsed.values());
|
|
636
|
-
function flatten(fragment) {
|
|
637
|
-
if (fragment.refs == null)
|
|
638
|
-
fragment.refs = [];
|
|
639
|
-
// work backwards so we don't change the html string length
|
|
640
|
-
// for the later replacements
|
|
641
|
-
for (let j = fragment.refs.length - 1; j >= 0; j--) {
|
|
642
|
-
const ref = fragment.refs[j];
|
|
643
|
-
if (claimed.has(ref.id) || !parsed.has(ref.id)) {
|
|
644
|
-
fragment.html = fragment.html.slice(0, ref.start)
|
|
645
|
-
+ fragment.html.slice(ref.end);
|
|
646
|
-
}
|
|
647
|
-
else {
|
|
648
|
-
const child = flatten(parsed.get(ref.id));
|
|
649
|
-
fragment.html = fragment.html.slice(0, ref.start)
|
|
650
|
-
+ child.html
|
|
651
|
-
+ fragment.html.slice(ref.end);
|
|
652
|
-
if (child.type === 'embed') {
|
|
653
|
-
claimed.add(child.id);
|
|
654
|
-
}
|
|
655
|
-
}
|
|
552
|
+
default:
|
|
553
|
+
if (element.tag !== undefined)
|
|
554
|
+
[element, fragment] = applyIndent(indent, key, element, fragment, doc, parsed, output, args);
|
|
555
|
+
verbatimText = m1[0].trim();
|
|
556
|
+
verbatimIndent = indent;
|
|
557
|
+
verbatimType = 1;
|
|
656
558
|
}
|
|
657
|
-
fragment.refs = [];
|
|
658
|
-
return fragment;
|
|
659
559
|
}
|
|
560
|
+
applyIndent(0, key, element, fragment, doc, parsed, output, args);
|
|
561
|
+
if (promises.length > 0)
|
|
562
|
+
await Promise.all(promises);
|
|
660
563
|
if (root?.mountable) {
|
|
661
564
|
output.mountable = true;
|
|
662
565
|
output.tail = root.html;
|
|
663
|
-
output.mountPoints = root.mountPoints;
|
|
566
|
+
output.mountPoints = root.mountPoints ?? [];
|
|
664
567
|
return output;
|
|
665
568
|
}
|
|
569
|
+
const arr = Array.from(parsed.values());
|
|
666
570
|
for (let i = 0; i < parsed.size + 1; i++) {
|
|
667
571
|
let fragment;
|
|
668
572
|
if (i === 0 && root == null) {
|
|
@@ -677,33 +581,31 @@ function longform(input, args) {
|
|
|
677
581
|
if (fragment.refs == null || fragment.refs.length === 0) {
|
|
678
582
|
continue;
|
|
679
583
|
}
|
|
680
|
-
flatten(fragment);
|
|
584
|
+
flatten(fragment, claimed, parsed);
|
|
681
585
|
}
|
|
682
586
|
if (root?.html != null) {
|
|
683
587
|
output.root = root.html;
|
|
684
588
|
output.selector = `[data-${key}-root]`;
|
|
685
589
|
}
|
|
686
|
-
for (let i = 0
|
|
590
|
+
for (let i = 0, l = arr.length, f = arr[i]; i < l; i++) {
|
|
687
591
|
let selector;
|
|
688
|
-
|
|
689
|
-
if (fragment == null || claimed.has(fragment.id)) {
|
|
592
|
+
if (f == null || claimed.has(f.id)) {
|
|
690
593
|
continue;
|
|
691
594
|
}
|
|
692
|
-
switch (
|
|
693
|
-
case 'embed':
|
|
595
|
+
switch (f.type) {
|
|
596
|
+
case 'embed':
|
|
694
597
|
if (args?.predictable) {
|
|
695
|
-
selector = `[id=${
|
|
598
|
+
selector = `[id=${f.id}]`;
|
|
696
599
|
break;
|
|
697
600
|
}
|
|
698
|
-
}
|
|
699
601
|
case 'bare':
|
|
700
|
-
case 'range': selector = `[data-${key}=${
|
|
602
|
+
case 'range': selector = `[data-${key}=${f.id}]`;
|
|
701
603
|
}
|
|
702
|
-
output.fragments[
|
|
703
|
-
id:
|
|
604
|
+
output.fragments[f.id] = {
|
|
605
|
+
id: f.id,
|
|
704
606
|
selector,
|
|
705
|
-
type:
|
|
706
|
-
html:
|
|
607
|
+
type: f.type,
|
|
608
|
+
html: f.html,
|
|
707
609
|
};
|
|
708
610
|
}
|
|
709
611
|
output.key = key;
|
|
@@ -721,9 +623,9 @@ function longform(input, args) {
|
|
|
721
623
|
* @param getFragment - A function which returns an already processed fragment's HTML string.
|
|
722
624
|
* @returns The processed template.
|
|
723
625
|
*/
|
|
724
|
-
function processTemplate(template, args, getFragment) {
|
|
725
|
-
const lf = template.replace(templateRe, (
|
|
726
|
-
if (ref
|
|
626
|
+
async function processTemplate(template, args, longformArgs, getFragment) {
|
|
627
|
+
const lf = template.replace(templateRe, (_line, _structured, param, ref) => {
|
|
628
|
+
if (ref !== undefined && typeof getFragment === 'function') {
|
|
727
629
|
const fragment = getFragment(ref);
|
|
728
630
|
if (fragment == null)
|
|
729
631
|
return '';
|
|
@@ -731,7 +633,156 @@ function processTemplate(template, args, getFragment) {
|
|
|
731
633
|
}
|
|
732
634
|
return args[param] != null ? escape(args[param].toString()) : '';
|
|
733
635
|
});
|
|
734
|
-
return Object.values(longform(lf).fragments)[0]?.html ?? null;
|
|
636
|
+
return Object.values((await longform(lf, longformArgs)).fragments)[0]?.html ?? null;
|
|
637
|
+
}
|
|
638
|
+
/**
|
|
639
|
+
* Closes any current in progress element definition
|
|
640
|
+
* and creates a new working element.
|
|
641
|
+
*/
|
|
642
|
+
function applyIndent(targetIndent, key, element, fragment, doc, parsed, output, args) {
|
|
643
|
+
if (element.tag !== undefined) {
|
|
644
|
+
if (Array.isArray(element.beforeRender)) {
|
|
645
|
+
const el = {
|
|
646
|
+
id: element.id,
|
|
647
|
+
tag: element.tag,
|
|
648
|
+
class: element.class,
|
|
649
|
+
attrs: element.attrs,
|
|
650
|
+
};
|
|
651
|
+
const chain = [];
|
|
652
|
+
for (let i = 0, l = element.chain.length, el = element.chain[i]; i < l; i++) {
|
|
653
|
+
chain.push({
|
|
654
|
+
id: el.id,
|
|
655
|
+
tag: el.tag,
|
|
656
|
+
class: el.class,
|
|
657
|
+
attrs: el.attrs,
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
for (let i = 0, l = element.beforeRender.length, def = element.beforeRender[i]; i < l; i++) {
|
|
661
|
+
def.element({
|
|
662
|
+
el,
|
|
663
|
+
chain,
|
|
664
|
+
doc,
|
|
665
|
+
inlineArgs: def.inlineArgs,
|
|
666
|
+
blockArgs: def.blockArgs,
|
|
667
|
+
});
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
const root = fragment.type === 'range'
|
|
671
|
+
? targetIndent < 2
|
|
672
|
+
: fragment.html === '';
|
|
673
|
+
fragment.html += `<${element.tag}`;
|
|
674
|
+
if (element.id !== undefined) {
|
|
675
|
+
fragment.html += ' id="' + element.id + '"';
|
|
676
|
+
}
|
|
677
|
+
if (element.class !== undefined) {
|
|
678
|
+
fragment.html += ' class="' + element.class + '"';
|
|
679
|
+
}
|
|
680
|
+
for (const attr of Object.entries(element.attrs)) {
|
|
681
|
+
if (attr[0] === 'id' || attr[0] === 'class')
|
|
682
|
+
continue;
|
|
683
|
+
if (attr[1] == null) {
|
|
684
|
+
fragment.html += ' ' + attr[0];
|
|
685
|
+
}
|
|
686
|
+
else {
|
|
687
|
+
fragment.html += ` ${attr[0]}="${attr[1]}"`;
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
if (root) {
|
|
691
|
+
if (fragment.type === 'root') {
|
|
692
|
+
fragment.html += ` data-${key}-root`;
|
|
693
|
+
}
|
|
694
|
+
else if (fragment.type === 'bare' || fragment.type === 'range') {
|
|
695
|
+
fragment.html += ` data-${key}="${fragment.id}"`;
|
|
696
|
+
}
|
|
697
|
+
else if (fragment.type === 'embed' && !args?.predictable) {
|
|
698
|
+
fragment.html += ` data-${key}="${fragment.id}"`;
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
if (element.mount !== undefined) {
|
|
702
|
+
fragment.html += ` data-${key}-mount="${element.mount}"`;
|
|
703
|
+
}
|
|
704
|
+
fragment.html += '>';
|
|
705
|
+
if (Array.isArray(element.chain)) {
|
|
706
|
+
let chained;
|
|
707
|
+
for (let i = 0, l = element.chain.length; i < l; i++) {
|
|
708
|
+
chained = element.chain[i];
|
|
709
|
+
fragment.html += '<' + chained.tag;
|
|
710
|
+
if (chained.id !== undefined) {
|
|
711
|
+
fragment.html += ' id="' + chained.id + '"';
|
|
712
|
+
}
|
|
713
|
+
if (chained.class != undefined) {
|
|
714
|
+
fragment.html += ' class="' + chained.class + '"';
|
|
715
|
+
}
|
|
716
|
+
for (const attr of Object.entries(chained.attrs)) {
|
|
717
|
+
if (attr[1] === undefined) {
|
|
718
|
+
fragment.html += ' ' + attr[0];
|
|
719
|
+
}
|
|
720
|
+
else {
|
|
721
|
+
fragment.html += ` ${attr[0]}="${attr[1]}"`;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
fragment.html += '>';
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
if (!voids.has(element.tag) && element.text != null) {
|
|
728
|
+
fragment.html += element.text;
|
|
729
|
+
}
|
|
730
|
+
if (!voids.has(element.tag)) {
|
|
731
|
+
fragment.els.push(element);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
if (targetIndent <= element.indent) {
|
|
735
|
+
element = makeElement(targetIndent);
|
|
736
|
+
while (fragment.els.length !== 0 && (targetIndent == null ||
|
|
737
|
+
fragment.els[fragment.els.length - 1].indent !== targetIndent - 1)) {
|
|
738
|
+
const element = fragment.els.pop();
|
|
739
|
+
if (Array.isArray(element.chain)) {
|
|
740
|
+
for (let i = 0, l = element.chain.length; i < l; i++) {
|
|
741
|
+
fragment.html += `</${element.chain[i].tag}>`;
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
fragment.html += `</${element?.tag}>`;
|
|
745
|
+
}
|
|
746
|
+
if (targetIndent === 0) {
|
|
747
|
+
if (fragment.template) {
|
|
748
|
+
output.templates[fragment.id] = fragment.html;
|
|
749
|
+
}
|
|
750
|
+
else if (fragment.type !== 'root') {
|
|
751
|
+
parsed.set(fragment.id, fragment);
|
|
752
|
+
}
|
|
753
|
+
fragment = makeFragment();
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
else {
|
|
757
|
+
element = makeElement(targetIndent);
|
|
758
|
+
}
|
|
759
|
+
return [element, fragment];
|
|
760
|
+
}
|
|
761
|
+
function flatten(fragment, claimed, parsed, locals = new Set()) {
|
|
762
|
+
let r;
|
|
763
|
+
if (Array.isArray(fragment.refs)) {
|
|
764
|
+
for (let i = fragment.refs.length - 1; i > -1; i--) {
|
|
765
|
+
r = fragment.refs[i];
|
|
766
|
+
if (locals.has(r.id) || claimed.has(r.id) || !parsed.has(r.id)) {
|
|
767
|
+
// cannot use fragment here. Clear the reference marker
|
|
768
|
+
fragment.html = fragment.html.slice(0, r.start)
|
|
769
|
+
+ fragment.html.slice(r.end);
|
|
770
|
+
}
|
|
771
|
+
else {
|
|
772
|
+
locals.add(fragment.id);
|
|
773
|
+
const child = flatten(parsed.get(r.id), claimed, parsed, locals);
|
|
774
|
+
fragment.html = fragment.html.slice(0, r.start)
|
|
775
|
+
+ child.html
|
|
776
|
+
+ fragment.html.slice(r.end);
|
|
777
|
+
if (child.type === 'embed') {
|
|
778
|
+
claimed.add(child.id);
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
// don't re-process the fragment
|
|
784
|
+
fragment.refs = undefined;
|
|
785
|
+
return fragment;
|
|
735
786
|
}
|
|
736
787
|
|
|
737
788
|
exports.longform = longform;
|