securemark 0.263.1 → 0.264.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.
Files changed (48) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/index.js +1205 -3025
  3. package/markdown.d.ts +1 -1
  4. package/package.json +18 -18
  5. package/src/combinator/data/parser/context.ts +3 -2
  6. package/src/parser/api/bind.test.ts +1 -1
  7. package/src/parser/api/parse.test.ts +3 -3
  8. package/src/parser/block/blockquote.test.ts +3 -3
  9. package/src/parser/block/dlist.ts +2 -3
  10. package/src/parser/block/extension/example.test.ts +1 -1
  11. package/src/parser/block/extension/fig.ts +2 -1
  12. package/src/parser/block/extension/figure.ts +2 -3
  13. package/src/parser/block/extension/table.ts +2 -3
  14. package/src/parser/block/heading.test.ts +1 -1
  15. package/src/parser/block/paragraph.test.ts +1 -1
  16. package/src/parser/block/paragraph.ts +2 -3
  17. package/src/parser/block/reply.ts +2 -3
  18. package/src/parser/context.ts +15 -19
  19. package/src/parser/inline/annotation.ts +1 -1
  20. package/src/parser/inline/bracket.test.ts +1 -1
  21. package/src/parser/inline/bracket.ts +1 -1
  22. package/src/parser/inline/deletion.test.ts +2 -2
  23. package/src/parser/inline/emphasis.test.ts +2 -2
  24. package/src/parser/inline/emphasis.ts +2 -2
  25. package/src/parser/inline/extension/index.test.ts +28 -32
  26. package/src/parser/inline/extension/index.ts +1 -1
  27. package/src/parser/inline/extension/indexee.ts +22 -12
  28. package/src/parser/inline/extension/placeholder.test.ts +10 -12
  29. package/src/parser/inline/extension/placeholder.ts +7 -7
  30. package/src/parser/inline/html.test.ts +1 -1
  31. package/src/parser/inline/html.ts +2 -2
  32. package/src/parser/inline/insertion.test.ts +2 -2
  33. package/src/parser/inline/link.test.ts +6 -5
  34. package/src/parser/inline/link.ts +43 -42
  35. package/src/parser/inline/mark.test.ts +11 -11
  36. package/src/parser/inline/mark.ts +12 -6
  37. package/src/parser/inline/strong.test.ts +2 -2
  38. package/src/parser/inline/strong.ts +2 -2
  39. package/src/parser/inline.test.ts +8 -7
  40. package/src/parser/source/escapable.test.ts +1 -1
  41. package/src/parser/source/escapable.ts +7 -1
  42. package/src/parser/source/text.test.ts +24 -35
  43. package/src/parser/source/text.ts +2 -15
  44. package/src/parser/visibility.ts +1 -6
  45. package/src/parser/locale/ja.test.ts +0 -14
  46. package/src/parser/locale/ja.ts +0 -3
  47. package/src/parser/locale.test.ts +0 -26
  48. package/src/parser/locale.ts +0 -61
