@podlite/schema 0.0.66 → 0.0.68

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.
Files changed (76) hide show
  1. package/CHANGELOG.podlite +44 -0
  2. package/esm/ast-helpers.d.ts +25 -1
  3. package/esm/ast-helpers.js +101 -2
  4. package/esm/ast-helpers.js.map +1 -1
  5. package/esm/attribute-names.d.ts +5 -0
  6. package/esm/attribute-names.js +36 -0
  7. package/esm/attribute-names.js.map +1 -0
  8. package/esm/block-names.d.ts +1 -0
  9. package/esm/block-names.js +6 -1
  10. package/esm/block-names.js.map +1 -1
  11. package/esm/exportAny.js +2 -2
  12. package/esm/exportAny.js.map +1 -1
  13. package/esm/exportHtml.js +60 -22
  14. package/esm/exportHtml.js.map +1 -1
  15. package/esm/exportMarkdown.js +62 -52
  16. package/esm/exportMarkdown.js.map +1 -1
  17. package/esm/folded-sections.d.ts +1 -0
  18. package/esm/folded-sections.js +80 -0
  19. package/esm/folded-sections.js.map +1 -0
  20. package/esm/grammarfc.js +6099 -15834
  21. package/esm/grammarfc.js.map +1 -1
  22. package/esm/guard.d.ts +5 -0
  23. package/esm/guard.js +39 -0
  24. package/esm/guard.js.map +1 -0
  25. package/esm/helpers/corePlugins.js +9 -3
  26. package/esm/helpers/corePlugins.js.map +1 -1
  27. package/esm/helpers/html-attr.d.ts +1 -0
  28. package/esm/helpers/html-attr.js +6 -0
  29. package/esm/helpers/html-attr.js.map +1 -0
  30. package/esm/index.d.ts +6 -2
  31. package/esm/index.js +6 -2
  32. package/esm/index.js.map +1 -1
  33. package/esm/pluggableParser.d.ts +1 -1
  34. package/esm/pluggableParser.js +7 -1
  35. package/esm/pluggableParser.js.map +1 -1
  36. package/esm/plugin-data-table.js +2 -1
  37. package/esm/plugin-data-table.js.map +1 -1
  38. package/esm/plugin-tables.js +5 -2
  39. package/esm/plugin-tables.js.map +1 -1
  40. package/esm/selectors.d.ts +1 -0
  41. package/esm/selectors.js +42 -19
  42. package/esm/selectors.js.map +1 -1
  43. package/esm/types.d.ts +30 -16
  44. package/esm/version.d.ts +1 -1
  45. package/esm/version.js +1 -1
  46. package/lib/ast-helpers.d.ts +25 -1
  47. package/lib/ast-helpers.js +106 -3
  48. package/lib/attribute-names.d.ts +5 -0
  49. package/lib/attribute-names.js +41 -0
  50. package/lib/block-names.d.ts +1 -0
  51. package/lib/block-names.js +8 -2
  52. package/lib/exportAny.js +1 -1
  53. package/lib/exportHtml.js +62 -24
  54. package/lib/exportMarkdown.js +61 -51
  55. package/lib/folded-sections.d.ts +1 -0
  56. package/lib/folded-sections.js +84 -0
  57. package/lib/grammarfc.js +6099 -15834
  58. package/lib/guard.d.ts +5 -0
  59. package/lib/guard.js +47 -0
  60. package/lib/helpers/corePlugins.js +9 -3
  61. package/lib/helpers/html-attr.d.ts +1 -0
  62. package/lib/helpers/html-attr.js +10 -0
  63. package/lib/index.d.ts +6 -2
  64. package/lib/index.js +11 -1
  65. package/lib/pluggableParser.d.ts +1 -1
  66. package/lib/pluggableParser.js +8 -1
  67. package/lib/plugin-data-table.js +2 -1
  68. package/lib/plugin-tables.js +5 -2
  69. package/lib/selectors.d.ts +1 -0
  70. package/lib/selectors.js +44 -20
  71. package/lib/types.d.ts +30 -16
  72. package/lib/version.d.ts +1 -1
  73. package/lib/version.js +1 -1
  74. package/package.json +1 -1
  75. package/schema/AstTree.json +626 -405
  76. package/schema/PodliteDocument.json +626 -405
