securemark 0.234.1 → 0.235.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 (36) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/securemark.js +128 -136
  3. package/markdown.d.ts +4 -16
  4. package/package-lock.json +46 -43
  5. package/package.json +1 -1
  6. package/src/combinator/control/constraint/block.ts +0 -2
  7. package/src/combinator/control/constraint/contract.ts +1 -1
  8. package/src/combinator/control/manipulation/context.ts +7 -0
  9. package/src/combinator/control/manipulation/match.ts +1 -1
  10. package/src/combinator/control/manipulation/scope.ts +1 -1
  11. package/src/combinator/data/parser/inits.ts +1 -1
  12. package/src/combinator/data/parser/sequence.ts +1 -1
  13. package/src/combinator/data/parser/some.ts +40 -18
  14. package/src/combinator/data/parser.ts +4 -1
  15. package/src/parser/api/normalize.ts +7 -6
  16. package/src/parser/block/heading.test.ts +1 -1
  17. package/src/parser/block/paragraph.test.ts +2 -0
  18. package/src/parser/inline/emphasis.test.ts +7 -4
  19. package/src/parser/inline/emphasis.ts +7 -7
  20. package/src/parser/inline/emstrong.ts +19 -18
  21. package/src/parser/inline/extension/index.test.ts +19 -18
  22. package/src/parser/inline/extension/index.ts +3 -4
  23. package/src/parser/inline/extension/indexer.test.ts +1 -0
  24. package/src/parser/inline/extension/indexer.ts +1 -0
  25. package/src/parser/inline/html.ts +4 -8
  26. package/src/parser/inline/htmlentity.test.ts +1 -0
  27. package/src/parser/inline/htmlentity.ts +9 -12
  28. package/src/parser/inline/link.ts +3 -4
  29. package/src/parser/inline/mark.test.ts +6 -3
  30. package/src/parser/inline/mark.ts +4 -4
  31. package/src/parser/inline/media.ts +8 -5
  32. package/src/parser/inline/ruby.ts +3 -4
  33. package/src/parser/inline/strong.test.ts +6 -4
  34. package/src/parser/inline/strong.ts +7 -7
  35. package/src/parser/inline.test.ts +15 -5
  36. package/src/parser/util.ts +11 -42
@@ -20,13 +20,19 @@ describe('Unit: parser/inline/extension/index', () => {
20
20
  assert.deepStrictEqual(inspect(parser('[#\\]')), undefined);
21
21
  assert.deepStrictEqual(inspect(parser('[#a')), undefined);
22
22
  assert.deepStrictEqual(inspect(parser('[#*a\nb*]')), undefined);
23
+ assert.deepStrictEqual(inspect(parser('[#a\n|#b]')), undefined);
23
24
  assert.deepStrictEqual(inspect(parser('[#a|#\n]')), undefined);
24
25
  assert.deepStrictEqual(inspect(parser('[#a|#\\\n]')), undefined);
26
+ assert.deepStrictEqual(inspect(parser('[#a|#(\n)]')), undefined);
27
+ assert.deepStrictEqual(inspect(parser('[#a|#(\\\n)]')), undefined);
25
28
  assert.deepStrictEqual(inspect(parser('[# |]')), undefined);
26
29
  assert.deepStrictEqual(inspect(parser('[# |#]')), undefined);
27
30
  assert.deepStrictEqual(inspect(parser('[# |#b]')), undefined);
28
- assert.deepStrictEqual(inspect(parser('[# |# ]')), undefined);
29
- assert.deepStrictEqual(inspect(parser('[# |# b]')), undefined);
31
+ assert.deepStrictEqual(inspect(parser('[# a|]')), undefined);
32
+ assert.deepStrictEqual(inspect(parser('[# a|#]')), undefined);
33
+ assert.deepStrictEqual(inspect(parser('[# a|#b]')), undefined);
34
+ assert.deepStrictEqual(inspect(parser('[# a|# ]')), undefined);
35
+ assert.deepStrictEqual(inspect(parser('[# a|# b]')), undefined);
30
36
  assert.deepStrictEqual(inspect(parser(' [#a]')), undefined);
31
37
  });
32
38
 
