Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a95962f991 | |||
| 0295f785e6 | |||
| 1fb7e0ebf1 | |||
| 7c349978ac |
@@ -16,7 +16,7 @@ jobs:
|
||||
if: ${{ github.event.workflow_run.conclusion == 'success' }}
|
||||
steps:
|
||||
- name: Download pr number
|
||||
uses: dawidd6/action-download-artifact@b6e2e70617bc3265edd6dab6c906732b2f1ae151 # v21
|
||||
uses: dawidd6/action-download-artifact@8305c0f1062bb0d184d09ef4493ecb9288447732 # v20
|
||||
with:
|
||||
workflow: ${{ github.event.workflow.id }}
|
||||
run_id: ${{ github.event.workflow_run.id }}
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
id: pr
|
||||
run: echo "id=$(<pr.txt)" >> $GITHUB_OUTPUT
|
||||
- name: Download artifact
|
||||
uses: dawidd6/action-download-artifact@b6e2e70617bc3265edd6dab6c906732b2f1ae151 # v21
|
||||
uses: dawidd6/action-download-artifact@8305c0f1062bb0d184d09ef4493ecb9288447732 # v20
|
||||
with:
|
||||
workflow: ${{ github.event.workflow.id }}
|
||||
run_id: ${{ github.event.workflow_run.id }}
|
||||
|
||||
Generated
+26
-29
@@ -37,7 +37,7 @@
|
||||
"html-react-parser": "4.2.0",
|
||||
"i18next": "23.12.2",
|
||||
"i18next-browser-languagedetector": "8.0.0",
|
||||
"i18next-http-backend": "3.0.5",
|
||||
"i18next-http-backend": "2.5.2",
|
||||
"immer": "9.0.16",
|
||||
"is-hotkey": "0.2.0",
|
||||
"jotai": "2.6.0",
|
||||
@@ -6041,7 +6041,7 @@
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
||||
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
|
||||
"optional": true
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.14.0",
|
||||
@@ -6885,7 +6885,7 @@
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
|
||||
"integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
|
||||
"optional": true,
|
||||
"devOptional": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
@@ -7489,15 +7489,6 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/cross-fetch": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.1.0.tgz",
|
||||
"integrity": "sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"node-fetch": "^2.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||
@@ -9507,7 +9498,7 @@
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
|
||||
"integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
|
||||
"optional": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"minipass": "^3.0.0"
|
||||
},
|
||||
@@ -9519,7 +9510,7 @@
|
||||
"version": "3.3.6",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
|
||||
"integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
|
||||
"optional": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
@@ -9531,7 +9522,7 @@
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"optional": true
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
@@ -10289,12 +10280,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/i18next-http-backend": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-3.0.5.tgz",
|
||||
"integrity": "sha512-QaWHnsxieEDcqKe+vo/RFqpiIFRi/KBqlOSPcUlvinBaISCeiTRCbtrazHAjtHtsLC66oDsROAH8frWkQzfMMQ==",
|
||||
"license": "MIT",
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-2.5.2.tgz",
|
||||
"integrity": "sha512-+K8HbDfrvc1/2X8jpb7RLhI9ZxBDpx3xogYkQwGKlWAUXLSEGXzgdt3EcUjLlBCdMwdQY+K+EUF6oh8oB6rwHw==",
|
||||
"dependencies": {
|
||||
"cross-fetch": "4.1.0"
|
||||
"cross-fetch": "4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/i18next-http-backend/node_modules/cross-fetch": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz",
|
||||
"integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==",
|
||||
"dependencies": {
|
||||
"node-fetch": "^2.6.12"
|
||||
}
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
@@ -10450,7 +10448,6 @@
|
||||
"integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
|
||||
}
|
||||
@@ -12287,7 +12284,7 @@
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
|
||||
"integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==",
|
||||
"optional": true,
|
||||
"devOptional": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
@@ -12296,7 +12293,7 @@
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
|
||||
"integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
|
||||
"optional": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"minipass": "^3.0.0",
|
||||
"yallist": "^4.0.0"
|
||||
@@ -12309,7 +12306,7 @@
|
||||
"version": "3.3.6",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
|
||||
"integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
|
||||
"optional": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
@@ -12321,13 +12318,13 @@
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"optional": true
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/mkdirp": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
||||
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
|
||||
"optional": true,
|
||||
"devOptional": true,
|
||||
"bin": {
|
||||
"mkdirp": "bin/cmd.js"
|
||||
},
|
||||
@@ -12468,7 +12465,7 @@
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
|
||||
"integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==",
|
||||
"optional": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"abbrev": "1"
|
||||
},
|
||||
@@ -17417,7 +17414,7 @@
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz",
|
||||
"integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==",
|
||||
"optional": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"chownr": "^2.0.0",
|
||||
"fs-minipass": "^2.0.0",
|
||||
@@ -17434,7 +17431,7 @@
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"optional": true
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/temp-dir": {
|
||||
"version": "2.0.0",
|
||||
|
||||
+1
-1
@@ -90,7 +90,7 @@
|
||||
"html-react-parser": "4.2.0",
|
||||
"i18next": "23.12.2",
|
||||
"i18next-browser-languagedetector": "8.0.0",
|
||||
"i18next-http-backend": "3.0.5",
|
||||
"i18next-http-backend": "2.5.2",
|
||||
"immer": "9.0.16",
|
||||
"is-hotkey": "0.2.0",
|
||||
"jotai": "2.6.0",
|
||||
|
||||
@@ -255,10 +255,67 @@ const parseCodeBlockNode = (node: Element): CodeBlockElement[] | ParagraphElemen
|
||||
},
|
||||
];
|
||||
};
|
||||
const parseListNode = (
|
||||
|
||||
const parseListMarkdown = (
|
||||
node: Element,
|
||||
processText: ProcessTextCallback
|
||||
): OrderedListElement[] | UnorderedListElement[] | ParagraphElement[] => {
|
||||
processText: ProcessTextCallback,
|
||||
depth = 0
|
||||
): ParagraphElement[] => {
|
||||
const md = isTag(node) && node.name === 'ul' ? '*' : '-';
|
||||
const prefix = node.attribs['data-md'] ?? md;
|
||||
const [starOrHyphen] = prefix.match(/^\*|-$/) ?? [];
|
||||
const [digitOrChar] = prefix.match(/^[\da-zA-Z]/) ?? [];
|
||||
|
||||
const digit = digitOrChar ? parseInt(digitOrChar, 10) : undefined;
|
||||
|
||||
const lines: ParagraphElement[] = [];
|
||||
let lineNo = digit === undefined || Number.isNaN(digit) ? digitOrChar ?? 1 : digit;
|
||||
const pushLine = (line: InlineElement[]) => {
|
||||
lines.push({
|
||||
type: BlockType.Paragraph,
|
||||
children: [
|
||||
{
|
||||
text: `${Array(depth + 1).join(' ')}${starOrHyphen ? `${starOrHyphen} ` : `${lineNo}. `}`,
|
||||
},
|
||||
...line,
|
||||
],
|
||||
});
|
||||
if (typeof lineNo === 'string') {
|
||||
lineNo = String.fromCharCode(lineNo.charCodeAt(0) + 1);
|
||||
} else {
|
||||
lineNo += 1;
|
||||
}
|
||||
};
|
||||
|
||||
node.children.forEach((child) => {
|
||||
if (isText(child)) {
|
||||
pushLine([{ text: processText(child.data) }]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isTag(child)) {
|
||||
if (child.name === 'ul' || child.name === 'ol') {
|
||||
lines.push(...parseListMarkdown(child, processText, depth + 1));
|
||||
return;
|
||||
}
|
||||
if (child.name === 'li') {
|
||||
child.children.forEach((c) => {
|
||||
if (isTag(c) && (c.name === 'ul' || c.name === 'ol')) {
|
||||
lines.push(...parseListMarkdown(c, processText, depth + 1));
|
||||
return;
|
||||
}
|
||||
pushLine(getInlineElement(c, processText));
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
pushLine(getInlineElement(child, processText));
|
||||
});
|
||||
|
||||
return lines;
|
||||
};
|
||||
const parseListLines = (children: ChildNode[], processText: ProcessTextCallback) => {
|
||||
const listLines: Array<InlineElement[]> = [];
|
||||
let lineHolder: InlineElement[] = [];
|
||||
|
||||
@@ -269,7 +326,7 @@ const parseListNode = (
|
||||
lineHolder = [];
|
||||
};
|
||||
|
||||
node.children.forEach((child) => {
|
||||
children.forEach((child) => {
|
||||
if (isText(child)) {
|
||||
lineHolder.push({ text: processText(child.data) });
|
||||
return;
|
||||
@@ -292,24 +349,23 @@ const parseListNode = (
|
||||
});
|
||||
appendLine();
|
||||
|
||||
const mdSequence = node.attribs['data-md'];
|
||||
if (mdSequence !== undefined) {
|
||||
const prefix = mdSequence || '-';
|
||||
const [starOrHyphen] = prefix.match(/^\*|-$/) ?? [];
|
||||
return listLines.map((lineChildren) => ({
|
||||
type: BlockType.Paragraph,
|
||||
children: [
|
||||
{ text: `${starOrHyphen ? `${starOrHyphen} ` : `${prefix}. `} ` },
|
||||
...lineChildren,
|
||||
],
|
||||
}));
|
||||
return listLines;
|
||||
};
|
||||
const parseListNode = (
|
||||
node: Element,
|
||||
processText: ProcessTextCallback
|
||||
): OrderedListElement[] | UnorderedListElement[] | ParagraphElement[] => {
|
||||
if (node.attribs['data-md'] !== undefined) {
|
||||
return parseListMarkdown(node, processText);
|
||||
}
|
||||
|
||||
const lines = parseListLines(node.childNodes, processText);
|
||||
|
||||
if (node.name === 'ol') {
|
||||
return [
|
||||
{
|
||||
type: BlockType.OrderedList,
|
||||
children: listLines.map((lineChildren) => ({
|
||||
children: lines.map((lineChildren) => ({
|
||||
type: BlockType.ListItem,
|
||||
children: lineChildren,
|
||||
})),
|
||||
@@ -320,7 +376,7 @@ const parseListNode = (
|
||||
return [
|
||||
{
|
||||
type: BlockType.UnorderedList,
|
||||
children: listLines.map((lineChildren) => ({
|
||||
children: lines.map((lineChildren) => ({
|
||||
type: BlockType.ListItem,
|
||||
children: lineChildren,
|
||||
})),
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
import { replaceMatch } from '../internal';
|
||||
import {
|
||||
BlockQuoteRule,
|
||||
CodeBlockRule,
|
||||
ESC_BLOCK_SEQ,
|
||||
HeadingRule,
|
||||
OrderedListRule,
|
||||
UnorderedListRule,
|
||||
} from './rules';
|
||||
import { BlockQuoteRule, CodeBlockRule, ESC_BLOCK_SEQ, HeadingRule, ListRule } from './rules';
|
||||
import { runBlockRule } from './runner';
|
||||
import { BlockMDParser } from './type';
|
||||
|
||||
@@ -23,8 +16,7 @@ export const parseBlockMD: BlockMDParser = (text, parseInline) => {
|
||||
|
||||
if (!result) result = runBlockRule(text, CodeBlockRule, parseBlockMD, parseInline);
|
||||
if (!result) result = runBlockRule(text, BlockQuoteRule, parseBlockMD, parseInline);
|
||||
if (!result) result = runBlockRule(text, OrderedListRule, parseBlockMD, parseInline);
|
||||
if (!result) result = runBlockRule(text, UnorderedListRule, parseBlockMD, parseInline);
|
||||
if (!result) result = runBlockRule(text, ListRule, parseBlockMD, parseInline);
|
||||
if (!result) result = runBlockRule(text, HeadingRule, parseBlockMD, parseInline);
|
||||
|
||||
// replace \n with <br/> because want to preserve empty lines
|
||||
|
||||
@@ -10,18 +10,22 @@ export const HeadingRule: BlockMDRule = {
|
||||
},
|
||||
};
|
||||
|
||||
const CODEBLOCK_MD_1 = '```';
|
||||
const CODEBLOCK_REG_1 = /^`{3}(\S*)\n((?:.*\n)+?)`{3} *(?!.)\n?/m;
|
||||
// opening fence: 3 or more backticks
|
||||
// capture the exact fence length in group 1
|
||||
// optional info string in group 2
|
||||
// code content in group 3
|
||||
// closing fence must match the exact same fence sequence via \1
|
||||
const CODEBLOCK_REG_1 = /^(`{3,})(?!`)(\S*)\n((?:.*\n)+?)\1 *(?!.)\n?/m;
|
||||
export const CodeBlockRule: BlockMDRule = {
|
||||
match: (text) => text.match(CODEBLOCK_REG_1),
|
||||
html: (match) => {
|
||||
const [, g1, g2] = match;
|
||||
const [, fence, g1, g2] = match;
|
||||
// use last identifier after dot, e.g. for "example.json" gets us "json" as language code.
|
||||
const langCode = g1 ? g1.substring(g1.lastIndexOf('.') + 1) : null;
|
||||
const filename = g1 !== langCode ? g1 : null;
|
||||
const classNameAtt = langCode ? ` class="language-${langCode}"` : '';
|
||||
const filenameAtt = filename ? ` data-label="${filename}"` : '';
|
||||
return `<pre data-md="${CODEBLOCK_MD_1}"><code${classNameAtt}${filenameAtt}>${g2}</code></pre>`;
|
||||
return `<pre data-md="${fence}"><code${classNameAtt}${filenameAtt}>${g2}</code></pre>`;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -48,55 +52,146 @@ export const BlockQuoteRule: BlockMDRule = {
|
||||
};
|
||||
|
||||
const ORDERED_LIST_MD_1 = '-';
|
||||
const O_LIST_ITEM_PREFIX = /^(-|[\da-zA-Z]\.) */;
|
||||
const O_LIST_START = /^([\d])\./;
|
||||
const O_LIST_TYPE = /^([aAiI])\./;
|
||||
const O_LIST_TRAILING_NEWLINE = /\n$/;
|
||||
const ORDERED_LIST_REG_1 = /(^(?:-|[\da-zA-Z]\.) +.+\n?)+/m;
|
||||
export const OrderedListRule: BlockMDRule = {
|
||||
match: (text) => text.match(ORDERED_LIST_REG_1),
|
||||
html: (match, parseInline) => {
|
||||
const [listText] = match;
|
||||
const [, listStart] = listText.match(O_LIST_START) ?? [];
|
||||
const [, listType] = listText.match(O_LIST_TYPE) ?? [];
|
||||
|
||||
const lines = listText
|
||||
.replace(O_LIST_TRAILING_NEWLINE, '')
|
||||
.split('\n')
|
||||
.map((lineText) => {
|
||||
const line = lineText.replace(O_LIST_ITEM_PREFIX, '');
|
||||
const txt = parseInline ? parseInline(line) : line;
|
||||
return `<li><p>${txt}</p></li>`;
|
||||
})
|
||||
.join('');
|
||||
|
||||
const dataMdAtt = `data-md="${listType || listStart || ORDERED_LIST_MD_1}"`;
|
||||
const startAtt = listStart ? ` start="${listStart}"` : '';
|
||||
const typeAtt = listType ? ` type="${listType}"` : '';
|
||||
return `<ol ${dataMdAtt}${startAtt}${typeAtt}>${lines}</ol>`;
|
||||
},
|
||||
};
|
||||
|
||||
const UNORDERED_LIST_MD_1 = '*';
|
||||
const U_LIST_ITEM_PREFIX = /^\* */;
|
||||
const U_LIST_TRAILING_NEWLINE = /\n$/;
|
||||
const UNORDERED_LIST_REG_1 = /(^\* +.+\n?)+/m;
|
||||
export const UnorderedListRule: BlockMDRule = {
|
||||
match: (text) => text.match(UNORDERED_LIST_REG_1),
|
||||
const LIST_ITEM_REG = /^( *)([-*]|[\da-zA-Z]\.) +(.+)$/;
|
||||
type ListType = 'ol' | 'ul';
|
||||
|
||||
function getListType(marker: string): ListType {
|
||||
return marker === '*' ? 'ul' : 'ol';
|
||||
}
|
||||
|
||||
function getOrderedMeta(marker: string) {
|
||||
const startMatch = marker.match(/^(\d)\./);
|
||||
const typeMatch = marker.match(/^([aAiI])\./);
|
||||
|
||||
return {
|
||||
start: startMatch?.[1],
|
||||
type: typeMatch?.[1],
|
||||
};
|
||||
}
|
||||
|
||||
interface ParsedLine {
|
||||
indent: number;
|
||||
marker: string;
|
||||
content: string;
|
||||
listType: ListType;
|
||||
}
|
||||
|
||||
function parseLines(text: string): ParsedLine[] {
|
||||
return text
|
||||
.replace(/\n$/, '')
|
||||
.split('\n')
|
||||
.map((line) => {
|
||||
const match = line.match(LIST_ITEM_REG);
|
||||
|
||||
if (!match) return null;
|
||||
|
||||
const [, spaces, marker, content] = match;
|
||||
|
||||
return {
|
||||
indent: spaces.length,
|
||||
marker,
|
||||
content,
|
||||
listType: getListType(marker),
|
||||
};
|
||||
})
|
||||
.filter(Boolean) as ParsedLine[];
|
||||
}
|
||||
|
||||
function openList(line: ParsedLine) {
|
||||
if (line.listType === 'ul') {
|
||||
return `<ul data-md="${UNORDERED_LIST_MD_1}">`;
|
||||
}
|
||||
const { type, start } = getOrderedMeta(line.marker);
|
||||
const dataMdAtt = `data-md="${type || start || ORDERED_LIST_MD_1}"`;
|
||||
const startAtt = start ? ` start="${start}"` : '';
|
||||
const typeAtt = type ? ` type="${type}"` : '';
|
||||
return `<ol ${dataMdAtt}${startAtt}${typeAtt}>`;
|
||||
}
|
||||
|
||||
function closeList(listType: ListType) {
|
||||
return listType === 'ul' ? '</ul>' : '</ol>';
|
||||
}
|
||||
|
||||
function buildList(lines: ParsedLine[], parseInline?: (s: string) => string): string {
|
||||
let html = '';
|
||||
|
||||
const stack: ('ul' | 'ol')[] = [];
|
||||
|
||||
lines.forEach((line, index) => {
|
||||
const prev = lines[index - 1];
|
||||
const next = lines[index + 1];
|
||||
|
||||
const content = parseInline ? parseInline(line.content) : line.content;
|
||||
|
||||
// FIRST ITEM
|
||||
if (!prev) {
|
||||
html += openList(line);
|
||||
stack.push(line.listType);
|
||||
}
|
||||
|
||||
// DEEPER INDENT > open nested list
|
||||
else if (line.indent > prev.indent) {
|
||||
html += openList(line);
|
||||
stack.push(line.listType);
|
||||
}
|
||||
|
||||
// SAME LEVEL
|
||||
else if (line.indent === prev.indent) {
|
||||
html += '</li>';
|
||||
|
||||
// different list type
|
||||
if (line.listType !== prev.listType) {
|
||||
html += closeList(stack.pop()!);
|
||||
|
||||
html += openList(line);
|
||||
stack.push(line.listType);
|
||||
}
|
||||
}
|
||||
|
||||
// GOING BACK UP
|
||||
else if (line.indent < prev.indent) {
|
||||
html += '</li>';
|
||||
|
||||
while (stack.length > line.indent + 1) {
|
||||
html += closeList(stack.pop()!);
|
||||
html += '</li>';
|
||||
}
|
||||
|
||||
if (line.listType !== stack[stack.length - 1]) {
|
||||
html += closeList(stack.pop()!);
|
||||
|
||||
html += openList(line);
|
||||
stack.push(line.listType);
|
||||
}
|
||||
}
|
||||
|
||||
html += `<li><p>${content}</p>`;
|
||||
|
||||
// LAST ITEM cleanup
|
||||
if (!next) {
|
||||
html += '</li>';
|
||||
|
||||
while (stack.length) {
|
||||
html += closeList(stack.pop()!);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
const LIST_REG_1 = /^(?: *(?:[-*]|[\da-zA-Z]\.) +.+\n?)+/m;
|
||||
export const ListRule: BlockMDRule = {
|
||||
match: (text) => text.match(LIST_REG_1),
|
||||
html: (match, parseInline) => {
|
||||
const [listText] = match;
|
||||
|
||||
const lines = listText
|
||||
.replace(U_LIST_TRAILING_NEWLINE, '')
|
||||
.split('\n')
|
||||
.map((lineText) => {
|
||||
const line = lineText.replace(U_LIST_ITEM_PREFIX, '');
|
||||
const txt = parseInline ? parseInline(line) : line;
|
||||
return `<li><p>${txt}</p></li>`;
|
||||
})
|
||||
.join('');
|
||||
const lines = parseLines(listText);
|
||||
|
||||
return `<ul data-md="${UNORDERED_LIST_MD_1}">${lines}</ul>`;
|
||||
const html = buildList(lines, parseInline);
|
||||
|
||||
return html;
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -120,12 +120,23 @@ export const CodeBlockBottomShadow = style({
|
||||
background: `linear-gradient(to top, #00000022, #00000000)`,
|
||||
});
|
||||
|
||||
const BaseList = style({});
|
||||
export const List = style([
|
||||
BaseList,
|
||||
DefaultReset,
|
||||
MarginSpaced,
|
||||
{
|
||||
padding: `0 ${config.space.S100}`,
|
||||
paddingLeft: config.space.S600,
|
||||
selectors: {
|
||||
'& &': {
|
||||
marginTop: config.space.S200,
|
||||
marginBottom: config.space.S200,
|
||||
},
|
||||
'li:last-child &': {
|
||||
marginBottom: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user