package/lib/exportHtml.js CHANGED
@@ -41,36 +41,58 @@ const handlers_1 = require("./helpers/handlers");
41
41
  const makeTransformer_1 = require("./helpers/makeTransformer");
42
42
  const config_1 = __importStar(require("./helpers/config"));
43
43
  const image_base_1 = require("./image-base");
44
+ const guard_1 = require("./guard");
44
45
  const writerHtml_1 = __importDefault(require("./writerHtml"));
45
46
  const plugin_clean_location_1 = __importDefault(require("./plugin-clean-location"));
46
47
  const ast_helpers_1 = require("./ast-helpers");
47
48
  const link_config_1 = require("./helpers/link-config");
48
49
  const entities_1 = require("entities");
49
- const quoteValue = (value) => value.replace(/"/g, '"');
50
+ const html_attr_1 = require("./helpers/html-attr");
51
+ // HTML gives an anchor without href to a link whose target the author never
52
+ // wrote; an empty href would claim the current document instead. The address is
53
+ // quoted like every other attribute value: a quote inside it would otherwise
54
+ // close the attribute and let the document write markup of its own.
55
+ const hrefAttr = (node, ctx) => {
56
+ const target = (0, ast_helpers_1.linkTarget)(node);
57
+ if (target === undefined)
58
+ return '';
59
+ const address = (0, ast_helpers_1.sameDocTarget)(target, ctx);
60
+ // A link level one refused points at nothing, and an anchor with no href is what
61
+ // html gives that case. An address shaped from a target that was never there only
62
+ // looks like a working link.
63
+ return address === undefined ? '' : ` href="${(0, html_attr_1.quoteAttribute)(String(address))}"`;
64
+ };
50
65
  const linkConfigAttrs = config => {
51
66
  const { newContext, title, lang, download } = (0, link_config_1.readLinkConfig)(config);
52
67
  const attrs = [];
53
68
  if (newContext)
54
69
  attrs.push(' target="_blank"');
55
70
  if (title !== undefined)
56
- attrs.push(` title="${quoteValue(title)}"`);
71
+ attrs.push(` title="${(0, html_attr_1.quoteAttribute)(title)}"`);
57
72
  if (lang !== undefined)
58
- attrs.push(` hreflang="${quoteValue(lang)}"`);
73
+ attrs.push(` hreflang="${(0, html_attr_1.quoteAttribute)(lang)}"`);
59
74
  if (download === true)
60
75
  attrs.push(' download');
61
76
  else if (typeof download === 'string')
62
- attrs.push(` download="${quoteValue(download)}"`);
77
+ attrs.push(` download="${(0, html_attr_1.quoteAttribute)(download)}"`);
63
78
  return attrs.join('');
64
79
  };
80
+ // Shaped by the same rule the address is: an anchor keeping a dot the address drops
81
+ // is how :id<v1.2> came to render an unreachable target. Every place that writes an
82
+ // anchor goes through here, or the two sides part again.
83
+ const anchorOf = (node, ctx) => {
84
+ const written = (0, ast_helpers_1.getExplicitNodeId)(node, ctx);
85
+ return written === null ? null : (ctx?.__anchors?.shape || ast_helpers_1.toFragment)(written);
86
+ };
65
87
  const openTag = (tag, node, ctx, attrs = '') => {
66
- const id = (0, ast_helpers_1.getExplicitNodeId)(node, ctx);
88
+ const id = anchorOf(node, ctx);
67
89
  return `<${tag}${id ? ` id="${id}"` : ''}${attrs}>`;
68
90
  };
69
91
  const rules = {
70
92
  ':text': (writer, processor) => (node, ctx, interator) => {
71
93
  // handle text with content
72
94
  if (node.value) {
73
- writer.write(ctx?.maskMode ? (0, handlers_1.maskText)(node.value) : node.value);
95
+ writer.write((0, guard_1.isCovered)(node, ctx) ? (0, handlers_1.maskText)(node.value) : node.value);
74
96
  }
75
97
  else {
76
98
  interator(node.content, ctx);
@@ -146,18 +168,12 @@ const rules = {
146
168
  'H<>': (0, handlers_1.wrapContent)('<sup>', '</sup>'),
147
169
  'J<>': (0, handlers_1.wrapContent)('<sub>', '</sub>'),
148
170
  'L<>': (0, handlers_1.setFn)((node, ctx) => {
149
- let { meta } = node;
150
- if (meta === null) {
151
- meta = node.content;
152
- }
153
- return (0, handlers_1.wrapContent)(`<a href="${(0, ast_helpers_1.sameDocTarget)(meta, ctx)}"${linkConfigAttrs((0, config_1.codeConfigWithDefaults)(node, ctx))}>`, `</a>`);
171
+ const attrs = linkConfigAttrs((0, config_1.codeConfigWithDefaults)(node, ctx));
172
+ return (0, handlers_1.wrapContent)(`<a${hrefAttr(node, ctx)}${attrs}>`, `</a>`);
154
173
  }),
155
174
  'W<>': (0, handlers_1.setFn)((node, ctx) => {
156
- let { meta } = node;
157
- if (meta === null) {
158
- meta = node.content;
159
- }
160
- return (0, handlers_1.wrapContent)(`<a href="${(0, ast_helpers_1.sameDocTarget)(meta, ctx)}"${linkConfigAttrs((0, config_1.codeConfigWithDefaults)(node, ctx))} class="backlink">`, `</a>`);
175
+ const attrs = linkConfigAttrs((0, config_1.codeConfigWithDefaults)(node, ctx));
176
+ return (0, handlers_1.wrapContent)(`<a${hrefAttr(node, ctx)}${attrs} class="backlink">`, `</a>`);
161
177
  }),
162
178
  /**
163
179
  * CSS rules for footnotes
@@ -243,12 +259,25 @@ const rules = {
243
259
  pod: handlers_1.content,
244
260
  ':code': (0, handlers_1.wrapContent)('<pre><code>', '</code></pre>'),
245
261
  code: (0, handlers_1.handleNested)((0, handlers_1.setFn)((node, ctx) => (0, handlers_1.wrapContent)(`${openTag('pre', node, ctx)}<code>`, '</code></pre>'))),
262
+ // a folded section is a heading plus the nodes under it; the disclosure is
263
+ // native so the reader needs no script to open it
264
+ _folded_section: (writer, processor) => (node, ctx, interator) => {
265
+ const [heading, ...rest] = (node.content || []);
266
+ const open = node.foldedState === false || node.foldedState === 0 || node.foldedState === '0';
267
+ writer.writeRaw(`<details class="folded-section"${open ? ' open' : ''}>`);
268
+ writer.writeRaw('<summary class="folded-section-summary">');
269
+ if (heading)
270
+ interator([heading], ctx);
271
+ writer.writeRaw('</summary><div class="folded-section-content">');
272
+ interator(rest, ctx);
273
+ writer.writeRaw('</div></details>');
274
+ },
246
275
  data: handlers_1.emptyContent,
247
276
  ':verbatim': (writer, processor) => (node, ctx, interator) => {
248
277
  if (node.error) {
249
278
  writer.emit('errors', node.location);
250
279
  }
251
- if (ctx?.maskMode) {
280
+ if ((0, guard_1.isCovered)(node, ctx)) {
252
281
  writer.write((0, handlers_1.maskText)(node.value));
253
282
  return;
254
283
  }
@@ -276,7 +305,7 @@ const rules = {
276
305
  // block =para
277
306
  // With an :id the block owns the <p> so the anchor lands on it; without one
278
307
  // the inner paragraph renders as before.
279
- para: (0, handlers_1.handleNested)((0, handlers_1.setFn)((node, ctx) => (0, ast_helpers_1.getExplicitNodeId)(node, ctx)
308
+ para: (0, handlers_1.handleNested)((0, handlers_1.setFn)((node, ctx) => anchorOf(node, ctx)
280
309
  ? (0, handlers_1.subUse)({ ':para': handlers_1.content }, (0, handlers_1.wrapContent)(openTag('p', node, ctx), '</p>'))
281
310
  : handlers_1.content)),
282
311
  ':para': (0, handlers_1.setFn)((node, ctx) => (0, handlers_1.wrapContent)(openTag('p', node, ctx), '</p>')),
@@ -315,7 +344,7 @@ const rules = {
315
344
  'comment:block': handlers_1.emptyContent,
316
345
  'boundary:block': (writer, processor) => (node, ctx) => {
317
346
  const conf = (0, config_1.default)(node, ctx);
318
- const id = (0, ast_helpers_1.getExplicitNodeId)(node, ctx);
347
+ const id = anchorOf(node, ctx);
319
348
  const idAttr = id ? ` id="${id}"` : '';
320
349
  if (conf.exists('caption')) {
321
350
  writer.writeRaw(`<hr${idAttr} title="`);
@@ -328,7 +357,7 @@ const rules = {
328
357
  },
329
358
  // The term opens the pair, so an :id on the definition lands on its <dt>.
330
359
  defn: (0, handlers_1.setFn)((node, ctx) => {
331
- const id = (0, ast_helpers_1.getExplicitNodeId)(node, ctx);
360
+ const id = anchorOf(node, ctx);
332
361
  return id
333
362
  ? (0, handlers_1.subUse)({ 'term:para': (0, handlers_1.wrapContent)(`<dt id="${id}">`, '</dt><dd>') }, (0, handlers_1.wrapContent)('', '</dd>'))
334
363
  : (0, handlers_1.wrapContent)('', '</dd>');
@@ -420,7 +449,12 @@ const rules = {
420
449
  ':toc-list': (0, handlers_1.setFn)((node, ctx) => (0, handlers_1.wrapContent)(`<ul class="toc-list listlevel${node.level}">`, '</ul>')),
421
450
  ':toc-item': (0, handlers_1.setFn)((node, ctx) => (0, handlers_1.wrapContent)('<li class="toc-item">', '</li>')),
422
451
  ':image': (writer, processor) => (node, ctx, interator) => {
423
- writer.writeRaw(`<img src="${(0, image_base_1.applyImageBase)(node.src, ctx?.base)}" alt="${node.alt || ''}"/>`);
452
+ const src = (0, html_attr_1.quoteAttribute)(String((0, image_base_1.applyImageBase)(node.src, ctx?.base) ?? ''));
453
+ // html reads a missing alt as "this image is part of the content" and an empty
454
+ // one as "decorative", so an alternative text the author never wrote is left out
455
+ const altText = (0, ast_helpers_1.writtenValue)(node.alt);
456
+ const alt = altText === undefined ? '' : ` alt="${(0, html_attr_1.quoteAttribute)(altText)}"`;
457
+ writer.writeRaw(`<img src="${src}"${alt}/>`);
424
458
  },
425
459
  };
426
460
  const toHtml = opt => (0, exportAny_1.default)({
@@ -459,15 +493,19 @@ const toHtml = opt => (0, exportAny_1.default)({
459
493
  })
460
494
  .use(rules)
461
495
  .use('*', (writer, processor) => (node, ctx, interator, defaultFn) => {
462
- if (!node || node.type !== 'block')
496
+ if (!node)
463
497
  return defaultFn();
464
498
  if (ctx?.maskMode)
465
499
  return defaultFn();
500
+ if (ctx?.renderMode === 'draft')
501
+ return defaultFn();
502
+ if (node.guarded)
503
+ return defaultFn(node, { ...(ctx || {}), maskMode: true }, interator);
504
+ if (node.type !== 'block')
505
+ return defaultFn();
466
506
  const conf = (0, config_1.default)(node, ctx || {});
467
507
  if (!conf.exists('masked') || !conf.getFirstValue('masked'))
468
508
  return defaultFn();
469
- if (ctx?.renderMode === 'draft')
470
- return defaultFn();
471
509
  return defaultFn(node, { ...(ctx || {}), maskMode: true }, interator);
472
510
  });
473
511
  exports.default = toHtml;
@@ -37,6 +37,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  const exportAny_1 = __importDefault(require("./exportAny"));
40
+ const guard_1 = require("./guard");
40
41
  const handlers_1 = require("./helpers/handlers");
41
42
  const config_1 = __importStar(require("./helpers/config"));
42
43
  const image_base_1 = require("./image-base");
@@ -51,10 +52,49 @@ const linkTitle = config => {
51
52
  const { title } = (0, link_config_1.readLinkConfig)(config);
52
53
  return title === undefined ? '' : ` "${title.replace(/"/g, '\\"')}"`;
53
54
  };
55
+ // Markdown has no form for a link whose target the author never wrote — an empty
56
+ // address there claims the current document — so the text is written on its own.
57
+ // A bracket or a space inside the address would end it where markdown looks for
58
+ // the closing one, so such an address is written in angle brackets, the form
59
+ // markdown keeps for exactly that. An angle of its own is percent-encoded rather
60
+ // than dropped, so the address survives the round trip — no source text reaches
61
+ // here holding one today, since the parser ends the code on it.
62
+ const markdownAddress = (address) => /[()\s]/.test(address) ? `<${address.replace(/</g, '%3C').replace(/>/g, '%3E')}>` : address;
63
+ const linkWrap = (node, ctx) => {
64
+ const target = (0, ast_helpers_1.linkTarget)(node);
65
+ if (target === undefined)
66
+ return (0, handlers_1.wrapContent)('', '');
67
+ const resolved = (0, ast_helpers_1.sameDocTarget)(target, ctx, markdownAnchors(ctx));
68
+ // Markdown has no anchor without an address either, so a refused link keeps its
69
+ // text and loses the brackets.
70
+ if (resolved === undefined)
71
+ return (0, handlers_1.wrapContent)('', '');
72
+ const address = String(resolved);
73
+ return (0, handlers_1.wrapContent)(`[`, `](${markdownAddress(address)}${linkTitle((0, config_1.codeConfigWithDefaults)(node, ctx))})`);
74
+ };
75
+ // A cell's own text is written whole. Trimming each fragment on its own eats the
76
+ // spaces that sit between them — between a word and a markup code, or around a
77
+ // sign the parser split the text on — so only the cell edges are stripped.
78
+ const writeCellParts = (cell, writer, interator, ctx) => {
79
+ const parts = cell.content || [];
80
+ const last = parts.length - 1;
81
+ parts.forEach((part, i) => {
82
+ if (typeof part !== 'string') {
83
+ interator([part], ctx);
84
+ return;
85
+ }
86
+ let text = part;
87
+ if (i === 0)
88
+ text = text.replace(/^\s+/, '');
89
+ if (i === last)
90
+ text = text.replace(/\s+$/, '');
91
+ writer.write(text);
92
+ });
93
+ };
54
94
  const rules = {
55
95
  ':text': (writer, processor) => (node, ctx, interator) => {
56
96
  if (node.value) {
57
- writer.write(ctx?.maskMode ? (0, handlers_1.maskText)(node.value) : node.value);
97
+ writer.write((0, guard_1.isCovered)(node, ctx) ? (0, handlers_1.maskText)(node.value) : node.value);
58
98
  }
59
99
  else {
60
100
  interator(node.content, ctx);
@@ -102,20 +142,8 @@ const rules = {
102
142
  },
103
143
  'H<>': (0, handlers_1.wrapContent)('<sup>', '</sup>'),
104
144
  'J<>': (0, handlers_1.wrapContent)('<sub>', '</sub>'),
105
- 'L<>': (0, handlers_1.setFn)((node, ctx) => {
106
- let { meta } = node;
107
- if (meta === null) {
108
- meta = node.content;
109
- }
110
- return (0, handlers_1.wrapContent)(`[`, `](${(0, ast_helpers_1.sameDocTarget)(meta, ctx, markdownAnchors(ctx))}${linkTitle((0, config_1.codeConfigWithDefaults)(node, ctx))})`);
111
- }),
112
- 'W<>': (0, handlers_1.setFn)((node, ctx) => {
113
- let { meta } = node;
114
- if (meta === null) {
115
- meta = node.content;
116
- }
117
- return (0, handlers_1.wrapContent)(`[`, `](${(0, ast_helpers_1.sameDocTarget)(meta, ctx, markdownAnchors(ctx))}${linkTitle((0, config_1.codeConfigWithDefaults)(node, ctx))})`);
118
- }),
145
+ 'L<>': (0, handlers_1.setFn)((node, ctx) => linkWrap(node, ctx)),
146
+ 'W<>': (0, handlers_1.setFn)((node, ctx) => linkWrap(node, ctx)),
119
147
  'N<>': (writer, processor) => {
120
148
  writer.addListener('end', () => {
121
149
  if (!writer.hasOwnProperty('FOOTNOTES')) {
@@ -183,7 +211,7 @@ const rules = {
183
211
  const conf = (0, config_1.default)(node, ctx);
184
212
  const lang = conf.exists('lang') ? conf.getFirstValue('lang') : '';
185
213
  writer.writeRaw('```' + lang + '\n');
186
- const masked = ctx?.maskMode;
214
+ const masked = (0, guard_1.isCovered)(node, ctx);
187
215
  if (node.content) {
188
216
  node.content.forEach(child => {
189
217
  if (typeof child === 'string') {
@@ -203,7 +231,7 @@ const rules = {
203
231
  const conf = (0, config_1.default)(node, ctx);
204
232
  const lang = conf.exists('lang') ? conf.getFirstValue('lang') : '';
205
233
  writer.writeRaw('```' + lang + '\n');
206
- const masked = ctx?.maskMode;
234
+ const masked = (0, guard_1.isCovered)(node, ctx);
207
235
  if (node.content) {
208
236
  node.content.forEach(child => {
209
237
  if (typeof child === 'string') {
@@ -224,7 +252,7 @@ const rules = {
224
252
  if (node.error) {
225
253
  writer.emit('errors', node.location);
226
254
  }
227
- writer.writeRaw(ctx?.maskMode ? (0, handlers_1.maskText)(node.value) : node.value);
255
+ writer.writeRaw((0, guard_1.isCovered)(node, ctx) ? (0, handlers_1.maskText)(node.value) : node.value);
228
256
  },
229
257
  ':blankline': handlers_1.emptyContent,
230
258
  ':ambient': handlers_1.emptyContent,
@@ -366,16 +394,7 @@ const rules = {
366
394
  if (rowspan)
367
395
  attrs += ` rowspan="${rowspan}"`;
368
396
  writer.writeRaw(`<${tag}${attrs}>`);
369
- if (cell.content) {
370
- cell.content.forEach(c => {
371
- if (typeof c === 'string') {
372
- writer.write(c.trim());
373
- }
374
- else {
375
- interator([c], ctx);
376
- }
377
- });
378
- }
397
+ writeCellParts(cell, writer, interator, ctx);
379
398
  writer.writeRaw(`</${tag}>`);
380
399
  });
381
400
  writer.writeRaw('</tr>\n');
@@ -393,15 +412,7 @@ const rules = {
393
412
  return null;
394
413
  writer.output = (str) => parts.push(str);
395
414
  try {
396
- ;
397
- (cell.content || []).forEach(c => {
398
- if (typeof c === 'string') {
399
- writer.write(c.trim());
400
- }
401
- else {
402
- interator([c], ctx);
403
- }
404
- });
415
+ writeCellParts(cell, writer, interator, ctx);
405
416
  }
406
417
  finally {
407
418
  writer.output = original;
@@ -419,16 +430,7 @@ const rules = {
419
430
  writer.writeRaw(' ');
420
431
  const folded = renderCell(cell);
421
432
  if (folded === null) {
422
- if (cell.content) {
423
- cell.content.forEach(c => {
424
- if (typeof c === 'string') {
425
- writer.write(c.trim());
426
- }
427
- else {
428
- interator([c], ctx);
429
- }
430
- });
431
- }
433
+ writeCellParts(cell, writer, interator, ctx);
432
434
  }
433
435
  else {
434
436
  writer.writeRaw(folded);
@@ -463,7 +465,11 @@ const rules = {
463
465
  ':toc-list': handlers_1.emptyContent,
464
466
  ':toc-item': handlers_1.emptyContent,
465
467
  ':image': (writer, processor) => (node, ctx, interator) => {
466
- writer.writeRaw(`![${node.alt || ''}](${(0, image_base_1.applyImageBase)(node.src, ctx?.base)})`);
468
+ // the alternative text sits between square brackets, where a `]` of its own
469
+ // would end it, and the address between round ones — the same shape a link has
470
+ const alt = ((0, ast_helpers_1.writtenValue)(node.alt) ?? '').replace(/([[\]\\])/g, '\\$1');
471
+ const src = markdownAddress(String((0, image_base_1.applyImageBase)(node.src, ctx?.base) ?? ''));
472
+ writer.writeRaw(`![${alt}](${src})`);
467
473
  },
468
474
  };
469
475
  const toMarkdown = opt => (0, exportAny_1.default)({
@@ -491,15 +497,19 @@ const toMarkdown = opt => (0, exportAny_1.default)({
491
497
  })
492
498
  .use(rules)
493
499
  .use('*', (writer, processor) => (node, ctx, interator, defaultFn) => {
494
- if (!node || node.type !== 'block')
500
+ if (!node)
495
501
  return defaultFn();
496
502
  if (ctx?.maskMode)
497
503
  return defaultFn();
504
+ if (ctx?.renderMode === 'draft')
505
+ return defaultFn();
506
+ if (node.guarded)
507
+ return defaultFn(node, { ...(ctx || {}), maskMode: true }, interator);
508
+ if (node.type !== 'block')
509
+ return defaultFn();
498
510
  const conf = (0, config_1.default)(node, ctx || {});
499
511
  if (!conf.exists('masked') || !conf.getFirstValue('masked'))
500
512
  return defaultFn();
501
- if (ctx?.renderMode === 'draft')
502
- return defaultFn();
503
513
  return defaultFn(node, { ...(ctx || {}), maskMode: true }, interator);
504
514
  });
505
515
  exports.default = toMarkdown;
@@ -0,0 +1 @@
1
+ export declare const applyFoldedSections: (node: any) => any;
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.applyFoldedSections = void 0;
4
+ // Folding of a section belongs to the tree, not to one renderer: the command
5
+ // line and every build-time producer read the same document as the editor does.
6
+ // Moved here from @podlite/to-jsx, where it lived and left exportHtml and
7
+ // exportMarkdown unable to fold anything.
8
+ // `:folded` on a heading folds the whole section — the heading plus every
9
+ // following node up to the next same-or-higher-level heading. Detect the
10
+ // attribute on =head nodes in a container's content array and wrap that
11
+ // range in a synthetic `_folded_section` block so the JSX renderer can emit
12
+ // <details> around it.
13
+ const getFoldedAttr = (node) => {
14
+ const config = node && node.config;
15
+ if (!Array.isArray(config))
16
+ return null;
17
+ const entry = config.find((c) => c && c.name === 'folded');
18
+ return entry ? entry.value : null;
19
+ };
20
+ const isHeadBlock = (node) => node && node.type === 'block' && node.name === 'head' && node.level !== undefined && node.level !== null;
21
+ const headLevel = (node) => Number(node.level);
22
+ const groupFoldedSections = (content) => {
23
+ if (!Array.isArray(content))
24
+ return content;
25
+ const result = [];
26
+ let i = 0;
27
+ while (i < content.length) {
28
+ const node = content[i];
29
+ if (isHeadBlock(node)) {
30
+ const folded = getFoldedAttr(node);
31
+ if (folded !== null) {
32
+ const level = headLevel(node);
33
+ const sectionNodes = [node];
34
+ let j = i + 1;
35
+ while (j < content.length) {
36
+ const next = content[j];
37
+ if (isHeadBlock(next) && headLevel(next) <= level)
38
+ break;
39
+ sectionNodes.push(next);
40
+ j++;
41
+ }
42
+ result.push({
43
+ type: 'block',
44
+ name: '_folded_section',
45
+ content: sectionNodes,
46
+ foldedState: folded,
47
+ location: node.location,
48
+ });
49
+ i = j;
50
+ continue;
51
+ }
52
+ }
53
+ result.push(node);
54
+ i++;
55
+ }
56
+ return result;
57
+ };
58
+ // Walk the AST and apply `groupFoldedSections` to every block's `content`
59
+ // array, so :folded heads inside named blocks, defn, nested etc. fold the
60
+ // same way they do at the pod-level. Inside an already-built
61
+ // `_folded_section`, the first element is the heading the wrapper belongs
62
+ // to — skip it when grouping the rest, otherwise the same head would be
63
+ // re-wrapped on every recursion. Nested folds (a folded heading inside a
64
+ // folded section's body) are still picked up by grouping the remainder.
65
+ const applyFoldedSections = (node) => {
66
+ if (!node || typeof node !== 'object')
67
+ return node;
68
+ if (Array.isArray(node))
69
+ return node.map(exports.applyFoldedSections);
70
+ if (!Array.isArray(node.content))
71
+ return node;
72
+ if (node.name === '_folded_section') {
73
+ const [head, ...rest] = node.content;
74
+ const grouped = groupFoldedSections(rest);
75
+ return {
76
+ ...node,
77
+ content: [(0, exports.applyFoldedSections)(head), ...grouped.map(exports.applyFoldedSections)],
78
+ };
79
+ }
80
+ const grouped = groupFoldedSections(node.content);
81
+ return { ...node, content: grouped.map(exports.applyFoldedSections) };
82
+ };
83
+ exports.applyFoldedSections = applyFoldedSections;
84
+ //# sourceMappingURL=folded-sections.js.map