@@ -64,26 +70,21 @@ describe('Unit: parser/inline/extension/index', () => {
64
70
  assert.deepStrictEqual(inspect(parser('[#|]')), [['<a class="index" href="#index:|">|</a>'], '']);
65
71
  assert.deepStrictEqual(inspect(parser('[#|#]')), [['<a class="index" href="#index:|#">|#</a>'], '']);
66
72
  assert.deepStrictEqual(inspect(parser('[#|#b]')), [['<a class="index" href="#index:|#b">|#b</a>'], '']);
67
- assert.deepStrictEqual(inspect(parser('[#a|]')), [['<a class="index" href="#index:a|">a|</a>'], '']);
68
- assert.deepStrictEqual(inspect(parser('[#a|#]')), [['<a class="index" href="#index:a|#">a|#</a>'], '']);
69
- assert.deepStrictEqual(inspect(parser('[#a|# ]')), [['<a class="index" href="#index:a|#">a|#</a>'], '']);
70
- assert.deepStrictEqual(inspect(parser('[#a|#\\ ]')), [['<a class="index" href="#index:a|#">a|#</a>'], '']);
71
- assert.deepStrictEqual(inspect(parser('[#a|#b]')), [['<a class="index" href="#index:b">a<span class="indexer" data-index="b"></span></a>'], '']);
72
- assert.deepStrictEqual(inspect(parser('[#a|#b ]')), [['<a class="index" href="#index:b">a<span class="indexer" data-index="b"></span></a>'], '']);
73
- assert.deepStrictEqual(inspect(parser('[#a|#b ]')), [['<a class="index" href="#index:b">a<span class="indexer" data-index="b"></span></a>'], '']);
74
- assert.deepStrictEqual(inspect(parser('[#a|#\\b]')), [['<a class="index" href="#index:b">a<span class="indexer" data-index="b"></span></a>'], '']);
75
- assert.deepStrictEqual(inspect(parser('[#a|#*b*]')), [['<a class="index" href="#index:*b*">a<span class="indexer" data-index="*b*"></span></a>'], '']);
76
- assert.deepStrictEqual(inspect(parser('[#a|#b c]')), [['<a class="index" href="#index:b_c">a<span class="indexer" data-index="b_c"></span></a>'], '']);
77
- assert.deepStrictEqual(inspect(parser('[#a|#b c]')), [['<a class="index" href="#index:b_c">a<span class="indexer" data-index="b_c"></span></a>'], '']);
78
- assert.deepStrictEqual(inspect(parser('[#a|#[]]')), [['<a class="index" href="#index:[]">a<span class="indexer" data-index="[]"></span></a>'], '']);
79
- assert.deepStrictEqual(inspect(parser('[#a|#&copy;]')), [['<a class="index" href="#index:&amp;copy;">a<span class="indexer" data-index="&amp;copy;"></span></a>'], '']);
73
+ assert.deepStrictEqual(inspect(parser('[#a|#b]')), [['<a class="index" href="#index:a|#b">a|#b</a>'], '']);
80
74
  assert.deepStrictEqual(inspect(parser('[#a |]')), [['<a class="index" href="#index:a_|">a |</a>'], '']);
81
75
  assert.deepStrictEqual(inspect(parser('[#a |#]')), [['<a class="index" href="#index:a_|#">a |#</a>'], '']);
76
+ assert.deepStrictEqual(inspect(parser('[#a |# ]')), [['<a class="index" href="#index:a_|#">a |#</a>'], '']);
77
+ assert.deepStrictEqual(inspect(parser('[#a |#\\ ]')), [['<a class="index" href="#index:a_|#">a |#</a>'], '']);
82
78
  assert.deepStrictEqual(inspect(parser('[#a |#b]')), [['<a class="index" href="#index:b">a<span class="indexer" data-index="b"></span></a>'], '']);
79
+ assert.deepStrictEqual(inspect(parser('[#a |#b ]')), [['<a class="index" href="#index:b">a<span class="indexer" data-index="b"></span></a>'], '']);
80
+ assert.deepStrictEqual(inspect(parser('[#a |#b ]')), [['<a class="index" href="#index:b">a<span class="indexer" data-index="b"></span></a>'], '']);
81
+ assert.deepStrictEqual(inspect(parser('[#a |#\\b]')), [['<a class="index" href="#index:b">a<span class="indexer" data-index="b"></span></a>'], '']);
82
+ assert.deepStrictEqual(inspect(parser('[#a |#*b*]')), [['<a class="index" href="#index:*b*">a<span class="indexer" data-index="*b*"></span></a>'], '']);
83
+ assert.deepStrictEqual(inspect(parser('[#a |#b c]')), [['<a class="index" href="#index:b_c">a<span class="indexer" data-index="b_c"></span></a>'], '']);
84
+ assert.deepStrictEqual(inspect(parser('[#a |#b c]')), [['<a class="index" href="#index:b_c">a<span class="indexer" data-index="b_c"></span></a>'], '']);
85
+ assert.deepStrictEqual(inspect(parser('[#a |#[]]')), [['<a class="index" href="#index:[]">a<span class="indexer" data-index="[]"></span></a>'], '']);
86
+ assert.deepStrictEqual(inspect(parser('[#a |#&copy;]')), [['<a class="index" href="#index:&amp;copy;">a<span class="indexer" data-index="&amp;copy;"></span></a>'], '']);
83
87
  assert.deepStrictEqual(inspect(parser('[#a |#b]')), [['<a class="index" href="#index:b">a<span class="indexer" data-index="b"></span></a>'], '']);
84
- assert.deepStrictEqual(inspect(parser('[#a &nbsp;|#b]')), [['<a class="index" href="#index:b">a<span class="indexer" data-index="b"></span></a>'], '']);
85
- assert.deepStrictEqual(inspect(parser('[#a <wbr>|#b]')), [['<a class="index" href="#index:b">a<span class="indexer" data-index="b"></span></a>'], '']);
86
- assert.deepStrictEqual(inspect(parser('[#a [# b #]|#b]')), [['<a class="index" href="#index:b">a <span class="comment"><input type="checkbox"><span>[# b #]</span></span><span class="indexer" data-index="b"></span></a>'], '']);
87
88
  });