@@ -22,12 +22,6 @@ describe('Unit: parser/inline/extension/placeholder', () => {
22
22
  assert.deepStrictEqual(inspect(parser('[^\\\na]')), undefined);
23
23
  assert.deepStrictEqual(inspect(parser('[^ !http://host]')), undefined);
24
24
  assert.deepStrictEqual(inspect(parser('[^a')), [['[^', 'a'], '']);
25
- assert.deepStrictEqual(inspect(parser('[^a\n]')), [['[^', 'a'], '\n]']);
26
- assert.deepStrictEqual(inspect(parser('[^a\n\n]')), [['[^', 'a'], '\n\n]']);
27
- assert.deepStrictEqual(inspect(parser('[^a\\\n]')), [['[^', 'a'], '\\\n]']);
28
- assert.deepStrictEqual(inspect(parser('[^a\\\n\\\n]')), [['[^', 'a'], '\\\n\\\n]']);
29
- assert.deepStrictEqual(inspect(parser('[^a\nb]')), [['[^', 'a'], '\nb]']);
30
- assert.deepStrictEqual(inspect(parser('[^a\\\nb]')), [['[^', 'a'], '\\\nb]']);
31
25
  assert.deepStrictEqual(inspect(parser('[[]')), undefined);
32
26
  assert.deepStrictEqual(inspect(parser('[]]')), undefined);
33
27
  assert.deepStrictEqual(inspect(parser('[[]]')), undefined);
@@ -39,12 +33,16 @@ describe('Unit: parser/inline/extension/placeholder', () => {
39
33
  it('valid', () => {
40
34
  assert.deepStrictEqual(inspect(parser('[^a]')), [['<span class="invalid">a</span>'], '']);
41
35
  assert.deepStrictEqual(inspect(parser('[^a b]')), [['<span class="invalid">a b</span>'], '']);
42
- assert.deepStrictEqual(inspect(parser('[^a ]')), [['<span class="invalid">a </span>'], '']);
43
- assert.deepStrictEqual(inspect(parser('[^a ]')), [['<span class="invalid">a </span>'], '']);
44
- assert.deepStrictEqual(inspect(parser('[^a\\ ]')), [['<span class="invalid">a </span>'], '']);
45
- assert.deepStrictEqual(inspect(parser('[^a\\ \\ ]')), [['<span class="invalid">a </span>'], '']);
46
- assert.deepStrictEqual(inspect(parser('[^a<wbr>]')), [['<span class="invalid">a<wbr></span>'], '']);
47
- assert.deepStrictEqual(inspect(parser('[^a<wbr><wbr>]')), [['<span class="invalid">a<wbr><wbr></span>'], '']);
36
+ assert.deepStrictEqual(inspect(parser('[^a ]')), [['<span class="invalid">a</span>'], '']);
37
+ assert.deepStrictEqual(inspect(parser('[^a ]')), [['<span class="invalid">a</span>'], '']);
38
+ assert.deepStrictEqual(inspect(parser('[^a\\ ]')), [['<span class="invalid">a</span>'], '']);
39
+ assert.deepStrictEqual(inspect(parser('[^a\\ \\ ]')), [['<span class="invalid">a</span>'], '']);
40
+ assert.deepStrictEqual(inspect(parser('[^a\n]')), [['<span class="invalid">a</span>'], '']);
41
+ assert.deepStrictEqual(inspect(parser('[^a\\\n]')), [['<span class="invalid">a</span>'], '']);
42
+ assert.deepStrictEqual(inspect(parser('[^a\nb]')), [['<span class="invalid">a<br>b</span>'], '']);
43
+ assert.deepStrictEqual(inspect(parser('[^a\\\nb]')), [['<span class="invalid">a<br>b</span>'], '']);
44
+ assert.deepStrictEqual(inspect(parser('[^a<wbr>]')), [['<span class="invalid">a</span>'], '']);
45
+ assert.deepStrictEqual(inspect(parser('[^a<wbr><wbr>]')), [['<span class="invalid">a</span>'], '']);
48
46
  assert.deepStrictEqual(inspect(parser('[^==]')), [['<span class="invalid">==</span>'], '']);
49
47
  assert.deepStrictEqual(inspect(parser('[^a[% b %]]')), [['<span class="invalid">a<span class="comment"><input type="checkbox"><span>[% b %]</span></span></span>'], '']);
50
48
  assert.deepStrictEqual(inspect(parser('[^a[% b %][% c %]]')), [['<span class="invalid">a<span class="comment"><input type="checkbox"><span>[% b %]</span></span><span class="comment"><input type="checkbox"><span>[% c %]</span></span></span>'], '']);
@@ -3,25 +3,25 @@ import { union, some, syntax, validate, surround, lazy } from '../../../combinat
3
3
  import { inline } from '../../inline';
4
4
  import { str } from '../../source';
5
5
  import { Syntax, State } from '../../context';
6
- import { startTight } from '../../visibility';
6
+ import { startTight, trimBlankEnd } from '../../visibility';
7
7
  import { unshift } from 'spica/array';
8
8
  import { html, defrag } from 'typed-dom/dom';
9
9
 
10
- // Don't use the symbols already used: !#$%@&*+~=
10
+ // Don't use the symbols already used: !#$%@&*+~=|
11
11
 
12
12
  // All syntax surrounded by square brackets shouldn't contain line breaks.
13
13
 
14
- export const placeholder: ExtensionParser.PlaceholderParser = lazy(() => validate(['[:', '[^'], surround(
15
- str(/^\[[:^]/),
14
+ export const placeholder: ExtensionParser.PlaceholderParser = lazy(() => validate('[', surround(
15
+ str(/^\[[:^|]/),
16
16
  syntax(Syntax.placeholder, 2, 1, State.none,
17
- startTight(some(union([inline]), ']', [[/^\\?\n/, 9], [']', 2]]))),
17
+ startTight(trimBlankEnd(some(union([inline]), ']', [[']', 2]])))),
18
18
  str(']'), false,
19
- ([as, bs], rest) => [[
19
+ ([, bs], rest) => [[
20
20
  html('span', {
21
21
  class: 'invalid',
22
22
  'data-invalid-syntax': 'extension',
23
23
  'data-invalid-type': 'syntax',
24
- 'data-invalid-message': `Reserved start symbol "${as[0][1]}" cannot be used in "[]"`,
24
+ 'data-invalid-message': `Invalid start symbol or linebreak`,
25
25
  }, defrag(bs)),
26
26
  ], rest],
27
27
  ([as, bs], rest) => [unshift(as, bs), rest])));
@@ -34,7 +34,7 @@ describe('Unit: parser/inline/html', () => {
34
34
  assert.deepStrictEqual(inspect(parser('<bdi><wbr></bdi>')), [['<span class="invalid">&lt;bdi&gt;<wbr>&lt;/bdi&gt;</span>'], '']);
35
35
  assert.deepStrictEqual(inspect(parser('<bdi>\n</bdi>')), [['<span class="invalid">&lt;bdi&gt;<br>&lt;/bdi&gt;</span>'], '']);
36
36
  assert.deepStrictEqual(inspect(parser('<bdi>\na</bdi>')), [['<span class="invalid">&lt;bdi&gt;<br>a&lt;/bdi&gt;</span>'], '']);
37
- assert.deepStrictEqual(inspect(parser('<bdi>\\\na</bdi>')), [['<span class="invalid">&lt;bdi&gt;<span class="linebreak"> </span>a&lt;/bdi&gt;</span>'], '']);
37
+ assert.deepStrictEqual(inspect(parser('<bdi>\\\na</bdi>')), [['<span class="invalid">&lt;bdi&gt;<br>a&lt;/bdi&gt;</span>'], '']);
38
38
  assert.deepStrictEqual(inspect(parser('<bdi>a')), [['<span class="invalid">&lt;bdi&gt;a</span>'], '']);
39
39
  assert.deepStrictEqual(inspect(parser('<bdi>a</BDO>')), [['<span class="invalid">&lt;bdi&gt;a&lt;/BDO&gt;</span>'], '']);
40
40
  assert.deepStrictEqual(inspect(parser('<BDI>a</BDI>')), [['<span class="invalid">&lt;BDI&gt;a&lt;/BDI&gt;</span>'], '']);
@@ -5,7 +5,7 @@ import { str } from '../source';
5
5
  import { Syntax, State } from '../context';
6
6
  import { isStartLooseNodes, blankWith } from '../visibility';
7
7
  import { memoize } from 'spica/memoize';
8
- import { Cache } from 'spica/cache';
8
+ import { Clock } from 'spica/clock';
9
9
  import { unshift, push, splice } from 'spica/array';
10
10
  import { html as h, defrag } from 'typed-dom/dom';
11
11
 
@@ -59,7 +59,7 @@ export const html: HTMLParser = lazy(() => validate('<', validate(/^<[a-z]+(?=[^
59
59
  ([as, bs = []], rest) =>
60
60
  [[elem(tag, as, bs, [])], rest]),
61
61
  ([, tag]) => tag,
62
- new Cache(10000))),
62
+ new Clock(10000))),
63
63
  ])))));
64
64
 
65
65
  export const attribute: HTMLParser.AttributeParser = union([
@@ -23,7 +23,7 @@ describe('Unit: parser/inline/insertion', () => {
23
23
  assert.deepStrictEqual(inspect(parser('++ a ++')), [['<ins> a </ins>'], '']);
24
24
  assert.deepStrictEqual(inspect(parser('++ a ++')), [['<ins> a </ins>'], '']);
25
25
  assert.deepStrictEqual(inspect(parser('++\na++')), [['<ins><br>a</ins>'], '']);
26
- assert.deepStrictEqual(inspect(parser('++\\\na++')), [['<ins><span class="linebreak"> </span>a</ins>'], '']);
26
+ assert.deepStrictEqual(inspect(parser('++\\\na++')), [['<ins><br>a</ins>'], '']);
27
27
  assert.deepStrictEqual(inspect(parser('++<wbr>a++')), [['<ins><wbr>a</ins>'], '']);
28
28
  assert.deepStrictEqual(inspect(parser('++a ++')), [['<ins>a </ins>'], '']);
29
29
  assert.deepStrictEqual(inspect(parser('++a \n ++')), [['<ins>a </ins>'], '']);
@@ -31,7 +31,7 @@ describe('Unit: parser/inline/insertion', () => {
31
31
  assert.deepStrictEqual(inspect(parser('++a\n ++')), [['<ins>a </ins>'], '']);
32
32
  assert.deepStrictEqual(inspect(parser('++a\n<wbr>++')), [['<ins>a<wbr></ins>'], '']);
33
33
  assert.deepStrictEqual(inspect(parser('++a\nb++')), [['<ins>a<br>b</ins>'], '']);
34
- assert.deepStrictEqual(inspect(parser('++a\\\nb++')), [['<ins>a<span class="linebreak"> </span>b</ins>'], '']);
34
+ assert.deepStrictEqual(inspect(parser('++a\\\nb++')), [['<ins>a<br>b</ins>'], '']);
35
35
  assert.deepStrictEqual(inspect(parser('++\\+++')), [['<ins>+</ins>'], '']);
36
36
  assert.deepStrictEqual(inspect(parser('++a+++')), [['<ins>a</ins>'], '+']);
37
37
  });
@@ -32,11 +32,11 @@ describe('Unit: parser/inline/link', () => {
32
32
  assert.deepStrictEqual(inspect(parser('[mailto:á]{http://evil}')), undefined);
33
33
  assert.deepStrictEqual(inspect(parser('[file:///]{http://evil}')), undefined);
34
34
  assert.deepStrictEqual(inspect(parser('[.http://á]{http://evil}')), undefined);
35
- assert.deepStrictEqual(inspect(parser('[0987654321]{tel:1234567890}')), undefined);
36
- assert.deepStrictEqual(inspect(parser('[1234567890-]{tel:1234567890}')), undefined);
37
- assert.deepStrictEqual(inspect(parser('[-1234567890]{tel:1234567890}')), undefined);
38
- assert.deepStrictEqual(inspect(parser('[123456789a]{tel:1234567890}')), undefined);
39
- assert.deepStrictEqual(inspect(parser('[1234567890]{tel:ttel:1234567890}')), undefined);
35
+ assert.deepStrictEqual(inspect(parser('[0987654321]{tel:1234567890}')), [['<a class="invalid">0987654321</a>'], '']);
36
+ assert.deepStrictEqual(inspect(parser('[1234567890-]{tel:1234567890}')), [['<a class="invalid">1234567890-</a>'], '']);
37
+ assert.deepStrictEqual(inspect(parser('[-1234567890]{tel:1234567890}')), [['<a class="invalid">-1234567890</a>'], '']);
38
+ assert.deepStrictEqual(inspect(parser('[123456789a]{tel:1234567890}')), [['<a class="invalid">123456789a</a>'], '']);
39
+ assert.deepStrictEqual(inspect(parser('[1234567890]{tel:ttel:1234567890}')), [['<a class="invalid">1234567890</a>'], '']);
40
40
  //assert.deepStrictEqual(inspect(parser('[#a]{b}')), undefined);
41
41
  //assert.deepStrictEqual(inspect(parser('[\\#a]{b}')), undefined);
42
42
  //assert.deepStrictEqual(inspect(parser('[c #a]{b}')), undefined);
@@ -145,6 +145,7 @@ describe('Unit: parser/inline/link', () => {
145
145
  assert.deepStrictEqual(inspect(parser('{tel:+1234567890}')), [[`<a class="tel" href="tel:+1234567890">tel:+1234567890</a>`], '']);
146
146
  assert.deepStrictEqual(inspect(parser('{tel:+12-345-67-890}')), [[`<a class="tel" href="tel:+12-345-67-890">tel:+12-345-67-890</a>`], '']);
147
147
  assert.deepStrictEqual(inspect(parser('[1234567890]{tel:1234567890}')), [[`<a class="tel" href="tel:1234567890">1234567890</a>`], '']);
148
+ assert.deepStrictEqual(inspect(parser('[1234567890]{tel:12-3456-7890}')), [[`<a class="tel" href="tel:12-3456-7890">1234567890</a>`], '']);
148
149
  assert.deepStrictEqual(inspect(parser('[12-3456-7890]{tel:1234567890}')), [[`<a class="tel" href="tel:1234567890">12-3456-7890</a>`], '']);
149
150
  assert.deepStrictEqual(inspect(parser('[+12-34567-890]{tel:+12-345-67890}')), [[`<a class="tel" href="tel:+12-345-67890">+12-34567-890</a>`], '']);
150
151
  });
@@ -98,22 +98,6 @@ function parse(
98
98
  const uri = new ReadonlyURL(
99
99
  resolve(INSECURE_URI, context.host ?? location, context.url ?? context.host ?? location),
100
100
  context.host?.href || location.href);
101
- switch (uri.protocol) {
102
- case 'tel:': {
103
- const tel = content.length === 0
104
- ? INSECURE_URI
105
- : content[0];
106
- const pattern = /^(?:tel:)?(?:\+(?!0))?\d+(?:-\d+)*$/i;
107
- if (content.length <= 1 &&
108
- typeof tel === 'string' &&
109
- pattern.test(tel) &&
110
- pattern.test(INSECURE_URI) &&
111
- tel.replace(/[^+\d]/g, '') === INSECURE_URI.replace(/[^+\d]/g, '')) {
112
- break;
113
- }
114
- return;
115
- }
116
- }
117
101
  const el = elem(
118
102
  INSECURE_URI,
119
103
  content,
@@ -135,35 +119,52 @@ function elem(
135
119
  case 'http:':
136
120
  case 'https:':
137
121
  assert(uri.host);
138
- if (INSECURE_URI.slice(0, 2) === '^/' &&
139
- /\/\.\.?(?:\/|$)/.test(INSECURE_URI.slice(0, INSECURE_URI.search(/[?#]|$/)))) {
140
- type = 'argument';
141
- message = 'Dot-segments cannot be used in subresource paths';
142
- break;
122
+ switch (true) {
123
+ case INSECURE_URI.slice(0, 2) === '^/'
124
+ && /\/\.\.?(?:\/|$)/.test(INSECURE_URI.slice(0, INSECURE_URI.search(/[?#]|$/))):
125
+ type = 'argument';
126
+ message = 'Dot-segments cannot be used in subresource paths';
127
+ break;
128
+ default:
129
+ return html('a',
130
+ {
131
+ class: content.length === 0 ? 'url' : 'link',
132
+ href: uri.source,
133
+ target: undefined
134
+ || uri.origin !== origin
135
+ || typeof content[0] === 'object' && content[0].classList.contains('media')
136
+ ? '_blank'
137
+ : undefined,
138
+ },
139
+ content.length === 0
140
+ ? decode(INSECURE_URI)
141
+ : content);
143
142
  }
144
- return html('a',
145
- {
146
- class: content.length === 0 ? 'url' : 'link',
147
- href: uri.source,
148
- target: undefined
149
- || uri.origin !== origin
150
- || typeof content[0] === 'object' && content[0].classList.contains('media')
151
- ? '_blank'
152
- : undefined,
153
- },
154
- content.length === 0
155
- ? decode(INSECURE_URI)
156
- : content);
143
+ break;
157
144
  case 'tel:':
158
145
  assert(content.length <= 1);
159
- return html('a',
160
- {
161
- class: 'tel',
162
- href: uri.source,
163
- },
164
- content.length === 0
165
- ? [INSECURE_URI]
166
- : content);
146
+ const tel = content.length === 0
147
+ ? INSECURE_URI
148
+ : content[0];
149
+ const pattern = /^(?:tel:)?(?:\+(?!0))?\d+(?:-\d+)*$/i;
150
+ switch (true) {
151
+ case content.length <= 1
152
+ && pattern.test(INSECURE_URI)
153
+ && typeof tel === 'string'
154
+ && pattern.test(tel)
155
+ && tel.replace(/[^+\d]/g, '') === INSECURE_URI.replace(/[^+\d]/g, ''):
156
+ return html('a',
157
+ {
158
+ class: 'tel',
159
+ href: uri.source,
160
+ },
161
+ content.length === 0
162
+ ? [INSECURE_URI]
163
+ : content);
164
+ default:
165
+ type = 'content';
166
+ message = 'Invalid content';
167
+ }
167
168
  }
168
169
  return html('a',
169
170
  {
@@ -15,8 +15,10 @@ describe('Unit: parser/inline/mark', () => {
15
15
  assert.deepStrictEqual(inspect(parser('==a ==')), [['==', 'a'], ' ==']);
16
16
  assert.deepStrictEqual(inspect(parser('==a ==')), [['==', 'a', ' '], ' ==']);
17
17
  assert.deepStrictEqual(inspect(parser('==a\n==')), [['==', 'a'], '\n==']);
18
+ assert.deepStrictEqual(inspect(parser('==a\nb==')), [['==', 'a'], '\nb==']);
18
19
  assert.deepStrictEqual(inspect(parser('==a\\ ==')), [['==', 'a'], '\\ ==']);
19
20
  assert.deepStrictEqual(inspect(parser('==a\\\n==')), [['==', 'a'], '\\\n==']);
21
+ assert.deepStrictEqual(inspect(parser('==a\\\nb==')), [['==', 'a'], '\\\nb==']);
20
22
  assert.deepStrictEqual(inspect(parser('== ==')), undefined);
21
23
  assert.deepStrictEqual(inspect(parser('== a==')), undefined);
22
24
  assert.deepStrictEqual(inspect(parser('== a ==')), undefined);
@@ -27,20 +29,18 @@ describe('Unit: parser/inline/mark', () => {
27
29
  });
28
30
 
29
31
  it('basic', () => {
30
- assert.deepStrictEqual(inspect(parser('==a==')), [['<mark>a</mark>'], '']);
31
- assert.deepStrictEqual(inspect(parser('==a=b==')), [['<mark>a=b</mark>'], '']);
32
- assert.deepStrictEqual(inspect(parser('==a\nb==')), [['<mark>a<br>b</mark>'], '']);
33
- assert.deepStrictEqual(inspect(parser('==a\\\nb==')), [['<mark>a<span class="linebreak"> </span>b</mark>'], '']);
34
- assert.deepStrictEqual(inspect(parser('==\\===')), [['<mark>=</mark>'], '']);
35
- assert.deepStrictEqual(inspect(parser('==a===')), [['<mark>a</mark>'], '=']);
32
+ assert.deepStrictEqual(inspect(parser('==a==')), [['<mark id="mark:a">a</mark>', '<a href="#mark:a"></a>'], '']);
33
+ assert.deepStrictEqual(inspect(parser('==a=b==')), [['<mark id="mark:a=b">a=b</mark>', '<a href="#mark:a=b"></a>'], '']);
34
+ assert.deepStrictEqual(inspect(parser('==\\===')), [['<mark id="mark:=">=</mark>', '<a href="#mark:="></a>'], '']);
35
+ assert.deepStrictEqual(inspect(parser('==a===')), [['<mark id="mark:a">a</mark>', '<a href="#mark:a"></a>'], '=']);
36
36
  });
37
37
 
38
38
  it('nest', () => {
39
- assert.deepStrictEqual(inspect(parser('==a ==b====')), [['<mark>a <mark>b</mark></mark>'], '']);
40
- assert.deepStrictEqual(inspect(parser('==a\\ ==b====')), [['<mark>a <mark>b</mark></mark>'], '']);
41
- assert.deepStrictEqual(inspect(parser('==a&Tab;==b====')), [['<mark>a\t<mark>b</mark></mark>'], '']);
42
- assert.deepStrictEqual(inspect(parser('==a<wbr>==b====')), [['<mark>a<wbr><mark>b</mark></mark>'], '']);
43
- assert.deepStrictEqual(inspect(parser('==_==a==_==')), [['<mark><em><mark>a</mark></em></mark>'], '']);
39
+ assert.deepStrictEqual(inspect(parser('==a ==b====')), [['<mark id="mark:a_b">a <mark id="mark:b">b</mark><a href="#mark:b"></a></mark>', '<a href="#mark:a_b"></a>'], '']);
40
+ assert.deepStrictEqual(inspect(parser('==a\\ ==b====')), [['<mark id="mark:a_b">a <mark id="mark:b">b</mark><a href="#mark:b"></a></mark>', '<a href="#mark:a_b"></a>'], '']);
41
+ assert.deepStrictEqual(inspect(parser('==a&Tab;==b====')), [['<mark id="mark:a_b">a\t<mark id="mark:b">b</mark><a href="#mark:b"></a></mark>', '<a href="#mark:a_b"></a>'], '']);
42
+ assert.deepStrictEqual(inspect(parser('==a<wbr>==b====')), [['<mark id="mark:ab">a<wbr><mark id="mark:b">b</mark><a href="#mark:b"></a></mark>', '<a href="#mark:ab"></a>'], '']);
43
+ assert.deepStrictEqual(inspect(parser('==_==a==_==')), [['<mark id="mark:a"><em><mark id="mark:a">a</mark><a href="#mark:a"></a></em></mark>', '<a href="#mark:a"></a>'], '']);
44
44
  });
45
45
 
46
46
  });
@@ -1,19 +1,25 @@
1
1
  import { MarkParser } from '../inline';
2
- import { union, some, syntax, surround, open, lazy } from '../../combinator';
2
+ import { union, some, syntax, constraint, surround, open, lazy } from '../../combinator';
3
3
  import { inline } from '../inline';
4
+ import { identity, text } from './extension/indexee';
4
5
  import { str } from '../source';
5
6
  import { startTight, blankWith } from '../visibility';
6
7
  import { Syntax, State } from '../context';
7
8
  import { unshift } from 'spica/array';
8
- import { html, defrag } from 'typed-dom/dom';
9
+ import { html, define, defrag } from 'typed-dom/dom';
9
10
 
10
11
  export const mark: MarkParser = lazy(() => surround(
11
12
  str('==', '='),
13
+ constraint(State.mark, false,
12
14
  syntax(Syntax.none, 1, 1, State.none,
13
15
  startTight(some(union([
14
- some(inline, blankWith('==')),
15
- open(some(inline, '='), mark),
16
- ])))),
16
+ some(inline, blankWith('=='), [[/^\\?\n/, 9]]),
17
+ open(some(inline, '=', [[/^\\?\n/, 9]]), mark),
18
+ ]))))),
17
19
  str('=='), false,
18
- ([, bs], rest) => [[html('mark', defrag(bs))], rest],
20
+ ([, bs], rest, { id }) => {
21
+ const el = html('mark', defrag(bs));
22
+ define(el, { id: id !== '' && identity(text(el), 'mark') || undefined });
23
+ return [[el, html('a', { href: el.id ? `#${el.id}` : undefined })], rest];
24
+ },
19
25
  ([as, bs], rest) => [unshift(as, bs), rest]));
@@ -12,8 +12,10 @@ describe('Unit: parser/inline/strong', () => {
12
12
  assert.deepStrictEqual(inspect(parser('*a *')), [['*', 'a'], ' *']);
13
13
  assert.deepStrictEqual(inspect(parser('*a *')), [['*', 'a', ' '], ' *']);
14
14
  assert.deepStrictEqual(inspect(parser('*a\n*')), [['*', 'a'], '\n*']);
15
+ assert.deepStrictEqual(inspect(parser('*a\nb*')), [['*', 'a'], '\nb*']);
15
16
  assert.deepStrictEqual(inspect(parser('*a\\ *')), [['*', 'a'], '\\ *']);
16
17
  assert.deepStrictEqual(inspect(parser('*a\\\n*')), [['*', 'a'], '\\\n*']);
18
+ assert.deepStrictEqual(inspect(parser('*a\\\nb*')), [['*', 'a'], '\\\nb*']);
17
19
  assert.deepStrictEqual(inspect(parser('* *')), undefined);
18
20
  assert.deepStrictEqual(inspect(parser('* a*')), undefined);
19
21
  assert.deepStrictEqual(inspect(parser('* a *')), undefined);
@@ -28,8 +30,6 @@ describe('Unit: parser/inline/strong', () => {
28
30
  it('basic', () => {
29
31
  assert.deepStrictEqual(inspect(parser('*a*')), [['<strong>a</strong>'], '']);
30
32
  assert.deepStrictEqual(inspect(parser('*ab*')), [['<strong>ab</strong>'], '']);
31
- assert.deepStrictEqual(inspect(parser('*a\nb*')), [['<strong>a<br>b</strong>'], '']);
32
- assert.deepStrictEqual(inspect(parser('*a\\\nb*')), [['<strong>a<span class="linebreak"> </span>b</strong>'], '']);
33
33
  });
34
34
 
35
35
  it('nest', () => {
@@ -11,8 +11,8 @@ export const strong: StrongParser = lazy(() => surround(
11
11
  str('*', '*'),
12
12
  syntax(Syntax.none, 1, 1, State.none,
13
13
  startTight(some(union([
14
- some(inline, blankWith('*')),
15
- open(some(inline, '*'), strong),
14
+ some(inline, blankWith('*'), [[/^\\?\n/, 9]]),
15
+ open(some(inline, '*', [[/^\\?\n/, 9]]), strong),
16
16
  ])))),
17
17
  str('*'), false,
18
18
  ([, bs], rest) => [[html('strong', defrag(bs))], rest],
@@ -51,7 +51,7 @@ describe('Unit: parser/inline', () => {
51
51
  assert.deepStrictEqual(inspect(parser('[]{{a}}')), [['[', ']', '<span class="template">{{a}}</span>'], '']);
52
52
  assert.deepStrictEqual(inspect(parser('![]{{a}}')), [['!', '[', ']', '<span class="template">{{a}}</span>'], '']);
53
53
  assert.deepStrictEqual(inspect(parser('[\n]{a}')), [['[', '<br>', ']', '<a class="url" href="a">a</a>'], '']);
54
- assert.deepStrictEqual(inspect(parser('[\\\n]{a}')), [['[', '<span class="linebreak"> </span>', ']', '<a class="url" href="a">a</a>'], '']);
54
+ assert.deepStrictEqual(inspect(parser('[\\\n]{a}')), [['[', '<br>', ']', '<a class="url" href="a">a</a>'], '']);
55
55
  assert.deepStrictEqual(inspect(parser('{}')), [['{', '}'], '']);
56
56
  assert.deepStrictEqual(inspect(parser('{a}')), [['<a class="url" href="a">a</a>'], '']);
57
57
  assert.deepStrictEqual(inspect(parser('{{a}}')), [['<span class="template">{{a}}</span>'], '']);
@@ -81,6 +81,7 @@ describe('Unit: parser/inline', () => {
81
81
  assert.deepStrictEqual(inspect(parser('[[<bdi>]]')), [['<sup class="reference"><span><span class="invalid">&lt;bdi&gt;</span></span></sup>'], '']);
82
82
  assert.deepStrictEqual(inspect(parser('[[${]]}$')), [['', '[', '', '[', '<span class="math" translate="no" data-src="${]]}$">${]]}$</span>'], '']);
83
83
  assert.deepStrictEqual(inspect(parser('"[[""]]')), [['"', '<sup class="reference"><span>""</span></sup>'], '']);
84
+ assert.deepStrictEqual(inspect(parser('[==a==]{b}')), [['<a class="link" href="b">==a==</a>'], '']);
84
85
  assert.deepStrictEqual(inspect(parser('[[a](b)]{c}')), [['<a class="link" href="c"><ruby>a<rp>(</rp><rt>b</rt><rp>)</rp></ruby></a>'], '']);
85
86
  assert.deepStrictEqual(inspect(parser('[[[[[[[{a}')), [['', '[', '', '[', '', '[', '', '[', '', '[', '', '[', '', '[', '<a class="url" href="a">a</a>'], '']);
86
87
  assert.deepStrictEqual(inspect(parser('<http://host>')), [['<', '<a class="url" href="http://host" target="_blank">http://host</a>', '>'], '']);
@@ -89,11 +90,11 @@ describe('Unit: parser/inline', () => {
89
90
  assert.deepStrictEqual(inspect(parser('[~~a~~]')), [['[', '<del>a</del>', ']'], '']);
90
91
  assert.deepStrictEqual(inspect(parser('[^http://host')), [['[^', '<a class="url" href="http://host" target="_blank">http://host</a>'], '']);
91
92
  assert.deepStrictEqual(inspect(parser('[^a@b')), [['[^', '<a class="email" href="mailto:a@b">a@b</a>'], '']);
92
- assert.deepStrictEqual(inspect(parser('[#a*b\nc*]')), [['[', '<a class="hashtag" href="/hashtags/a">#a</a>', '<strong>b<br>c</strong>', ']'], '']);
93
- assert.deepStrictEqual(inspect(parser('[*a\nb*]{/}')), [['[', '<strong>a<br>b</strong>', ']', '<a class="url" href="/">/</a>'], '']);
94
- assert.deepStrictEqual(inspect(parser('[*a\nb]*')), [['[', '*', 'a', '<br>', 'b', ']', '*'], '']);
95
- assert.deepStrictEqual(inspect(parser('[*[a\nb*]')), [['', '[', '*', '[', 'a', '<br>', 'b', '*', ']'], '']);
96
- assert.deepStrictEqual(inspect(parser('[[*a\nb*]]')), [['[', '[', '<strong>a<br>b</strong>', ']', ']'], '']);
93
+ assert.deepStrictEqual(inspect(parser('[#a++b\nc++]')), [['[', '<a class="hashtag" href="/hashtags/a">#a</a>', '<ins>b<br>c</ins>', ']'], '']);
94
+ assert.deepStrictEqual(inspect(parser('[++a\nb++]{/}')), [['[', '<ins>a<br>b</ins>', ']', '<a class="url" href="/">/</a>'], '']);
95
+ assert.deepStrictEqual(inspect(parser('[++a\nb]++')), [['[', '++', 'a', '<br>', 'b', ']', '++'], '']);
96
+ assert.deepStrictEqual(inspect(parser('[++[a\nb++]')), [['', '[', '++', '[', 'a', '<br>', 'b', '++', ']'], '']);
97
+ assert.deepStrictEqual(inspect(parser('[[++a\nb++]]')), [['[', '[', '<ins>a<br>b</ins>', ']', ']'], '']);
97
98
  assert.deepStrictEqual(inspect(parser('"[% *"*"*')), [['"', '[%', ' ', '*', '"', '*', '"', '*'], '']);
98
99
  assert.deepStrictEqual(inspect(parser('"[% "*"* %]')), [['"', '[%', ' ', '"', '*', '"', '*', ' ', '%', ']'], '']);
99
100
  });
@@ -158,7 +159,7 @@ describe('Unit: parser/inline', () => {
158
159
  assert.deepStrictEqual(inspect(parser('0aあい#b')), [['0a', 'あ', 'い#b'], '']);
159
160
  assert.deepStrictEqual(inspect(parser('「#あ」')), [['「', '<a class="hashtag" href="/hashtags/あ">#あ</a>', '」'], '']);
160
161
  assert.deepStrictEqual(inspect(parser('a\n#b')), [['a', '<br>', '<a class="hashtag" href="/hashtags/b">#b</a>'], '']);
161
- assert.deepStrictEqual(inspect(parser('a\\\n#b')), [['a', '<span class="linebreak"> </span>', '<a class="hashtag" href="/hashtags/b">#b</a>'], '']);
162
+ assert.deepStrictEqual(inspect(parser('a\\\n#b')), [['a', '<br>', '<a class="hashtag" href="/hashtags/b">#b</a>'], '']);
162
163
  assert.deepStrictEqual(inspect(parser('_a_#b')), [['<em>a</em>', '<a class="hashtag" href="/hashtags/b">#b</a>'], '']);
163
164
  assert.deepStrictEqual(inspect(parser('*a*#b')), [['<strong>a</strong>', '<a class="hashtag" href="/hashtags/b">#b</a>'], '']);
164
165
  assert.deepStrictEqual(inspect(parser('((a))#b')), [['<sup class="annotation"><span>a</span></sup>', '<a class="hashtag" href="/hashtags/b">#b</a>'], '']);
@@ -39,7 +39,7 @@ describe('Unit: parser/source/escsource', () => {
39
39
  assert.deepStrictEqual(inspect(parser('\\a')), [['\\a'], '']);
40
40
  assert.deepStrictEqual(inspect(parser('\\$')), [['\\$'], '']);
41
41
  assert.deepStrictEqual(inspect(parser('\\ ')), [['\\ '], '']);
42
- assert.deepStrictEqual(inspect(parser('\\\n')), [['\\\n'], '']);
42
+ assert.deepStrictEqual(inspect(parser('\\\n')), [['\\', '\n'], '']);
43
43
  });
44
44
 
45
45
  });
@@ -15,7 +15,13 @@ export const escsource: EscapableSourceParser = creation(1, false, ({ source })
15
15
  case '\x1B':
16
16
  return [[source.slice(1, 2)], source.slice(2)];
17
17
  case '\\':
18
- return [[source.slice(0, 2)], source.slice(2)];
18
+ switch (source[1]) {
19
+ case undefined:
20
+ case '\n':
21
+ return [[source[0]], source.slice(1)];
22
+ default:
23
+ return [[source.slice(0, 2)], source.slice(2)];
24
+ }
19
25
  default:
20
26
  const b = source[0] !== '\n' && source[0].trimStart() === '';
21
27
  const i = b
@@ -18,14 +18,28 @@ describe('Unit: parser/text/text', () => {
18
18
  assert.deepStrictEqual(inspect(parser('a\nb')), [['a', '<br>', 'b'], '']);
19
19
  });
20
20
 
21
+ it('escape', () => {
22
+ assert.deepStrictEqual(inspect(parser('\\')), [[], '']);
23
+ assert.deepStrictEqual(inspect(parser('\\\\')), [['\\'], '']);
24
+ assert.deepStrictEqual(inspect(parser('\\\\\\')), [['\\'], '']);
25
+ assert.deepStrictEqual(inspect(parser('\\\\\\\\')), [['\\', '\\'], '']);
26
+ assert.deepStrictEqual(inspect(parser('\\ ')), [[' '], '']);
27
+ assert.deepStrictEqual(inspect(parser('\\_')), [['_'], '']);
28
+ assert.deepStrictEqual(inspect(parser('\\0')), [['0'], '']);
29
+ assert.deepStrictEqual(inspect(parser('\\a')), [['a'], '']);
30
+ assert.deepStrictEqual(inspect(parser('\\\\a')), [['\\', 'a'], '']);
31
+ assert.deepStrictEqual(inspect(parser('\\ ')), [[' '], '']);
32
+ assert.deepStrictEqual(inspect(parser('\\。')), [['。'], '']);
33
+ });
34
+
21
35
  it('space', () => {
22
36
  assert.deepStrictEqual(inspect(parser(' ')), [[], '']);
23
37
  assert.deepStrictEqual(inspect(parser(' ')), [[], '']);
24
38
  assert.deepStrictEqual(inspect(parser(' ')), [[], '']);
25
39
  assert.deepStrictEqual(inspect(parser(' \n')), [['<br>'], '']);
26
40
  assert.deepStrictEqual(inspect(parser(' \n')), [['<br>'], '']);
27
- assert.deepStrictEqual(inspect(parser(' \\\n')), [['<span class="linebreak"> </span>'], '']);
28
- assert.deepStrictEqual(inspect(parser(' \\\n')), [['<span class="linebreak"> </span>'], '']);
41
+ assert.deepStrictEqual(inspect(parser(' \\\n')), [['<br>'], '']);
42
+ assert.deepStrictEqual(inspect(parser(' \\\n')), [['<br>'], '']);
29
43
  assert.deepStrictEqual(inspect(parser(' a')), [[' ', 'a'], '']);
30
44
  assert.deepStrictEqual(inspect(parser(' a')), [[' ', ' ', 'a'], '']);
31
45
  assert.deepStrictEqual(inspect(parser(' a')), [[' ', ' ', 'a'], '']);
@@ -33,8 +47,8 @@ describe('Unit: parser/text/text', () => {
33
47
  assert.deepStrictEqual(inspect(parser('a ')), [['a'], '']);
34
48
  assert.deepStrictEqual(inspect(parser('a \n')), [['a', '<br>'], '']);
35
49
  assert.deepStrictEqual(inspect(parser('a \n')), [['a', '<br>'], '']);
36
- assert.deepStrictEqual(inspect(parser('a \\\n')), [['a', '<span class="linebreak"> </span>'], '']);
37
- assert.deepStrictEqual(inspect(parser('a \\\n')), [['a', '<span class="linebreak"> </span>'], '']);
50
+ assert.deepStrictEqual(inspect(parser('a \\\n')), [['a', '<br>'], '']);
51
+ assert.deepStrictEqual(inspect(parser('a \\\n')), [['a', '<br>'], '']);
38
52
  assert.deepStrictEqual(inspect(parser('a b')), [['a', ' ', 'b'], '']);
39
53
  assert.deepStrictEqual(inspect(parser('a b')), [['a', ' ', ' ', 'b'], '']);
40
54
  assert.deepStrictEqual(inspect(parser('a b')), [['a', ' ', ' ', 'b'], '']);
@@ -51,27 +65,13 @@ describe('Unit: parser/text/text', () => {
51
65
  assert.deepStrictEqual(inspect(parser('。\n')), [['。', '<br>'], '']);
52
66
  });
53
67
 
54
- it('\\', () => {
55
- assert.deepStrictEqual(inspect(parser('\\')), [[], '']);
56
- assert.deepStrictEqual(inspect(parser('\\\\')), [['\\'], '']);
57
- assert.deepStrictEqual(inspect(parser('\\\\\\')), [['\\'], '']);
58
- assert.deepStrictEqual(inspect(parser('\\\\\\\\')), [['\\', '\\'], '']);
59
- assert.deepStrictEqual(inspect(parser('\\ ')), [[' '], '']);
60
- assert.deepStrictEqual(inspect(parser('\\_')), [['_'], '']);
61
- assert.deepStrictEqual(inspect(parser('\\0')), [['0'], '']);
62
- assert.deepStrictEqual(inspect(parser('\\a')), [['a'], '']);
63
- assert.deepStrictEqual(inspect(parser('\\\\a')), [['\\', 'a'], '']);
64
- assert.deepStrictEqual(inspect(parser('\\ ')), [[' '], '']);
65
- assert.deepStrictEqual(inspect(parser('\\。')), [['。'], '']);
66
- });
67
-
68
68
  it('softbreak', () => {
69
- assert.deepStrictEqual(inspect(parser('\\\n')), [['<span class="linebreak"> </span>'], '']);
70
- assert.deepStrictEqual(inspect(parser('\\\n ')), [['<span class="linebreak"> </span>'], '']);
71
- assert.deepStrictEqual(inspect(parser('\\\na')), [['<span class="linebreak"> </span>', 'a'], '']);
72
- assert.deepStrictEqual(inspect(parser('a\\\n')), [['a', '<span class="linebreak"> </span>'], '']);
73
- assert.deepStrictEqual(inspect(parser('a\\\nb\\\n')), [['a', '<span class="linebreak"> </span>', 'b', '<span class="linebreak"> </span>'], '']);
74
- assert.deepStrictEqual(inspect(parser('\\\\\\\n')), [['\\', '<span class="linebreak"> </span>'], '']);
69
+ assert.deepStrictEqual(inspect(parser('\\\n')), [['<br>'], '']);
70
+ assert.deepStrictEqual(inspect(parser('\\\n ')), [['<br>'], '']);
71
+ assert.deepStrictEqual(inspect(parser('\\\na')), [['<br>', 'a'], '']);
72
+ assert.deepStrictEqual(inspect(parser('a\\\n')), [['a', '<br>'], '']);
73
+ assert.deepStrictEqual(inspect(parser('a\\\nb\\\n')), [['a', '<br>', 'b', '<br>'], '']);
74
+ assert.deepStrictEqual(inspect(parser('\\\\\\\n')), [['\\', '<br>'], '']);
75
75
  });
76
76
 
77
77
  it('account', () => {
@@ -116,17 +116,6 @@ describe('Unit: parser/text/text', () => {
116
116
  assert.deepStrictEqual(inspect(parser('>>>>0')), [['>', '>', '>', '>', '0'], '']);
117
117
  });
118
118
 
119
- it('localize', () => {
120
- assert.deepStrictEqual(inspect(parser('a\\\n0')), [['a', '<span class="linebreak"> </span>', '0'], '']);
121
- assert.deepStrictEqual(inspect(parser('あ\\\n0')), [['あ', '<span class="linebreak"></span>', '0'], '']);
122
- assert.deepStrictEqual(inspect(parser('ア\\\n0')), [['ア', '<span class="linebreak"></span>', '0'], '']);
123
- assert.deepStrictEqual(inspect(parser('亜\\\n0')), [['亜', '<span class="linebreak"></span>', '0'], '']);
124
- assert.deepStrictEqual(inspect(parser('、\\\n0')), [['、', '<span class="linebreak"></span>', '0'], '']);
125
- assert.deepStrictEqual(inspect(parser('。\\\n0')), [['。', '<span class="linebreak"></span>', '0'], '']);
126
- assert.deepStrictEqual(inspect(parser('!?\\\n0')), [['!', '?', '<span class="linebreak"></span>', '0'], '']);
127
- assert.deepStrictEqual(inspect(parser('\\あ\\\n0')), [['あ', '<span class="linebreak"></span>', '0'], '']);
128
- });
129
-
130
119
  });
131
120
 
132
121
  });
@@ -3,10 +3,9 @@ import { union, creation, focus } from '../../combinator';
3
3
  import { str } from './str';
4
4
  import { html } from 'typed-dom/dom';
5
5
 
6
- export const delimiter = /[\s\x00-\x7F]|\S[#>]|[\p{Ideo}\p{scx=Hiragana}\p{scx=Katakana}~!?][^\S\n]*(?=\\\n)/u;
6
+ export const delimiter = /[\s\x00-\x7F]|\S[#>]/u;
7
7
  export const nonWhitespace = /[\S\n]|$/;
8
8
  export const nonAlphanumeric = /[^0-9A-Za-z]|\S[#>]|$/;
9
- const nssb = /^[\p{Ideo}\p{scx=Hiragana}\p{scx=Katakana}~!?][^\S\n]*(?=\\\n)/u;
10
9
  const repeat = str(/^(.)\1*/);
11
10
 
12
11
  export const text: TextParser = creation(1, false, ({ source, context }) => {
@@ -16,26 +15,14 @@ export const text: TextParser = creation(1, false, ({ source, context }) => {
16
15
  case -1:
17
16
  return [[source], ''];
18
17
  case 0:
19
- switch (source[0]) {
20
- case '\x1B':
21
- case '\\':
22
- if (!nssb.test(source.slice(1))) break;
23
- assert(source[0] !== '\x1B');
24
- return text({ source: source.slice(1), context });
25
- default:
26
- const i = source.match(nssb)?.[0].length ?? -1;
27
- if (i !== -1) return [[source[0], html('span', { class: 'linebreak' })], source.slice(i + 2)];
28
- }
29
18
  switch (source[0]) {
30
19
  case '\x1B':
31
20
  case '\\':
32
21
  switch (source[1]) {
33
22
  case undefined:
34
- assert(source[0] !== '\x1B');
35
- return [[], ''];
36
23
  case '\n':
37
24
  assert(source[0] !== '\x1B');
38
- return [[html('span', { class: 'linebreak' }, ' ')], source.slice(2)];
25
+ return [[], source.slice(1)];
39
26
  default:
40
27
  return [[source.slice(1, 2)], source.slice(2)];
41
28
  }
@@ -108,10 +108,7 @@ export function isStartLooseNodes(nodes: readonly (HTMLElement | string)[]): boo
108
108
  for (let i = 0; i < nodes.length; ++i) {
109
109
  const node = nodes[i];
110
110
  if (isVisible(node)) return true;
111
- if (typeof node === 'object') {
112
- if (node.tagName === 'BR') break;
113
- if (node.className === 'linebreak') break;
114
- }
111
+ if (typeof node === 'object' && node.tagName === 'BR') break;
115
112
  }
116
113
  return false;
117
114
  }
@@ -143,8 +140,6 @@ function isVisible(node: HTMLElement | string, strpos?: number): boolean {
143
140
  case 'BR':
144
141
  case 'WBR':
145
142
  return false;
146
- case 'SPAN':
147
- return node.className !== 'linebreak';
148
143
  default:
149
144
  return true;
150
145
  }