securemark 0.247.2 → 0.249.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 +12 -0
- package/design.md +7 -4
- package/dist/index.js +111 -246
- package/markdown.d.ts +10 -11
- package/package.json +6 -6
- package/src/combinator/control/manipulation/indent.ts +1 -2
- package/src/parser/inline/annotation.test.ts +2 -2
- package/src/parser/inline/annotation.ts +4 -5
- package/src/parser/inline/comment.ts +1 -1
- package/src/parser/inline/deletion.ts +3 -3
- package/src/parser/inline/emphasis.test.ts +5 -4
- package/src/parser/inline/emphasis.ts +8 -3
- package/src/parser/inline/emstrong.ts +14 -6
- package/src/parser/inline/extension/index.ts +1 -2
- package/src/parser/inline/extension/label.ts +1 -2
- package/src/parser/inline/html.test.ts +8 -5
- package/src/parser/inline/html.ts +17 -86
- package/src/parser/inline/insertion.ts +3 -3
- package/src/parser/inline/link.test.ts +2 -2
- package/src/parser/inline/link.ts +3 -4
- package/src/parser/inline/mark.test.ts +5 -4
- package/src/parser/inline/mark.ts +3 -3
- package/src/parser/inline/media.ts +3 -3
- package/src/parser/inline/reference.test.ts +5 -5
- package/src/parser/inline/reference.ts +8 -10
- package/src/parser/inline/ruby.ts +4 -4
- package/src/parser/inline/strong.test.ts +5 -4
- package/src/parser/inline/strong.ts +7 -3
- package/src/parser/inline.test.ts +1 -0
- package/src/parser/processor/figure.ts +18 -19
- package/src/parser/source/escapable.test.ts +4 -3
- package/src/parser/source/escapable.ts +3 -3
- package/src/parser/source/text.test.ts +4 -4
- package/src/parser/source/text.ts +3 -3
- package/src/parser/source/unescapable.test.ts +4 -3
- package/src/parser/source/unescapable.ts +3 -3
- package/src/parser/util.ts +26 -13
- package/src/renderer/render/media/image.ts +3 -3
- package/src/renderer/render/media/video.ts +2 -2
package/markdown.d.ts
CHANGED
|
@@ -28,12 +28,6 @@ export namespace MarkdownParser {
|
|
|
28
28
|
readonly autolink?: boolean;
|
|
29
29
|
};
|
|
30
30
|
};
|
|
31
|
-
readonly state?: {
|
|
32
|
-
readonly in?: {
|
|
33
|
-
readonly supsub?: boolean;
|
|
34
|
-
readonly small?: boolean;
|
|
35
|
-
};
|
|
36
|
-
};
|
|
37
31
|
readonly caches?: {
|
|
38
32
|
readonly code?: Collection<string, HTMLElement>;
|
|
39
33
|
readonly math?: Collection<string, HTMLElement>;
|
|
@@ -987,7 +981,6 @@ export namespace MarkdownParser {
|
|
|
987
981
|
HTMLParser.OpenTagParser,
|
|
988
982
|
HTMLParser.TagParser,
|
|
989
983
|
HTMLParser.TagParser,
|
|
990
|
-
HTMLParser.TagParser,
|
|
991
984
|
]> {
|
|
992
985
|
}
|
|
993
986
|
export namespace HTMLParser {
|
|
@@ -1001,7 +994,6 @@ export namespace MarkdownParser {
|
|
|
1001
994
|
Inline<'html/tag'>,
|
|
1002
995
|
Parser<HTMLElement | string, Context, [
|
|
1003
996
|
InlineParser,
|
|
1004
|
-
InlineParser,
|
|
1005
997
|
]> {
|
|
1006
998
|
}
|
|
1007
999
|
export namespace TagParser {
|
|
@@ -1034,7 +1026,7 @@ export namespace MarkdownParser {
|
|
|
1034
1026
|
Inline<'mark'>,
|
|
1035
1027
|
Parser<HTMLElement | string, Context, [
|
|
1036
1028
|
InlineParser,
|
|
1037
|
-
|
|
1029
|
+
MarkParser,
|
|
1038
1030
|
]> {
|
|
1039
1031
|
}
|
|
1040
1032
|
export interface EmStrongParser extends
|
|
@@ -1050,7 +1042,10 @@ export namespace MarkdownParser {
|
|
|
1050
1042
|
Inline<'strong'>,
|
|
1051
1043
|
Parser<HTMLElement | string, Context, [
|
|
1052
1044
|
InlineParser,
|
|
1053
|
-
|
|
1045
|
+
Parser<HTMLElement | string, Context, [
|
|
1046
|
+
EmStrongParser,
|
|
1047
|
+
StrongParser,
|
|
1048
|
+
]>,
|
|
1054
1049
|
]> {
|
|
1055
1050
|
}
|
|
1056
1051
|
export interface EmphasisParser extends
|
|
@@ -1059,7 +1054,11 @@ export namespace MarkdownParser {
|
|
|
1059
1054
|
Parser<HTMLElement | string, Context, [
|
|
1060
1055
|
StrongParser,
|
|
1061
1056
|
InlineParser,
|
|
1062
|
-
|
|
1057
|
+
Parser<HTMLElement | string, Context, [
|
|
1058
|
+
EmStrongParser,
|
|
1059
|
+
StrongParser,
|
|
1060
|
+
EmphasisParser,
|
|
1061
|
+
]>,
|
|
1063
1062
|
]> {
|
|
1064
1063
|
}
|
|
1065
1064
|
export interface CodeParser extends
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "securemark",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.249.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",
|
|
@@ -34,10 +34,10 @@
|
|
|
34
34
|
"@types/mocha": "9.1.1",
|
|
35
35
|
"@types/power-assert": "1.5.8",
|
|
36
36
|
"@types/prismjs": "1.26.0",
|
|
37
|
-
"@typescript-eslint/parser": "^5.
|
|
37
|
+
"@typescript-eslint/parser": "^5.26.0",
|
|
38
38
|
"babel-loader": "^8.2.5",
|
|
39
39
|
"babel-plugin-unassert": "^3.2.0",
|
|
40
|
-
"concurrently": "^7.2.
|
|
40
|
+
"concurrently": "^7.2.1",
|
|
41
41
|
"eslint": "^8.16.0",
|
|
42
42
|
"eslint-plugin-redos": "^4.4.0",
|
|
43
43
|
"eslint-webpack-plugin": "^3.1.1",
|
|
@@ -51,10 +51,10 @@
|
|
|
51
51
|
"mocha": "^10.0.0",
|
|
52
52
|
"npm-check-updates": "^13.0.3",
|
|
53
53
|
"semver": "^7.3.7",
|
|
54
|
-
"spica": "0.0.
|
|
54
|
+
"spica": "0.0.554",
|
|
55
55
|
"ts-loader": "^9.3.0",
|
|
56
|
-
"typed-dom": "^0.0.
|
|
57
|
-
"typescript": "4.
|
|
56
|
+
"typed-dom": "^0.0.297",
|
|
57
|
+
"typescript": "4.7.2",
|
|
58
58
|
"webpack": "^5.72.1",
|
|
59
59
|
"webpack-cli": "^4.9.2",
|
|
60
60
|
"webpack-merge": "^5.8.0"
|
|
@@ -7,7 +7,6 @@ import { bind } from '../monad/bind';
|
|
|
7
7
|
import { match } from './match';
|
|
8
8
|
import { open } from './surround';
|
|
9
9
|
import { memoize } from 'spica/memoize';
|
|
10
|
-
import { join } from 'spica/array';
|
|
11
10
|
|
|
12
11
|
export function indent<P extends Parser<unknown>>(parser: P, separation?: boolean): P;
|
|
13
12
|
export function indent<T>(parser: Parser<T>, separation = false): Parser<T> {
|
|
@@ -19,7 +18,7 @@ export function indent<T>(parser: Parser<T>, separation = false): Parser<T> {
|
|
|
19
18
|
some(line(open(indent, source => [[unline(source)], '']))),
|
|
20
19
|
([, indent]) => indent.length * 2 + +(indent[0] === ' '), [])), separation),
|
|
21
20
|
(nodes, rest, context) => {
|
|
22
|
-
const result = parser(join(
|
|
21
|
+
const result = parser(nodes.join('\n'), context);
|
|
23
22
|
return result && exec(result) === ''
|
|
24
23
|
? [eval(result), rest]
|
|
25
24
|
: undefined;
|
|
@@ -16,9 +16,7 @@ describe('Unit: parser/inline/annotation', () => {
|
|
|
16
16
|
assert.deepStrictEqual(inspect(parser('(( ))')), undefined);
|
|
17
17
|
assert.deepStrictEqual(inspect(parser('((\n))')), undefined);
|
|
18
18
|
assert.deepStrictEqual(inspect(parser('((\na))')), undefined);
|
|
19
|
-
assert.deepStrictEqual(inspect(parser('((\\ a))')), undefined);
|
|
20
19
|
assert.deepStrictEqual(inspect(parser('((\\\na))')), undefined);
|
|
21
|
-
assert.deepStrictEqual(inspect(parser('((<wbr>a))')), undefined);
|
|
22
20
|
assert.deepStrictEqual(inspect(parser('((a\n))')), undefined);
|
|
23
21
|
assert.deepStrictEqual(inspect(parser('((a\\\n))')), undefined);
|
|
24
22
|
assert.deepStrictEqual(inspect(parser('((a\nb))')), undefined);
|
|
@@ -33,6 +31,8 @@ describe('Unit: parser/inline/annotation', () => {
|
|
|
33
31
|
it('basic', () => {
|
|
34
32
|
assert.deepStrictEqual(inspect(parser('(( a))')), [['<sup class="annotation">a</sup>'], '']);
|
|
35
33
|
assert.deepStrictEqual(inspect(parser('(( a ))')), [['<sup class="annotation">a</sup>'], '']);
|
|
34
|
+
assert.deepStrictEqual(inspect(parser('((\\ a))')), [['<sup class="annotation">a</sup>'], '']);
|
|
35
|
+
assert.deepStrictEqual(inspect(parser('((<wbr>a))')), [['<sup class="annotation">a</sup>'], '']);
|
|
36
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>'], '']);
|
|
@@ -2,13 +2,12 @@ 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 { trimBlankInline } from '../util';
|
|
6
6
|
import { html, defrag } from 'typed-dom/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
|
-
startLoose(
|
|
12
11
|
context({ syntax: { inline: {
|
|
13
12
|
annotation: false,
|
|
14
13
|
// Redundant
|
|
@@ -19,7 +18,7 @@ export const annotation: AnnotationParser = lazy(() => creator(validate('((', ')
|
|
|
19
18
|
//label: true,
|
|
20
19
|
//link: true,
|
|
21
20
|
//autolink: true,
|
|
22
|
-
}},
|
|
23
|
-
|
|
21
|
+
}}, delimiters: undefined },
|
|
22
|
+
trimBlankInline(some(union([inline]), ')', /^\\?\n/)))),
|
|
24
23
|
'))'),
|
|
25
|
-
ns => [html('sup', { class: 'annotation' },
|
|
24
|
+
ns => [html('sup', { class: 'annotation' }, defrag(ns))]))));
|
|
@@ -12,7 +12,7 @@ export const comment: CommentParser = lazy(() => creator(validate('[#', match(
|
|
|
12
12
|
([, fence]) =>
|
|
13
13
|
surround(
|
|
14
14
|
open(str(`[${fence}`), some(text, new RegExp(String.raw`^\s+${fence}\]|^\S`)), true),
|
|
15
|
-
union([
|
|
15
|
+
some(union([inline]), new RegExp(String.raw`^\s+${fence}\]`)),
|
|
16
16
|
close(some(text, /^\S/), str(`${fence}]`)), true,
|
|
17
17
|
([as, bs = [], cs], rest) => [[
|
|
18
18
|
html('span', { class: 'comment' }, [
|
|
@@ -2,15 +2,15 @@ import { DeletionParser } from '../inline';
|
|
|
2
2
|
import { union, some, creator, surround, open, lazy } from '../../combinator';
|
|
3
3
|
import { inline } from '../inline';
|
|
4
4
|
import { str } from '../source';
|
|
5
|
-
import {
|
|
5
|
+
import { blankWith } from '../util';
|
|
6
6
|
import { html, defrag } from 'typed-dom/dom';
|
|
7
7
|
import { unshift } from 'spica/array';
|
|
8
8
|
|
|
9
9
|
export const deletion: DeletionParser = lazy(() => creator(surround(
|
|
10
10
|
str('~~'),
|
|
11
11
|
some(union([
|
|
12
|
-
some(inline,
|
|
13
|
-
open(
|
|
12
|
+
some(inline, blankWith('\n', '~~')),
|
|
13
|
+
open('\n', some(inline, '~'), true),
|
|
14
14
|
])),
|
|
15
15
|
str('~~'), false,
|
|
16
16
|
([, bs], rest) => [[html('del', defrag(bs))], rest],
|
|
@@ -9,10 +9,11 @@ describe('Unit: parser/inline/emphasis', () => {
|
|
|
9
9
|
it('invalid', () => {
|
|
10
10
|
assert.deepStrictEqual(inspect(parser('*')), undefined);
|
|
11
11
|
assert.deepStrictEqual(inspect(parser('*a')), [['*', 'a'], '']);
|
|
12
|
-
assert.deepStrictEqual(inspect(parser('*a *')), [['*', 'a', '
|
|
13
|
-
assert.deepStrictEqual(inspect(parser('*a
|
|
14
|
-
assert.deepStrictEqual(inspect(parser('*a
|
|
15
|
-
assert.deepStrictEqual(inspect(parser('*a
|
|
12
|
+
assert.deepStrictEqual(inspect(parser('*a *')), [['*', 'a'], ' *']);
|
|
13
|
+
assert.deepStrictEqual(inspect(parser('*a *')), [['*', 'a', ' '], ' *']);
|
|
14
|
+
assert.deepStrictEqual(inspect(parser('*a\n*')), [['*', 'a'], '\n*']);
|
|
15
|
+
assert.deepStrictEqual(inspect(parser('*a\\ *')), [['*', 'a'], '\\ *']);
|
|
16
|
+
assert.deepStrictEqual(inspect(parser('*a\\\n*')), [['*', 'a'], '\\\n*']);
|
|
16
17
|
assert.deepStrictEqual(inspect(parser('*a**b')), [['*', 'a', '**', 'b'], '']);
|
|
17
18
|
assert.deepStrictEqual(inspect(parser('*a**b*')), [['*', 'a', '**', 'b', '*'], '']);
|
|
18
19
|
assert.deepStrictEqual(inspect(parser('* *')), undefined);
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { EmphasisParser } from '../inline';
|
|
2
2
|
import { union, some, creator, surround, open, lazy } from '../../combinator';
|
|
3
3
|
import { inline } from '../inline';
|
|
4
|
+
import { emstrong } from './emstrong';
|
|
4
5
|
import { strong } from './strong';
|
|
5
6
|
import { str } from '../source';
|
|
6
|
-
import { startTight,
|
|
7
|
+
import { startTight, blankWith } from '../util';
|
|
7
8
|
import { html, defrag } from 'typed-dom/dom';
|
|
8
9
|
import { unshift } from 'spica/array';
|
|
9
10
|
|
|
@@ -11,8 +12,12 @@ export const emphasis: EmphasisParser = lazy(() => creator(surround(
|
|
|
11
12
|
str('*'),
|
|
12
13
|
startTight(some(union([
|
|
13
14
|
strong,
|
|
14
|
-
some(inline,
|
|
15
|
-
open(some(inline, '*'),
|
|
15
|
+
some(inline, blankWith('*')),
|
|
16
|
+
open(some(inline, '*'), union([
|
|
17
|
+
emstrong,
|
|
18
|
+
strong,
|
|
19
|
+
emphasis,
|
|
20
|
+
])),
|
|
16
21
|
])), '*'),
|
|
17
22
|
str('*'), false,
|
|
18
23
|
([, bs], rest) => [[html('em', defrag(bs))], rest],
|
|
@@ -4,25 +4,33 @@ import { Result, IntermediateParser } from '../../combinator/data/parser';
|
|
|
4
4
|
import { union, some, creator, surround, open, lazy, bind } from '../../combinator';
|
|
5
5
|
import { inline } from '../inline';
|
|
6
6
|
import { strong } from './strong';
|
|
7
|
+
import { emphasis } from './emphasis';
|
|
7
8
|
import { str } from '../source';
|
|
8
|
-
import { startTight,
|
|
9
|
+
import { startTight, blankWith } from '../util';
|
|
9
10
|
import { html, defrag } from 'typed-dom/dom';
|
|
10
11
|
import { unshift } from 'spica/array';
|
|
11
12
|
|
|
12
13
|
const substrong: IntermediateParser<StrongParser> = lazy(() => some(union([
|
|
13
|
-
some(inline,
|
|
14
|
-
open(some(inline, '*'),
|
|
14
|
+
some(inline, blankWith('**')),
|
|
15
|
+
open(some(inline, '*'), union([
|
|
16
|
+
emstrong,
|
|
17
|
+
strong,
|
|
18
|
+
])),
|
|
15
19
|
])));
|
|
16
20
|
const subemphasis: IntermediateParser<EmphasisParser> = lazy(() => some(union([
|
|
17
21
|
strong,
|
|
18
|
-
some(inline,
|
|
19
|
-
open(some(inline, '*'),
|
|
22
|
+
some(inline, blankWith('*')),
|
|
23
|
+
open(some(inline, '*'), union([
|
|
24
|
+
emstrong,
|
|
25
|
+
strong,
|
|
26
|
+
emphasis,
|
|
27
|
+
])),
|
|
20
28
|
])));
|
|
21
29
|
|
|
22
30
|
export const emstrong: EmStrongParser = lazy(() => creator(surround(
|
|
23
31
|
str('***'),
|
|
24
32
|
startTight(some(union([
|
|
25
|
-
some(inline,
|
|
33
|
+
some(inline, blankWith('*')),
|
|
26
34
|
open(some(inline, '*'), inline),
|
|
27
35
|
]))),
|
|
28
36
|
str(/^\*{1,3}/), false,
|
|
@@ -6,7 +6,6 @@ import { indexee, identity } from './indexee';
|
|
|
6
6
|
import { txt, str, stropt } from '../../source';
|
|
7
7
|
import { startTight, trimNodeEnd } from '../../util';
|
|
8
8
|
import { html, define, defrag } from 'typed-dom/dom';
|
|
9
|
-
import { join } from 'spica/array';
|
|
10
9
|
|
|
11
10
|
import IndexParser = ExtensionParser.IndexParser;
|
|
12
11
|
|
|
@@ -44,7 +43,7 @@ const signature: IndexParser.SignatureParser = lazy(() => creator(fmap(open(
|
|
|
44
43
|
'|#',
|
|
45
44
|
startTight(some(union([bracket, txt]), ']'))),
|
|
46
45
|
ns => [
|
|
47
|
-
html('span', { class: 'indexer', 'data-index': identity(join(
|
|
46
|
+
html('span', { class: 'indexer', 'data-index': identity(ns.join('')).slice(6) }),
|
|
48
47
|
])));
|
|
49
48
|
|
|
50
49
|
const bracket: IndexParser.SignatureParser.BracketParser = lazy(() => creator(union([
|
|
@@ -3,7 +3,6 @@ import { ExtensionParser } from '../../inline';
|
|
|
3
3
|
import { union, validate, guard, creator, surround, clear, fmap } from '../../../combinator';
|
|
4
4
|
import { str } from '../../source';
|
|
5
5
|
import { html } from 'typed-dom/dom';
|
|
6
|
-
import { join } from 'spica/array';
|
|
7
6
|
|
|
8
7
|
const body = str(/^\$[A-Za-z]*(?:(?:-[A-Za-z][0-9A-Za-z]*)+|-(?:(?:0|[1-9][0-9]*)\.)*(?:0|[1-9][0-9]*)(?![0-9A-Za-z]))/);
|
|
9
8
|
|
|
@@ -48,5 +47,5 @@ function increment(number: string, position: number): string {
|
|
|
48
47
|
? 0
|
|
49
48
|
: 1;
|
|
50
49
|
}
|
|
51
|
-
return join(
|
|
50
|
+
return ms.join('.');
|
|
52
51
|
}
|
|
@@ -29,11 +29,12 @@ describe('Unit: parser/inline/html', () => {
|
|
|
29
29
|
assert.deepStrictEqual(inspect(parser('<small>z')), undefined);
|
|
30
30
|
assert.deepStrictEqual(inspect(parser('<small></small>')), undefined);
|
|
31
31
|
assert.deepStrictEqual(inspect(parser('<small> </small>')), undefined);
|
|
32
|
+
assert.deepStrictEqual(inspect(parser('<small>\\ </small>')), undefined);
|
|
33
|
+
assert.deepStrictEqual(inspect(parser('<small>	</small>')), undefined);
|
|
34
|
+
assert.deepStrictEqual(inspect(parser('<small><wbr></small>')), undefined);
|
|
32
35
|
assert.deepStrictEqual(inspect(parser('<small>\n</small>')), undefined);
|
|
33
36
|
assert.deepStrictEqual(inspect(parser('<small>\na</small>')), undefined);
|
|
34
|
-
assert.deepStrictEqual(inspect(parser('<small>\\ a</small>')), undefined);
|
|
35
37
|
assert.deepStrictEqual(inspect(parser('<small>\\\na</small>')), undefined);
|
|
36
|
-
assert.deepStrictEqual(inspect(parser('<small><wbr></small>')), undefined);
|
|
37
38
|
assert.deepStrictEqual(inspect(parser('<small>a')), undefined);
|
|
38
39
|
assert.deepStrictEqual(inspect(parser('<small>a</BDO>')), undefined);
|
|
39
40
|
assert.deepStrictEqual(inspect(parser('<SMALL>a</SMALL>')), undefined);
|
|
@@ -41,7 +42,7 @@ describe('Unit: parser/inline/html', () => {
|
|
|
41
42
|
assert.deepStrictEqual(inspect(parser('</small>')), undefined);
|
|
42
43
|
assert.deepStrictEqual(inspect(parser('<small/>')), undefined);
|
|
43
44
|
assert.deepStrictEqual(inspect(parser('<b><b><b>a</b></b></b>')), [['<span class="invalid"><b><span class="invalid"><b><span class="invalid"><b>a</b></span></b></span></b></span>'], '']);
|
|
44
|
-
assert.deepStrictEqual(inspect(parser('<small><small><small>a</small></small></small>')), [['<small><
|
|
45
|
+
assert.deepStrictEqual(inspect(parser('<small><small><small>a</small></small></small>')), [['<small><small><small>a</small></small></small>'], '']);
|
|
45
46
|
assert.deepStrictEqual(inspect(parser('<x a="*b*"')), undefined);
|
|
46
47
|
assert.deepStrictEqual(inspect(parser('<x a="*b*">')), undefined);
|
|
47
48
|
assert.deepStrictEqual(inspect(parser('<x a="*b*">c')), undefined);
|
|
@@ -60,6 +61,8 @@ describe('Unit: parser/inline/html', () => {
|
|
|
60
61
|
assert.deepStrictEqual(inspect(parser('<small> a</small>')), [['<small> a</small>'], '']);
|
|
61
62
|
assert.deepStrictEqual(inspect(parser('<small> a </small>')), [['<small> a </small>'], '']);
|
|
62
63
|
assert.deepStrictEqual(inspect(parser('<small> a </small>')), [['<small> a </small>'], '']);
|
|
64
|
+
assert.deepStrictEqual(inspect(parser('<small>\\ a</small>')), [['<small> a</small>'], '']);
|
|
65
|
+
assert.deepStrictEqual(inspect(parser('<small><wbr>a</small>')), [['<small><wbr>a</small>'], '']);
|
|
63
66
|
assert.deepStrictEqual(inspect(parser('<small>a</small>')), [['<small>a</small>'], '']);
|
|
64
67
|
assert.deepStrictEqual(inspect(parser('<small>a</small>a')), [['<small>a</small>'], 'a']);
|
|
65
68
|
assert.deepStrictEqual(inspect(parser('<small>a </small>')), [['<small>a </small>'], '']);
|
|
@@ -72,8 +75,8 @@ describe('Unit: parser/inline/html', () => {
|
|
|
72
75
|
});
|
|
73
76
|
|
|
74
77
|
it('nest', () => {
|
|
75
|
-
assert.deepStrictEqual(inspect(parser('<small><small>a</small></small>')), [['<small><
|
|
76
|
-
assert.deepStrictEqual(inspect(parser('<small>a<small>b</small>c</small>')), [['<small>a<
|
|
78
|
+
assert.deepStrictEqual(inspect(parser('<small><small>a</small></small>')), [['<small><small>a</small></small>'], '']);
|
|
79
|
+
assert.deepStrictEqual(inspect(parser('<small>a<small>b</small>c</small>')), [['<small>a<small>b</small>c</small>'], '']);
|
|
77
80
|
assert.deepStrictEqual(inspect(parser('<small>`a`</small>')), [['<small><code data-src="`a`">a</code></small>'], '']);
|
|
78
81
|
});
|
|
79
82
|
|
|
@@ -1,15 +1,13 @@
|
|
|
1
|
-
import { undefined } from 'spica/global';
|
|
2
|
-
import { isFrozen, ObjectEntries } from 'spica/alias';
|
|
3
|
-
import { MarkdownParser } from '../../../markdown';
|
|
1
|
+
import { undefined, Object } from 'spica/global';
|
|
4
2
|
import { HTMLParser } from '../inline';
|
|
5
|
-
import { union, some, validate,
|
|
3
|
+
import { union, some, validate, creator, surround, open, match, lazy } from '../../combinator';
|
|
6
4
|
import { inline } from '../inline';
|
|
7
5
|
import { str } from '../source';
|
|
8
|
-
import { startLoose,
|
|
6
|
+
import { startLoose, blankWith } from '../util';
|
|
9
7
|
import { html as h, defrag } from 'typed-dom/dom';
|
|
10
8
|
import { memoize } from 'spica/memoize';
|
|
11
9
|
import { Cache } from 'spica/cache';
|
|
12
|
-
import { unshift, push, splice
|
|
10
|
+
import { unshift, push, splice } from 'spica/array';
|
|
13
11
|
|
|
14
12
|
const tags = Object.freeze(['wbr', 'sup', 'sub', 'small', 'bdo', 'bdi']);
|
|
15
13
|
const attrspec = {
|
|
@@ -31,78 +29,30 @@ export const html: HTMLParser = lazy(() => creator(validate('<', validate(/^<[a-
|
|
|
31
29
|
[[h(tag as 'span', attributes('html', [], attrspec[tag], bs))], rest]),
|
|
32
30
|
([, tag]) => tags.indexOf(tag), [])),
|
|
33
31
|
match(
|
|
34
|
-
/^(?=<(sup|sub|small)(?=[^\S\n]|>))/,
|
|
32
|
+
/^(?=<(sup|sub|small|bdo|bdi)(?=[^\S\n]|>))/,
|
|
35
33
|
memoize(
|
|
36
34
|
([, tag]) =>
|
|
37
|
-
validate(`<${tag}`, `</${tag}>`,
|
|
38
|
-
surround<HTMLParser.TagParser, string>(surround(
|
|
39
|
-
str(`<${tag}`), some(attribute), str(/^\s*>/), true),
|
|
40
|
-
startLoose(
|
|
41
|
-
context((() => {
|
|
42
|
-
switch (tag) {
|
|
43
|
-
case 'sup':
|
|
44
|
-
case 'sub':
|
|
45
|
-
return {
|
|
46
|
-
state: { in: { supsub: true } },
|
|
47
|
-
syntax: { inline: {
|
|
48
|
-
annotation: false,
|
|
49
|
-
reference: false,
|
|
50
|
-
media: false,
|
|
51
|
-
}},
|
|
52
|
-
};
|
|
53
|
-
case 'small':
|
|
54
|
-
return {
|
|
55
|
-
state: { in: { small: true } },
|
|
56
|
-
syntax: { inline: {
|
|
57
|
-
media: false,
|
|
58
|
-
}},
|
|
59
|
-
};
|
|
60
|
-
default:
|
|
61
|
-
assert(false);
|
|
62
|
-
return {};
|
|
63
|
-
}
|
|
64
|
-
})(),
|
|
65
|
-
some(union([
|
|
66
|
-
some(inline, blank(/\n?/, `</${tag}>`)),
|
|
67
|
-
open(/^\n?/, some(inline, '</'), true),
|
|
68
|
-
]), `</${tag}>`)), `</${tag}>`),
|
|
69
|
-
str(`</${tag}>`), false,
|
|
70
|
-
([as, bs, cs], rest, context) =>
|
|
71
|
-
[[elem(tag, as, defrag(bs), cs, context)], rest])),
|
|
72
|
-
([, tag]) => tags.indexOf(tag), [])),
|
|
73
|
-
match(
|
|
74
|
-
/^(?=<(bdo|bdi)(?=[^\S\n]|>))/,
|
|
75
|
-
memoize(
|
|
76
|
-
([, tag]) =>
|
|
77
|
-
validate(`<${tag}`, `</${tag}>`,
|
|
78
35
|
surround<HTMLParser.TagParser, string>(surround(
|
|
79
36
|
str(`<${tag}`), some(attribute), str(/^\s*>/), true),
|
|
80
37
|
startLoose(some(union([
|
|
81
|
-
some(inline,
|
|
82
|
-
open(/^\n?/, some(inline, '</'), true),
|
|
38
|
+
open(/^\n?/, some(inline, blankWith('\n', `</${tag}>`)), true),
|
|
83
39
|
]), `</${tag}>`), `</${tag}>`),
|
|
84
40
|
str(`</${tag}>`), false,
|
|
85
|
-
([as, bs, cs], rest
|
|
86
|
-
[[elem(tag, as, defrag(bs), cs
|
|
87
|
-
([as, bs], rest) =>
|
|
88
|
-
as.length === 1 ? [unshift(as, bs), rest] : undefined)),
|
|
41
|
+
([as, bs, cs], rest) =>
|
|
42
|
+
[[elem(tag, as, defrag(bs), cs)], rest]),
|
|
89
43
|
([, tag]) => tags.indexOf(tag), [])),
|
|
90
44
|
match(
|
|
91
45
|
/^(?=<([a-z]+)(?=[^\S\n]|>))/,
|
|
92
46
|
memoize(
|
|
93
47
|
([, tag]) =>
|
|
94
|
-
validate(`<${tag}`, `</${tag}>`,
|
|
95
48
|
surround<HTMLParser.TagParser, string>(surround(
|
|
96
49
|
str(`<${tag}`), some(attribute), str(/^\s*>/), true),
|
|
97
50
|
startLoose(some(union([
|
|
98
|
-
some(inline,
|
|
99
|
-
open(/^\n?/, some(inline, '</'), true),
|
|
51
|
+
open(/^\n?/, some(inline, blankWith('\n', `</${tag}>`)), true),
|
|
100
52
|
]), `</${tag}>`), `</${tag}>`),
|
|
101
53
|
str(`</${tag}>`), false,
|
|
102
|
-
([as, bs, cs], rest
|
|
103
|
-
[[elem(tag, as, defrag(bs), cs
|
|
104
|
-
([as, bs], rest) =>
|
|
105
|
-
as.length === 1 ? [unshift(as, bs), rest] : undefined)),
|
|
54
|
+
([as, bs, cs], rest) =>
|
|
55
|
+
[[elem(tag, as, defrag(bs), cs)], rest]),
|
|
106
56
|
([, tag]) => tag,
|
|
107
57
|
new Cache(10000))),
|
|
108
58
|
])))));
|
|
@@ -111,35 +61,16 @@ export const attribute: HTMLParser.TagParser.AttributeParser = union([
|
|
|
111
61
|
str(/^[^\S\n]+[a-z]+(?:-[a-z]+)*(?:="(?:\\[^\n]|[^\\\n"])*")?(?=[^\S\n]|>)/),
|
|
112
62
|
]);
|
|
113
63
|
|
|
114
|
-
function elem(tag: string, as: string[], bs: (HTMLElement | string)[], cs: string[]
|
|
64
|
+
function elem(tag: string, as: string[], bs: (HTMLElement | string)[], cs: string[]): HTMLElement {
|
|
115
65
|
assert(as.length > 0);
|
|
116
66
|
assert(as[0][0] === '<' && as[as.length - 1].slice(-1) === '>');
|
|
117
67
|
assert(bs.length === defrag(bs).length);
|
|
118
68
|
assert(cs.length === 1);
|
|
119
69
|
if (!tags.includes(tag)) return invalid('tag', `Invalid HTML tag <${tag}>`, as, bs, cs);
|
|
120
|
-
switch (tag) {
|
|
121
|
-
case 'sup':
|
|
122
|
-
case 'sub':
|
|
123
|
-
switch (true) {
|
|
124
|
-
case context.state?.in?.supsub:
|
|
125
|
-
return invalid('nest', `<${tag}> HTML tag cannot be used in <sup> or <sub> HTML tag`, as, bs, cs);
|
|
126
|
-
}
|
|
127
|
-
break;
|
|
128
|
-
case 'small':
|
|
129
|
-
switch (true) {
|
|
130
|
-
case context.state?.in?.supsub:
|
|
131
|
-
case context.state?.in?.small:
|
|
132
|
-
return invalid('nest', `<${tag}> HTML tag cannot be used in <sup>, <sub>, or <small> HTML tag`, as, bs, cs);
|
|
133
|
-
}
|
|
134
|
-
break;
|
|
135
|
-
}
|
|
136
70
|
const attrs = attributes('html', [], attrspec[tag], as.slice(1, -1));
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
default:
|
|
141
|
-
return h(tag as 'span', attrs, bs);
|
|
142
|
-
}
|
|
71
|
+
return 'data-invalid-syntax' in attrs
|
|
72
|
+
? invalid('attribute', 'Invalid HTML attribute', as, bs, cs)
|
|
73
|
+
: h(tag as 'span', attrs, bs);
|
|
143
74
|
}
|
|
144
75
|
function invalid(type: string, message: string, as: (HTMLElement | string)[], bs: (HTMLElement | string)[], cs: (HTMLElement | string)[]): HTMLElement {
|
|
145
76
|
return h('span', {
|
|
@@ -152,7 +83,7 @@ function invalid(type: string, message: string, as: (HTMLElement | string)[], bs
|
|
|
152
83
|
|
|
153
84
|
const requiredAttributes = memoize(
|
|
154
85
|
(spec: Readonly<Record<string, readonly (string | undefined)[] | undefined>>) =>
|
|
155
|
-
|
|
86
|
+
Object.entries(spec).flatMap(([k, v]) => v && Object.isFrozen(v) ? [k] : []),
|
|
156
87
|
new WeakMap());
|
|
157
88
|
|
|
158
89
|
export function attributes(
|
|
@@ -182,7 +113,7 @@ export function attributes(
|
|
|
182
113
|
}
|
|
183
114
|
invalid ||= !!spec && !requiredAttributes(spec).every(name => name in attrs);
|
|
184
115
|
if (invalid) {
|
|
185
|
-
attrs['class'] =
|
|
116
|
+
attrs['class'] = (classes.includes('invalid') ? classes : unshift(classes, ['invalid'])).join(' ');
|
|
186
117
|
attrs['data-invalid-syntax'] = syntax;
|
|
187
118
|
attrs['data-invalid-type'] = 'argument';
|
|
188
119
|
attrs['data-invalid-message'] = 'Invalid argument';
|
|
@@ -2,15 +2,15 @@ import { InsertionParser } from '../inline';
|
|
|
2
2
|
import { union, some, creator, surround, open, lazy } from '../../combinator';
|
|
3
3
|
import { inline } from '../inline';
|
|
4
4
|
import { str } from '../source';
|
|
5
|
-
import {
|
|
5
|
+
import { blankWith } from '../util';
|
|
6
6
|
import { html, defrag } from 'typed-dom/dom';
|
|
7
7
|
import { unshift } from 'spica/array';
|
|
8
8
|
|
|
9
9
|
export const insertion: InsertionParser = lazy(() => creator(surround(
|
|
10
10
|
str('++'),
|
|
11
11
|
some(union([
|
|
12
|
-
some(inline,
|
|
13
|
-
open(
|
|
12
|
+
some(inline, blankWith('\n', '++')),
|
|
13
|
+
open('\n', some(inline, '+'), true),
|
|
14
14
|
])),
|
|
15
15
|
str('++'), false,
|
|
16
16
|
([, bs], rest) => [[html('ins', defrag(bs))], rest],
|
|
@@ -67,8 +67,6 @@ describe('Unit: parser/inline/link', () => {
|
|
|
67
67
|
assert.deepStrictEqual(inspect(parser('[[]{b}')), undefined);
|
|
68
68
|
assert.deepStrictEqual(inspect(parser('[]]{b}')), undefined);
|
|
69
69
|
assert.deepStrictEqual(inspect(parser('[a]{}')), undefined);
|
|
70
|
-
assert.deepStrictEqual(inspect(parser('[\\ a]{b}')), undefined);
|
|
71
|
-
assert.deepStrictEqual(inspect(parser('[ \\ a]{b}')), undefined);
|
|
72
70
|
assert.deepStrictEqual(inspect(parser('[a\nb]{b}')), undefined);
|
|
73
71
|
assert.deepStrictEqual(inspect(parser('[a\\\nb]{b}')), undefined);
|
|
74
72
|
assert.deepStrictEqual(inspect(parser('[<wbr>]{b}')), undefined);
|
|
@@ -118,6 +116,8 @@ describe('Unit: parser/inline/link', () => {
|
|
|
118
116
|
assert.deepStrictEqual(inspect(parser('[]{^/b}', { host: new URL('/0.0,0.0,0z', location.origin) })), [[`<a href="/0.0,0.0,0z/b">^/b</a>`], '']);
|
|
119
117
|
assert.deepStrictEqual(inspect(parser('[ a]{b}')), [['<a href="b">a</a>'], '']);
|
|
120
118
|
assert.deepStrictEqual(inspect(parser('[ a ]{b}')), [['<a href="b">a</a>'], '']);
|
|
119
|
+
assert.deepStrictEqual(inspect(parser('[\\ a]{b}')), [['<a href="b">a</a>'], '']);
|
|
120
|
+
assert.deepStrictEqual(inspect(parser('[ \\ a]{b}')), [['<a href="b">a</a>'], '']);
|
|
121
121
|
assert.deepStrictEqual(inspect(parser('[a ]{b}')), [['<a href="b">a</a>'], '']);
|
|
122
122
|
assert.deepStrictEqual(inspect(parser('[a ]{b}')), [['<a href="b">a</a>'], '']);
|
|
123
123
|
assert.deepStrictEqual(inspect(parser('[a]{b}')), [['<a href="b">a</a>'], '']);
|
|
@@ -6,7 +6,7 @@ import { inline, media, shortmedia } from '../inline';
|
|
|
6
6
|
import { attributes } from './html';
|
|
7
7
|
import { autolink } from '../autolink';
|
|
8
8
|
import { str } from '../source';
|
|
9
|
-
import {
|
|
9
|
+
import { trimBlankInline, stringify } from '../util';
|
|
10
10
|
import { html, define, defrag } from 'typed-dom/dom';
|
|
11
11
|
import { ReadonlyURL } from 'spica/url';
|
|
12
12
|
|
|
@@ -26,7 +26,6 @@ export const link: LinkParser = lazy(() => creator(10, validate(['[', '{'], '}',
|
|
|
26
26
|
surround('[', shortmedia, ']'),
|
|
27
27
|
surround(
|
|
28
28
|
'[',
|
|
29
|
-
startLoose(
|
|
30
29
|
context({ syntax: { inline: {
|
|
31
30
|
annotation: false,
|
|
32
31
|
reference: false,
|
|
@@ -37,7 +36,7 @@ export const link: LinkParser = lazy(() => creator(10, validate(['[', '{'], '}',
|
|
|
37
36
|
media: false,
|
|
38
37
|
autolink: false,
|
|
39
38
|
}}},
|
|
40
|
-
|
|
39
|
+
trimBlankInline(some(inline, ']', /^\\?\n/))),
|
|
41
40
|
']',
|
|
42
41
|
true),
|
|
43
42
|
]))),
|
|
@@ -52,7 +51,7 @@ export const link: LinkParser = lazy(() => creator(10, validate(['[', '{'], '}',
|
|
|
52
51
|
assert(!INSECURE_URI.match(/\s/));
|
|
53
52
|
const el = elem(
|
|
54
53
|
INSECURE_URI,
|
|
55
|
-
|
|
54
|
+
defrag(content),
|
|
56
55
|
new ReadonlyURL(
|
|
57
56
|
resolve(INSECURE_URI, context.host ?? location, context.url ?? context.host ?? location),
|
|
58
57
|
context.host?.href || location.href),
|
|
@@ -12,10 +12,11 @@ describe('Unit: parser/inline/mark', () => {
|
|
|
12
12
|
assert.deepStrictEqual(inspect(parser('==')), undefined);
|
|
13
13
|
assert.deepStrictEqual(inspect(parser('==a')), [['==', 'a'], '']);
|
|
14
14
|
assert.deepStrictEqual(inspect(parser('==a=')), [['==', 'a', '='], '']);
|
|
15
|
-
assert.deepStrictEqual(inspect(parser('==a ==')), [['==', 'a', '
|
|
16
|
-
assert.deepStrictEqual(inspect(parser('==a
|
|
17
|
-
assert.deepStrictEqual(inspect(parser('==a
|
|
18
|
-
assert.deepStrictEqual(inspect(parser('==a
|
|
15
|
+
assert.deepStrictEqual(inspect(parser('==a ==')), [['==', 'a'], ' ==']);
|
|
16
|
+
assert.deepStrictEqual(inspect(parser('==a ==')), [['==', 'a', ' '], ' ==']);
|
|
17
|
+
assert.deepStrictEqual(inspect(parser('==a\n==')), [['==', 'a'], '\n==']);
|
|
18
|
+
assert.deepStrictEqual(inspect(parser('==a\\ ==')), [['==', 'a'], '\\ ==']);
|
|
19
|
+
assert.deepStrictEqual(inspect(parser('==a\\\n==')), [['==', 'a'], '\\\n==']);
|
|
19
20
|
assert.deepStrictEqual(inspect(parser('== ==')), undefined);
|
|
20
21
|
assert.deepStrictEqual(inspect(parser('== a==')), undefined);
|
|
21
22
|
assert.deepStrictEqual(inspect(parser('== a ==')), undefined);
|
|
@@ -2,15 +2,15 @@ import { MarkParser } from '../inline';
|
|
|
2
2
|
import { union, some, creator, surround, open, lazy } from '../../combinator';
|
|
3
3
|
import { inline } from '../inline';
|
|
4
4
|
import { str } from '../source';
|
|
5
|
-
import { startTight,
|
|
5
|
+
import { startTight, blankWith } from '../util';
|
|
6
6
|
import { html, defrag } from 'typed-dom/dom';
|
|
7
7
|
import { unshift } from 'spica/array';
|
|
8
8
|
|
|
9
9
|
export const mark: MarkParser = lazy(() => creator(surround(
|
|
10
10
|
str('=='),
|
|
11
11
|
startTight(some(union([
|
|
12
|
-
some(inline,
|
|
13
|
-
open(some(inline, '='),
|
|
12
|
+
some(inline, blankWith('==')),
|
|
13
|
+
open(some(inline, '='), mark),
|
|
14
14
|
]))),
|
|
15
15
|
str('=='), false,
|
|
16
16
|
([, bs], rest) => [[html('mark', defrag(bs))], rest],
|