@raspberrypifoundation/rpf-markdown-core 0.1.0 → 0.1.2

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 (3) hide show
  1. package/dist/index.cjs +291 -213
  2. package/dist/index.js +292 -214
  3. package/package.json +4 -3
package/dist/index.cjs CHANGED
@@ -45,12 +45,16 @@ function escapeHtml(value) {
45
45
  }
46
46
 
47
47
  // src/block_renderers.ts
48
+ var parseMarkdown = (input) => import_marked.marked.parse(input);
49
+ function setMarkdownParser(parse) {
50
+ parseMarkdown = parse;
51
+ }
48
52
  var CALLOUT_HEADINGS = {
49
53
  debug: "Debugging",
50
54
  tip: "Tip"
51
55
  };
52
56
  function renderMarkdown(input) {
53
- return import_marked.marked.parse(input);
57
+ return parseMarkdown(input);
54
58
  }
55
59
  function renderAccordion({
56
60
  modifier,
@@ -408,7 +412,8 @@ function getOrCreateScratchblocksRenderer() {
408
412
  return rendererCache;
409
413
  }
410
414
  function parseScratchblocksBlock(fencedBlock) {
411
- const fenceMatch = fencedBlock.match(SCRATCHBLOCKS_FENCED_BLOCK_REGEX);
415
+ const normalizedBlock = fencedBlock.replace(/\r\n/g, "\n").trimEnd();
416
+ const fenceMatch = normalizedBlock.match(SCRATCHBLOCKS_FENCED_BLOCK_REGEX);
412
417
  if (!fenceMatch) {
413
418
  throw new Error(SCRATCHBLOCKS_PARSE_ERROR);
414
419
  }
@@ -416,14 +421,14 @@ function parseScratchblocksBlock(fencedBlock) {
416
421
  const code = fenceMatch[3] ?? "";
417
422
  const style = mapFenceInfoToStyle(infoString);
418
423
  try {
419
- const renderer = getOrCreateScratchblocksRenderer();
420
- const doc = renderer.api.parse(code, { languages: ["en"] });
421
- const svgElement = renderer.api.render(doc, {
424
+ const renderer2 = getOrCreateScratchblocksRenderer();
425
+ const doc = renderer2.api.parse(code, { languages: ["en"] });
426
+ const svgElement = renderer2.api.render(doc, {
422
427
  style,
423
428
  languages: ["en"],
424
429
  scale: 1
425
430
  });
426
- const svg = embedStylesIntoSvg(svgElement.outerHTML, renderer.stylesHtml);
431
+ const svg = embedStylesIntoSvg(svgElement.outerHTML, renderer2.stylesHtml);
427
432
  const html = `<div class="c-project-scratchblocks" data-style="${style}">${svg}</div>`;
428
433
  return { html, style, svg };
429
434
  } catch (error) {
@@ -976,240 +981,313 @@ function createLegacyBlockExtension(name, blockName, render, appendNewline = tru
976
981
  }
977
982
  };
978
983
  }
979
- function processEditorProject(content) {
980
- import_marked2.marked.use(markedSmartypantsLite());
981
- import_marked2.marked.use({
982
- extensions: [
983
- {
984
- name: "htmlLineBreak",
985
- level: "block",
986
- start(src) {
987
- return src.search(/^[ \t]*\{\.page-break\}[ \t]*(?:\n|$)/m);
988
- },
989
- tokenizer(src) {
990
- const match = /^[ \t]*\{\.page-break\}[ \t]*(?:\n|$)/.exec(src);
991
- if (match) {
992
- return { type: "htmlLineBreak", raw: match[0] };
993
- }
994
- },
995
- renderer() {
996
- return "<div class=page-break></div>\n";
984
+ var RAW_HTML_BLOCK_TAGS = "article|aside|blockquote|body|details|dialog|div|dl|fieldset|figure|footer|form|h[1-6]|head|header|hr|html|iframe|main|nav|ol|p|pre|script|section|style|svg|table|ul|video";
985
+ var RAW_HTML_VOID_TAGS = /* @__PURE__ */ new Set(["hr", "br", "img", "input", "wbr", "col"]);
986
+ var RAW_HTML_BLOCK_AT_START = new RegExp(
987
+ `^[ \\t]*(?:<!--|<!doctype\\b|<(?:${RAW_HTML_BLOCK_TAGS})(?=[\\s/>]))`,
988
+ "i"
989
+ );
990
+ var RAW_HTML_BLOCK_SEARCH = new RegExp(RAW_HTML_BLOCK_AT_START.source, "im");
991
+ function consumeTrailingNewline(src, end) {
992
+ const after = /^[ \t]*\r?\n/.exec(src.slice(end));
993
+ return src.slice(0, end + (after?.[0].length ?? 0));
994
+ }
995
+ function matchRawHtmlBlock(src) {
996
+ if (!RAW_HTML_BLOCK_AT_START.test(src)) return void 0;
997
+ const leading = /^[ \t]*/.exec(src)?.[0].length ?? 0;
998
+ const rest = src.slice(leading);
999
+ if (rest.startsWith("<!--")) {
1000
+ const end = rest.indexOf("-->");
1001
+ if (end === -1) return void 0;
1002
+ return consumeTrailingNewline(src, leading + end + 3);
1003
+ }
1004
+ const doctype = /^<!doctype\b[^>]*>/i.exec(rest);
1005
+ if (doctype) {
1006
+ return consumeTrailingNewline(src, leading + doctype[0].length);
1007
+ }
1008
+ const open = /^<([a-z][a-z0-9]*)(?=[\s/>])[^>]*>/i.exec(rest);
1009
+ if (!open) return void 0;
1010
+ const tag = (open[1] ?? "").toLowerCase();
1011
+ if (open[0].endsWith("/>") || RAW_HTML_VOID_TAGS.has(tag)) {
1012
+ return consumeTrailingNewline(src, leading + open[0].length);
1013
+ }
1014
+ const tagRe = new RegExp(`<(/?)${tag}(?=[\\s/>])[^>]*>`, "ig");
1015
+ tagRe.lastIndex = leading;
1016
+ let depth = 0;
1017
+ let match;
1018
+ while ((match = tagRe.exec(src)) !== null) {
1019
+ if (match[1] === "/") {
1020
+ depth--;
1021
+ } else if (!match[0].endsWith("/>")) {
1022
+ depth++;
1023
+ }
1024
+ if (depth === 0) {
1025
+ return consumeTrailingNewline(src, tagRe.lastIndex);
1026
+ }
1027
+ }
1028
+ return void 0;
1029
+ }
1030
+ var renderer = new import_marked2.Marked();
1031
+ renderer.use(markedSmartypantsLite());
1032
+ renderer.use({
1033
+ extensions: [
1034
+ // Generic raw HTML passthrough. marked registers extension tokenizers by
1035
+ // unshifting, so dispatch order is the reverse of this array — placing
1036
+ // this first means it is tried LAST, letting the specific HTML
1037
+ // transformers below (callouts, output, wrapped code) take precedence.
1038
+ // Any other top-level HTML element is captured whole and emitted verbatim
1039
+ // so its interior is never re-parsed as markdown.
1040
+ {
1041
+ name: "rawHtmlBlock",
1042
+ level: "block",
1043
+ start(src) {
1044
+ const index = src.search(RAW_HTML_BLOCK_SEARCH);
1045
+ return index < 0 ? void 0 : index;
1046
+ },
1047
+ tokenizer(src) {
1048
+ const raw = matchRawHtmlBlock(src);
1049
+ if (raw) {
1050
+ return { type: "rawHtmlBlock", raw, text: raw };
997
1051
  }
998
1052
  },
999
- {
1000
- name: "preservedFence",
1001
- level: "block",
1002
- start(src) {
1003
- return src.search(/^[ \t]*(`{3,}|~{3,})/m);
1004
- },
1005
- tokenizer(src) {
1006
- const match = src.match(
1007
- /^[ \t]*(`{3,}|~{3,})([^\r\n]*)\r?\n([\s\S]*?)\r?\n[ \t]*\1[ \t]*(?:\n|$)/
1008
- );
1009
- if (!match) return;
1010
- return {
1011
- type: "preservedFence",
1012
- raw: match[0],
1013
- text: match[3] ?? "",
1014
- info: (match[2] ?? "").trim()
1015
- };
1016
- },
1017
- renderer(token) {
1018
- return renderMarkedCodeToken(token, token.raw);
1053
+ renderer(token) {
1054
+ return token.text;
1055
+ }
1056
+ },
1057
+ {
1058
+ name: "htmlLineBreak",
1059
+ level: "block",
1060
+ start(src) {
1061
+ return src.search(/^[ \t]*\{\.page-break\}[ \t]*(?:\n|$)/m);
1062
+ },
1063
+ tokenizer(src) {
1064
+ const match = /^[ \t]*\{\.page-break\}[ \t]*(?:\n|$)/.exec(src);
1065
+ if (match) {
1066
+ return { type: "htmlLineBreak", raw: match[0] };
1019
1067
  }
1020
1068
  },
1021
- // RFM alert blocks: > [!TASK], > [!ACCORDION], etc.
1022
- {
1023
- name: "rfmBlock",
1024
- level: "block",
1025
- start(src) {
1026
- return src.search(/^[ \t]*>+[ \t]?\[!/m);
1027
- },
1028
- tokenizer(src) {
1029
- const raw = matchRfmBlock(src);
1030
- if (raw !== void 0) {
1031
- return { type: "rfmBlock", raw, text: raw };
1032
- }
1033
- },
1034
- renderer(token) {
1035
- const raw = token.text;
1036
- const hintBlocks = splitTopLevelRfmHintBlocks(raw);
1037
- if (hintBlocks.length > 1) {
1038
- return `${renderHintsPanel(hintBlocks.map(getRfmHintBody))}
1039
- `;
1040
- }
1041
- return `${parseRfmBlock(raw).html}
1042
- `;
1069
+ renderer() {
1070
+ return "<div class=page-break></div>\n";
1071
+ }
1072
+ },
1073
+ {
1074
+ name: "preservedFence",
1075
+ level: "block",
1076
+ start(src) {
1077
+ return src.search(/^[ \t]*(`{3,}|~{3,})/m);
1078
+ },
1079
+ tokenizer(src) {
1080
+ const match = src.match(
1081
+ /^[ \t]*(`{3,}|~{3,})([^\r\n]*)\r?\n([\s\S]*?)\r?\n[ \t]*\1[ \t]*(?:\n|$)/
1082
+ );
1083
+ if (!match) return;
1084
+ return {
1085
+ type: "preservedFence",
1086
+ raw: match[0],
1087
+ text: match[3] ?? "",
1088
+ info: (match[2] ?? "").trim()
1089
+ };
1090
+ },
1091
+ renderer(token) {
1092
+ return renderMarkedCodeToken(token, token.raw);
1093
+ }
1094
+ },
1095
+ // RFM alert blocks: > [!TASK], > [!ACCORDION], etc.
1096
+ {
1097
+ name: "rfmBlock",
1098
+ level: "block",
1099
+ start(src) {
1100
+ return src.search(/^[ \t]*>+[ \t]?\[!/m);
1101
+ },
1102
+ tokenizer(src) {
1103
+ const raw = matchRfmBlock(src);
1104
+ if (raw !== void 0) {
1105
+ return { type: "rfmBlock", raw, text: raw };
1043
1106
  }
1044
1107
  },
1045
- // RPF callout blocks: <div class="c-project-callout c-project-callout--{type}">
1046
- {
1047
- name: "rpfCallout",
1048
- level: "block",
1049
- start(src) {
1050
- return src.search(
1051
- /<div\s+class="c-project-callout\s+c-project-callout--/
1052
- );
1053
- },
1054
- tokenizer(src) {
1055
- const match = /^<div\s+class="c-project-callout\s+c-project-callout--[a-z0-9-]+">[\s\S]*?<\/div>/.exec(
1056
- src
1057
- );
1058
- if (match) {
1059
- return { type: "rpfCallout", raw: match[0], text: match[0] };
1060
- }
1061
- },
1062
- renderer(token) {
1063
- return `${parseCalloutBlock(token.text).html}
1108
+ renderer(token) {
1109
+ const raw = token.text;
1110
+ const hintBlocks = splitTopLevelRfmHintBlocks(raw);
1111
+ if (hintBlocks.length > 1) {
1112
+ return `${renderHintsPanel(hintBlocks.map(getRfmHintBody))}
1064
1113
  `;
1065
1114
  }
1066
- },
1067
- // RPF task blocks: --- task --- ... --- /task ---
1068
- {
1069
- name: "rpfTask",
1070
- level: "block",
1071
- start(src) {
1072
- return src.search(/^--- task ---$/m);
1073
- },
1074
- tokenizer(src) {
1075
- const match = /^--- task ---\n[\s\S]*?\n--- \/task ---/.exec(src);
1076
- if (match) {
1077
- return { type: "rpfTask", raw: match[0], text: match[0] };
1078
- }
1079
- },
1080
- renderer(token) {
1081
- return `${parseTaskBlock(token.text).html}
1115
+ return `${parseRfmBlock(raw).html}
1082
1116
  `;
1117
+ }
1118
+ },
1119
+ // RPF callout blocks: <div class="c-project-callout c-project-callout--{type}">
1120
+ {
1121
+ name: "rpfCallout",
1122
+ level: "block",
1123
+ start(src) {
1124
+ return src.search(
1125
+ /<div\s+class="c-project-callout\s+c-project-callout--/
1126
+ );
1127
+ },
1128
+ tokenizer(src) {
1129
+ const match = /^<div\s+class="c-project-callout\s+c-project-callout--[a-z0-9-]+">[\s\S]*?<\/div>/.exec(
1130
+ src
1131
+ );
1132
+ if (match) {
1133
+ return { type: "rpfCallout", raw: match[0], text: match[0] };
1083
1134
  }
1084
1135
  },
1085
- // RPF collapse blocks: --- collapse --- ... --- /collapse ---
1086
- createLegacyBlockExtension("rpfCollapse", "collapse", parseCollapseBlock),
1087
- // RPF hints blocks: --- hints --- ... --- /hints ---
1088
- createLegacyBlockExtension("rpfHints", "hints", parseHintsBlock),
1089
- // RPF hint blocks: --- hint --- ... --- /hint ---
1090
- createLegacyBlockExtension("rpfHint", "hint", parseHintBlock),
1091
- // RPF challenge blocks: --- challenge --- ... --- /challenge ---
1092
- createLegacyBlockExtension(
1093
- "rpfChallenge",
1094
- "challenge",
1095
- parseChallengeBlock,
1096
- false
1097
- ),
1098
- // RPF print visibility blocks.
1099
- createLegacyBlockExtension("rpfNoPrint", "no-print", parseNoPrintBlock),
1100
- createLegacyBlockExtension(
1101
- "rpfPrintOnly",
1102
- "print-only",
1103
- parsePrintOnlyBlock
1104
- ),
1105
- // RPF save block: --- save ---
1106
- {
1107
- name: "rpfSave",
1108
- level: "block",
1109
- start(src) {
1110
- return src.search(/^[ \t]*--- save ---$/m);
1111
- },
1112
- tokenizer(src) {
1113
- const match = /^[ \t]*---\s+save\s+---[ \t]*(?:\n|$)/.exec(src);
1114
- if (match) {
1115
- return { type: "rpfSave", raw: match[0], text: match[0] };
1116
- }
1117
- },
1118
- renderer() {
1119
- return `${parseSaveBlock()}
1136
+ renderer(token) {
1137
+ return `${parseCalloutBlock(token.text).html}
1120
1138
  `;
1139
+ }
1140
+ },
1141
+ // RPF task blocks: --- task --- ... --- /task ---
1142
+ {
1143
+ name: "rpfTask",
1144
+ level: "block",
1145
+ start(src) {
1146
+ return src.search(/^--- task ---$/m);
1147
+ },
1148
+ tokenizer(src) {
1149
+ const match = /^--- task ---\n[\s\S]*?\n--- \/task ---/.exec(src);
1150
+ if (match) {
1151
+ return { type: "rpfTask", raw: match[0], text: match[0] };
1121
1152
  }
1122
1153
  },
1123
- // RPF code blocks: --- code --- ... --- /code ---
1124
- // Also handles the wrapped form: <div class="c-project-code">\n--- code ---\n...\n--- /code ---\n</div>
1125
- {
1126
- name: "rpfCode",
1127
- level: "block",
1128
- start(src) {
1129
- const bare = src.search(/^--- code ---$/m);
1130
- const wrapped = src.search(/^<div class="c-project-code">/m);
1131
- if (bare === -1) return wrapped;
1132
- if (wrapped === -1) return bare;
1133
- return Math.min(bare, wrapped);
1134
- },
1135
- tokenizer(src) {
1136
- const wrappedMatch = /^<div class="c-project-code">\n(--- code ---\n[\s\S]*?\n--- \/code ---)\n\s*<\/div>/.exec(
1137
- src
1138
- );
1139
- if (wrappedMatch) {
1140
- return {
1141
- type: "rpfCode",
1142
- raw: wrappedMatch[0],
1143
- text: wrappedMatch[1]
1144
- };
1145
- }
1146
- const match = /^--- code ---\n[\s\S]*?\n--- \/code ---/.exec(src);
1147
- if (match) {
1148
- return { type: "rpfCode", raw: match[0], text: match[0] };
1149
- }
1150
- },
1151
- renderer(token) {
1152
- return `${parseRpfCodeBlock(token.text).html}
1154
+ renderer(token) {
1155
+ return `${parseTaskBlock(token.text).html}
1153
1156
  `;
1157
+ }
1158
+ },
1159
+ // RPF collapse blocks: --- collapse --- ... --- /collapse ---
1160
+ createLegacyBlockExtension("rpfCollapse", "collapse", parseCollapseBlock),
1161
+ // RPF hints blocks: --- hints --- ... --- /hints ---
1162
+ createLegacyBlockExtension("rpfHints", "hints", parseHintsBlock),
1163
+ // RPF hint blocks: --- hint --- ... --- /hint ---
1164
+ createLegacyBlockExtension("rpfHint", "hint", parseHintBlock),
1165
+ // RPF challenge blocks: --- challenge --- ... --- /challenge ---
1166
+ createLegacyBlockExtension(
1167
+ "rpfChallenge",
1168
+ "challenge",
1169
+ parseChallengeBlock,
1170
+ false
1171
+ ),
1172
+ // RPF print visibility blocks.
1173
+ createLegacyBlockExtension("rpfNoPrint", "no-print", parseNoPrintBlock),
1174
+ createLegacyBlockExtension(
1175
+ "rpfPrintOnly",
1176
+ "print-only",
1177
+ parsePrintOnlyBlock
1178
+ ),
1179
+ // RPF save block: --- save ---
1180
+ {
1181
+ name: "rpfSave",
1182
+ level: "block",
1183
+ start(src) {
1184
+ return src.search(/^[ \t]*--- save ---$/m);
1185
+ },
1186
+ tokenizer(src) {
1187
+ const match = /^[ \t]*---\s+save\s+---[ \t]*(?:\n|$)/.exec(src);
1188
+ if (match) {
1189
+ return { type: "rpfSave", raw: match[0], text: match[0] };
1154
1190
  }
1155
1191
  },
1156
- // RPF output blocks: <div class="c-project-output"> ... </div>
1157
- {
1158
- name: "rpfOutput",
1159
- level: "block",
1160
- start(src) {
1161
- return src.search(/^<div\s+class="c-project-output">\s*$/m);
1162
- },
1163
- tokenizer(src) {
1164
- const match = /^<div\s+class="c-project-output">\s[\s\S]*?<\/div>/.exec(src);
1165
- if (match) {
1166
- return { type: "rpfOutput", raw: match[0], text: match[0] };
1167
- }
1168
- },
1169
- renderer(token) {
1170
- return `${parseOutputBlock(token.text)}
1192
+ renderer() {
1193
+ return `${parseSaveBlock()}
1171
1194
  `;
1195
+ }
1196
+ },
1197
+ // RPF code blocks: --- code --- ... --- /code ---
1198
+ // Also handles the wrapped form: <div class="c-project-code">\n--- code ---\n...\n--- /code ---\n</div>
1199
+ {
1200
+ name: "rpfCode",
1201
+ level: "block",
1202
+ start(src) {
1203
+ const bare = src.search(/^--- code ---$/m);
1204
+ const wrapped = src.search(/^<div class="c-project-code">/m);
1205
+ if (bare === -1) return wrapped;
1206
+ if (wrapped === -1) return bare;
1207
+ return Math.min(bare, wrapped);
1208
+ },
1209
+ tokenizer(src) {
1210
+ const wrappedMatch = /^<div class="c-project-code">\n(--- code ---\n[\s\S]*?\n--- \/code ---)\n\s*<\/div>/.exec(
1211
+ src
1212
+ );
1213
+ if (wrappedMatch) {
1214
+ return {
1215
+ type: "rpfCode",
1216
+ raw: wrappedMatch[0],
1217
+ text: wrappedMatch[1]
1218
+ };
1219
+ }
1220
+ const match = /^--- code ---\n[\s\S]*?\n--- \/code ---/.exec(src);
1221
+ if (match) {
1222
+ return { type: "rpfCode", raw: match[0], text: match[0] };
1172
1223
  }
1224
+ },
1225
+ renderer(token) {
1226
+ return `${parseRpfCodeBlock(token.text).html}
1227
+ `;
1173
1228
  }
1174
- ],
1175
- renderer: {
1176
- // Override fenced code blocks to use Prism with our custom options.
1177
- code(token) {
1178
- return renderMarkedCodeToken(token);
1229
+ },
1230
+ // RPF output blocks: <div class="c-project-output"> ... </div>
1231
+ {
1232
+ name: "rpfOutput",
1233
+ level: "block",
1234
+ start(src) {
1235
+ return src.search(/^<div\s+class="c-project-output">\s*$/m);
1179
1236
  },
1180
- listitem(item) {
1181
- const itemTokens = Array.isArray(item.tokens) ? item.tokens : [];
1182
- const raw = typeof item.raw === "string" ? item.raw : "";
1183
- const rawFenceBlocks = extractListItemFenceBlocks(raw);
1184
- let rawFenceIndex = 0;
1185
- const firstNewline = raw.indexOf("\n");
1186
- const hasBlankLineAfterMarker = firstNewline !== -1 && /^\n[ \t]*\n/.test(raw.slice(firstNewline));
1187
- let body = "";
1188
- for (let index = 0; index < itemTokens.length; index++) {
1189
- const token = itemTokens[index];
1190
- if (index === 0 && !item.task && !hasBlankLineAfterMarker && itemTokens.length > 1 && token?.type === "paragraph") {
1191
- body += this.parser.parseInline(token.tokens ?? []);
1192
- continue;
1193
- }
1194
- if (token?.type === "code" || token?.type === "preservedFence") {
1195
- body += renderMarkedCodeToken(token, rawFenceBlocks[rawFenceIndex]);
1196
- rawFenceIndex++;
1197
- continue;
1198
- }
1199
- body += this.parser.parse([token]).trimStart();
1237
+ tokenizer(src) {
1238
+ const match = /^<div\s+class="c-project-output">\s[\s\S]*?<\/div>/.exec(
1239
+ src
1240
+ );
1241
+ if (match) {
1242
+ return { type: "rpfOutput", raw: match[0], text: match[0] };
1200
1243
  }
1201
- return `<li>${body}</li>
1244
+ },
1245
+ renderer(token) {
1246
+ return `${parseOutputBlock(token.text)}
1202
1247
  `;
1203
1248
  }
1249
+ }
1250
+ ],
1251
+ renderer: {
1252
+ // Override fenced code blocks to use Prism with our custom options.
1253
+ code(token) {
1254
+ return renderMarkedCodeToken(token);
1204
1255
  },
1205
- hooks: {
1206
- postprocess(html) {
1207
- return applyInlineCodeClasses(html);
1256
+ listitem(item) {
1257
+ const itemTokens = Array.isArray(item.tokens) ? item.tokens : [];
1258
+ const raw = typeof item.raw === "string" ? item.raw : "";
1259
+ const rawFenceBlocks = extractListItemFenceBlocks(raw);
1260
+ let rawFenceIndex = 0;
1261
+ const firstNewline = raw.indexOf("\n");
1262
+ const hasBlankLineAfterMarker = firstNewline !== -1 && /^\n[ \t]*\n/.test(raw.slice(firstNewline));
1263
+ let body = "";
1264
+ for (let index = 0; index < itemTokens.length; index++) {
1265
+ const token = itemTokens[index];
1266
+ if (index === 0 && !item.task && !hasBlankLineAfterMarker && itemTokens.length > 1 && token?.type === "paragraph") {
1267
+ body += this.parser.parseInline(token.tokens ?? []);
1268
+ continue;
1269
+ }
1270
+ if (token?.type === "code" || token?.type === "preservedFence") {
1271
+ body += renderMarkedCodeToken(token, rawFenceBlocks[rawFenceIndex]);
1272
+ rawFenceIndex++;
1273
+ continue;
1274
+ }
1275
+ body += this.parser.parse([token]).trimStart();
1208
1276
  }
1277
+ return `<li>${body}</li>
1278
+ `;
1209
1279
  }
1210
- });
1211
- import_marked2.marked.use((0, import_marked_gfm_heading_id.gfmHeadingId)({ prefix: "" }));
1212
- return import_marked2.marked.parse(content);
1280
+ },
1281
+ hooks: {
1282
+ postprocess(html) {
1283
+ return applyInlineCodeClasses(html);
1284
+ }
1285
+ }
1286
+ });
1287
+ renderer.use((0, import_marked_gfm_heading_id.gfmHeadingId)({ prefix: "" }));
1288
+ setMarkdownParser((input) => renderer.parse(input));
1289
+ function processEditorProject(content) {
1290
+ return renderer.parse(content);
1213
1291
  }
1214
1292
  // Annotate the CommonJS export names for ESM import in node:
1215
1293
  0 && (module.exports = {
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/index.ts
2
- import { marked as marked2 } from "marked";
2
+ import { Marked } from "marked";
3
3
  import { gfmHeadingId } from "marked-gfm-heading-id";
4
4
 
5
5
  // src/block_renderers.ts
@@ -11,12 +11,16 @@ function escapeHtml(value) {
11
11
  }
12
12
 
13
13
  // src/block_renderers.ts
14
+ var parseMarkdown = (input) => marked.parse(input);
15
+ function setMarkdownParser(parse) {
16
+ parseMarkdown = parse;
17
+ }
14
18
  var CALLOUT_HEADINGS = {
15
19
  debug: "Debugging",
16
20
  tip: "Tip"
17
21
  };
18
22
  function renderMarkdown(input) {
19
- return marked.parse(input);
23
+ return parseMarkdown(input);
20
24
  }
21
25
  function renderAccordion({
22
26
  modifier,
@@ -373,7 +377,8 @@ function getOrCreateScratchblocksRenderer() {
373
377
  return rendererCache;
374
378
  }
375
379
  function parseScratchblocksBlock(fencedBlock) {
376
- const fenceMatch = fencedBlock.match(SCRATCHBLOCKS_FENCED_BLOCK_REGEX);
380
+ const normalizedBlock = fencedBlock.replace(/\r\n/g, "\n").trimEnd();
381
+ const fenceMatch = normalizedBlock.match(SCRATCHBLOCKS_FENCED_BLOCK_REGEX);
377
382
  if (!fenceMatch) {
378
383
  throw new Error(SCRATCHBLOCKS_PARSE_ERROR);
379
384
  }
@@ -381,14 +386,14 @@ function parseScratchblocksBlock(fencedBlock) {
381
386
  const code = fenceMatch[3] ?? "";
382
387
  const style = mapFenceInfoToStyle(infoString);
383
388
  try {
384
- const renderer = getOrCreateScratchblocksRenderer();
385
- const doc = renderer.api.parse(code, { languages: ["en"] });
386
- const svgElement = renderer.api.render(doc, {
389
+ const renderer2 = getOrCreateScratchblocksRenderer();
390
+ const doc = renderer2.api.parse(code, { languages: ["en"] });
391
+ const svgElement = renderer2.api.render(doc, {
387
392
  style,
388
393
  languages: ["en"],
389
394
  scale: 1
390
395
  });
391
- const svg = embedStylesIntoSvg(svgElement.outerHTML, renderer.stylesHtml);
396
+ const svg = embedStylesIntoSvg(svgElement.outerHTML, renderer2.stylesHtml);
392
397
  const html = `<div class="c-project-scratchblocks" data-style="${style}">${svg}</div>`;
393
398
  return { html, style, svg };
394
399
  } catch (error) {
@@ -941,240 +946,313 @@ function createLegacyBlockExtension(name, blockName, render, appendNewline = tru
941
946
  }
942
947
  };
943
948
  }
944
- function processEditorProject(content) {
945
- marked2.use(markedSmartypantsLite());
946
- marked2.use({
947
- extensions: [
948
- {
949
- name: "htmlLineBreak",
950
- level: "block",
951
- start(src) {
952
- return src.search(/^[ \t]*\{\.page-break\}[ \t]*(?:\n|$)/m);
953
- },
954
- tokenizer(src) {
955
- const match = /^[ \t]*\{\.page-break\}[ \t]*(?:\n|$)/.exec(src);
956
- if (match) {
957
- return { type: "htmlLineBreak", raw: match[0] };
958
- }
959
- },
960
- renderer() {
961
- return "<div class=page-break></div>\n";
949
+ var RAW_HTML_BLOCK_TAGS = "article|aside|blockquote|body|details|dialog|div|dl|fieldset|figure|footer|form|h[1-6]|head|header|hr|html|iframe|main|nav|ol|p|pre|script|section|style|svg|table|ul|video";
950
+ var RAW_HTML_VOID_TAGS = /* @__PURE__ */ new Set(["hr", "br", "img", "input", "wbr", "col"]);
951
+ var RAW_HTML_BLOCK_AT_START = new RegExp(
952
+ `^[ \\t]*(?:<!--|<!doctype\\b|<(?:${RAW_HTML_BLOCK_TAGS})(?=[\\s/>]))`,
953
+ "i"
954
+ );
955
+ var RAW_HTML_BLOCK_SEARCH = new RegExp(RAW_HTML_BLOCK_AT_START.source, "im");
956
+ function consumeTrailingNewline(src, end) {
957
+ const after = /^[ \t]*\r?\n/.exec(src.slice(end));
958
+ return src.slice(0, end + (after?.[0].length ?? 0));
959
+ }
960
+ function matchRawHtmlBlock(src) {
961
+ if (!RAW_HTML_BLOCK_AT_START.test(src)) return void 0;
962
+ const leading = /^[ \t]*/.exec(src)?.[0].length ?? 0;
963
+ const rest = src.slice(leading);
964
+ if (rest.startsWith("<!--")) {
965
+ const end = rest.indexOf("-->");
966
+ if (end === -1) return void 0;
967
+ return consumeTrailingNewline(src, leading + end + 3);
968
+ }
969
+ const doctype = /^<!doctype\b[^>]*>/i.exec(rest);
970
+ if (doctype) {
971
+ return consumeTrailingNewline(src, leading + doctype[0].length);
972
+ }
973
+ const open = /^<([a-z][a-z0-9]*)(?=[\s/>])[^>]*>/i.exec(rest);
974
+ if (!open) return void 0;
975
+ const tag = (open[1] ?? "").toLowerCase();
976
+ if (open[0].endsWith("/>") || RAW_HTML_VOID_TAGS.has(tag)) {
977
+ return consumeTrailingNewline(src, leading + open[0].length);
978
+ }
979
+ const tagRe = new RegExp(`<(/?)${tag}(?=[\\s/>])[^>]*>`, "ig");
980
+ tagRe.lastIndex = leading;
981
+ let depth = 0;
982
+ let match;
983
+ while ((match = tagRe.exec(src)) !== null) {
984
+ if (match[1] === "/") {
985
+ depth--;
986
+ } else if (!match[0].endsWith("/>")) {
987
+ depth++;
988
+ }
989
+ if (depth === 0) {
990
+ return consumeTrailingNewline(src, tagRe.lastIndex);
991
+ }
992
+ }
993
+ return void 0;
994
+ }
995
+ var renderer = new Marked();
996
+ renderer.use(markedSmartypantsLite());
997
+ renderer.use({
998
+ extensions: [
999
+ // Generic raw HTML passthrough. marked registers extension tokenizers by
1000
+ // unshifting, so dispatch order is the reverse of this array — placing
1001
+ // this first means it is tried LAST, letting the specific HTML
1002
+ // transformers below (callouts, output, wrapped code) take precedence.
1003
+ // Any other top-level HTML element is captured whole and emitted verbatim
1004
+ // so its interior is never re-parsed as markdown.
1005
+ {
1006
+ name: "rawHtmlBlock",
1007
+ level: "block",
1008
+ start(src) {
1009
+ const index = src.search(RAW_HTML_BLOCK_SEARCH);
1010
+ return index < 0 ? void 0 : index;
1011
+ },
1012
+ tokenizer(src) {
1013
+ const raw = matchRawHtmlBlock(src);
1014
+ if (raw) {
1015
+ return { type: "rawHtmlBlock", raw, text: raw };
962
1016
  }
963
1017
  },
964
- {
965
- name: "preservedFence",
966
- level: "block",
967
- start(src) {
968
- return src.search(/^[ \t]*(`{3,}|~{3,})/m);
969
- },
970
- tokenizer(src) {
971
- const match = src.match(
972
- /^[ \t]*(`{3,}|~{3,})([^\r\n]*)\r?\n([\s\S]*?)\r?\n[ \t]*\1[ \t]*(?:\n|$)/
973
- );
974
- if (!match) return;
975
- return {
976
- type: "preservedFence",
977
- raw: match[0],
978
- text: match[3] ?? "",
979
- info: (match[2] ?? "").trim()
980
- };
981
- },
982
- renderer(token) {
983
- return renderMarkedCodeToken(token, token.raw);
1018
+ renderer(token) {
1019
+ return token.text;
1020
+ }
1021
+ },
1022
+ {
1023
+ name: "htmlLineBreak",
1024
+ level: "block",
1025
+ start(src) {
1026
+ return src.search(/^[ \t]*\{\.page-break\}[ \t]*(?:\n|$)/m);
1027
+ },
1028
+ tokenizer(src) {
1029
+ const match = /^[ \t]*\{\.page-break\}[ \t]*(?:\n|$)/.exec(src);
1030
+ if (match) {
1031
+ return { type: "htmlLineBreak", raw: match[0] };
984
1032
  }
985
1033
  },
986
- // RFM alert blocks: > [!TASK], > [!ACCORDION], etc.
987
- {
988
- name: "rfmBlock",
989
- level: "block",
990
- start(src) {
991
- return src.search(/^[ \t]*>+[ \t]?\[!/m);
992
- },
993
- tokenizer(src) {
994
- const raw = matchRfmBlock(src);
995
- if (raw !== void 0) {
996
- return { type: "rfmBlock", raw, text: raw };
997
- }
998
- },
999
- renderer(token) {
1000
- const raw = token.text;
1001
- const hintBlocks = splitTopLevelRfmHintBlocks(raw);
1002
- if (hintBlocks.length > 1) {
1003
- return `${renderHintsPanel(hintBlocks.map(getRfmHintBody))}
1004
- `;
1005
- }
1006
- return `${parseRfmBlock(raw).html}
1007
- `;
1034
+ renderer() {
1035
+ return "<div class=page-break></div>\n";
1036
+ }
1037
+ },
1038
+ {
1039
+ name: "preservedFence",
1040
+ level: "block",
1041
+ start(src) {
1042
+ return src.search(/^[ \t]*(`{3,}|~{3,})/m);
1043
+ },
1044
+ tokenizer(src) {
1045
+ const match = src.match(
1046
+ /^[ \t]*(`{3,}|~{3,})([^\r\n]*)\r?\n([\s\S]*?)\r?\n[ \t]*\1[ \t]*(?:\n|$)/
1047
+ );
1048
+ if (!match) return;
1049
+ return {
1050
+ type: "preservedFence",
1051
+ raw: match[0],
1052
+ text: match[3] ?? "",
1053
+ info: (match[2] ?? "").trim()
1054
+ };
1055
+ },
1056
+ renderer(token) {
1057
+ return renderMarkedCodeToken(token, token.raw);
1058
+ }
1059
+ },
1060
+ // RFM alert blocks: > [!TASK], > [!ACCORDION], etc.
1061
+ {
1062
+ name: "rfmBlock",
1063
+ level: "block",
1064
+ start(src) {
1065
+ return src.search(/^[ \t]*>+[ \t]?\[!/m);
1066
+ },
1067
+ tokenizer(src) {
1068
+ const raw = matchRfmBlock(src);
1069
+ if (raw !== void 0) {
1070
+ return { type: "rfmBlock", raw, text: raw };
1008
1071
  }
1009
1072
  },
1010
- // RPF callout blocks: <div class="c-project-callout c-project-callout--{type}">
1011
- {
1012
- name: "rpfCallout",
1013
- level: "block",
1014
- start(src) {
1015
- return src.search(
1016
- /<div\s+class="c-project-callout\s+c-project-callout--/
1017
- );
1018
- },
1019
- tokenizer(src) {
1020
- const match = /^<div\s+class="c-project-callout\s+c-project-callout--[a-z0-9-]+">[\s\S]*?<\/div>/.exec(
1021
- src
1022
- );
1023
- if (match) {
1024
- return { type: "rpfCallout", raw: match[0], text: match[0] };
1025
- }
1026
- },
1027
- renderer(token) {
1028
- return `${parseCalloutBlock(token.text).html}
1073
+ renderer(token) {
1074
+ const raw = token.text;
1075
+ const hintBlocks = splitTopLevelRfmHintBlocks(raw);
1076
+ if (hintBlocks.length > 1) {
1077
+ return `${renderHintsPanel(hintBlocks.map(getRfmHintBody))}
1029
1078
  `;
1030
1079
  }
1031
- },
1032
- // RPF task blocks: --- task --- ... --- /task ---
1033
- {
1034
- name: "rpfTask",
1035
- level: "block",
1036
- start(src) {
1037
- return src.search(/^--- task ---$/m);
1038
- },
1039
- tokenizer(src) {
1040
- const match = /^--- task ---\n[\s\S]*?\n--- \/task ---/.exec(src);
1041
- if (match) {
1042
- return { type: "rpfTask", raw: match[0], text: match[0] };
1043
- }
1044
- },
1045
- renderer(token) {
1046
- return `${parseTaskBlock(token.text).html}
1080
+ return `${parseRfmBlock(raw).html}
1047
1081
  `;
1082
+ }
1083
+ },
1084
+ // RPF callout blocks: <div class="c-project-callout c-project-callout--{type}">
1085
+ {
1086
+ name: "rpfCallout",
1087
+ level: "block",
1088
+ start(src) {
1089
+ return src.search(
1090
+ /<div\s+class="c-project-callout\s+c-project-callout--/
1091
+ );
1092
+ },
1093
+ tokenizer(src) {
1094
+ const match = /^<div\s+class="c-project-callout\s+c-project-callout--[a-z0-9-]+">[\s\S]*?<\/div>/.exec(
1095
+ src
1096
+ );
1097
+ if (match) {
1098
+ return { type: "rpfCallout", raw: match[0], text: match[0] };
1048
1099
  }
1049
1100
  },
1050
- // RPF collapse blocks: --- collapse --- ... --- /collapse ---
1051
- createLegacyBlockExtension("rpfCollapse", "collapse", parseCollapseBlock),
1052
- // RPF hints blocks: --- hints --- ... --- /hints ---
1053
- createLegacyBlockExtension("rpfHints", "hints", parseHintsBlock),
1054
- // RPF hint blocks: --- hint --- ... --- /hint ---
1055
- createLegacyBlockExtension("rpfHint", "hint", parseHintBlock),
1056
- // RPF challenge blocks: --- challenge --- ... --- /challenge ---
1057
- createLegacyBlockExtension(
1058
- "rpfChallenge",
1059
- "challenge",
1060
- parseChallengeBlock,
1061
- false
1062
- ),
1063
- // RPF print visibility blocks.
1064
- createLegacyBlockExtension("rpfNoPrint", "no-print", parseNoPrintBlock),
1065
- createLegacyBlockExtension(
1066
- "rpfPrintOnly",
1067
- "print-only",
1068
- parsePrintOnlyBlock
1069
- ),
1070
- // RPF save block: --- save ---
1071
- {
1072
- name: "rpfSave",
1073
- level: "block",
1074
- start(src) {
1075
- return src.search(/^[ \t]*--- save ---$/m);
1076
- },
1077
- tokenizer(src) {
1078
- const match = /^[ \t]*---\s+save\s+---[ \t]*(?:\n|$)/.exec(src);
1079
- if (match) {
1080
- return { type: "rpfSave", raw: match[0], text: match[0] };
1081
- }
1082
- },
1083
- renderer() {
1084
- return `${parseSaveBlock()}
1101
+ renderer(token) {
1102
+ return `${parseCalloutBlock(token.text).html}
1085
1103
  `;
1104
+ }
1105
+ },
1106
+ // RPF task blocks: --- task --- ... --- /task ---
1107
+ {
1108
+ name: "rpfTask",
1109
+ level: "block",
1110
+ start(src) {
1111
+ return src.search(/^--- task ---$/m);
1112
+ },
1113
+ tokenizer(src) {
1114
+ const match = /^--- task ---\n[\s\S]*?\n--- \/task ---/.exec(src);
1115
+ if (match) {
1116
+ return { type: "rpfTask", raw: match[0], text: match[0] };
1086
1117
  }
1087
1118
  },
1088
- // RPF code blocks: --- code --- ... --- /code ---
1089
- // Also handles the wrapped form: <div class="c-project-code">\n--- code ---\n...\n--- /code ---\n</div>
1090
- {
1091
- name: "rpfCode",
1092
- level: "block",
1093
- start(src) {
1094
- const bare = src.search(/^--- code ---$/m);
1095
- const wrapped = src.search(/^<div class="c-project-code">/m);
1096
- if (bare === -1) return wrapped;
1097
- if (wrapped === -1) return bare;
1098
- return Math.min(bare, wrapped);
1099
- },
1100
- tokenizer(src) {
1101
- const wrappedMatch = /^<div class="c-project-code">\n(--- code ---\n[\s\S]*?\n--- \/code ---)\n\s*<\/div>/.exec(
1102
- src
1103
- );
1104
- if (wrappedMatch) {
1105
- return {
1106
- type: "rpfCode",
1107
- raw: wrappedMatch[0],
1108
- text: wrappedMatch[1]
1109
- };
1110
- }
1111
- const match = /^--- code ---\n[\s\S]*?\n--- \/code ---/.exec(src);
1112
- if (match) {
1113
- return { type: "rpfCode", raw: match[0], text: match[0] };
1114
- }
1115
- },
1116
- renderer(token) {
1117
- return `${parseRpfCodeBlock(token.text).html}
1119
+ renderer(token) {
1120
+ return `${parseTaskBlock(token.text).html}
1118
1121
  `;
1122
+ }
1123
+ },
1124
+ // RPF collapse blocks: --- collapse --- ... --- /collapse ---
1125
+ createLegacyBlockExtension("rpfCollapse", "collapse", parseCollapseBlock),
1126
+ // RPF hints blocks: --- hints --- ... --- /hints ---
1127
+ createLegacyBlockExtension("rpfHints", "hints", parseHintsBlock),
1128
+ // RPF hint blocks: --- hint --- ... --- /hint ---
1129
+ createLegacyBlockExtension("rpfHint", "hint", parseHintBlock),
1130
+ // RPF challenge blocks: --- challenge --- ... --- /challenge ---
1131
+ createLegacyBlockExtension(
1132
+ "rpfChallenge",
1133
+ "challenge",
1134
+ parseChallengeBlock,
1135
+ false
1136
+ ),
1137
+ // RPF print visibility blocks.
1138
+ createLegacyBlockExtension("rpfNoPrint", "no-print", parseNoPrintBlock),
1139
+ createLegacyBlockExtension(
1140
+ "rpfPrintOnly",
1141
+ "print-only",
1142
+ parsePrintOnlyBlock
1143
+ ),
1144
+ // RPF save block: --- save ---
1145
+ {
1146
+ name: "rpfSave",
1147
+ level: "block",
1148
+ start(src) {
1149
+ return src.search(/^[ \t]*--- save ---$/m);
1150
+ },
1151
+ tokenizer(src) {
1152
+ const match = /^[ \t]*---\s+save\s+---[ \t]*(?:\n|$)/.exec(src);
1153
+ if (match) {
1154
+ return { type: "rpfSave", raw: match[0], text: match[0] };
1119
1155
  }
1120
1156
  },
1121
- // RPF output blocks: <div class="c-project-output"> ... </div>
1122
- {
1123
- name: "rpfOutput",
1124
- level: "block",
1125
- start(src) {
1126
- return src.search(/^<div\s+class="c-project-output">\s*$/m);
1127
- },
1128
- tokenizer(src) {
1129
- const match = /^<div\s+class="c-project-output">\s[\s\S]*?<\/div>/.exec(src);
1130
- if (match) {
1131
- return { type: "rpfOutput", raw: match[0], text: match[0] };
1132
- }
1133
- },
1134
- renderer(token) {
1135
- return `${parseOutputBlock(token.text)}
1157
+ renderer() {
1158
+ return `${parseSaveBlock()}
1136
1159
  `;
1160
+ }
1161
+ },
1162
+ // RPF code blocks: --- code --- ... --- /code ---
1163
+ // Also handles the wrapped form: <div class="c-project-code">\n--- code ---\n...\n--- /code ---\n</div>
1164
+ {
1165
+ name: "rpfCode",
1166
+ level: "block",
1167
+ start(src) {
1168
+ const bare = src.search(/^--- code ---$/m);
1169
+ const wrapped = src.search(/^<div class="c-project-code">/m);
1170
+ if (bare === -1) return wrapped;
1171
+ if (wrapped === -1) return bare;
1172
+ return Math.min(bare, wrapped);
1173
+ },
1174
+ tokenizer(src) {
1175
+ const wrappedMatch = /^<div class="c-project-code">\n(--- code ---\n[\s\S]*?\n--- \/code ---)\n\s*<\/div>/.exec(
1176
+ src
1177
+ );
1178
+ if (wrappedMatch) {
1179
+ return {
1180
+ type: "rpfCode",
1181
+ raw: wrappedMatch[0],
1182
+ text: wrappedMatch[1]
1183
+ };
1137
1184
  }
1185
+ const match = /^--- code ---\n[\s\S]*?\n--- \/code ---/.exec(src);
1186
+ if (match) {
1187
+ return { type: "rpfCode", raw: match[0], text: match[0] };
1188
+ }
1189
+ },
1190
+ renderer(token) {
1191
+ return `${parseRpfCodeBlock(token.text).html}
1192
+ `;
1138
1193
  }
1139
- ],
1140
- renderer: {
1141
- // Override fenced code blocks to use Prism with our custom options.
1142
- code(token) {
1143
- return renderMarkedCodeToken(token);
1194
+ },
1195
+ // RPF output blocks: <div class="c-project-output"> ... </div>
1196
+ {
1197
+ name: "rpfOutput",
1198
+ level: "block",
1199
+ start(src) {
1200
+ return src.search(/^<div\s+class="c-project-output">\s*$/m);
1144
1201
  },
1145
- listitem(item) {
1146
- const itemTokens = Array.isArray(item.tokens) ? item.tokens : [];
1147
- const raw = typeof item.raw === "string" ? item.raw : "";
1148
- const rawFenceBlocks = extractListItemFenceBlocks(raw);
1149
- let rawFenceIndex = 0;
1150
- const firstNewline = raw.indexOf("\n");
1151
- const hasBlankLineAfterMarker = firstNewline !== -1 && /^\n[ \t]*\n/.test(raw.slice(firstNewline));
1152
- let body = "";
1153
- for (let index = 0; index < itemTokens.length; index++) {
1154
- const token = itemTokens[index];
1155
- if (index === 0 && !item.task && !hasBlankLineAfterMarker && itemTokens.length > 1 && token?.type === "paragraph") {
1156
- body += this.parser.parseInline(token.tokens ?? []);
1157
- continue;
1158
- }
1159
- if (token?.type === "code" || token?.type === "preservedFence") {
1160
- body += renderMarkedCodeToken(token, rawFenceBlocks[rawFenceIndex]);
1161
- rawFenceIndex++;
1162
- continue;
1163
- }
1164
- body += this.parser.parse([token]).trimStart();
1202
+ tokenizer(src) {
1203
+ const match = /^<div\s+class="c-project-output">\s[\s\S]*?<\/div>/.exec(
1204
+ src
1205
+ );
1206
+ if (match) {
1207
+ return { type: "rpfOutput", raw: match[0], text: match[0] };
1165
1208
  }
1166
- return `<li>${body}</li>
1209
+ },
1210
+ renderer(token) {
1211
+ return `${parseOutputBlock(token.text)}
1167
1212
  `;
1168
1213
  }
1214
+ }
1215
+ ],
1216
+ renderer: {
1217
+ // Override fenced code blocks to use Prism with our custom options.
1218
+ code(token) {
1219
+ return renderMarkedCodeToken(token);
1169
1220
  },
1170
- hooks: {
1171
- postprocess(html) {
1172
- return applyInlineCodeClasses(html);
1221
+ listitem(item) {
1222
+ const itemTokens = Array.isArray(item.tokens) ? item.tokens : [];
1223
+ const raw = typeof item.raw === "string" ? item.raw : "";
1224
+ const rawFenceBlocks = extractListItemFenceBlocks(raw);
1225
+ let rawFenceIndex = 0;
1226
+ const firstNewline = raw.indexOf("\n");
1227
+ const hasBlankLineAfterMarker = firstNewline !== -1 && /^\n[ \t]*\n/.test(raw.slice(firstNewline));
1228
+ let body = "";
1229
+ for (let index = 0; index < itemTokens.length; index++) {
1230
+ const token = itemTokens[index];
1231
+ if (index === 0 && !item.task && !hasBlankLineAfterMarker && itemTokens.length > 1 && token?.type === "paragraph") {
1232
+ body += this.parser.parseInline(token.tokens ?? []);
1233
+ continue;
1234
+ }
1235
+ if (token?.type === "code" || token?.type === "preservedFence") {
1236
+ body += renderMarkedCodeToken(token, rawFenceBlocks[rawFenceIndex]);
1237
+ rawFenceIndex++;
1238
+ continue;
1239
+ }
1240
+ body += this.parser.parse([token]).trimStart();
1173
1241
  }
1242
+ return `<li>${body}</li>
1243
+ `;
1174
1244
  }
1175
- });
1176
- marked2.use(gfmHeadingId({ prefix: "" }));
1177
- return marked2.parse(content);
1245
+ },
1246
+ hooks: {
1247
+ postprocess(html) {
1248
+ return applyInlineCodeClasses(html);
1249
+ }
1250
+ }
1251
+ });
1252
+ renderer.use(gfmHeadingId({ prefix: "" }));
1253
+ setMarkdownParser((input) => renderer.parse(input));
1254
+ function processEditorProject(content) {
1255
+ return renderer.parse(content);
1178
1256
  }
1179
1257
  export {
1180
1258
  processEditorProject
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@raspberrypifoundation/rpf-markdown-core",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "type": "module",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",
@@ -23,7 +23,7 @@
23
23
  "registry": "https://npm.pkg.github.com"
24
24
  },
25
25
  "scripts": {
26
- "build": "tsup src/index.ts --format esm,cjs --dts",
26
+ "build": "tsup src/index.ts --format esm,cjs --dts --clean",
27
27
  "dev": "tsup src/index.ts --format esm,cjs --dts --watch",
28
28
  "test": "vitest run",
29
29
  "prepack": "npm run build"
@@ -46,5 +46,6 @@
46
46
  "tsx": "^4.21.0",
47
47
  "typescript": "^6.0.2",
48
48
  "vitest": "^4.1.2"
49
- }
49
+ },
50
+ "packageManager": "yarn@4.12.0+sha512.f45ab632439a67f8bc759bf32ead036a1f413287b9042726b7cc4818b7b49e14e9423ba49b18f9e06ea4941c1ad062385b1d8760a8d5091a1a31e5f6219afca8"
50
51
  }