securemark 0.294.4 → 0.294.5
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 +4 -0
- package/dist/index.js +140 -89
- 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/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/hashtag.ts +10 -8
- 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/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.ts +2 -0
- package/src/parser/source/text.ts +8 -4
- package/src/parser/util.ts +1 -1
- package/src/parser/visibility.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "securemark",
|
|
3
|
-
"version": "0.294.
|
|
3
|
+
"version": "0.294.5",
|
|
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
|
|
@@ -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),
|
package/src/parser/header.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { normalize } from './api/normalize';
|
|
|
8
8
|
import { html, defrag } from 'typed-dom/dom';
|
|
9
9
|
|
|
10
10
|
export const header: MarkdownParser.HeaderParser = lazy(() => validate(
|
|
11
|
-
/---+
|
|
11
|
+
/---+ *\r?\n(?=\S)/y,
|
|
12
12
|
inits([
|
|
13
13
|
rewrite(
|
|
14
14
|
({ context }) => {
|
|
@@ -24,7 +24,7 @@ export const header: MarkdownParser.HeaderParser = lazy(() => validate(
|
|
|
24
24
|
block(
|
|
25
25
|
union([
|
|
26
26
|
validate(({ context }) => context.header ?? true,
|
|
27
|
-
focus(/(---+)
|
|
27
|
+
focus(/(---+) *\r?\n(?:[A-Za-z][0-9A-Za-z]*(?:-[0-9A-Za-z]+)*:[ \t]+\S[^\r\n]*\r?\n){1,100}\1 *(?:$|\r?\n)/y,
|
|
28
28
|
convert(source =>
|
|
29
29
|
normalize(source.slice(source.indexOf('\n') + 1, source.trimEnd().lastIndexOf('\n'))).replace(/(\S)\s+$/mg, '$1'),
|
|
30
30
|
fmap(
|
|
@@ -48,7 +48,7 @@ export const header: MarkdownParser.HeaderParser = lazy(() => validate(
|
|
|
48
48
|
]);
|
|
49
49
|
},
|
|
50
50
|
]))),
|
|
51
|
-
clear(str(/
|
|
51
|
+
clear(str(/ *\r?\n/y)),
|
|
52
52
|
])));
|
|
53
53
|
|
|
54
54
|
const field: MarkdownParser.HeaderParser.FieldParser = line(({ context: { source, position } }) => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AutolinkParser } from '../../inline';
|
|
2
2
|
import { State, Backtrack } from '../../context';
|
|
3
3
|
import { List, Data } from '../../../combinator/data/parser';
|
|
4
|
-
import { union,
|
|
4
|
+
import { union, state, constraint, rewrite, surround, convert, fmap, lazy } from '../../../combinator';
|
|
5
5
|
import { unsafelink } from '../link';
|
|
6
6
|
import { str } from '../../source';
|
|
7
7
|
import { define } from 'typed-dom/dom';
|
|
@@ -9,13 +9,11 @@ import { define } from 'typed-dom/dom';
|
|
|
9
9
|
// https://example/@user must be a user page or a redirect page going there.
|
|
10
10
|
|
|
11
11
|
export const account: AutolinkParser.AccountParser = lazy(() => rewrite(
|
|
12
|
-
|
|
12
|
+
surround(
|
|
13
13
|
/(?<![0-9a-z])@/yi,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
]),
|
|
18
|
-
false,
|
|
14
|
+
str(/[0-9a-z](?:(?:[0-9a-z]|-(?=[0-9a-z])){0,61}[0-9a-z])?(?:\.[0-9a-z](?:(?:[0-9a-z]|-(?=[0-9a-z])){0,61}[0-9a-z])?)*\//yi),
|
|
15
|
+
str(/[a-z][0-9a-z]*(?:[-.][0-9a-z]+)*(?![-.]?[0-9a-z@#]|>>|:\S)/yi),
|
|
16
|
+
true, undefined, undefined,
|
|
19
17
|
[3 | Backtrack.autolink]),
|
|
20
18
|
constraint(State.autolink, state(State.autolink, fmap(convert(
|
|
21
19
|
source =>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AutolinkParser } from '../../inline';
|
|
2
2
|
import { State, Backtrack } from '../../context';
|
|
3
3
|
import { List, Data } from '../../../combinator/data/parser';
|
|
4
|
-
import { union,
|
|
4
|
+
import { union, sequence, some, state, constraint, verify, rewrite, surround, convert, fmap, lazy } from '../../../combinator';
|
|
5
5
|
import { unsafelink } from '../link';
|
|
6
6
|
import { emoji } from './hashtag';
|
|
7
7
|
import { str } from '../../source';
|
|
@@ -11,23 +11,23 @@ import { define } from 'typed-dom/dom';
|
|
|
11
11
|
|
|
12
12
|
export const channel: AutolinkParser.ChannelParser = lazy(() => rewrite(
|
|
13
13
|
sequence([
|
|
14
|
-
|
|
14
|
+
surround(
|
|
15
15
|
/(?<![0-9a-z])@/yi,
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
]),
|
|
20
|
-
false,
|
|
16
|
+
str(/[0-9a-z](?:(?:[0-9a-z]|-(?=[0-9a-z])){0,61}[0-9a-z])?(?:\.[0-9a-z](?:(?:[0-9a-z]|-(?=[0-9a-z])){0,61}[0-9a-z])?)*\//yi),
|
|
17
|
+
str(/[a-z][0-9a-z]*(?:[-.][0-9a-z]+)*(?![-.]?[0-9a-z@]|>>|:\S)/yi),
|
|
18
|
+
true, undefined, undefined,
|
|
21
19
|
[3 | Backtrack.autolink]),
|
|
22
|
-
some(
|
|
20
|
+
some(verify(surround(
|
|
23
21
|
'#',
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
([
|
|
29
|
-
|
|
30
|
-
|
|
22
|
+
str(new RegExp([
|
|
23
|
+
/(?!['_])(?:[^\p{C}\p{S}\p{P}\s]|emoji|'(?=[0-9A-Za-z])|_(?=[^\p{C}\p{S}\p{P}\s]|emoji))+/yu.source,
|
|
24
|
+
].join('').replace(/emoji/g, emoji), 'yu')),
|
|
25
|
+
str(new RegExp([
|
|
26
|
+
/(?![0-9a-z@]|>>|:\S|[^\p{C}\p{S}\p{P}\s]|emoji)/yu.source,
|
|
27
|
+
].join('').replace(/emoji/g, emoji), 'yu')),
|
|
28
|
+
false, undefined, undefined,
|
|
29
|
+
[3 | Backtrack.autolink]),
|
|
30
|
+
([{ value }]) => !/^[0-9]{1,4}$|^[0-9]{5}/.test(value as string))),
|
|
31
31
|
]),
|
|
32
32
|
constraint(State.autolink, state(State.autolink, fmap(convert(
|
|
33
33
|
source =>
|