88
89
 
89
90
  });
@@ -23,11 +23,10 @@ export const index: IndexParser = lazy(() => creator(validate('[#', ']', '\n', f
23
23
  media: false,
24
24
  autolink: false,
25
25
  }}},
26
- open(str(/^\|?/, false),
27
26
  some(union([
28
27
  signature,
29
28
  inline,
30
- ]), ']', /^\\?\n/), true)))),
29
+ ]), ']', /^\\?\n/)))),
31
30
  ']'),
32
31
  ns => [html('a', trimNodeEnd(defrag(ns)))])),
33
32
  ([el]: [HTMLAnchorElement]) => [
@@ -41,8 +40,8 @@ export const index: IndexParser = lazy(() => creator(validate('[#', ']', '\n', f
41
40
  ]))));
42
41
 
43
42
  const signature: IndexParser.SignatureParser = lazy(() => creator(fmap(open(
44
- '|#',
45
- startTight(some(union([bracket, txt]), ']', /^\\?\n/))),
43
+ /^\s+\|#/,
44
+ startTight(some(union([bracket, txt]), ']'))),
46
45
  ns => [
47
46
  html('span', { class: 'indexer', 'data-index': identity(join(ns)).slice(6) }),
48
47
  ])));
@@ -15,6 +15,7 @@ describe('Unit: parser/inline/extension/indexer', () => {
15
15
  assert.deepStrictEqual(inspect(parser(' [#]')), undefined);
16
16
  assert.deepStrictEqual(inspect(parser(' [#]]')), undefined);
17
17
  assert.deepStrictEqual(inspect(parser(' [#a]]')), undefined);
18
+ assert.deepStrictEqual(inspect(parser(' [#&a;]')), undefined);
18
19
  });
19
20
 
20
21
  it('valid', () => {
@@ -10,6 +10,7 @@ export const indexer: ExtensionParser.IndexerParser = creator(fmap(verify(surrou
10
10
  }}},
11
11
  union([index])),
12
12
  /^\s*$/),
13
+ // Indexer is invisible but invalids must be visible.
13
14
  ([el]) => el.getElementsByClassName('invalid').length === 0),
14
15
  ([el]) => [
15
16
  html('span', { class: 'indexer', 'data-index': el.getAttribute('href')!.slice(7) }),
@@ -26,11 +26,9 @@ export const html: HTMLParser = lazy(() => creator(validate('<', validate(/^<[a-
26
26
  memoize(
27
27
  ([, tag]) =>
28
28
  surround(
29
- str(`<${tag}`), some(union([attribute])), str('>'), true,
30
- ([, as = []], rest) => [
31
- [h(tag as 'span', attributes('html', [], attrspec[tag], as))],
32
- rest
33
- ]),
29
+ `<${tag}`, some(union([attribute])), '>', true,
30
+ ([, bs = []], rest) =>
31
+ [[h(tag as 'span', attributes('html', [], attrspec[tag], bs))], rest]),
34
32
  ([, tag]) => tag)),
35
33
  match(
36
34
  /^(?=<(sup|sub|small|bdo|bdi)(?=[^\S\n]|>))/,
@@ -80,9 +78,7 @@ export const html: HTMLParser = lazy(() => creator(validate('<', validate(/^<[a-
80
78
  ([as, bs, cs], rest) =>
81
79
  [[elem(tag, as, trimNodeEndBR(defrag(bs)), cs, {})], rest],
82
80
  ([as, bs], rest) =>
83
- as.length === 1
84
- ? [unshift(as, bs), rest]
85
- : undefined)),
81
+ as.length === 1 ? [unshift(as, bs), rest] : undefined)),
86
82
  ([, tag]) => tag,
87
83
  new Cache(1000))),
88
84
  ])))));
