securemark 0.219.4 → 0.223.0
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 +18 -0
- package/dist/securemark.js +724 -578
- package/package-lock.json +389 -864
- package/package.json +12 -12
- package/src/combinator/control/manipulation/fallback.ts +8 -0
- package/src/combinator/control/manipulation/fence.ts +1 -1
- package/src/combinator/control/manipulation/indent.test.ts +0 -1
- package/src/combinator/control/manipulation/indent.ts +1 -1
- package/src/combinator/control/manipulation/recovery.ts +2 -2
- package/src/combinator.ts +1 -0
- package/src/parser/api/normalize.test.ts +9 -6
- package/src/parser/api/normalize.ts +30 -29
- package/src/parser/block/ilist.ts +4 -2
- package/src/parser/block/olist.test.ts +9 -8
- package/src/parser/block/olist.ts +21 -4
- package/src/parser/block/paragraph/mention/cite.ts +3 -3
- package/src/parser/block/table.test.ts +1 -1
- package/src/parser/block/table.ts +19 -9
- package/src/parser/block/ulist.test.ts +6 -5
- package/src/parser/block/ulist.ts +13 -2
- package/src/parser/block.ts +5 -7
- package/src/parser/inline/annotation.test.ts +2 -2
- package/src/parser/inline/annotation.ts +4 -4
- package/src/parser/inline/emphasis.ts +2 -2
- package/src/parser/inline/emstrong.ts +4 -4
- package/src/parser/inline/extension/index.ts +2 -2
- package/src/parser/inline/html.test.ts +13 -13
- package/src/parser/inline/html.ts +4 -4
- package/src/parser/inline/link.test.ts +5 -5
- package/src/parser/inline/link.ts +4 -4
- package/src/parser/inline/mark.ts +2 -2
- package/src/parser/inline/math.ts +2 -2
- package/src/parser/inline/media.test.ts +3 -2
- package/src/parser/inline/media.ts +5 -6
- package/src/parser/inline/reference.test.ts +2 -2
- package/src/parser/inline/reference.ts +4 -4
- package/src/parser/inline/ruby.ts +2 -2
- package/src/parser/inline/strong.ts +2 -2
- package/src/parser/util.ts +56 -9
- package/src/renderer/render/math.ts +1 -1
- package/tsconfig.json +6 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "securemark",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.223.0",
|
|
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",
|
|
@@ -31,38 +31,38 @@
|
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@types/dompurify": "2.3.1",
|
|
34
|
-
"@types/jquery": "3.5.
|
|
34
|
+
"@types/jquery": "3.5.9",
|
|
35
35
|
"@types/mathjax": "0.0.37",
|
|
36
36
|
"@types/mocha": "9.0.0",
|
|
37
|
-
"@types/power-assert": "1.5.
|
|
37
|
+
"@types/power-assert": "1.5.8",
|
|
38
38
|
"@types/prismjs": "1.16.6",
|
|
39
39
|
"browserify": "^17.0.0",
|
|
40
40
|
"browserify-shim": "^3.8.14",
|
|
41
|
-
"concurrently": "^6.
|
|
41
|
+
"concurrently": "^6.4.0",
|
|
42
42
|
"del": "^6.0.0",
|
|
43
43
|
"gulp": "^4.0.2",
|
|
44
44
|
"gulp-derequire": "^3.0.0",
|
|
45
|
-
"gulp-footer": "^2.0
|
|
45
|
+
"gulp-footer": "^2.1.0",
|
|
46
46
|
"gulp-header": "^2.0.9",
|
|
47
47
|
"gulp-load-plugins": "^2.0.7",
|
|
48
48
|
"gulp-mocha": "^8.0.0",
|
|
49
49
|
"gulp-rename": "^2.0.0",
|
|
50
50
|
"gulp-unassert": "^2.0.0",
|
|
51
|
-
"karma": "^6.3.
|
|
51
|
+
"karma": "^6.3.9",
|
|
52
52
|
"karma-chrome-launcher": "^3.1.0",
|
|
53
53
|
"karma-coverage-istanbul-instrumenter": "^1.0.3",
|
|
54
54
|
"karma-coverage-istanbul-reporter": "^3.0.3",
|
|
55
55
|
"karma-espower-preprocessor": "^1.2.0",
|
|
56
|
-
"karma-firefox-launcher": "^2.1.
|
|
56
|
+
"karma-firefox-launcher": "^2.1.2",
|
|
57
57
|
"karma-mocha": "^2.0.1",
|
|
58
|
-
"mocha": "^9.1.
|
|
59
|
-
"npm-check-updates": "^
|
|
58
|
+
"mocha": "^9.1.3",
|
|
59
|
+
"npm-check-updates": "^12.0.2",
|
|
60
60
|
"power-assert": "^1.6.1",
|
|
61
61
|
"semver": "^7.3.5",
|
|
62
|
-
"spica": "0.0.
|
|
62
|
+
"spica": "0.0.492",
|
|
63
63
|
"tsify": "^5.0.4",
|
|
64
|
-
"typed-dom": "0.0.
|
|
65
|
-
"typescript": "4.
|
|
64
|
+
"typed-dom": "0.0.247",
|
|
65
|
+
"typescript": "4.5.2",
|
|
66
66
|
"vinyl-buffer": "^1.0.1",
|
|
67
67
|
"vinyl-source-stream": "^2.0.0"
|
|
68
68
|
},
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Parser, Tree, Context } from '../../data/parser';
|
|
2
|
+
import { union } from '../../data/parser/union';
|
|
3
|
+
|
|
4
|
+
export function fallback<P extends Parser<unknown>>(parser: P, otherwise: Parser<Tree<P>, Context<P>>): P;
|
|
5
|
+
export function fallback<T, P extends Parser<T>>(parser: P, otherwise: Parser<T, Context<P>>): P;
|
|
6
|
+
export function fallback<T>(parser: Parser<T>, otherwise: Parser<T>): Parser<T> {
|
|
7
|
+
return union([parser, otherwise]);
|
|
8
|
+
}
|
|
@@ -2,7 +2,7 @@ import { Parser, Ctx } from '../../data/parser';
|
|
|
2
2
|
import { firstline, isEmpty } from '../constraint/line';
|
|
3
3
|
import { unshift } from 'spica/array';
|
|
4
4
|
|
|
5
|
-
export function fence<D extends Parser<unknown, C>[]
|
|
5
|
+
export function fence<C extends Ctx, D extends Parser<unknown, C>[]>(opener: RegExp, limit: number, separation: boolean = true): Parser<string, C, D> {
|
|
6
6
|
return source => {
|
|
7
7
|
if (source === '') return;
|
|
8
8
|
const matches = source.match(opener);
|
|
@@ -16,7 +16,6 @@ describe('Unit: combinator/indent', () => {
|
|
|
16
16
|
assert.deepStrictEqual(inspect(parser(' a\n a')), [['a'], ' a']);
|
|
17
17
|
assert.deepStrictEqual(inspect(parser(' \ta')), [['\ta'], '']);
|
|
18
18
|
assert.deepStrictEqual(inspect(parser('\ta')), [['a'], '']);
|
|
19
|
-
assert.deepStrictEqual(inspect(parser(' a')), [['a'], '']);
|
|
20
19
|
});
|
|
21
20
|
|
|
22
21
|
});
|
|
@@ -12,7 +12,7 @@ export function indent<P extends Parser<unknown>>(parser: P): P;
|
|
|
12
12
|
export function indent<T>(parser: Parser<T>): Parser<T> {
|
|
13
13
|
assert(parser);
|
|
14
14
|
return bind(match(
|
|
15
|
-
/^(?=(([ \t
|
|
15
|
+
/^(?=(([ \t])\2*))/,
|
|
16
16
|
reduce<string[], Parser<string>, string>(
|
|
17
17
|
([, indent]) =>
|
|
18
18
|
some(line(open(indent, source => [[unline(source)], '']))),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Parser, Result, Ctx } from '../../data/parser';
|
|
1
|
+
import { Parser, Result, Ctx, Tree, Context } from '../../data/parser';
|
|
2
2
|
|
|
3
|
-
export function recover<
|
|
3
|
+
export function recover<P extends Parser<unknown>>(parser: P, fallback: (source: string, context: Context<P>, reason: unknown) => Result<Tree<P>>): P;
|
|
4
4
|
export function recover<T>(parser: Parser<T>, fallback: (source: string, context: Ctx, reason: unknown) => Result<T>): Parser<T> {
|
|
5
5
|
return (source, context) => {
|
|
6
6
|
try {
|
package/src/combinator.ts
CHANGED
|
@@ -18,6 +18,7 @@ export * from './combinator/control/manipulation/convert';
|
|
|
18
18
|
export * from './combinator/control/manipulation/trim';
|
|
19
19
|
export * from './combinator/control/manipulation/duplicate';
|
|
20
20
|
export * from './combinator/control/manipulation/reverse';
|
|
21
|
+
export * from './combinator/control/manipulation/fallback';
|
|
21
22
|
export * from './combinator/control/manipulation/recovery';
|
|
22
23
|
export * from './combinator/control/manipulation/lazy';
|
|
23
24
|
export * from './combinator/control/monad/fmap';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { normalize } from './normalize';
|
|
1
|
+
import { normalize, escape } from './normalize';
|
|
2
2
|
|
|
3
3
|
describe('Unit: parser/normalize', () => {
|
|
4
4
|
describe('normalize', () => {
|
|
@@ -45,11 +45,6 @@ describe('Unit: parser/normalize', () => {
|
|
|
45
45
|
assert(normalize('\x7F') === '\uFFFD');
|
|
46
46
|
});
|
|
47
47
|
|
|
48
|
-
it('escape', () => {
|
|
49
|
-
assert(normalize('\u200B') === '​');
|
|
50
|
-
assert(normalize('\u200D') === '‍');
|
|
51
|
-
});
|
|
52
|
-
|
|
53
48
|
it('sanitize', () => {
|
|
54
49
|
assert(normalize('\u2006') === '\uFFFD');
|
|
55
50
|
assert(normalize('\u202A') === '\uFFFD');
|
|
@@ -74,4 +69,12 @@ describe('Unit: parser/normalize', () => {
|
|
|
74
69
|
|
|
75
70
|
});
|
|
76
71
|
|
|
72
|
+
describe('escape', () => {
|
|
73
|
+
it('', () => {
|
|
74
|
+
assert(escape('\u200B') === '​');
|
|
75
|
+
assert(escape('\u200D') === '‍');
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
});
|
|
79
|
+
|
|
77
80
|
});
|
|
@@ -1,20 +1,41 @@
|
|
|
1
1
|
import { htmlentity } from '../inline/htmlentity';
|
|
2
2
|
import { eval } from '../../combinator/data/parser';
|
|
3
3
|
|
|
4
|
+
const UNICODE_REPLACEMENT_CHARACTER = '\uFFFD';
|
|
5
|
+
assert(UNICODE_REPLACEMENT_CHARACTER.trim());
|
|
6
|
+
|
|
7
|
+
export function normalize(source: string): string {
|
|
8
|
+
return format(sanitize(source));
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function format(source: string): string {
|
|
12
|
+
return source
|
|
13
|
+
.replace(/\r\n?/g, '\n');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function sanitize(source: string): string {
|
|
17
|
+
return source
|
|
18
|
+
.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]|[\u2006\u200B-\u200F\u202A-\u202F\u2060\uFEFF]|(^|[^\u1820\u1821])\u180E/g, `$1${UNICODE_REPLACEMENT_CHARACTER}`)
|
|
19
|
+
.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]?|[\uDC00-\uDFFF]/g, char =>
|
|
20
|
+
char.length === 1
|
|
21
|
+
? UNICODE_REPLACEMENT_CHARACTER
|
|
22
|
+
: char);
|
|
23
|
+
}
|
|
24
|
+
|
|
4
25
|
// https://dev.w3.org/html5/html-author/charref
|
|
5
26
|
// https://en.wikipedia.org/wiki/Whitespace_character
|
|
6
27
|
const unreadableHTMLEntityNames = [
|
|
7
28
|
//'Tab',
|
|
8
29
|
//'NewLine',
|
|
9
|
-
|
|
10
|
-
|
|
30
|
+
'NonBreakingSpace',
|
|
31
|
+
'nbsp',
|
|
11
32
|
'shy',
|
|
12
|
-
|
|
13
|
-
|
|
33
|
+
'ensp',
|
|
34
|
+
'emsp',
|
|
14
35
|
'emsp13',
|
|
15
36
|
'emsp14',
|
|
16
|
-
|
|
17
|
-
|
|
37
|
+
'numsp',
|
|
38
|
+
'puncsp',
|
|
18
39
|
'ThinSpace',
|
|
19
40
|
'thinsp',
|
|
20
41
|
'VeryThinSpace',
|
|
@@ -28,7 +49,7 @@ const unreadableHTMLEntityNames = [
|
|
|
28
49
|
'zwnj',
|
|
29
50
|
'lrm',
|
|
30
51
|
'rlm',
|
|
31
|
-
|
|
52
|
+
'MediumSpace',
|
|
32
53
|
'NoBreak',
|
|
33
54
|
'ApplyFunction',
|
|
34
55
|
'af',
|
|
@@ -78,30 +99,10 @@ const unreadableSpecialCharacters = [
|
|
|
78
99
|
// ZERO WIDTH NON-BREAKING SPACE
|
|
79
100
|
'\uFEFF',
|
|
80
101
|
];
|
|
81
|
-
|
|
82
|
-
const UNICODE_REPLACEMENT_CHARACTER = '\uFFFD';
|
|
83
|
-
assert(UNICODE_REPLACEMENT_CHARACTER.trim());
|
|
84
102
|
assert(unreadableSpecialCharacters.every(c => sanitize(c) === UNICODE_REPLACEMENT_CHARACTER));
|
|
85
103
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function format(source: string): string {
|
|
91
|
-
return source
|
|
92
|
-
.replace(/\r\n?/g, '\n');
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function sanitize(source: string): string {
|
|
96
|
-
return source
|
|
97
|
-
.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]|[\u2006\u200B-\u200F\u202A-\u202F\u2060\uFEFF]|(^|[^\u1820\u1821])\u180E/g, `$1${UNICODE_REPLACEMENT_CHARACTER}`)
|
|
98
|
-
.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]?|[\uDC00-\uDFFF]/g, char =>
|
|
99
|
-
char.length === 1
|
|
100
|
-
? UNICODE_REPLACEMENT_CHARACTER
|
|
101
|
-
: char);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function escape(source: string): string {
|
|
104
|
+
// 特殊不可視文字はエディタおよびソースビューアの等幅および強調表示により可視化する
|
|
105
|
+
export function escape(source: string): string {
|
|
105
106
|
return source
|
|
106
107
|
.replace(unreadableEscapableCharacter, char =>
|
|
107
108
|
`&${unreadableHTMLEntityNames[unreadableEscapableCharacters.indexOf(char)]};`);
|
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
import { IListParser } from '../block';
|
|
2
|
-
import { union, inits, some, block, line, validate, indent, context, creator, open, convert, trim, lazy, fmap } from '../../combinator';
|
|
2
|
+
import { union, inits, some, block, line, validate, indent, rewrite, context, creator, open, convert, trim, fallback, lazy, fmap } from '../../combinator';
|
|
3
3
|
import { ulist_, fillFirstLine } from './ulist';
|
|
4
4
|
import { olist_ } from './olist';
|
|
5
5
|
import { inline } from '../inline';
|
|
6
|
+
import { contentline } from '../source';
|
|
6
7
|
import { html, defrag } from 'typed-dom';
|
|
7
8
|
|
|
8
9
|
export const ilist: IListParser = lazy(() => block(fmap(validate(
|
|
9
10
|
/^[-+*](?=[^\S\n]|\n[^\S\n]*\S)/,
|
|
10
11
|
context({ syntax: { inline: { media: false } } },
|
|
11
12
|
some(creator(union([
|
|
12
|
-
fmap(
|
|
13
|
+
fmap(fallback(
|
|
13
14
|
inits([
|
|
14
15
|
line(open(/^[-+*](?:$|\s)/, trim(some(inline)), true)),
|
|
15
16
|
indent(union([ulist_, olist_, ilist_])),
|
|
16
17
|
]),
|
|
18
|
+
rewrite(contentline, source => [[html('span', source.replace('\n', ''))], ''])),
|
|
17
19
|
ns => [html('li', defrag(fillFirstLine(ns)))]),
|
|
18
20
|
]))))),
|
|
19
21
|
es => [
|
|
@@ -26,15 +26,8 @@ describe('Unit: parser/block/olist', () => {
|
|
|
26
26
|
assert.deepStrictEqual(inspect(parser('1.1.')), undefined);
|
|
27
27
|
assert.deepStrictEqual(inspect(parser('1.a')), undefined);
|
|
28
28
|
assert.deepStrictEqual(inspect(parser('1.\n')), undefined);
|
|
29
|
-
assert.deepStrictEqual(inspect(parser('1. a\n 1. a\n 1. a')), undefined);
|
|
30
|
-
assert.deepStrictEqual(inspect(parser('1. \n1 ')), undefined);
|
|
31
|
-
assert.deepStrictEqual(inspect(parser('1. \n1--')), undefined);
|
|
32
|
-
assert.deepStrictEqual(inspect(parser('1. \n1--. ')), undefined);
|
|
33
|
-
assert.deepStrictEqual(inspect(parser('1. !http://host')), [['<ol><li>!<a href="http://host" target="_blank">http://host</a></li></ol>'], '']);
|
|
34
29
|
assert.deepStrictEqual(inspect(parser('(1)')), undefined);
|
|
35
30
|
assert.deepStrictEqual(inspect(parser('(1)\n')), undefined);
|
|
36
|
-
assert.deepStrictEqual(inspect(parser('(1) \n(1)--')), undefined);
|
|
37
|
-
assert.deepStrictEqual(inspect(parser('(1) \n(1)-- ')), undefined);
|
|
38
31
|
assert.deepStrictEqual(inspect(parser('(I) ')), undefined);
|
|
39
32
|
assert.deepStrictEqual(inspect(parser('(A) ')), undefined);
|
|
40
33
|
assert.deepStrictEqual(inspect(parser(' 1.')), undefined);
|
|
@@ -69,6 +62,8 @@ describe('Unit: parser/block/olist', () => {
|
|
|
69
62
|
assert.deepStrictEqual(inspect(parser('(1) \n(1)')), [['<ol data-format="paren"><li></li><li></li></ol>'], '']);
|
|
70
63
|
// filled
|
|
71
64
|
assert.deepStrictEqual(inspect(parser('(1) \n(1) ')), [['<ol data-format="paren"><li></li><li></li></ol>'], '']);
|
|
65
|
+
// invalid
|
|
66
|
+
assert.deepStrictEqual(inspect(parser('0. \n0 ')), [['<ol><li></li><li><span class="invalid">0 </span></li></ol>'], '']);
|
|
72
67
|
});
|
|
73
68
|
|
|
74
69
|
it('nest', () => {
|
|
@@ -78,9 +73,11 @@ describe('Unit: parser/block/olist', () => {
|
|
|
78
73
|
assert.deepStrictEqual(inspect(parser('0. 1\n 0. ')), [['<ol><li>1<ol><li></li></ol></li></ol>'], '']);
|
|
79
74
|
assert.deepStrictEqual(inspect(parser('0. 1\n 0.\n')), [['<ol><li>1<ol><li></li></ol></li></ol>'], '']);
|
|
80
75
|
assert.deepStrictEqual(inspect(parser('0. 1\n 0. 2')), [['<ol><li>1<ol><li>2</li></ol></li></ol>'], '']);
|
|
76
|
+
assert.deepStrictEqual(inspect(parser('0. 1\n 0. 2\n0. 3')), [['<ol><li>1<ol><li>2</li></ol></li><li>3</li></ol>'], '']);
|
|
81
77
|
assert.deepStrictEqual(inspect(parser('0. 1\n 0. 2\n 0. 3')), [['<ol><li>1<ol><li>2</li><li>3</li></ol></li></ol>'], '']);
|
|
82
78
|
assert.deepStrictEqual(inspect(parser('0. 1\n 0. 2\n 0. 3')), [['<ol><li>1<ol><li>2<ol><li>3</li></ol></li></ol></li></ol>'], '']);
|
|
83
|
-
assert.deepStrictEqual(inspect(parser('0. 1\n
|
|
79
|
+
assert.deepStrictEqual(inspect(parser('0. 1\n 0. 2\n 0. 3')), [['<ol><li>1<ol><li>2</li></ol></li><li><span class="invalid"> 0. 3</span></li></ol>'], '']);
|
|
80
|
+
assert.deepStrictEqual(inspect(parser('0. !http://host')), [['<ol><li>!<a href="http://host" target="_blank">http://host</a></li></ol>'], '']);
|
|
84
81
|
});
|
|
85
82
|
|
|
86
83
|
it('index', () => {
|
|
@@ -102,6 +99,10 @@ describe('Unit: parser/block/olist', () => {
|
|
|
102
99
|
assert.deepStrictEqual(inspect(parser('(1)-1-1 ')), [['<ol data-format="paren"><li data-marker="(1)-1-1"></li></ol>'], '']);
|
|
103
100
|
assert.deepStrictEqual(inspect(parser('(1) \n(1)-')), [['<ol data-format="paren"><li></li><li data-marker="(1)-"></li></ol>'], '']);
|
|
104
101
|
assert.deepStrictEqual(inspect(parser('(1) \n(1)-1')), [['<ol data-format="paren"><li></li><li data-marker="(1)-1"></li></ol>'], '']);
|
|
102
|
+
assert.deepStrictEqual(inspect(parser('1. \n1--')), [['<ol><li></li><li><span class="invalid">1--</span></li></ol>'], '']);
|
|
103
|
+
assert.deepStrictEqual(inspect(parser('1. \n1--. ')), [['<ol><li></li><li><span class="invalid">1--. </span></li></ol>'], '']);
|
|
104
|
+
assert.deepStrictEqual(inspect(parser('(1) \n(1)--')), [['<ol data-format="paren"><li></li><li><span class="invalid">(1)--</span></li></ol>'], '']);
|
|
105
|
+
assert.deepStrictEqual(inspect(parser('(1) \n(1)-- ')), [['<ol data-format="paren"><li></li><li><span class="invalid">(1)-- </span></li></ol>'], '']);
|
|
105
106
|
});
|
|
106
107
|
|
|
107
108
|
it('type', () => {
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { undefined } from 'spica/global';
|
|
2
2
|
import { OListParser } from '../block';
|
|
3
|
-
import { union, inits, subsequence, some, block, line, indent, focus, context, creator, open, match, convert, trim, trimStart, lazy, fmap } from '../../combinator';
|
|
3
|
+
import { union, inits, subsequence, some, block, line, indent, focus, rewrite, context, creator, open, match, convert, trim, trimStart, fallback, lazy, fmap } from '../../combinator';
|
|
4
4
|
import { checkbox, ulist_, fillFirstLine } from './ulist';
|
|
5
5
|
import { ilist_ } from './ilist';
|
|
6
6
|
import { inline } from '../inline';
|
|
7
|
+
import { contentline } from '../source';
|
|
7
8
|
import { html, define, defrag } from 'typed-dom';
|
|
8
9
|
import { memoize } from 'spica/memoize';
|
|
9
10
|
import { shift } from 'spica/array';
|
|
@@ -17,11 +18,12 @@ export const olist: OListParser = lazy(() => block(match(
|
|
|
17
18
|
const list = (type: string, delim: string): OListParser => fmap(
|
|
18
19
|
context({ syntax: { inline: { media: false } } },
|
|
19
20
|
some(creator(union([
|
|
20
|
-
fmap(
|
|
21
|
+
fmap(fallback(
|
|
21
22
|
inits([
|
|
22
23
|
line(open(items[delim], trim(subsequence([checkbox, trimStart(some(inline))])), true)),
|
|
23
24
|
indent(union([ulist_, olist_, ilist_])),
|
|
24
25
|
]),
|
|
26
|
+
iitem),
|
|
25
27
|
(ns: [string, ...(HTMLElement | string)[]]) => [html('li', { 'data-marker': ns[0] }, defrag(fillFirstLine(shift(ns)[1])))]),
|
|
26
28
|
])))),
|
|
27
29
|
es => [format(html('ol', es), type, delim)]);
|
|
@@ -45,6 +47,16 @@ export const olist_: OListParser = convert(
|
|
|
45
47
|
.replace(/^\(((?:[0-9]+|[a-z]+))\)?((?:-(?!-)[0-9]*)*(?=$|\n))/, `($1)$2 `),
|
|
46
48
|
olist);
|
|
47
49
|
|
|
50
|
+
const iitem = rewrite(contentline, source => [[
|
|
51
|
+
'',
|
|
52
|
+
html('span', {
|
|
53
|
+
class: 'invalid',
|
|
54
|
+
'data-invalid-syntax': 'listitem',
|
|
55
|
+
'data-invalid-type': 'syntax',
|
|
56
|
+
'data-invalid-description': 'Fix the indent or the head of list items.',
|
|
57
|
+
}, source.replace('\n', ''))
|
|
58
|
+
], '']);
|
|
59
|
+
|
|
48
60
|
function type(index: string): string {
|
|
49
61
|
switch (index) {
|
|
50
62
|
case 'i':
|
|
@@ -102,8 +114,13 @@ function format(el: HTMLOListElement, type: string, delim: string): HTMLOListEle
|
|
|
102
114
|
const marker = el.firstElementChild?.getAttribute('data-marker')!.match(initial(type))?.[0] ?? '';
|
|
103
115
|
for (let es = el.children, len = es.length, i = 0; i < len; ++i) {
|
|
104
116
|
const el = es[i];
|
|
105
|
-
|
|
106
|
-
|
|
117
|
+
switch (el.getAttribute('data-marker')) {
|
|
118
|
+
case '':
|
|
119
|
+
case marker:
|
|
120
|
+
el.removeAttribute('data-marker');
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
break;
|
|
107
124
|
}
|
|
108
125
|
return el;
|
|
109
126
|
}
|
|
@@ -10,10 +10,10 @@ export const cite: ParagraphParser.MentionParser.CiteParser = creator(line(fmap(
|
|
|
10
10
|
str(/^>*(?=>>)/),
|
|
11
11
|
anchor,
|
|
12
12
|
]))),
|
|
13
|
-
([el,
|
|
13
|
+
([el, quotes = '']: [HTMLElement, string?]) => [
|
|
14
14
|
html('span', { class: 'cite' }, defrag([
|
|
15
|
-
|
|
16
|
-
define(el, { 'data-depth': `${
|
|
15
|
+
quotes + '>',
|
|
16
|
+
define(el, { 'data-depth': `${quotes.length + 1}` }, el.innerText.slice(1)),
|
|
17
17
|
])),
|
|
18
18
|
html('br'),
|
|
19
19
|
])));
|
|
@@ -13,7 +13,6 @@ describe('Unit: parser/block/table', () => {
|
|
|
13
13
|
assert.deepStrictEqual(inspect(parser('||')), undefined);
|
|
14
14
|
assert.deepStrictEqual(inspect(parser('|||')), undefined);
|
|
15
15
|
assert.deepStrictEqual(inspect(parser('|\n|')), undefined);
|
|
16
|
-
assert.deepStrictEqual(inspect(parser('|\n|\n|')), undefined);
|
|
17
16
|
assert.deepStrictEqual(inspect(parser('|h')), undefined);
|
|
18
17
|
assert.deepStrictEqual(inspect(parser('|h')), undefined);
|
|
19
18
|
assert.deepStrictEqual(inspect(parser('|h\n')), undefined);
|
|
@@ -26,6 +25,7 @@ describe('Unit: parser/block/table', () => {
|
|
|
26
25
|
});
|
|
27
26
|
|
|
28
27
|
it('valid', () => {
|
|
28
|
+
assert.deepStrictEqual(inspect(parser('|\n|\n|')), [['<table><thead><tr></tr></thead><tbody><tr class="invalid"><td>|</td></tr><tr></tr></tbody></table>'], '']);
|
|
29
29
|
assert.deepStrictEqual(inspect(parser('|\n|-\n|')), [['<table><thead><tr></tr></thead><tbody><tr></tr></tbody></table>'], '']);
|
|
30
30
|
assert.deepStrictEqual(inspect(parser('||\n|-|\n||')), [['<table><thead><tr><th></th></tr></thead><tbody><tr><td></td></tr></tbody></table>'], '']);
|
|
31
31
|
assert.deepStrictEqual(inspect(parser('|||\n|-|-|\n|||')), [['<table><thead><tr><th></th><th></th></tr></thead><tbody><tr><td></td><td></td></tr></tbody></table>'], '']);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { TableParser } from '../block';
|
|
2
|
-
import { union, sequence, some, block, line, validate, focus, creator, surround, open, lazy, fmap } from '../../combinator';
|
|
2
|
+
import { union, sequence, some, block, line, validate, focus, rewrite, creator, surround, open, fallback, lazy, fmap } from '../../combinator';
|
|
3
3
|
import { inline } from '../inline';
|
|
4
|
+
import { contentline } from '../source';
|
|
4
5
|
import { html, defrag } from 'typed-dom';
|
|
5
6
|
import { push } from 'spica/array';
|
|
6
7
|
|
|
@@ -9,7 +10,7 @@ import AlignParser = TableParser.AlignParser;
|
|
|
9
10
|
import CellParser = TableParser.CellParser;
|
|
10
11
|
|
|
11
12
|
export const table: TableParser = lazy(() => block(fmap(validate(
|
|
12
|
-
/^\|[^\n]*(?:\n\|[^\n]*){2
|
|
13
|
+
/^\|[^\n]*(?:\n\|[^\n]*){2}/,
|
|
13
14
|
sequence([
|
|
14
15
|
row(some(head), true),
|
|
15
16
|
row(some(align), false),
|
|
@@ -18,13 +19,21 @@ export const table: TableParser = lazy(() => block(fmap(validate(
|
|
|
18
19
|
rows => [
|
|
19
20
|
html('table', [
|
|
20
21
|
html('thead', [rows.shift()!]),
|
|
21
|
-
html('tbody', format(rows
|
|
22
|
+
html('tbody', format(rows)),
|
|
22
23
|
]),
|
|
23
24
|
])));
|
|
24
25
|
|
|
25
|
-
const row = <P extends CellParser | AlignParser>(parser: P, optional: boolean): RowParser<P> => creator(fmap(
|
|
26
|
+
const row = <P extends CellParser | AlignParser>(parser: P, optional: boolean): RowParser<P> => creator(fallback(fmap(
|
|
26
27
|
line(surround(/^(?=\|)/, some(union([parser])), /^\|?\s*$/, optional)),
|
|
27
|
-
es => [html('tr', es)])
|
|
28
|
+
es => [html('tr', es)]),
|
|
29
|
+
rewrite(contentline, source => [[
|
|
30
|
+
html('tr', {
|
|
31
|
+
class: 'invalid',
|
|
32
|
+
'data-invalid-syntax': 'tablerow',
|
|
33
|
+
'data-invalid-type': 'syntax',
|
|
34
|
+
'data-invalid-description': 'Invalid table row.',
|
|
35
|
+
}, [html('td', source.replace('\n', ''))])
|
|
36
|
+
], ''])));
|
|
28
37
|
|
|
29
38
|
const align: AlignParser = creator(fmap(open(
|
|
30
39
|
'|',
|
|
@@ -49,11 +58,12 @@ const data: CellParser.DataParser = creator(fmap(
|
|
|
49
58
|
cell,
|
|
50
59
|
ns => [html('td', defrag(ns))]));
|
|
51
60
|
|
|
52
|
-
function format(rows: HTMLTableRowElement[]
|
|
53
|
-
|
|
61
|
+
function format(rows: HTMLTableRowElement[]): HTMLTableRowElement[] {
|
|
62
|
+
const aligns = rows[0].classList.contains('invalid')
|
|
63
|
+
? []
|
|
64
|
+
: push([], rows.shift()!.children).map(el => el.textContent!);
|
|
54
65
|
for (let i = 0, len = rows.length; i < len; ++i) {
|
|
55
|
-
const
|
|
56
|
-
const cols = row.children;
|
|
66
|
+
const cols = rows[i].children;
|
|
57
67
|
for (let i = 0, len = cols.length; i < len; ++i) {
|
|
58
68
|
if (i > 0 && !aligns[i]) {
|
|
59
69
|
aligns[i] = aligns[i - 1];
|
|
@@ -17,11 +17,6 @@ describe('Unit: parser/block/ulist', () => {
|
|
|
17
17
|
assert.deepStrictEqual(inspect(parser('-[ ]')), undefined);
|
|
18
18
|
assert.deepStrictEqual(inspect(parser('-[x]')), undefined);
|
|
19
19
|
assert.deepStrictEqual(inspect(parser('-\n')), undefined);
|
|
20
|
-
assert.deepStrictEqual(inspect(parser('-\n+')), undefined);
|
|
21
|
-
assert.deepStrictEqual(inspect(parser('-\n0')), undefined);
|
|
22
|
-
assert.deepStrictEqual(inspect(parser('-\n -\n 0')), undefined);
|
|
23
|
-
assert.deepStrictEqual(inspect(parser('- 0\n - 0\n - 0')), undefined);
|
|
24
|
-
assert.deepStrictEqual(inspect(parser('- !http://host')), [['<ul><li>!<a href="http://host" target="_blank">http://host</a></li></ul>'], '']);
|
|
25
20
|
assert.deepStrictEqual(inspect(parser(' -')), undefined);
|
|
26
21
|
assert.deepStrictEqual(inspect(parser('+')), undefined);
|
|
27
22
|
assert.deepStrictEqual(inspect(parser('*')), undefined);
|
|
@@ -43,6 +38,9 @@ describe('Unit: parser/block/ulist', () => {
|
|
|
43
38
|
// filled
|
|
44
39
|
assert.deepStrictEqual(inspect(parser('- 1\n- 2')), [['<ul><li>1</li><li>2</li></ul>'], '']);
|
|
45
40
|
assert.deepStrictEqual(inspect(parser('- 1\n- 2\n- 3')), [['<ul><li>1</li><li>2</li><li>3</li></ul>'], '']);
|
|
41
|
+
// invalid
|
|
42
|
+
assert.deepStrictEqual(inspect(parser('-\n+')), [['<ul><li></li><li><span class="invalid">+</span></li></ul>'], '']);
|
|
43
|
+
assert.deepStrictEqual(inspect(parser('-\n0')), [['<ul><li></li><li><span class="invalid">0</span></li></ul>'], '']);
|
|
46
44
|
});
|
|
47
45
|
|
|
48
46
|
it('nest', () => {
|
|
@@ -51,11 +49,14 @@ describe('Unit: parser/block/ulist', () => {
|
|
|
51
49
|
assert.deepStrictEqual(inspect(parser('- 1\n - 2\n- 3')), [['<ul><li>1<ul><li>2</li></ul></li><li>3</li></ul>'], '']);
|
|
52
50
|
assert.deepStrictEqual(inspect(parser('- 1\n - 2\n - 3')), [['<ul><li>1<ul><li>2</li><li>3</li></ul></li></ul>'], '']);
|
|
53
51
|
assert.deepStrictEqual(inspect(parser('- 1\n - 2\n - 3')), [['<ul><li>1<ul><li>2<ul><li>3</li></ul></li></ul></li></ul>'], '']);
|
|
52
|
+
assert.deepStrictEqual(inspect(parser('- 1\n - 2\n - 3')), [['<ul><li>1<ul><li>2</li></ul></li><li><span class="invalid"> - 3</span></li></ul>'], '']);
|
|
53
|
+
assert.deepStrictEqual(inspect(parser('-\n -\n +\n -\n +\n+')), [['<ul><li><br><ul><li></li><li><span class="invalid">+</span></li><li></li><li><span class="invalid">+</span></li></ul></li><li><span class="invalid">+</span></li></ul>'], '']);
|
|
54
54
|
assert.deepStrictEqual(inspect(parser('- 1\n + 2')), [['<ul><li>1<ul class="invalid"><li>2</li></ul></li></ul>'], '']);
|
|
55
55
|
assert.deepStrictEqual(inspect(parser('- 1\n 0')), [['<ul><li>1<ol><li></li></ol></li></ul>'], '']);
|
|
56
56
|
assert.deepStrictEqual(inspect(parser('- 1\n 0.')), [['<ul><li>1<ol><li></li></ol></li></ul>'], '']);
|
|
57
57
|
assert.deepStrictEqual(inspect(parser('- 1\n 0. ')), [['<ul><li>1<ol><li></li></ol></li></ul>'], '']);
|
|
58
58
|
assert.deepStrictEqual(inspect(parser('- 1\n 0. 2')), [['<ul><li>1<ol><li>2</li></ol></li></ul>'], '']);
|
|
59
|
+
assert.deepStrictEqual(inspect(parser('- !http://host')), [['<ul><li>!<a href="http://host" target="_blank">http://host</a></li></ul>'], '']);
|
|
59
60
|
});
|
|
60
61
|
|
|
61
62
|
it('checkbox', () => {
|
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
import { UListParser } from '../block';
|
|
2
|
-
import { union, inits, subsequence, some, block, line, validate, indent, focus, context, creator, open, convert, trim, trimStart, lazy, fmap } from '../../combinator';
|
|
2
|
+
import { union, inits, subsequence, some, block, line, validate, indent, focus, rewrite, context, creator, open, convert, trim, trimStart, fallback, lazy, fmap } from '../../combinator';
|
|
3
3
|
import { olist_ } from './olist';
|
|
4
4
|
import { ilist_ } from './ilist';
|
|
5
5
|
import { inline } from '../inline';
|
|
6
6
|
import { html, defrag } from 'typed-dom';
|
|
7
7
|
import { unshift } from 'spica/array';
|
|
8
|
+
import { contentline } from '../source';
|
|
8
9
|
|
|
9
10
|
export const ulist: UListParser = lazy(() => block(fmap(validate(
|
|
10
11
|
/^-(?=[^\S\n]|\n[^\S\n]*\S)/,
|
|
11
12
|
context({ syntax: { inline: { media: false } } },
|
|
12
13
|
some(creator(union([
|
|
13
|
-
fmap(
|
|
14
|
+
fmap(fallback(
|
|
14
15
|
inits([
|
|
15
16
|
line(open(/^-(?:$|\s)/, trim(subsequence([checkbox, trimStart(some(inline))])), true)),
|
|
16
17
|
indent(union([ulist_, olist_, ilist_])),
|
|
17
18
|
]),
|
|
19
|
+
iitem),
|
|
18
20
|
ns => [html('li', defrag(fillFirstLine(ns)))]),
|
|
19
21
|
]))))),
|
|
20
22
|
es => [format(html('ul', es))])));
|
|
@@ -29,6 +31,15 @@ export const ulist_: UListParser = convert(
|
|
|
29
31
|
source => source.replace(/^-(?=$|\n)/, `$& `),
|
|
30
32
|
ulist);
|
|
31
33
|
|
|
34
|
+
const iitem = rewrite(contentline, source => [[
|
|
35
|
+
html('span', {
|
|
36
|
+
class: 'invalid',
|
|
37
|
+
'data-invalid-syntax': 'listitem',
|
|
38
|
+
'data-invalid-type': 'syntax',
|
|
39
|
+
'data-invalid-description': 'Fix the indent or the head of list items.',
|
|
40
|
+
}, source.replace('\n', ''))
|
|
41
|
+
], '']);
|
|
42
|
+
|
|
32
43
|
export function fillFirstLine(ns: (HTMLElement | string)[]): (HTMLElement | string)[] {
|
|
33
44
|
return ns.length === 1
|
|
34
45
|
&& typeof ns[0] === 'object'
|
package/src/parser/block.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { undefined } from 'spica/global';
|
|
2
2
|
import { MarkdownParser } from '../../markdown';
|
|
3
|
-
import { union, reset, creator, open, recover } from '../combinator';
|
|
3
|
+
import { union, reset, creator, open, fallback, recover } from '../combinator';
|
|
4
4
|
import { emptyline } from './source';
|
|
5
5
|
import { horizontalrule } from './block/horizontalrule';
|
|
6
6
|
import { heading } from './block/heading';
|
|
@@ -49,12 +49,10 @@ export const block: BlockParser = creator(error(
|
|
|
49
49
|
paragraph
|
|
50
50
|
]))));
|
|
51
51
|
|
|
52
|
-
function error(parser:
|
|
53
|
-
return recover(
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
parser,
|
|
57
|
-
]),
|
|
52
|
+
function error(parser: BlockParser): BlockParser {
|
|
53
|
+
return recover<BlockParser>(fallback(
|
|
54
|
+
open('\0', source => { throw new Error(source.split('\n', 1)[0]); }),
|
|
55
|
+
parser),
|
|
58
56
|
(source, { id }, reason) => [[
|
|
59
57
|
html('h1',
|
|
60
58
|
{
|
|
@@ -14,8 +14,6 @@ describe('Unit: parser/inline/annotation', () => {
|
|
|
14
14
|
assert.deepStrictEqual(inspect(parser('(())')), undefined);
|
|
15
15
|
assert.deepStrictEqual(inspect(parser('(()))')), undefined);
|
|
16
16
|
assert.deepStrictEqual(inspect(parser('(( ))')), undefined);
|
|
17
|
-
assert.deepStrictEqual(inspect(parser('(( a))')), undefined);
|
|
18
|
-
assert.deepStrictEqual(inspect(parser('(( a ))')), undefined);
|
|
19
17
|
assert.deepStrictEqual(inspect(parser('((\n))')), undefined);
|
|
20
18
|
assert.deepStrictEqual(inspect(parser('((\na))')), undefined);
|
|
21
19
|
assert.deepStrictEqual(inspect(parser('((\\ a))')), undefined);
|
|
@@ -34,6 +32,8 @@ describe('Unit: parser/inline/annotation', () => {
|
|
|
34
32
|
});
|
|
35
33
|
|
|
36
34
|
it('basic', () => {
|
|
35
|
+
assert.deepStrictEqual(inspect(parser('(( a))')), [['<sup class="annotation">a</sup>'], '']);
|
|
36
|
+
assert.deepStrictEqual(inspect(parser('(( a ))')), [['<sup class="annotation">a</sup>'], '']);
|
|
37
37
|
assert.deepStrictEqual(inspect(parser('((a))')), [['<sup class="annotation">a</sup>'], '']);
|
|
38
38
|
assert.deepStrictEqual(inspect(parser('((a ))')), [['<sup class="annotation">a</sup>'], '']);
|
|
39
39
|
assert.deepStrictEqual(inspect(parser('((a ))')), [['<sup class="annotation">a</sup>'], '']);
|
|
@@ -2,13 +2,13 @@ import { undefined } from 'spica/global';
|
|
|
2
2
|
import { AnnotationParser } from '../inline';
|
|
3
3
|
import { union, some, validate, guard, context, creator, surround, lazy, fmap } from '../../combinator';
|
|
4
4
|
import { inline } from '../inline';
|
|
5
|
-
import {
|
|
5
|
+
import { startLoose, visible, trimNode } from '../util';
|
|
6
6
|
import { html, defrag } from 'typed-dom';
|
|
7
7
|
|
|
8
8
|
export const annotation: AnnotationParser = lazy(() => creator(validate('((', '))', '\n', fmap(surround(
|
|
9
9
|
'((',
|
|
10
10
|
guard(context => context.syntax?.inline?.annotation ?? true,
|
|
11
|
-
|
|
11
|
+
startLoose(visible(
|
|
12
12
|
context({ syntax: { inline: {
|
|
13
13
|
annotation: false,
|
|
14
14
|
// Redundant
|
|
@@ -20,6 +20,6 @@ export const annotation: AnnotationParser = lazy(() => creator(validate('((', ')
|
|
|
20
20
|
//link: true,
|
|
21
21
|
//autolink: true,
|
|
22
22
|
}}, state: undefined },
|
|
23
|
-
union([some(inline, ')', /^\\?\n/)])))),
|
|
23
|
+
union([some(inline, ')', /^\\?\n/)]))))),
|
|
24
24
|
'))'),
|
|
25
|
-
ns => [html('sup', { class: 'annotation' },
|
|
25
|
+
ns => [html('sup', { class: 'annotation' }, trimNode(defrag(ns)))]))));
|
|
@@ -3,7 +3,7 @@ import { union, some, creator, surround, close, lazy } from '../../combinator';
|
|
|
3
3
|
import { inline } from '../inline';
|
|
4
4
|
import { strong } from './strong';
|
|
5
5
|
import { str } from '../source';
|
|
6
|
-
import { startTight,
|
|
6
|
+
import { startTight, isEndTightNodes, trimEndBR } from '../util';
|
|
7
7
|
import { html, defrag } from 'typed-dom';
|
|
8
8
|
import { unshift } from 'spica/array';
|
|
9
9
|
|
|
@@ -12,7 +12,7 @@ export const emphasis: EmphasisParser = lazy(() => creator(surround(close(
|
|
|
12
12
|
startTight(some(union([strong, some(inline, '*')]))),
|
|
13
13
|
str('*'), false,
|
|
14
14
|
([as, bs, cs], rest) =>
|
|
15
|
-
|
|
15
|
+
isEndTightNodes(bs)
|
|
16
16
|
? [[html('em', defrag(trimEndBR(bs)))], rest]
|
|
17
17
|
: [unshift(as, bs), cs[0] + rest],
|
|
18
18
|
([as, bs], rest) => [unshift(as, bs), rest])));
|