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