securemark 0.294.4 → 0.294.6
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/CHANGELOG.md +8 -0
- package/dist/index.js +159 -114
- package/markdown.d.ts +3 -1
- package/package.json +3 -3
- package/src/combinator/control/manipulation/fence.ts +2 -0
- package/src/combinator/control/manipulation/indent.ts +1 -1
- package/src/combinator/control/manipulation/match.ts +11 -8
- package/src/combinator/data/parser.ts +3 -0
- package/src/parser/api/normalize.test.ts +9 -1
- package/src/parser/api/normalize.ts +17 -10
- package/src/parser/api/parse.test.ts +3 -3
- package/src/parser/block/blockquote.test.ts +3 -9
- package/src/parser/block/blockquote.ts +4 -4
- package/src/parser/block/dlist.ts +4 -4
- package/src/parser/block/extension/example.ts +1 -3
- package/src/parser/block/extension/fig.test.ts +0 -1
- package/src/parser/block/extension/fig.ts +6 -6
- package/src/parser/block/extension/figbase.ts +1 -1
- package/src/parser/block/extension/figure.test.ts +1 -1
- package/src/parser/block/extension/figure.ts +6 -6
- package/src/parser/block/extension/message.ts +1 -1
- package/src/parser/block/extension/table.ts +4 -4
- package/src/parser/block/heading.ts +4 -4
- package/src/parser/block/reply/cite.ts +1 -1
- package/src/parser/block/reply/quote.ts +2 -2
- package/src/parser/block/sidefence.test.ts +1 -3
- package/src/parser/block/sidefence.ts +4 -4
- package/src/parser/block/table.ts +2 -2
- package/src/parser/block.ts +1 -1
- package/src/parser/header.ts +3 -3
- package/src/parser/inline/autolink/account.ts +5 -7
- package/src/parser/inline/autolink/channel.ts +15 -15
- package/src/parser/inline/autolink/hashnum.ts +2 -2
- package/src/parser/inline/autolink/hashtag.test.ts +6 -2
- package/src/parser/inline/autolink/hashtag.ts +12 -10
- package/src/parser/inline/autolink.ts +1 -1
- package/src/parser/inline/code.ts +12 -18
- package/src/parser/inline/deletion.ts +3 -3
- package/src/parser/inline/emstrong.ts +3 -3
- package/src/parser/inline/extension/indexer.ts +1 -1
- package/src/parser/inline/html.ts +1 -1
- package/src/parser/inline/htmlentity.ts +13 -16
- package/src/parser/inline/insertion.ts +3 -3
- package/src/parser/inline/italic.ts +3 -3
- package/src/parser/inline/link.ts +3 -3
- package/src/parser/inline/mark.ts +3 -3
- package/src/parser/inline/remark.ts +3 -3
- package/src/parser/inline/ruby.ts +7 -2
- package/src/parser/inline.ts +2 -0
- package/src/parser/source/text.ts +11 -5
- package/src/parser/util.ts +1 -1
- package/src/parser/visibility.ts +1 -1
package/markdown.d.ts
CHANGED
|
@@ -1057,7 +1057,9 @@ export namespace MarkdownParser {
|
|
|
1057
1057
|
export interface UnsafeHTMLEntityParser extends
|
|
1058
1058
|
// ©
|
|
1059
1059
|
Inline<'unsafehtmlentity'>,
|
|
1060
|
-
Parser<string, Context, [
|
|
1060
|
+
Parser<string, Context, [
|
|
1061
|
+
SourceParser.StrParser,
|
|
1062
|
+
]> {
|
|
1061
1063
|
}
|
|
1062
1064
|
export interface ShortMediaParser extends
|
|
1063
1065
|
// !https://host
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "securemark",
|
|
3
|
-
"version": "0.294.
|
|
3
|
+
"version": "0.294.6",
|
|
4
4
|
"description": "Secure markdown renderer working on browsers for user input data.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"homepage": "https://github.com/falsandtru/securemark",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"LICENSE"
|
|
29
29
|
],
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"spica": "0.0.
|
|
31
|
+
"spica": "0.0.810"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@types/dompurify": "3.0.5",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"babel-plugin-unassert": "^3.2.0",
|
|
43
43
|
"concurrently": "^8.2.2",
|
|
44
44
|
"eslint": "^9.8.0",
|
|
45
|
-
"eslint-plugin-redos": "^4.
|
|
45
|
+
"eslint-plugin-redos": "^4.5.0",
|
|
46
46
|
"eslint-webpack-plugin": "^4.2.0",
|
|
47
47
|
"glob": "^11.0.0",
|
|
48
48
|
"karma": "^6.4.4",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Parser, List, Data, Ctx, failsafe } from '../../data/parser';
|
|
2
|
+
import { consume } from '../../../combinator';
|
|
2
3
|
import { firstline, isBlank } from '../constraint/line';
|
|
3
4
|
import { push } from 'spica/array';
|
|
4
5
|
|
|
@@ -12,6 +13,7 @@ export function fence<C extends Ctx, D extends Parser<unknown, C>[]>(opener: Reg
|
|
|
12
13
|
const matches = opener.exec(source);
|
|
13
14
|
if (!matches) return;
|
|
14
15
|
assert(matches[0] === firstline(source, position));
|
|
16
|
+
consume(matches[0].length, context);
|
|
15
17
|
const delim = matches[1];
|
|
16
18
|
assert(delim && delim === delim.trim());
|
|
17
19
|
if (matches[0].includes(delim, delim.length)) return;
|
|
@@ -26,7 +26,7 @@ export function indent<N>(opener: RegExp | Parser<N>, parser: Parser<N> | boolea
|
|
|
26
26
|
context.position = source.length;
|
|
27
27
|
return new List([new Data(source.slice(position))]);
|
|
28
28
|
}))),
|
|
29
|
-
([indent]) => indent.length
|
|
29
|
+
([indent]) => indent.length * 2 + -(indent[0] === ' '), [], 2 ** 4 - 1)), separation),
|
|
30
30
|
(lines, context) => {
|
|
31
31
|
assert(parser = parser as Parser<N>);
|
|
32
32
|
return parser(subinput(trimBlockEnd(lines.foldl((acc, node) => acc + node.value, '')), context));
|
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
import { Parser, failsafe } from '../../data/parser';
|
|
2
2
|
import { consume } from '../../../combinator';
|
|
3
3
|
|
|
4
|
-
export function match<P extends Parser<unknown>>(pattern: RegExp, f: (matched: RegExpMatchArray) => P
|
|
5
|
-
export function match<N>(pattern: RegExp, f: (matched: RegExpMatchArray) => Parser<N
|
|
4
|
+
export function match<P extends Parser<unknown>>(pattern: RegExp, f: (matched: RegExpMatchArray) => P): P;
|
|
5
|
+
export function match<N>(pattern: RegExp, f: (matched: RegExpMatchArray) => Parser<N>): Parser<N> {
|
|
6
6
|
assert(!pattern.flags.match(/[gm]/) && pattern.sticky && !pattern.source.startsWith('^'));
|
|
7
|
+
const count = typeof pattern === 'object'
|
|
8
|
+
? /[^^\\*+][*+]/.test(pattern.source)
|
|
9
|
+
: false;
|
|
7
10
|
return failsafe(input => {
|
|
8
11
|
const { context } = input;
|
|
9
12
|
const { source, position } = context;
|
|
10
13
|
if (position === source.length) return;
|
|
11
14
|
pattern.lastIndex = position;
|
|
12
|
-
const
|
|
13
|
-
if (!
|
|
14
|
-
assert(source.startsWith(
|
|
15
|
-
|
|
16
|
-
const result = f(
|
|
17
|
-
context.position += result && context.position === position ?
|
|
15
|
+
const params = pattern.exec(source);
|
|
16
|
+
if (!params) return;
|
|
17
|
+
assert(source.startsWith(params[0], position));
|
|
18
|
+
count && consume(params[0].length, context);
|
|
19
|
+
const result = f(params)(input);
|
|
20
|
+
context.position += result && context.position === position ? params[0].length : 0;
|
|
18
21
|
assert(context.position > position || !result);
|
|
19
22
|
return context.position > position
|
|
20
23
|
? result
|
|
@@ -39,8 +39,11 @@ export interface CtxOptions {
|
|
|
39
39
|
// 区間別テーブルは固定サイズであるためプールして再使用できる。
|
|
40
40
|
// 従って分割時のデータ構造は区間ごとに探索木を動的に生成しデータ数に応じてテーブルに移行するのが最も効率的である。
|
|
41
41
|
// これにより最悪時間計算量線形化に要する最悪空間計算量が+1nに局限される。
|
|
42
|
+
// またはテーブルの参照が高速なら変換せず併用してもよい。
|
|
42
43
|
// 木とテーブルいずれにおいてもバックトラックデータとオーバーヘッドを合わせた追加データサイズの最大値は
|
|
43
44
|
// セグメントサイズに制約されるため入力サイズに対する最大追加データサイズの平均比率はかなり小さくなる。
|
|
45
|
+
// 必要なテーブルの最大サイズは最大セグメントサイズであるため最大追加データサイズは入力サイズにかかわらず
|
|
46
|
+
// 10KB*並列数に留まり最大数百文字以下の短文ならば数百byte*並列数となる。
|
|
44
47
|
//
|
|
45
48
|
// 1. データ数が規定数を超えたら区間テーブルを生成しデータを振り分ける。
|
|
46
49
|
// - 子ノードのポインタだけ保持するとしても1ノード複数データ保持で圧縮できるかは微妙。
|
|
@@ -67,12 +67,20 @@ describe('Unit: parser/normalize', () => {
|
|
|
67
67
|
assert(normalize('\x01---\na: b\x01\n---\n\n!> \x01---\na: b\x01\n---') === '\uFFFD---\na: b\uFFFD\n---\n\n!> \uFFFD---\na: b\uFFFD\n---');
|
|
68
68
|
});
|
|
69
69
|
|
|
70
|
+
it('emoji', () => {
|
|
71
|
+
assert(normalize('😀') === '😀');
|
|
72
|
+
assert(normalize('🤚🏽') === '🤚🏽');
|
|
73
|
+
assert(normalize('👨👩👧') === '👨👩👧');
|
|
74
|
+
assert(normalize('🇺🇳') === '🇺🇳');
|
|
75
|
+
assert(normalize('#️⃣*️⃣0️⃣1️⃣2️⃣3️⃣4️⃣5️⃣6️⃣7️⃣8️⃣9️⃣') === '#️⃣*️⃣0️⃣1️⃣2️⃣3️⃣4️⃣5️⃣6️⃣7️⃣8️⃣9️⃣');
|
|
76
|
+
});
|
|
77
|
+
|
|
70
78
|
});
|
|
71
79
|
|
|
72
80
|
describe('escape', () => {
|
|
73
81
|
it('', () => {
|
|
74
82
|
assert(escape('\u200B') === '​');
|
|
75
|
-
assert(escape('\
|
|
83
|
+
assert(escape('\u200F') === '‏');
|
|
76
84
|
});
|
|
77
85
|
|
|
78
86
|
});
|
|
@@ -15,7 +15,7 @@ function format(source: string): string {
|
|
|
15
15
|
|
|
16
16
|
function sanitize(source: string): string {
|
|
17
17
|
return source
|
|
18
|
-
.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]|[\u2006\u200B-\u200F\u202A-\u202F\u2060\uFEFF]|(?<![\u1820\u1821])\u180E/g, UNICODE_REPLACEMENT_CHARACTER)
|
|
18
|
+
.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]|(?!\u200D)[\u2006\u200B-\u200F\u202A-\u202F\u2060\uFEFF]|(?<![\u1820\u1821])\u180E/g, UNICODE_REPLACEMENT_CHARACTER)
|
|
19
19
|
.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]?|[\uDC00-\uDFFF]/g, char =>
|
|
20
20
|
char.length === 1
|
|
21
21
|
? UNICODE_REPLACEMENT_CHARACTER
|
|
@@ -58,13 +58,20 @@ export const invisibleHTMLEntityNames = [
|
|
|
58
58
|
'InvisibleComma',
|
|
59
59
|
'ic',
|
|
60
60
|
] as const;
|
|
61
|
-
const
|
|
62
|
-
|
|
61
|
+
const unreadableEscapeHTMLEntityNames = invisibleHTMLEntityNames.filter(name => ![
|
|
62
|
+
'Tab',
|
|
63
|
+
'NewLine',
|
|
64
|
+
'NonBreakingSpace',
|
|
65
|
+
'nbsp',
|
|
66
|
+
'zwj',
|
|
67
|
+
'zwnj',
|
|
68
|
+
].includes(name));
|
|
69
|
+
const unreadableEscapeCharacters = unreadableEscapeHTMLEntityNames
|
|
63
70
|
.map(name => unsafehtmlentity(input(`&${name};`, {}))!.head!.value);
|
|
64
|
-
assert(
|
|
65
|
-
assert(
|
|
66
|
-
const
|
|
67
|
-
assert(!
|
|
71
|
+
assert(unreadableEscapeCharacters.length === unreadableEscapeHTMLEntityNames.length);
|
|
72
|
+
assert(unreadableEscapeCharacters.every(c => c.length === 1));
|
|
73
|
+
const unreadableEscapeCharacter = new RegExp(`[${unreadableEscapeCharacters.join('')}]`, 'g');
|
|
74
|
+
assert(!unreadableEscapeCharacter.source.includes('&'));
|
|
68
75
|
|
|
69
76
|
// https://www.pandanoir.info/entry/2018/03/11/193000
|
|
70
77
|
// http://anti.rosx.net/etc/memo/002_space.html
|
|
@@ -77,7 +84,7 @@ const unreadableSpecialCharacters = [
|
|
|
77
84
|
// ZERO WIDTH NON-JOINER
|
|
78
85
|
'\u200C',
|
|
79
86
|
// ZERO WIDTH JOINER
|
|
80
|
-
'\u200D',
|
|
87
|
+
//'\u200D',
|
|
81
88
|
// LEFT-TO-RIGHT MARK
|
|
82
89
|
'\u200E',
|
|
83
90
|
// RIGHT-TO-LEFT MARK
|
|
@@ -104,6 +111,6 @@ assert(unreadableSpecialCharacters.every(c => sanitize(c) === UNICODE_REPLACEMEN
|
|
|
104
111
|
// 特殊不可視文字はエディタおよびソースビューアでは等幅および強調表示により可視化する
|
|
105
112
|
export function escape(source: string): string {
|
|
106
113
|
return source
|
|
107
|
-
.replace(
|
|
108
|
-
`&${
|
|
114
|
+
.replace(unreadableEscapeCharacter, char =>
|
|
115
|
+
`&${unreadableEscapeHTMLEntityNames[unreadableEscapeCharacters.indexOf(char)]};`);
|
|
109
116
|
}
|
|
@@ -361,9 +361,9 @@ describe('Unit: parser/api/parse', () => {
|
|
|
361
361
|
|
|
362
362
|
it('backtrack', function () {
|
|
363
363
|
this.timeout(5000);
|
|
364
|
-
// 最悪計算量での実行速度はCommonMarkの公式JS実装の32n
|
|
364
|
+
// 最悪計算量での実行速度はCommonMarkの公式JS実装の32nより速い。
|
|
365
365
|
// 5n = annotation/reference + link + url/math + ruby + text
|
|
366
|
-
const source =
|
|
366
|
+
const source = `((([[[[#$[${'.'.repeat(19997)}`;
|
|
367
367
|
assert.deepStrictEqual(
|
|
368
368
|
[...parse(source, {}, { resources: { clock: 100000, recursions: [100] } }).children]
|
|
369
369
|
.map(el => el.tagName),
|
|
@@ -372,7 +372,7 @@ describe('Unit: parser/api/parse', () => {
|
|
|
372
372
|
|
|
373
373
|
it('backtrack error', function () {
|
|
374
374
|
this.timeout(5000);
|
|
375
|
-
const source =
|
|
375
|
+
const source = `((([[[[#$[${'.'.repeat(19997 + 1)}`;
|
|
376
376
|
assert.deepStrictEqual(
|
|
377
377
|
[...parse(source, {}, { resources: { clock: 100000, recursions: [100] } }).children]
|
|
378
378
|
.map(el => el.tagName),
|
|
@@ -14,6 +14,9 @@ describe('Unit: parser/block/blockquote', () => {
|
|
|
14
14
|
assert.deepStrictEqual(inspect(parser('>'), ctx), undefined);
|
|
15
15
|
assert.deepStrictEqual(inspect(parser('>a'), ctx), undefined);
|
|
16
16
|
assert.deepStrictEqual(inspect(parser('>\n'), ctx), undefined);
|
|
17
|
+
assert.deepStrictEqual(inspect(parser('>\na'), ctx), undefined);
|
|
18
|
+
assert.deepStrictEqual(inspect(parser('!>\n'), ctx), undefined);
|
|
19
|
+
assert.deepStrictEqual(inspect(parser('!>\na'), ctx), undefined);
|
|
17
20
|
assert.deepStrictEqual(inspect(parser(' > '), ctx), undefined);
|
|
18
21
|
assert.deepStrictEqual(inspect(parser('>>'), ctx), undefined);
|
|
19
22
|
});
|
|
@@ -38,10 +41,6 @@ describe('Unit: parser/block/blockquote', () => {
|
|
|
38
41
|
assert.deepStrictEqual(inspect(parser('> a\\\nb'), ctx), [['<blockquote><pre>a\\<br>b</pre></blockquote>'], '']);
|
|
39
42
|
assert.deepStrictEqual(inspect(parser('> a '), ctx), [['<blockquote><pre> a </pre></blockquote>'], '']);
|
|
40
43
|
assert.deepStrictEqual(inspect(parser('> \na'), ctx), [['<blockquote><pre><br>a</pre></blockquote>'], '']);
|
|
41
|
-
assert.deepStrictEqual(inspect(parser('>\na'), ctx), [['<blockquote><pre><br>a</pre></blockquote>'], '']);
|
|
42
|
-
assert.deepStrictEqual(inspect(parser('>\n a'), ctx), [['<blockquote><pre><br> a</pre></blockquote>'], '']);
|
|
43
|
-
assert.deepStrictEqual(inspect(parser('>\n>'), ctx), [['<blockquote><pre><br></pre></blockquote>'], '']);
|
|
44
|
-
assert.deepStrictEqual(inspect(parser('>\n> a'), ctx), [['<blockquote><pre><br>a</pre></blockquote>'], '']);
|
|
45
44
|
assert.deepStrictEqual(inspect(parser('> http://host'), ctx), [['<blockquote><pre><a class="url" href="http://host" target="_blank">http://host</a></pre></blockquote>'], '']);
|
|
46
45
|
assert.deepStrictEqual(inspect(parser('> http://host)'), ctx), [['<blockquote><pre><a class="url" href="http://host)" target="_blank">http://host)</a></pre></blockquote>'], '']);
|
|
47
46
|
assert.deepStrictEqual(inspect(parser('> !http://host'), ctx), [['<blockquote><pre>!<a class="url" href="http://host" target="_blank">http://host</a></pre></blockquote>'], '']);
|
|
@@ -92,11 +91,6 @@ describe('Unit: parser/block/blockquote', () => {
|
|
|
92
91
|
assert.deepStrictEqual(inspect(parser('!>> > a\n> b'), ctx), [['<blockquote><blockquote><section><blockquote><pre>a</pre></blockquote><h2>References</h2><ol class="references"></ol></section></blockquote><section><p>b</p><h2>References</h2><ol class="references"></ol></section></blockquote>'], '']);
|
|
93
92
|
assert.deepStrictEqual(inspect(parser('!> !> a'), ctx), [['<blockquote><section><blockquote><section><p>a</p><h2>References</h2><ol class="references"></ol></section></blockquote><h2>References</h2><ol class="references"></ol></section></blockquote>'], '']);
|
|
94
93
|
assert.deepStrictEqual(inspect(parser('!> \na'), ctx), [['<blockquote><section><p>a</p><h2>References</h2><ol class="references"></ol></section></blockquote>'], '']);
|
|
95
|
-
assert.deepStrictEqual(inspect(parser('!>\n'), ctx), undefined);
|
|
96
|
-
assert.deepStrictEqual(inspect(parser('!>\na'), ctx), [['<blockquote><section><p>a</p><h2>References</h2><ol class="references"></ol></section></blockquote>'], '']);
|
|
97
|
-
assert.deepStrictEqual(inspect(parser('!>\n a'), ctx), [['<blockquote><section><p> a</p><h2>References</h2><ol class="references"></ol></section></blockquote>'], '']);
|
|
98
|
-
assert.deepStrictEqual(inspect(parser('!>\n>'), ctx), [['<blockquote><section><h2>References</h2><ol class="references"></ol></section></blockquote>'], '']);
|
|
99
|
-
assert.deepStrictEqual(inspect(parser('!>\n> a'), ctx), [['<blockquote><section><p>a</p><h2>References</h2><ol class="references"></ol></section></blockquote>'], '']);
|
|
100
94
|
assert.deepStrictEqual(inspect(parser('!>> ## a\n> ## a'), ctx), [['<blockquote><blockquote><section><h2>a</h2><h2>References</h2><ol class="references"></ol></section></blockquote><section><h2>a</h2><h2>References</h2><ol class="references"></ol></section></blockquote>'], '']);
|
|
101
95
|
assert.deepStrictEqual(inspect(parser('!>> ~ a\n> ~ a'), ctx), [['<blockquote><blockquote><section><dl><dt>a</dt><dd></dd></dl><h2>References</h2><ol class="references"></ol></section></blockquote><section><dl><dt>a</dt><dd></dd></dl><h2>References</h2><ol class="references"></ol></section></blockquote>'], '']);
|
|
102
96
|
assert.deepStrictEqual(inspect(parser('!>> ~~~figure $test-a\n>> > \n>>\n~~~\n> ~~~figure $test-a\n> > \n>\n[#a]\n~~~'), ctx), [['<blockquote><blockquote><section><figure data-type="quote" data-label="test-a" data-group="test" data-number="1"><figcaption><span class="figindex">Test 1. </span><span class="figtext"></span></figcaption><div><blockquote></blockquote></div></figure><h2>References</h2><ol class="references"></ol></section></blockquote><section><figure data-type="quote" data-label="test-a" data-group="test" data-number="1"><figcaption><span class="figindex">Test 1. </span><span class="figtext"><a class="index">a</a></span></figcaption><div><blockquote></blockquote></div></figure><h2>References</h2><ol class="references"></ol></section></blockquote>'], '']);
|
|
@@ -9,7 +9,7 @@ import { parse } from '../api/parse';
|
|
|
9
9
|
import { html, defrag } from 'typed-dom/dom';
|
|
10
10
|
|
|
11
11
|
export const segment: BlockquoteParser.SegmentParser = block(union([
|
|
12
|
-
validate(/!?>+
|
|
12
|
+
validate(/!?>+ /y, some(contentline)),
|
|
13
13
|
]));
|
|
14
14
|
|
|
15
15
|
export const blockquote: BlockquoteParser = lazy(() => block(rewrite(segment, union([
|
|
@@ -17,9 +17,9 @@ export const blockquote: BlockquoteParser = lazy(() => block(rewrite(segment, un
|
|
|
17
17
|
open(/!(?=>)/y, markdown),
|
|
18
18
|
]))));
|
|
19
19
|
|
|
20
|
-
const opener = /(?=>>+(
|
|
21
|
-
const indent = block(open(opener, some(contentline, />(
|
|
22
|
-
const unindent = (source: string) => source.replace(/(?<=^|\n)>(?:[
|
|
20
|
+
const opener = /(?=>>+(?:$|[ \n]))/y;
|
|
21
|
+
const indent = block(open(opener, some(contentline, />(?:$|[ \n])/y)), false);
|
|
22
|
+
const unindent = (source: string) => source.replace(/(?<=^|\n)>(?: |(?=>*(?:$|[ \n])))|\n$/g, '');
|
|
23
23
|
|
|
24
24
|
const source: BlockquoteParser.SourceParser = lazy(() => fmap(
|
|
25
25
|
some(recursion(Recursion.blockquote, union([
|
|
@@ -9,7 +9,7 @@ import { unwrap } from '../util';
|
|
|
9
9
|
import { html, defrag } from 'typed-dom/dom';
|
|
10
10
|
|
|
11
11
|
export const dlist: DListParser = lazy(() => block(fmap(validate(
|
|
12
|
-
/~
|
|
12
|
+
/~ +(?=\S)/y,
|
|
13
13
|
some(inits([
|
|
14
14
|
state(State.annotation | State.reference | State.index | State.label | State.link,
|
|
15
15
|
some(term)),
|
|
@@ -18,15 +18,15 @@ export const dlist: DListParser = lazy(() => block(fmap(validate(
|
|
|
18
18
|
ns => new List([new Data(html('dl', unwrap(fillTrailingDescription(ns))))]))));
|
|
19
19
|
|
|
20
20
|
const term: DListParser.TermParser = line(indexee(fmap(open(
|
|
21
|
-
/~
|
|
21
|
+
/~ +(?=\S)/y,
|
|
22
22
|
visualize(trimBlank(some(union([indexer, inline])))),
|
|
23
23
|
true),
|
|
24
24
|
ns => new List([new Data(html('dt', { 'data-index': dataindex(ns) }, defrag(unwrap(ns))))]))));
|
|
25
25
|
|
|
26
26
|
const desc: DListParser.DescriptionParser = block(fmap(open(
|
|
27
|
-
/:
|
|
27
|
+
/: +(?=\S)|/y,
|
|
28
28
|
rewrite(
|
|
29
|
-
some(anyline, /[~:]
|
|
29
|
+
some(anyline, /[~:] +(?=\S)/y),
|
|
30
30
|
visualize(trimBlankEnd(some(union([inline]))))),
|
|
31
31
|
true),
|
|
32
32
|
ns => new List([new Data(html('dd', defrag(unwrap(ns))))])),
|
|
@@ -7,10 +7,8 @@ import { unwrap, invalid } from '../../util';
|
|
|
7
7
|
import { parse } from '../../api/parse';
|
|
8
8
|
import { html } from 'typed-dom/dom';
|
|
9
9
|
|
|
10
|
-
const opener = /(~{3,})(?:example\/(\S+))?(?!\S)([^\n]*)(?:$|\n)/y;
|
|
11
|
-
|
|
12
10
|
export const example: ExtensionParser.ExampleParser = recursion(Recursion.block, block(fmap(
|
|
13
|
-
fence(
|
|
11
|
+
fence(/(~{3,})(?:example\/(\S+))?(?!\S)([^\n]*)(?:$|\n)/y, 300),
|
|
14
12
|
// Bug: Type mismatch between outer and inner.
|
|
15
13
|
(nodes: List<Data<string>>, context) => {
|
|
16
14
|
const [body, overflow, closer, opener, delim, type = 'markdown', param] = unwrap(nodes);
|
|
@@ -40,7 +40,6 @@ describe('Unit: parser/block/extension/fig', () => {
|
|
|
40
40
|
assert.deepStrictEqual(inspect(parser('[$group-name]\n~~~table\n~~~\n'), ctx), [['<figure data-type="table" data-label="group-name" data-group="group"><figcaption><span class="figindex"></span><span class="figtext"></span></figcaption><div><table></table></div></figure>'], '']);
|
|
41
41
|
assert.deepStrictEqual(inspect(parser('[$group-name]\n> '), ctx), [['<figure data-type="quote" data-label="group-name" data-group="group"><figcaption><span class="figindex"></span><span class="figtext"></span></figcaption><div><blockquote></blockquote></div></figure>'], '']);
|
|
42
42
|
assert.deepStrictEqual(inspect(parser('[$group-name]\n> \n'), ctx), [['<figure data-type="quote" data-label="group-name" data-group="group"><figcaption><span class="figindex"></span><span class="figtext"></span></figcaption><div><blockquote></blockquote></div></figure>'], '']);
|
|
43
|
-
assert.deepStrictEqual(inspect(parser('[$group-name]\n>\n~~~'), ctx), [['<figure data-type="quote" data-label="group-name" data-group="group"><figcaption><span class="figindex"></span><span class="figtext"></span></figcaption><div><blockquote><pre><br>~~~</pre></blockquote></div></figure>'], '']);
|
|
44
43
|
assert.deepStrictEqual(inspect(parser('[$group-name]\n!> *a*'), ctx), [['<figure data-type="quote" data-label="group-name" data-group="group"><figcaption><span class="figindex"></span><span class="figtext"></span></figcaption><div><blockquote><section><p><em>a</em></p><h2>References</h2><ol class="references"></ol></section></blockquote></div></figure>'], '']);
|
|
45
44
|
assert.deepStrictEqual(inspect(parser('[$group-name]\n![]{https://host}'), ctx), [['<figure data-type="media" data-label="group-name" data-group="group"><figcaption><span class="figindex"></span><span class="figtext"></span></figcaption><div><a href="https://host" target="_blank"><img class="media" data-src="https://host" alt="https://host"></a></div></figure>'], '']);
|
|
46
45
|
assert.deepStrictEqual(inspect(parser('[$group-name]\n![]{https://host}\n'), ctx), [['<figure data-type="media" data-label="group-name" data-group="group"><figcaption><span class="figindex"></span><span class="figtext"></span></figcaption><div><a href="https://host" target="_blank"><img class="media" data-src="https://host" alt="https://host"></a></div></figure>'], '']);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ExtensionParser } from '../../block';
|
|
2
|
-
import { union, sequence, some, block, line,
|
|
2
|
+
import { union, sequence, some, block, line, verify, rewrite, close, convert } from '../../../combinator';
|
|
3
3
|
import { contentline } from '../../source';
|
|
4
4
|
import { figure } from './figure';
|
|
5
5
|
import { segment as seg_label } from '../../inline/extension/label';
|
|
@@ -12,9 +12,9 @@ import { media, lineshortmedia } from '../../inline';
|
|
|
12
12
|
|
|
13
13
|
import FigParser = ExtensionParser.FigParser;
|
|
14
14
|
|
|
15
|
-
export const segment: FigParser.SegmentParser = block(
|
|
15
|
+
export const segment: FigParser.SegmentParser = block(
|
|
16
16
|
sequence([
|
|
17
|
-
line(close(seg_label, /(
|
|
17
|
+
line(close(seg_label, /(?!\S).*\n/y)),
|
|
18
18
|
union([
|
|
19
19
|
seg_code,
|
|
20
20
|
seg_math,
|
|
@@ -23,12 +23,12 @@ export const segment: FigParser.SegmentParser = block(validate(/\[?\$/y,
|
|
|
23
23
|
seg_placeholder,
|
|
24
24
|
some(contentline),
|
|
25
25
|
]),
|
|
26
|
-
]))
|
|
26
|
+
]));
|
|
27
27
|
|
|
28
28
|
export const fig: FigParser = block(rewrite(segment, verify(convert(
|
|
29
29
|
(source, context) => {
|
|
30
30
|
// Bug: TypeScript
|
|
31
|
-
const fence = (/^[^\n]*\n
|
|
31
|
+
const fence = (/^[^\n]*\n!?>+ /.test(source) && source.match(/^~{3,}(?=[^\S\n]*$)/mg) as string[] || [])
|
|
32
32
|
.reduce((max, fence) => fence > max ? fence : max, '~~') + '~';
|
|
33
33
|
return parser({ context })
|
|
34
34
|
? `${fence}figure ${source.replace(/^(.+\n.+\n)([\S\s]+?)\n?$/, '$1\n$2')}\n${fence}`
|
|
@@ -39,7 +39,7 @@ export const fig: FigParser = block(rewrite(segment, verify(convert(
|
|
|
39
39
|
([{ value: el }]) => el.tagName === 'FIGURE')));
|
|
40
40
|
|
|
41
41
|
const parser = sequence([
|
|
42
|
-
line(close(seg_label, /(
|
|
42
|
+
line(close(seg_label, /(?!\S).*\n/y)),
|
|
43
43
|
line(union([
|
|
44
44
|
media,
|
|
45
45
|
lineshortmedia,
|
|
@@ -5,7 +5,7 @@ import { label } from '../../inline/extension/label';
|
|
|
5
5
|
import { html } from 'typed-dom/dom';
|
|
6
6
|
|
|
7
7
|
export const figbase: ExtensionParser.FigbaseParser = block(fmap(
|
|
8
|
-
validate(/\[?\$-(?:[0-9]+\.)*0\]?
|
|
8
|
+
validate(/\[?\$-(?:[0-9]+\.)*0\]?(?:$|[ \n])/y,
|
|
9
9
|
line(union([label]))),
|
|
10
10
|
([{ value: el }]) => {
|
|
11
11
|
const label = el.getAttribute('data-label')!;
|
|
@@ -71,7 +71,7 @@ describe('Unit: parser/block/extension/figure', () => {
|
|
|
71
71
|
assert.deepStrictEqual(inspect(parser('~~~figure [$-0.0]\n$$\n\n$$\n~~~'), ctx), [['<figure data-type="math" data-label="$-0.0" data-group="$" class="invalid"><figcaption><span class="figindex"></span><span class="figtext"></span></figcaption><div><div class="math" translate="no">$$\n\n$$</div></div></figure>'], '']);
|
|
72
72
|
assert.deepStrictEqual(inspect(parser('~~~figure [$-name]\n!https://host\n~~~'), ctx), [['<figure data-type="media" data-label="$-name" data-group="$" class="invalid"><figcaption><span class="figindex"></span><span class="figtext"></span></figcaption><div><a href="https://host" target="_blank"><img class="media" data-src="https://host" alt="https://host"></a></div></figure>'], '']);
|
|
73
73
|
assert.deepStrictEqual(inspect(parser('~~~figure [$-name]\n$$\n\n$$\n\ncaption\n~~~'), ctx), [['<figure data-type="math" data-label="$-name" data-group="$" class="invalid"><figcaption><span class="figindex"></span><span class="figtext">caption</span></figcaption><div><div class="math" translate="no">$$\n\n$$</div></div></figure>'], '']);
|
|
74
|
-
assert.deepStrictEqual(inspect(parser(`~~~figure [$group-name]\n${'
|
|
74
|
+
assert.deepStrictEqual(inspect(parser(`~~~figure [$group-name]\n${'> \n'.repeat(500)}\n~~~`), ctx, '>'), [['<figure data-type="quote" data-label="group-name" data-group="group">'], '']);
|
|
75
75
|
assert.deepStrictEqual(inspect(parser(`~~~figure [$group-name]\n~~~\n0${'\n'.repeat(300)}~~~\n~~~`), ctx, '>'), [['<figure data-type="example" data-label="group-name" data-group="group">'], '']);
|
|
76
76
|
});
|
|
77
77
|
|
|
@@ -21,7 +21,7 @@ import { html, defrag } from 'typed-dom/dom';
|
|
|
21
21
|
import FigureParser = ExtensionParser.FigureParser;
|
|
22
22
|
|
|
23
23
|
export const segment: FigureParser.SegmentParser = block(match(
|
|
24
|
-
/(~{3,})(?:figure
|
|
24
|
+
/(~{3,})(?:figure )?(?=\[?\$)/y,
|
|
25
25
|
memoize(
|
|
26
26
|
([, fence], closer = new RegExp(String.raw`${fence}[^\S\n]*(?:$|\n)`, 'y')) => close(
|
|
27
27
|
sequence([
|
|
@@ -44,12 +44,12 @@ export const segment: FigureParser.SegmentParser = block(match(
|
|
|
44
44
|
]),
|
|
45
45
|
]),
|
|
46
46
|
closer),
|
|
47
|
-
([, fence]) => fence.length
|
|
47
|
+
([, fence]) => fence.length - 1, [], 2 ** 4 - 1)));
|
|
48
48
|
|
|
49
49
|
export const figure: FigureParser = block(fallback(rewrite(segment, fmap(
|
|
50
50
|
convert(source => source.slice(source.match(/^~+(?:\w+\s+)?/)![0].length, source.trimEnd().lastIndexOf('\n')),
|
|
51
51
|
sequence([
|
|
52
|
-
line(sequence([label, str(/(
|
|
52
|
+
line(sequence([label, str(/(?!\S).*\n/y)])),
|
|
53
53
|
inits([
|
|
54
54
|
block(union([
|
|
55
55
|
ulist,
|
|
@@ -83,7 +83,7 @@ export const figure: FigureParser = block(fallback(rewrite(segment, fmap(
|
|
|
83
83
|
]);
|
|
84
84
|
})),
|
|
85
85
|
fmap(
|
|
86
|
-
fence(/(~{3,})(?:figure|\[
|
|
86
|
+
fence(/(~{3,})(?:figure(?=$|[ \n])|\[?\$)[^\n]*(?:$|\n)/y, 300),
|
|
87
87
|
(nodes, context) => {
|
|
88
88
|
const [body, overflow, closer, opener, delim] = unwrap<string>(nodes);
|
|
89
89
|
const violation =
|
|
@@ -95,11 +95,11 @@ export const figure: FigureParser = block(fallback(rewrite(segment, fmap(
|
|
|
95
95
|
'fence',
|
|
96
96
|
`Invalid trailing line after the closing delimiter "${delim}"`,
|
|
97
97
|
] ||
|
|
98
|
-
!seg_label(subinput(opener.match(/^~+(?:figure
|
|
98
|
+
!seg_label(subinput(opener.match(/^~+(?:figure )?(\[?\$\S+)/)?.[1] ?? '', context)) && [
|
|
99
99
|
'label',
|
|
100
100
|
'Invalid label',
|
|
101
101
|
] ||
|
|
102
|
-
/^~+(?:figure
|
|
102
|
+
/^~+(?:figure )?(\[?\$\S+)[^\S\n]+\S/.test(opener) && [
|
|
103
103
|
'argument',
|
|
104
104
|
'Invalid argument',
|
|
105
105
|
] ||
|
|
@@ -20,7 +20,7 @@ import { html } from 'typed-dom/dom';
|
|
|
20
20
|
import MessageParser = ExtensionParser.MessageParser;
|
|
21
21
|
|
|
22
22
|
export const message: MessageParser = block(fmap(
|
|
23
|
-
fence(/(~{3,})message\/(\S+)([^\n]*)(?:$|\n)/y, 300),
|
|
23
|
+
fence(/(~{3,})message\/(\S+)(?!\S)([^\n]*)(?:$|\n)/y, 300),
|
|
24
24
|
// Bug: Type mismatch between outer and inner.
|
|
25
25
|
(nodes: List<Data<string>>, context) => {
|
|
26
26
|
const [body, overflow, closer, opener, delim, type, param] = unwrap(nodes);
|
|
@@ -83,10 +83,10 @@ const align: AlignParser = line(fmap(
|
|
|
83
83
|
union([str(alignment)]),
|
|
84
84
|
([{ value }]) => new List([new Data(value.split('/').map(s => s.split('')) as [string[], string[]?])])));
|
|
85
85
|
|
|
86
|
-
const delimiter = /[-=<>]+(?:\/[-=^v]*)?(?=[^\S\n]*\n)|[#:](?:(?!:\D|0)\d*:(?!0)\d*)?(?:!+[+]?)?(
|
|
86
|
+
const delimiter = /[-=<>]+(?:\/[-=^v]*)?(?=[^\S\n]*\n)|[#:](?:(?!:\D|0)\d*:(?!0)\d*)?(?:!+[+]?)?(?=[ \n])/y;
|
|
87
87
|
|
|
88
88
|
const head: CellParser.HeadParser = block(fmap(open(
|
|
89
|
-
str(/#(?:(?!:\D|0)\d*:(?!0)\d*)?(?:!+[+]?)?(
|
|
89
|
+
str(/#(?:(?!:\D|0)\d*:(?!0)\d*)?(?:!+[+]?)?(?=[ \n])/y),
|
|
90
90
|
rewrite(
|
|
91
91
|
inits([
|
|
92
92
|
anyline,
|
|
@@ -108,7 +108,7 @@ const head: CellParser.HeadParser = block(fmap(open(
|
|
|
108
108
|
false);
|
|
109
109
|
|
|
110
110
|
const data: CellParser.DataParser = block(fmap(open(
|
|
111
|
-
str(/:(?:(?!:\D|0)\d*:(?!0)\d*)?(?:!+[+]?)?(
|
|
111
|
+
str(/:(?:(?!:\D|0)\d*:(?!0)\d*)?(?:!+[+]?)?(?=[ \n])/y),
|
|
112
112
|
rewrite(
|
|
113
113
|
inits([
|
|
114
114
|
anyline,
|
|
@@ -133,7 +133,7 @@ const dataline: CellParser.DatalineParser = line(
|
|
|
133
133
|
rewrite(
|
|
134
134
|
contentline,
|
|
135
135
|
union([
|
|
136
|
-
validate(
|
|
136
|
+
validate(/!+ /y, convert(source => `:${source}`, data, false)),
|
|
137
137
|
convert(source => `: ${source}`, data, false),
|
|
138
138
|
])));
|
|
139
139
|
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { HeadingParser } from '../block';
|
|
2
2
|
import { State } from '../context';
|
|
3
3
|
import { List, Data } from '../../combinator/data/parser';
|
|
4
|
-
import { union, some, state, block, line,
|
|
4
|
+
import { union, some, state, block, line, focus, rewrite, open, fmap } from '../../combinator';
|
|
5
5
|
import { inline, indexee, indexer, dataindex } from '../inline';
|
|
6
6
|
import { str } from '../source';
|
|
7
7
|
import { visualize, trimBlank } from '../visibility';
|
|
8
8
|
import { unwrap, invalid } from '../util';
|
|
9
9
|
import { html, defrag } from 'typed-dom/dom';
|
|
10
10
|
|
|
11
|
-
export const segment: HeadingParser.SegmentParser = block(
|
|
12
|
-
/#+
|
|
13
|
-
some(line(({ context: { source } }) => new List([new Data(source)])))))
|
|
11
|
+
export const segment: HeadingParser.SegmentParser = block(focus(
|
|
12
|
+
/#+ +\S[^\n]*(?:\n#+(?=$|[ \n])[^\n]*)*(?:$|\n)/y,
|
|
13
|
+
some(line(({ context: { source } }) => new List([new Data(source)])))));
|
|
14
14
|
|
|
15
15
|
export const heading: HeadingParser = block(rewrite(segment,
|
|
16
16
|
// その他の表示制御は各所のCSSで行う。
|
|
@@ -17,7 +17,7 @@ export const cite: ReplyParser.CiteParser = line(fmap(
|
|
|
17
17
|
// リンクの実装は後で検討
|
|
18
18
|
focus(/>>#\S*(?=\s*$)/y, ({ context: { source } }) => new List([new Data(html('a', { class: 'anchor' }, source))])),
|
|
19
19
|
focus(/>>https?:\/\/\S+(?=\s*$)/y, ({ context: { source } }) => new List([new Data(html('a', { class: 'anchor', href: source.slice(2).trimEnd(), target: '_blank' }, source))])),
|
|
20
|
-
focus(
|
|
20
|
+
focus(/>>\S+(?=\s*$)/y, ({ context: { source } }) => new List([new Data(source)])),
|
|
21
21
|
])),
|
|
22
22
|
nodes => {
|
|
23
23
|
const quotes = nodes.head!.value as string;
|
|
@@ -7,14 +7,14 @@ import { linebreak, unescsource, anyline } from '../../source';
|
|
|
7
7
|
import { unwrap } from '../../util';
|
|
8
8
|
import { html, defrag } from 'typed-dom/dom';
|
|
9
9
|
|
|
10
|
-
export const syntax = />+
|
|
10
|
+
export const syntax = />+ /y;
|
|
11
11
|
|
|
12
12
|
export const quote: ReplyParser.QuoteParser = lazy(() => block(fmap(
|
|
13
13
|
rewrite(
|
|
14
14
|
some(validate(syntax, anyline)),
|
|
15
15
|
convert(
|
|
16
16
|
// TODO: インデント数を渡してインデント数前の行頭確認を行う実装に置き換える
|
|
17
|
-
source => source.replace(/(?<=^>+
|
|
17
|
+
source => source.replace(/(?<=^>+ )/mg, '\r'),
|
|
18
18
|
some(union([
|
|
19
19
|
// quote補助関数が残した数式をパースする。
|
|
20
20
|
math,
|
|
@@ -39,9 +39,7 @@ describe('Unit: parser/block/sidefence', () => {
|
|
|
39
39
|
assert.deepStrictEqual(inspect(parser('| a '), ctx), [['<blockquote class="invalid"><pre> a </pre></blockquote>'], '']);
|
|
40
40
|
assert.deepStrictEqual(inspect(parser('| \na'), ctx), undefined);
|
|
41
41
|
assert.deepStrictEqual(inspect(parser('|\na'), ctx), undefined);
|
|
42
|
-
assert.deepStrictEqual(inspect(parser('|\n a'), ctx), undefined);
|
|
43
|
-
assert.deepStrictEqual(inspect(parser('|\n|'), ctx), [['<blockquote class="invalid"><pre><br></pre></blockquote>'], '']);
|
|
44
|
-
assert.deepStrictEqual(inspect(parser('|\n| a'), ctx), [['<blockquote class="invalid"><pre><br>a</pre></blockquote>'], '']);
|
|
42
|
+
assert.deepStrictEqual(inspect(parser('|\n| a'), ctx), undefined);
|
|
45
43
|
assert.deepStrictEqual(inspect(parser('| http://host'), ctx), [['<blockquote class="invalid"><pre><a class="url" href="http://host" target="_blank">http://host</a></pre></blockquote>'], '']);
|
|
46
44
|
assert.deepStrictEqual(inspect(parser('| http://host)'), ctx), [['<blockquote class="invalid"><pre><a class="url" href="http://host)" target="_blank">http://host)</a></pre></blockquote>'], '']);
|
|
47
45
|
assert.deepStrictEqual(inspect(parser('| !http://host'), ctx), [['<blockquote class="invalid"><pre>!<a class="url" href="http://host" target="_blank">http://host</a></pre></blockquote>'], '']);
|
|
@@ -8,7 +8,7 @@ import { unwrap, invalid } from '../util';
|
|
|
8
8
|
import { html, define, defrag } from 'typed-dom/dom';
|
|
9
9
|
|
|
10
10
|
export const sidefence: SidefenceParser = lazy(() => block(fmap(focus(
|
|
11
|
-
|
|
11
|
+
/\|+ [^\n]*(?:\n\|+(?=$|[ \n])[^\n]*)*(?:$|\n)/y,
|
|
12
12
|
union([source])),
|
|
13
13
|
([{ value }]) => new List([
|
|
14
14
|
new Data(define(value, {
|
|
@@ -17,13 +17,13 @@ export const sidefence: SidefenceParser = lazy(() => block(fmap(focus(
|
|
|
17
17
|
})),
|
|
18
18
|
]))));
|
|
19
19
|
|
|
20
|
-
const opener = /(?=\|\|+(
|
|
21
|
-
const unindent = (source: string) => source.replace(/(?<=^|\n)\|(?:[
|
|
20
|
+
const opener = /(?=\|\|+(?:$|[ \n]))/y;
|
|
21
|
+
const unindent = (source: string) => source.replace(/(?<=^|\n)\|(?: |(?=\|*(?:$|[ \n])))|\n$/g, '');
|
|
22
22
|
|
|
23
23
|
const source: SidefenceParser.SourceParser = lazy(() => fmap(
|
|
24
24
|
some(recursion(Recursion.block, union([
|
|
25
25
|
focus(
|
|
26
|
-
/(?:\|\|+(
|
|
26
|
+
/(?:\|\|+(?=$|[ \n])[^\n]*(?:$|\n))+/y,
|
|
27
27
|
convert(unindent, source, false, true)),
|
|
28
28
|
rewrite(
|
|
29
29
|
some(contentline, opener),
|
|
@@ -28,7 +28,7 @@ export const table: TableParser = lazy(() => block(fmap(validate(
|
|
|
28
28
|
]))));
|
|
29
29
|
|
|
30
30
|
const row = <P extends CellParser | AlignParser>(parser: P, optional: boolean): RowParser<P> => fallback(fmap(
|
|
31
|
-
line(surround(/(?=\|)/y, some(union([parser])),
|
|
31
|
+
line(surround(/(?=\|)/y, some(union([parser])), /\|?\s*$/y, optional)),
|
|
32
32
|
ns => new List([new Data(html('tr', unwrap(ns)))])),
|
|
33
33
|
rewrite(contentline, ({ context: { source } }) => new List([
|
|
34
34
|
new Data(html('tr', {
|
|
@@ -53,7 +53,7 @@ const cell: CellParser = surround(
|
|
|
53
53
|
close(medialink, /\s*(?=\||$)/y),
|
|
54
54
|
close(media, /\s*(?=\||$)/y),
|
|
55
55
|
close(shortmedia, /\s*(?=\||$)/y),
|
|
56
|
-
trimBlank(some(inline, /\|/y, [[
|
|
56
|
+
trimBlank(some(inline, /\|/y, [[/\|?\s*$/y, 9]])),
|
|
57
57
|
]),
|
|
58
58
|
/[^|]*/y, true);
|
|
59
59
|
|
package/src/parser/block.ts
CHANGED
|
@@ -114,7 +114,7 @@ export const block: BlockParser = reset(
|
|
|
114
114
|
]) as any));
|
|
115
115
|
|
|
116
116
|
function error(parser: BlockParser): BlockParser {
|
|
117
|
-
const reg = new RegExp(String.raw`^${Command.Error}
|
|
117
|
+
const reg = new RegExp(String.raw`^${Command.Error}[^\n]*\n`)
|
|
118
118
|
return recover<BlockParser>(fallback(
|
|
119
119
|
open(Command.Error, ({ context: { source, position } }) => { throw new Error(source.slice(position).split('\n', 1)[0]); }),
|
|
120
120
|
parser),
|