@@ -14,6 +14,7 @@ describe('Unit: parser/inline/htmlentity', () => {
14
14
  assert.deepStrictEqual(inspect(parser('& ;')), undefined);
15
15
  assert.deepStrictEqual(inspect(parser('&\n;')), undefined);
16
16
  assert.deepStrictEqual(inspect(parser('&a;')), [['<span class="invalid">&amp;a;</span>'], '']);
17
+ assert.deepStrictEqual(inspect(parser('&NewLine;')), undefined);
17
18
  assert.deepStrictEqual(inspect(parser('&#;')), undefined);
18
19
  assert.deepStrictEqual(inspect(parser('&#g;')), undefined);
19
20
  assert.deepStrictEqual(inspect(parser('&#x;')), undefined);
@@ -2,18 +2,15 @@ import { HTMLEntityParser, UnsafeHTMLEntityParser } from '../inline';
2
2
  import { union, validate, focus, creator, fmap } from '../../combinator';
3
3
  import { html } from 'typed-dom';
4
4
 
5
- const parser = html('textarea');
5
+ export const unsafehtmlentity: UnsafeHTMLEntityParser = creator(validate('&', focus(
6
+ /^&(?!NewLine;)[0-9A-Za-z]+;/,
7
+ (parser => entity => (
8
+ parser.innerHTML = entity,
9
+ entity = parser.textContent!,
10
+ [[`${entity[0] !== '&' || entity.length === 1 ? '' : '\0'}${entity}`], '']
11
+ ))(html('b')))));
6
12
 
7
- export const unsafehtmlentity: UnsafeHTMLEntityParser = creator(validate('&', fmap(focus(
8
- /^&[0-9A-Za-z]+;/,
9
- entity => [[(parser.innerHTML = entity, parser.value)], '']),
10
- ([str]) => [
11
- str[0] !== '&' || str.length < 3
12
- ? str
13
- : `\0${str}`,
14
- ])));
15
-
16
- export const htmlentity: HTMLEntityParser = creator(validate('&', fmap(
13
+ export const htmlentity: HTMLEntityParser = fmap(
17
14
  union([unsafehtmlentity]),
18
15
  ([str]) => [
19
16
  str[0] === '\0'
@@ -24,4 +21,4 @@ export const htmlentity: HTMLEntityParser = creator(validate('&', fmap(
24
21
  'data-invalid-description': 'Invalid HTML entity.',
25
22
  }, str.slice(1))
26
23
  : str,
27
- ])));
24
+ ]);
@@ -16,8 +16,7 @@ const optspec = {
16
16
  } as const;
17
17
  ObjectSetPrototypeOf(optspec, null);
18
18
 
19
- export const link: LinkParser = lazy(() => creator(10, bind(
20
- validate(['[', '{'], '}', '\n',
19
+ export const link: LinkParser = lazy(() => creator(10, validate(['[', '{'], '}', '\n', bind(
21
20
  guard(context => context.syntax?.inline?.link ?? true,
22
21
  reverse(tails([
23
22
  context({ syntax: { inline: {
@@ -44,7 +43,7 @@ export const link: LinkParser = lazy(() => creator(10, bind(
44
43
  true),
45
44
  ]))),
46
45
  dup(surround(/^{(?![{}])/, inits([uri, some(option)]), /^[^\S\n]?}/)),
47
- ])))),
46
+ ]))),
48
47
  ([params, content = []]: [string[], (HTMLElement | string)[]], rest, context) => {
49
48
  assert(params.every(p => typeof p === 'string'));
50
49
  if (eval(some(autolink)(stringify(content), context))?.some(node => typeof node === 'object')) return;
@@ -62,7 +61,7 @@ export const link: LinkParser = lazy(() => creator(10, bind(
62
61
  if (el.classList.contains('invalid')) return [[el], rest];
63
62
  assert(el.classList.length === 0);
64
63
  return [[define(el, attributes('link', [], optspec, params))], rest];
65
- })));
64
+ }))));
66
65
 
67
66
  export const uri: LinkParser.ParameterParser.UriParser = union([
68
67
  open(/^[^\S\n]/, str(/^\S+/)),
@@ -14,8 +14,8 @@ describe('Unit: parser/inline/mark', () => {
14
14
  assert.deepStrictEqual(inspect(parser('==a=')), [['==', 'a', '='], '']);
15
15
  assert.deepStrictEqual(inspect(parser('==a ==')), [['==', 'a', ' ', '=='], '']);
16
16
  assert.deepStrictEqual(inspect(parser('==a\n==')), [['==', 'a', '<br>', '=='], '']);
17
- assert.deepStrictEqual(inspect(parser('==a\\ ==')), [['==', 'a', ' '], '==']);
18
- assert.deepStrictEqual(inspect(parser('==a\\\n==')), [['==', 'a', '<span class="linebreak"> </span>'], '==']);
17
+ assert.deepStrictEqual(inspect(parser('==a\\ ==')), [['==', 'a', ' ', '=='], '']);
18
+ assert.deepStrictEqual(inspect(parser('==a\\\n==')), [['==', 'a', '<span class="linebreak"> </span>', '=='], '']);
19
19
  assert.deepStrictEqual(inspect(parser('== ==')), undefined);
20
20
  assert.deepStrictEqual(inspect(parser('== a==')), undefined);
21
21
  assert.deepStrictEqual(inspect(parser('== a ==')), undefined);
@@ -35,8 +35,11 @@ describe('Unit: parser/inline/mark', () => {
35
35
  });
36
36
 
37
37
  it('nest', () => {
38
- assert.deepStrictEqual(inspect(parser('==*==a==*==')), [['<mark><em><mark>a</mark></em></mark>'], '']);
39
38
  assert.deepStrictEqual(inspect(parser('==a ==b====')), [['<mark>a <mark>b</mark></mark>'], '']);
39
+ assert.deepStrictEqual(inspect(parser('==a\\ ==b====')), [['<mark>a <mark>b</mark></mark>'], '']);
40
+ assert.deepStrictEqual(inspect(parser('==a&Tab;==b====')), [['<mark>a\t<mark>b</mark></mark>'], '']);
41
+ assert.deepStrictEqual(inspect(parser('==a<wbr>==b====')), [['<mark>a<wbr><mark>b</mark></mark>'], '']);
42
+ assert.deepStrictEqual(inspect(parser('==*==a==*==')), [['<mark><em><mark>a</mark></em></mark>'], '']);
40
43
  });
41
44
 
42
45
  });
@@ -1,16 +1,16 @@
1
1
  import { MarkParser } from '../inline';
2
- import { union, sequence, some, creator, surround, lazy } from '../../combinator';
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, isEndTightNodes } from '../util';
5
+ import { startTight, isEndTightNodes, delimiter } from '../util';
6
6
  import { html, defrag } from 'typed-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, /^\s*==/),
13
- sequence([some(inline, '='), inline]),
12
+ some(inline, delimiter(/==/)),
13
+ open(some(inline, '='), inline),
14
14
  ]))),
15
15
  str('=='), false,
16
16
  ([as, bs, cs], rest) =>
@@ -18,14 +18,17 @@ const optspec = {
18
18
  } as const;
19
19
  ObjectSetPrototypeOf(optspec, null);
20
20
 
21
- export const media: MediaParser = lazy(() => creator(10, bind(verify(fmap(open(
21
+ export const media: MediaParser = lazy(() => creator(10, validate(['![', '!{'], '}', '\n', bind(verify(fmap(open(
22
22
  '!',
23
- validate(['[', '{'], '}', '\n',
24
23
  guard(context => context.syntax?.inline?.media ?? true,
25
24
  tails([
26
- dup(surround(/^\[(?!\s*\\\s)/, some(union([unsafehtmlentity, bracket, txt]), ']', /^\\?\n/), ']', true)),
25
+ dup(surround(
26
+ /^\[(?!\s*\\\s)/,
27
+ some(union([unsafehtmlentity, bracket, txt]), ']', /^\\?\n/),
28
+ ']',
29
+ true)),
27
30
  dup(surround(/^{(?![{}])/, inits([uri, some(option)]), /^[^\S\n]?}/)),
28
- ])))),
31
+ ]))),
29
32
  ([as, bs]) => bs ? [[join(as).trim() || join(as)], bs] : [[''], as]),
30
33
  ([[text]]) => text === '' || text.trim() !== ''),
31
34
  ([[text], params], rest, context) => {
@@ -51,7 +54,7 @@ export const media: MediaParser = lazy(() => creator(10, bind(verify(fmap(open(
51
54
  link as MediaParser,
52
55
  ([link]) => [define(link, { target: '_blank' }, [el])])
53
56
  (`{ ${INSECURE_URI}${join(params)} }${rest}`, context);
54
- })));
57
+ }))));
55
58
 
56
59
  const bracket: MediaParser.TextParser.BracketParser = lazy(() => union([
57
60
  surround(str('('), some(union([unsafehtmlentity, bracket, txt]), ')'), str(')'), true, undefined, ([as, bs = []], rest) => [unshift(as, bs), rest]),
@@ -8,12 +8,11 @@ import { isStartTightNodes } from '../util';
8
8
  import { html, defrag } from 'typed-dom';
9
9
  import { unshift, push, join } from 'spica/array';
10
10
 
11
- export const ruby: RubyParser = lazy(() => creator(bind(verify(
12
- validate('[', ')', '\n',
11
+ export const ruby: RubyParser = lazy(() => creator(validate('[', ')', '\n', bind(verify(
13
12
  sequence([
14
13
  surround('[', focus(/^(?:\\[^\n]|[^\\\[\]\n])+(?=]\()/, text), ']'),
15
14
  surround('(', focus(/^(?:\\[^\n]|[^\\\(\)\n])+(?=\))/, text), ')'),
16
- ])),
15
+ ]),
17
16
  ([texts]) => isStartTightNodes(texts)),
18
17
  ([texts, rubies], rest) => {
19
18
  const tail = typeof texts[texts.length - 1] === 'object'
@@ -44,7 +43,7 @@ export const ruby: RubyParser = lazy(() => creator(bind(verify(
44
43
  [html('rp', '('), html('rt', join(rubies, ' ').trim()), html('rp', ')')]), tail)))
45
44
  ], rest];
46
45
  }
47
- })));
46
+ }))));
48
47
 
49
48
  const text: RubyParser.TextParser = creator((source, context) => {
50
49
  const acc = [''];
@@ -12,8 +12,8 @@ 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', '<br>', '**'], '']);
15
- assert.deepStrictEqual(inspect(parser('**a\\ **')), [['**', 'a', ' '], '**']);
16
- assert.deepStrictEqual(inspect(parser('**a\\\n**')), [['**', 'a', '<span class="linebreak"> </span>'], '**']);
15
+ assert.deepStrictEqual(inspect(parser('**a\\ **')), [['**', 'a', ' ', '**'], '']);
16
+ assert.deepStrictEqual(inspect(parser('**a\\\n**')), [['**', 'a', '<span class="linebreak"> </span>', '**'], '']);
17
17
  assert.deepStrictEqual(inspect(parser('**a*b**')), [['**', 'a', '<em>b</em>', '*'], '']);
18
18
  assert.deepStrictEqual(inspect(parser('** **')), undefined);
19
19
  assert.deepStrictEqual(inspect(parser('** a**')), undefined);
@@ -35,10 +35,12 @@ describe('Unit: parser/inline/strong', () => {
35
35
  });
36
36
 
37
37
  it('nest', () => {
38
- assert.deepStrictEqual(inspect(parser('**a*b*c**')), [['<strong>a<em>b</em>c</strong>'], '']);
39
- assert.deepStrictEqual(inspect(parser('**a*b*c**d')), [['<strong>a<em>b</em>c</strong>'], 'd']);
40
38
  assert.deepStrictEqual(inspect(parser('**a *b***')), [['<strong>a <em>b</em></strong>'], '']);
41
39
  assert.deepStrictEqual(inspect(parser('**a **b****')), [['<strong>a <strong>b</strong></strong>'], '']);
40
+ assert.deepStrictEqual(inspect(parser('**a&Tab;**b****')), [['<strong>a\t<strong>b</strong></strong>'], '']);
41
+ assert.deepStrictEqual(inspect(parser('**a<wbr>**b****')), [['<strong>a<wbr><strong>b</strong></strong>'], '']);
42
+ assert.deepStrictEqual(inspect(parser('**a*b*c**')), [['<strong>a<em>b</em>c</strong>'], '']);
43
+ assert.deepStrictEqual(inspect(parser('**a*b*c**d')), [['<strong>a<em>b</em>c</strong>'], 'd']);
42
44
  assert.deepStrictEqual(inspect(parser('**`a`**')), [['<strong><code data-src="`a`">a</code></strong>'], '']);
43
45
  assert.deepStrictEqual(inspect(parser('**<small>**')), [['<strong>&lt;small&gt;</strong>'], '']);
44
46
  assert.deepStrictEqual(inspect(parser('**(*a*)**')), [['<strong><span class="paren">(<em>a</em>)</span></strong>'], '']);
@@ -1,17 +1,17 @@
1
1
  import { StrongParser } from '../inline';
2
- import { union, sequence, some, creator, surround, close, lazy } from '../../combinator';
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, isEndTightNodes } from '../util';
5
+ import { startTight, isEndTightNodes, delimiter } from '../util';
6
6
  import { html, defrag } from 'typed-dom';
7
7
  import { unshift } from 'spica/array';
8
8
 
9
- export const strong: StrongParser = lazy(() => creator(surround(close(
10
- str('**'), /^(?!\*)/),
9
+ export const strong: StrongParser = lazy(() => creator(surround(
10
+ str('**'),
11
11
  startTight(some(union([
12
- some(inline, /^\s*\*\*/),
13
- sequence([some(inline, '*'), inline]),
14
- ]))),
12
+ some(inline, delimiter(/\*\*/)),
13
+ open(some(inline, '*'), inline),
14
+ ])), '*'),
15
15
  str('**'), false,
16
16
  ([as, bs, cs], rest) =>
17
17
  isEndTightNodes(bs)
@@ -64,18 +64,28 @@ describe('Unit: parser/inline', () => {
64
64
  assert.deepStrictEqual(inspect(parser('***a*b*c***')), [['<strong><em>a</em>b<em>c</em></strong>'], '']);
65
65
  assert.deepStrictEqual(inspect(parser('***a*b*c***d')), [['<strong><em>a</em>b<em>c</em></strong>', 'd'], '']);
66
66
  assert.deepStrictEqual(inspect(parser('***a*b**c****')), [['<strong><em>a</em>b</strong>', 'c', '****'], '']);
67
- assert.deepStrictEqual(inspect(parser('***a*b *c***')), [['<strong><em>a</em>b <em>c</em></strong>'], '']);
68
- assert.deepStrictEqual(inspect(parser('***a\\ *b**')), [['***', 'a', ' ', '<em>b</em>', '*'], '']);
67
+ assert.deepStrictEqual(inspect(parser('***a* **b****')), [['<strong><em>a</em> <strong>b</strong></strong>'], '']);
68
+ assert.deepStrictEqual(inspect(parser('***a*\\ **b****')), [['<strong><em>a</em> <strong>b</strong></strong>'], '']);
69
+ assert.deepStrictEqual(inspect(parser('***a*&Tab;**b****')), [['<strong><em>a</em>\t<strong>b</strong></strong>'], '']);
70
+ assert.deepStrictEqual(inspect(parser('***a*<wbr>**b****')), [['<strong><em>a</em><wbr><strong>b</strong></strong>'], '']);
71
+ assert.deepStrictEqual(inspect(parser('***a *b****')), [['<em><strong>a <em>b</em></strong></em>'], '']);
72
+ assert.deepStrictEqual(inspect(parser('***a\\ *b****')), [['<em><strong>a <em>b</em></strong></em>'], '']);
73
+ assert.deepStrictEqual(inspect(parser('***a&Tab;*b****')), [['<em><strong>a\t<em>b</em></strong></em>'], '']);
74
+ assert.deepStrictEqual(inspect(parser('***a<wbr>*b****')), [['<em><strong>a<wbr><em>b</em></strong></em>'], '']);
75
+ assert.deepStrictEqual(inspect(parser('***a*b **')), [['**', '<em>a</em>', 'b', ' ', '**'], '']);
69
76
  assert.deepStrictEqual(inspect(parser('***a*b\\ **')), [['**', '<em>a</em>', 'b', ' ', '**'], '']);
70
- assert.deepStrictEqual(inspect(parser('***a* b**')), [['<strong><em>a</em> b</strong>'], '']);
71
77
  assert.deepStrictEqual(inspect(parser('***a**b*')), [['<em><strong>a</strong>b</em>'], '']);
72
78
  assert.deepStrictEqual(inspect(parser('***a**b*c')), [['<em><strong>a</strong>b</em>', 'c'], '']);
73
79
  assert.deepStrictEqual(inspect(parser('***a**b*c**')), [['<em><strong>a</strong>b</em>', 'c', '**'], '']);
74
80
  assert.deepStrictEqual(inspect(parser('***a**b**c***')), [['<em><strong>a</strong>b<strong>c</strong></em>'], '']);
75
81
  assert.deepStrictEqual(inspect(parser('***a**b**c***d')), [['<em><strong>a</strong>b<strong>c</strong></em>', 'd'], '']);
76
- assert.deepStrictEqual(inspect(parser('***a**b **c***')), [['<em><strong>a</strong>b <strong>c</strong></em>'], '']);
77
- assert.deepStrictEqual(inspect(parser('***a** b*')), [['<em><strong>a</strong> b</em>'], '']);
82
+ assert.deepStrictEqual(inspect(parser('***a** *b**')), [['<em><strong>a</strong> <em>b</em></em>'], '']);
83
+ assert.deepStrictEqual(inspect(parser('***a**\\ *b**')), [['<em><strong>a</strong> <em>b</em></em>'], '']);
84
+ assert.deepStrictEqual(inspect(parser('***a**&Tab;*b**')), [['<em><strong>a</strong>\t<em>b</em></em>'], '']);
85
+ assert.deepStrictEqual(inspect(parser('***a**<wbr>*b**')), [['<em><strong>a</strong><wbr><em>b</em></em>'], '']);
86
+ assert.deepStrictEqual(inspect(parser('***a **b*')), [['***', 'a', ' ', '**', 'b', '*'], '']);
78
87
  assert.deepStrictEqual(inspect(parser('***a\\ **b*')), [['***', 'a', ' ', '**', 'b', '*'], '']);
88
+ assert.deepStrictEqual(inspect(parser('***a**b *')), [['*', '<strong>a</strong>', 'b', ' ', '*'], '']);
79
89
  assert.deepStrictEqual(inspect(parser('***a**b\\ *')), [['*', '<strong>a</strong>', 'b', ' ', '*'], '']);
80
90
  assert.deepStrictEqual(inspect(parser('***a*')), [['**', '<em>a</em>'], '']);
81
91
  assert.deepStrictEqual(inspect(parser('***a**')), [['*', '<strong>a</strong>'], '']);
@@ -4,47 +4,16 @@ import { Parser, eval } from '../combinator/data/parser';
4
4
  import { union, some, verify, convert } from '../combinator';
5
5
  import { unsafehtmlentity } from './inline/htmlentity';
6
6
  import { linebreak, unescsource } from './source';
7
+ import { invisibleHTMLEntityNames } from './api/normalize';
7
8
  import { push, pop } from 'spica/array';
8
9
 
9
- // https://dev.w3.org/html5/html-author/charref
10
- const invisibleHTMLEntityNames = [
11
- 'Tab',
12
- 'NewLine',
13
- 'NonBreakingSpace',
14
- 'nbsp',
15
- 'shy',
16
- 'ensp',
17
- 'emsp',
18
- 'emsp13',
19
- 'emsp14',
20
- 'numsp',
21
- 'puncsp',
22
- 'ThinSpace',
23
- 'thinsp',
24
- 'VeryThinSpace',
25
- 'hairsp',
26
- 'ZeroWidthSpace',
27
- 'NegativeVeryThinSpace',
28
- 'NegativeThinSpace',
29
- 'NegativeMediumSpace',
30
- 'NegativeThickSpace',
31
- 'zwj',
32
- 'zwnj',
33
- 'lrm',
34
- 'rlm',
35
- 'MediumSpace',
36
- 'NoBreak',
37
- 'ApplyFunction',
38
- 'af',
39
- 'InvisibleTimes',
40
- 'it',
41
- 'InvisibleComma',
42
- 'ic',
43
- ];
44
- const blankline = new RegExp(String.raw`^(?!$)(?:\\$|\\?[^\S\n]|&(?:${invisibleHTMLEntityNames.join('|')});|<wbr>)+$`, 'gm');
10
+ export function delimiter(opener: RegExp): RegExp {
11
+ return new RegExp(String.raw`^(?:\s+|\\\s|&(?:${invisibleHTMLEntityNames.join('|')});|<wbr>)?${opener.source}`);
12
+ }
45
13
 
46
14
  export function visualize<P extends Parser<HTMLElement | string>>(parser: P): P;
47
15
  export function visualize<T extends HTMLElement | string>(parser: Parser<T>): Parser<T> {
16
+ const blankline = new RegExp(String.raw`^(?:\\$|\\?[^\S\n]|&(?:${invisibleHTMLEntityNames.join('|')});|<wbr>)+$`, 'gm');
48
17
  return union([
49
18
  convert(
50
19
  source => source.replace(blankline, line => line.replace(/[\\&<]/g, '\x1B$&')),
@@ -79,18 +48,18 @@ export function startLoose<T extends HTMLElement | string>(parser: Parser<T>, ex
79
48
  export function isStartLoose(source: string, context: MarkdownParser.Context, except?: string): boolean {
80
49
  source &&= source.replace(/^[^\S\n]+/, '');
81
50
  if (source === '') return true;
82
- return source.slice(0, except?.length ?? 0) !== except
83
- && isStartTight(source, context);
51
+ return isStartTight(source, context, except);
84
52
  }
85
- export function startTight<P extends Parser<unknown>>(parser: P): P;
86
- export function startTight<T>(parser: Parser<T>): Parser<T> {
53
+ export function startTight<P extends Parser<unknown>>(parser: P, except?: string): P;
54
+ export function startTight<T>(parser: Parser<T>, except?: string): Parser<T> {
87
55
  return (source, context) =>
88
- isStartTight(source, context)
56
+ isStartTight(source, context, except)
89
57
  ? parser(source, context)
90
58
  : undefined;
91
59
  }
92
- function isStartTight(source: string, context: MarkdownParser.Context): boolean {
60
+ function isStartTight(source: string, context: MarkdownParser.Context, except?: string): boolean {
93
61
  if (source === '') return true;
62
+ if (except && source.slice(0, except.length) === except) return false;
94
63
  switch (source[0]) {
95
64
  case ' ':
96
65
  case ' ':