securemark 0.288.0 → 0.288.2

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 (54) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/index.js +7261 -7317
  3. package/markdown.d.ts +1 -7
  4. package/package.json +1 -1
  5. package/src/combinator/control/manipulation/surround.ts +5 -0
  6. package/src/combinator/data/parser/context/delimiter.ts +2 -2
  7. package/src/combinator/data/parser.ts +1 -0
  8. package/src/parser/api/parse.test.ts +2 -2
  9. package/src/parser/block/codeblock.ts +9 -6
  10. package/src/parser/block/extension/aside.ts +7 -8
  11. package/src/parser/block/extension/example.ts +7 -8
  12. package/src/parser/block/extension/figure.ts +77 -74
  13. package/src/parser/block/extension/message.ts +7 -8
  14. package/src/parser/block/extension/placeholder.ts +6 -3
  15. package/src/parser/block/extension/table.ts +13 -24
  16. package/src/parser/block/heading.test.ts +1 -1
  17. package/src/parser/block/heading.ts +2 -3
  18. package/src/parser/block/ilist.ts +17 -7
  19. package/src/parser/block/mathblock.ts +8 -6
  20. package/src/parser/block/mediablock.ts +5 -8
  21. package/src/parser/block/olist.ts +5 -7
  22. package/src/parser/block/reply/cite.ts +2 -6
  23. package/src/parser/block/reply/quote.ts +3 -2
  24. package/src/parser/block/reply.ts +1 -1
  25. package/src/parser/block/sidefence.ts +2 -3
  26. package/src/parser/block/table.ts +2 -3
  27. package/src/parser/block/ulist.ts +3 -17
  28. package/src/parser/context.ts +1 -2
  29. package/src/parser/header.ts +2 -3
  30. package/src/parser/inline/annotation.test.ts +1 -0
  31. package/src/parser/inline/bracket.ts +18 -24
  32. package/src/parser/inline/extension/index.test.ts +1 -1
  33. package/src/parser/inline/extension/index.ts +17 -7
  34. package/src/parser/inline/extension/indexee.ts +2 -3
  35. package/src/parser/inline/extension/indexer.test.ts +2 -1
  36. package/src/parser/inline/extension/placeholder.ts +2 -3
  37. package/src/parser/inline/html.ts +21 -23
  38. package/src/parser/inline/htmlentity.ts +6 -8
  39. package/src/parser/inline/link.test.ts +4 -0
  40. package/src/parser/inline/link.ts +3 -5
  41. package/src/parser/inline/math.ts +3 -3
  42. package/src/parser/inline/media.test.ts +17 -13
  43. package/src/parser/inline/media.ts +15 -9
  44. package/src/parser/inline/reference.ts +3 -7
  45. package/src/parser/inline/ruby.ts +4 -5
  46. package/src/parser/inline.test.ts +5 -3
  47. package/src/parser/source/escapable.test.ts +5 -5
  48. package/src/parser/source/escapable.ts +7 -1
  49. package/src/parser/source/str.ts +4 -6
  50. package/src/parser/source/text.ts +1 -0
  51. package/src/parser/source/unescapable.test.ts +5 -5
  52. package/src/parser/source/unescapable.ts +13 -8
  53. package/src/parser/util.ts +12 -0
  54. package/src/parser/visibility.ts +2 -3
@@ -7,10 +7,10 @@ describe('Unit: parser/inline/media', () => {
7
7
  const parser = (source: string) => some(media)({ source, context: {} });
8
8
 
9
9
  it('xss', () => {
10
- assert.deepStrictEqual(inspect(parser('![]{javascript:alert}')), [['<img class="media invalid" data-src="javascript:alert" alt="">'], '']);
11
- assert.deepStrictEqual(inspect(parser('![]{vbscript:alert}')), [['<img class="media invalid" data-src="vbscript:alert" alt="">'], '']);
12
- assert.deepStrictEqual(inspect(parser('![]{data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K}')), [['<img class="media invalid" data-src="data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K" alt="">'], '']);
13
- assert.deepStrictEqual(inspect(parser('![]{any:alert}')), [['<img class="media invalid" data-src="any:alert" alt="">'], '']);
10
+ assert.deepStrictEqual(inspect(parser('![]{javascript:alert}')), [['<img class="invalid" data-src="javascript:alert" alt="">'], '']);
11
+ assert.deepStrictEqual(inspect(parser('![]{vbscript:alert}')), [['<img class="invalid" data-src="vbscript:alert" alt="">'], '']);
12
+ assert.deepStrictEqual(inspect(parser('![]{data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K}')), [['<img class="invalid" data-src="data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K" alt="">'], '']);
13
+ assert.deepStrictEqual(inspect(parser('![]{any:alert}')), [['<img class="invalid" data-src="any:alert" alt="">'], '']);
14
14
  assert.deepStrictEqual(inspect(parser('![]{"}')), [['<a href="&quot;" target="_blank"><img class="media" data-src="&quot;" alt=""></a>'], '']);
15
15
  assert.deepStrictEqual(inspect(parser('![]{\\}')), [['<a href="\\" target="_blank"><img class="media" data-src="\\" alt=""></a>'], '']);
16
16
  assert.deepStrictEqual(inspect(parser('![\\"]{/}')), [['<a href="/" target="_blank"><img class="media" data-src="/" alt="&quot;"></a>'], '']);
@@ -30,6 +30,8 @@ describe('Unit: parser/inline/media', () => {
30
30
  assert.deepStrictEqual(inspect(parser('![]{ }')), undefined);
31
31
  assert.deepStrictEqual(inspect(parser('![]]{/}')), undefined);
32
32
  assert.deepStrictEqual(inspect(parser('![]{{}')), undefined);
33
+ assert.deepStrictEqual(inspect(parser('![]{}}')), undefined);
34
+ assert.deepStrictEqual(inspect(parser('![]{{}}')), undefined);
33
35
  assert.deepStrictEqual(inspect(parser('![]{{b}}')), undefined);
34
36
  assert.deepStrictEqual(inspect(parser('![]{b\nc}')), undefined);
35
37
  assert.deepStrictEqual(inspect(parser('![]{a\\\nc}')), undefined);
@@ -42,20 +44,20 @@ describe('Unit: parser/inline/media', () => {
42
44
  assert.deepStrictEqual(inspect(parser('![\\ ]{b}')), undefined);
43
45
  assert.deepStrictEqual(inspect(parser('![\\\n]{b}')), undefined);
44
46
  assert.deepStrictEqual(inspect(parser('![&Tab;]{b}')), undefined);
45
- assert.deepStrictEqual(inspect(parser('![&a;]{b}')), [['<img class="media invalid" data-src="b" alt="&amp;a;">'], '']);
47
+ assert.deepStrictEqual(inspect(parser('![&a;]{b}')), [['<img class="invalid" data-src="b" alt="&amp;a;">'], '']);
46
48
  assert.deepStrictEqual(inspect(parser('![[]{b}')), undefined);
47
49
  assert.deepStrictEqual(inspect(parser('![]]{b}')), undefined);
48
50
  assert.deepStrictEqual(inspect(parser('![a]{}')), undefined);
49
51
  assert.deepStrictEqual(inspect(parser('![a\nb]{b}')), undefined);
50
52
  assert.deepStrictEqual(inspect(parser('![a\\\nb]{b}')), undefined);
51
- assert.deepStrictEqual(inspect(parser('![]{ttp://host}')), [['<img class="media invalid" data-src="ttp://host" alt="">'], '']);
52
- assert.deepStrictEqual(inspect(parser('![]{tel:1234567890}')), [['<img class="media invalid" data-src="tel:1234567890" alt="">'], '']);
53
- //assert.deepStrictEqual(inspect(parser('![]{http://[::ffff:0:0%1]}')), [['<img class="media invalid" alt="">'], '']);
54
- //assert.deepStrictEqual(inspect(parser('![]{http://[::ffff:0:0/96]}')), [['<img class="media invalid" alt="">'], '']);
55
- assert.deepStrictEqual(inspect(parser('![]{.}')), [['<img class="media invalid" data-src="." alt="">'], '']);
56
- assert.deepStrictEqual(inspect(parser('![]{..}')), [['<img class="media invalid" data-src=".." alt="">'], '']);
57
- assert.deepStrictEqual(inspect(parser('![]{../}')), [['<img class="media invalid" data-src="../" alt="">'], '']);
58
- assert.deepStrictEqual(inspect(parser('![]{/../b}')), [['<img class="media invalid" data-src="/../b" alt="">'], '']);
53
+ assert.deepStrictEqual(inspect(parser('![]{ttp://host}')), [['<img class="invalid" data-src="ttp://host" alt="">'], '']);
54
+ assert.deepStrictEqual(inspect(parser('![]{tel:1234567890}')), [['<img class="invalid" data-src="tel:1234567890" alt="">'], '']);
55
+ //assert.deepStrictEqual(inspect(parser('![]{http://[::ffff:0:0%1]}')), [['<img class="invalid" alt="">'], '']);
56
+ //assert.deepStrictEqual(inspect(parser('![]{http://[::ffff:0:0/96]}')), [['<img class="invalid" alt="">'], '']);
57
+ assert.deepStrictEqual(inspect(parser('![]{.}')), [['<img class="invalid" data-src="." alt="">'], '']);
58
+ assert.deepStrictEqual(inspect(parser('![]{..}')), [['<img class="invalid" data-src=".." alt="">'], '']);
59
+ assert.deepStrictEqual(inspect(parser('![]{../}')), [['<img class="invalid" data-src="../" alt="">'], '']);
60
+ assert.deepStrictEqual(inspect(parser('![]{/../b}')), [['<img class="invalid" data-src="/../b" alt="">'], '']);
59
61
  assert.deepStrictEqual(inspect(parser(' ![]{b}')), undefined);
60
62
  assert.deepStrictEqual(inspect(parser('[]{/}')), undefined);
61
63
  });
@@ -69,6 +71,8 @@ describe('Unit: parser/inline/media', () => {
69
71
  assert.deepStrictEqual(inspect(parser('![]{ b }')), [['<a href="b" target="_blank"><img class="media" data-src="b" alt=""></a>'], '']);
70
72
  assert.deepStrictEqual(inspect(parser('![]{ b }')), [['<a href="b" target="_blank"><img class="media" data-src="b" alt=""></a>'], '']);
71
73
  assert.deepStrictEqual(inspect(parser('![]{ b }')), [['<a href="b" target="_blank"><img class="media" data-src="b" alt=""></a>'], '']);
74
+ assert.deepStrictEqual(inspect(parser('![]{"}')), [['<a href="&quot;" target="_blank"><img class="media" data-src="&quot;" alt=""></a>'], '']);
75
+ assert.deepStrictEqual(inspect(parser('![]{"}"}')), [['<a href="&quot;" target="_blank"><img class="media" data-src="&quot;" alt=""></a>'], '"}']);
72
76
  assert.deepStrictEqual(inspect(parser('![]{\\}')), [['<a href="\\" target="_blank"><img class="media" data-src="\\" alt=""></a>'], '']);
73
77
  assert.deepStrictEqual(inspect(parser('![]{\\ }')), [['<a href="\\" target="_blank"><img class="media" data-src="\\" alt=""></a>'], '']);
74
78
  assert.deepStrictEqual(inspect(parser('![]{\\b}')), [['<a href="\\b" target="_blank"><img class="media" data-src="\\b" alt=""></a>'], '']);
@@ -5,7 +5,7 @@ import { unsafelink, uri, option as linkoption, resolve } from './link';
5
5
  import { attributes } from './html';
6
6
  import { unsafehtmlentity } from './htmlentity';
7
7
  import { txt, linebreak, str } from '../source';
8
- import { markInvalid } from '../util';
8
+ import { invalid } from '../util';
9
9
  import { ReadonlyURL } from 'spica/url';
10
10
  import { push } from 'spica/array';
11
11
  import { html, define } from 'typed-dom/dom';
@@ -51,7 +51,7 @@ export const media: MediaParser = lazy(() => constraint(State.media, false, vali
51
51
  || (cache = context.caches?.media?.get(url.href)?.cloneNode(true))
52
52
  || html('img', { class: 'media', 'data-src': url.source, alt: text });
53
53
  assert(!el.matches('.invalid'));
54
- cache?.hasAttribute('alt') && cache?.setAttribute('alt', text);
54
+ cache?.hasAttribute('alt') && cache.setAttribute('alt', text);
55
55
  if (!sanitize(el, url, text)) return [[el], rest];
56
56
  assert(!el.matches('.invalid'));
57
57
  define(el, attributes('media', push([], el.classList), optspec, params));
@@ -98,19 +98,25 @@ function sanitize(target: HTMLElement, uri: ReadonlyURL, alt: string): boolean {
98
98
  case 'https:':
99
99
  assert(uri.host);
100
100
  if (/\/\.\.?(?:\/|$)/.test('/' + uri.source.slice(0, uri.source.search(/[?#]|$/)))) {
101
- markInvalid(target, 'media', 'argument',
102
- 'Dot-segments cannot be used in media paths; use subresource paths instead');
101
+ define(target, {
102
+ class: 'invalid',
103
+ ...invalid('media', 'argument',
104
+ 'Dot-segments cannot be used in media paths; use subresource paths instead')
105
+ });
103
106
  return false;
104
107
  }
105
108
  break;
106
109
  default:
107
- markInvalid(target, 'media', 'argument', 'Invalid protocol');
110
+ define(target, { class: 'invalid', ...invalid('media', 'argument', 'Invalid protocol') });
108
111
  return false;
109
112
  }
110
- if (alt.includes(Command.Escape)) {
111
- define(target, { alt: target.getAttribute('alt')?.replace(CmdRegExp.Escape, '') });
112
- markInvalid(target, 'media', 'content',
113
- `Cannot use invalid HTML entitiy "${alt.match(/&[0-9A-Za-z]+;/)![0]}"`);
113
+ if (alt.includes(Command.Error)) {
114
+ define(target, {
115
+ class: 'invalid',
116
+ alt: target.getAttribute('alt')?.replace(CmdRegExp.Error, ''),
117
+ ...invalid('media', 'argument',
118
+ `Cannot use invalid HTML entitiy "${alt.match(/&[0-9A-Za-z]+;/)![0]}"`)
119
+ });
114
120
  return false;
115
121
  }
116
122
  return true;
@@ -6,9 +6,10 @@ import { str } from '../source';
6
6
  import { blank, trimBlankStart, trimBlankNodeEnd } from '../visibility';
7
7
  import { html, defrag } from 'typed-dom/dom';
8
8
  import { unshift } from 'spica/array';
9
+ import { invalid } from '../util';
9
10
 
10
11
  export const reference: ReferenceParser = lazy(() => constraint(State.reference, false, surround(
11
- '[[',
12
+ str('[['),
12
13
  precedence(1, state(State.annotation | State.reference | State.media,
13
14
  subsequence([
14
15
  abbr,
@@ -38,12 +39,7 @@ const abbr: ReferenceParser.AbbrParser = surround(
38
39
  function attributes(ns: (string | HTMLElement)[]): Record<string, string | undefined> {
39
40
  switch (ns[0]) {
40
41
  case '':
41
- return {
42
- class: 'invalid',
43
- 'data-invalid-syntax': 'reference',
44
- 'data-invalid-type': 'syntax',
45
- 'data-invalid-message': 'Invalid abbreviation',
46
- };
42
+ return { class: 'invalid', ...invalid('reference', 'syntax', 'Invalid abbreviation') };
47
43
  case '\n':
48
44
  const abbr = ns[1] as string;
49
45
  ns[0] = ns[1] = '';
@@ -5,6 +5,7 @@ import { sequence, surround, dup, lazy, fmap } from '../../combinator';
5
5
  import { unsafehtmlentity } from './htmlentity';
6
6
  import { text as txt, str } from '../source';
7
7
  import { isTightNodeStart } from '../visibility';
8
+ import { invalid } from '../util';
8
9
  import { unshift, push } from 'spica/array';
9
10
  import { html, defrag } from 'typed-dom/dom';
10
11
 
@@ -100,13 +101,11 @@ function attributes(texts: string[], rubies: string[]): Record<string, string> {
100
101
  let attrs: Record<string, string> | undefined;
101
102
  for (const ss of [texts, rubies]) {
102
103
  for (let i = 0; i < ss.length; ++i) {
103
- if (!ss[i].includes(Command.Escape)) continue;
104
- ss[i] = ss[i].replace(CmdRegExp.Escape, '');
104
+ if (!ss[i].includes(Command.Error)) continue;
105
+ ss[i] = ss[i].replace(CmdRegExp.Error, '');
105
106
  attrs ??= {
106
107
  class: 'invalid',
107
- 'data-invalid-syntax': 'ruby',
108
- 'data-invalid-type': ss === texts ? 'content' : 'argument',
109
- 'data-invalid-message': 'Invalid HTML entity',
108
+ ...invalid('ruby', ss === texts ? 'content' : 'argument', 'Invalid HTML entity'),
110
109
  };
111
110
  }
112
111
  }
@@ -72,6 +72,9 @@ describe('Unit: parser/inline', () => {
72
72
  assert.deepStrictEqual(inspect(parser('***a*b*c***')), [['<strong><em>a</em>b<em>c</em></strong>'], '']);
73
73
  assert.deepStrictEqual(inspect(parser('*(*a*)*')), [['<em><span class="paren">(<em>a</em>)</span></em>'], '']);
74
74
  assert.deepStrictEqual(inspect(parser('**(**a**)**')), [['<strong><span class="paren">(<strong>a</strong>)</span></strong>'], '']);
75
+ assert.deepStrictEqual(inspect(parser('*[*]')), [['*', '[', '*', ']'], '']);
76
+ assert.deepStrictEqual(inspect(parser('*<*>')), [['<em>&lt;</em>', '>'], '']);
77
+ assert.deepStrictEqual(inspect(parser('*a((b))*')), [['<em>a<sup class="annotation"><span>b</span></sup></em>'], '']);
75
78
  assert.deepStrictEqual(inspect(parser('*++ ++*')), [['<em><ins> </ins></em>'], '']);
76
79
  assert.deepStrictEqual(inspect(parser('*++ a ++*')), [['<em><ins> a </ins></em>'], '']);
77
80
  assert.deepStrictEqual(inspect(parser('*++ a ++*')), [['<em><ins> a </ins></em>'], '']);
@@ -81,9 +84,6 @@ describe('Unit: parser/inline', () => {
81
84
  assert.deepStrictEqual(inspect(parser('<bdi>[[<bdi>[[a]]</bdi>]]</bdi>')), [['<bdi><sup class="reference"><span><bdi>[[a]]</bdi></span></sup></bdi>'], '']);
82
85
  assert.deepStrictEqual(inspect(parser('<bdi>[#</bdi>]')), [['<bdi>[#</bdi>', ']'], '']);
83
86
  assert.deepStrictEqual(inspect(parser('"<bdi>("")</bdi>')), [['"', '<bdi><span class="paren">("")</span></bdi>'], '']);
84
- assert.deepStrictEqual(inspect(parser('*[*]')), [['*', '[', '*', ']'], '']);
85
- assert.deepStrictEqual(inspect(parser('*<*>')), [['<em>&lt;</em>', '>'], '']);
86
- assert.deepStrictEqual(inspect(parser('*a((b))*')), [['<em>a<sup class="annotation"><span>b</span></sup></em>'], '']);
87
87
  assert.deepStrictEqual(inspect(parser('++\na\n++\n~~\nb\n~~\nc')), [['<ins><br>a</ins>', '<br>', '<del><br>b</del>', '<br>', 'c'], '']);
88
88
  assert.deepStrictEqual(inspect(parser('``a`')), [['``', 'a', '`'], '']);
89
89
  assert.deepStrictEqual(inspect(parser('[@a]')), [['[', '<a class="account" href="/@a">@a</a>', ']'], '']);
@@ -127,6 +127,8 @@ describe('Unit: parser/inline', () => {
127
127
  assert.deepStrictEqual(inspect(parser('((${))}$')), [['(', '(', '<span class="math" translate="no" data-src="${))}$">${))}$</span>'], '']);
128
128
  assert.deepStrictEqual(inspect(parser('((a\nb))')), [['<span class="paren">(<span class="paren">(a<br>b)</span>)</span>'], '']);
129
129
  assert.deepStrictEqual(inspect(parser('(((a\nb)))')), [['<span class="paren">(<span class="paren">(<span class="paren">(a<br>b)</span>)</span>)</span>'], '']);
130
+ assert.deepStrictEqual(inspect(parser('(([[a] ]))')), [['<sup class="annotation"><span>[[a] ]</span></sup>'], '']);
131
+ assert.deepStrictEqual(inspect(parser('(([["*(*"] ]))')), [['<sup class="annotation"><span>[["*(*"] ]</span></sup>'], '']);
130
132
  assert.deepStrictEqual(inspect(parser('(([:a\n]')), [['(', '(', '<span class="invalid">a<br></span>'], '']);
131
133
  assert.deepStrictEqual(inspect(parser('"((""))')), [['"', '(', '(', '"', '"', ')', ')'], '']);
132
134
  assert.deepStrictEqual(inspect(parser('[[[a]]')), [['[', '<sup class="reference"><span>a</span></sup>'], '']);
@@ -20,13 +20,13 @@ describe('Unit: parser/source/escsource', () => {
20
20
  assert.deepStrictEqual(inspect(parser(' ')), [[' '], '']);
21
21
  assert.deepStrictEqual(inspect(parser(' ')), [[' ', ' '], '']);
22
22
  assert.deepStrictEqual(inspect(parser(' ')), [[' ', ' '], '']);
23
- assert.deepStrictEqual(inspect(parser(' \n')), [[' ', '\n'], '']);
24
- assert.deepStrictEqual(inspect(parser(' \n')), [[' ', ' ', '\n'], '']);
25
- assert.deepStrictEqual(inspect(parser(' \n')), [[' ', ' ', '\n'], '']);
23
+ //assert.deepStrictEqual(inspect(parser(' \n')), [[' ', '\n'], '']);
24
+ //assert.deepStrictEqual(inspect(parser(' \n')), [[' ', ' ', '\n'], '']);
25
+ //assert.deepStrictEqual(inspect(parser(' \n')), [[' ', ' ', '\n'], '']);
26
26
  });
27
27
 
28
28
  it('linebreak', () => {
29
- assert.deepStrictEqual(inspect(parser('\n\n')), [['\n', '\n'], '']);
29
+ //assert.deepStrictEqual(inspect(parser('\n\n')), [['\n', '\n'], '']);
30
30
  });
31
31
 
32
32
  it('\\', () => {
@@ -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
  });
@@ -20,19 +20,25 @@ export const escsource: EscapableSourceParser = ({ source, context }) => {
20
20
  consume(-1, context);
21
21
  return [[], source.slice(1)];
22
22
  case Command.Escape:
23
+ assert(false);
23
24
  consume(1, context);
24
25
  return [[source.slice(1, 2)], source.slice(2)];
25
26
  case '\\':
26
27
  switch (source[1]) {
27
28
  case undefined:
29
+ return [[source[0]], ''];
28
30
  case '\n':
29
31
  return [[source[0]], source.slice(1)];
30
32
  default:
31
33
  consume(1, context);
32
34
  return [[source.slice(0, 2)], source.slice(2)];
33
35
  }
36
+ case '\n':
37
+ assert(false);
38
+ return [[source[0]], source.slice(1)];
34
39
  default:
35
- const b = source[0] !== '\n' && source[0].trimStart() === '';
40
+ assert(source[0] !== '\n');
41
+ const b = source[0].trimStart() === '';
36
42
  const i = b
37
43
  ? source.search(nonWhitespace)
38
44
  : 1;
@@ -19,11 +19,9 @@ export function str(pattern: string | RegExp, not?: string): Parser<string, Cont
19
19
  : ({ source, context }) => {
20
20
  if (source === '') return;
21
21
  const m = source.match(pattern);
22
- count && m && consume(m[0].length, context);
23
- if (m && not && source.slice(m[0].length, m[0].length + not.length) === not) return;
24
- //assert(!m || m[0]);
25
- return m
26
- ? [[m[0]], source.slice(m[0].length)]
27
- : undefined;
22
+ if (m === null) return;
23
+ count && consume(m[0].length, context);
24
+ if (not && source.slice(m[0].length, m[0].length + not.length) === not) return;
25
+ return [[m[0]], source.slice(m[0].length)];
28
26
  };
29
27
  }
@@ -27,6 +27,7 @@ export const text: TextParser = ({ source, context }) => {
27
27
  case '\\':
28
28
  switch (source[1]) {
29
29
  case undefined:
30
+ return [[], ''];
30
31
  case '\n':
31
32
  assert(source[0] !== Command.Escape);
32
33
  return [[], source.slice(1)];
@@ -20,13 +20,13 @@ describe('Unit: parser/source/unescapable', () => {
20
20
  assert.deepStrictEqual(inspect(parser(' ')), [[' '], '']);
21
21
  assert.deepStrictEqual(inspect(parser(' ')), [[' ', ' '], '']);
22
22
  assert.deepStrictEqual(inspect(parser(' ')), [[' ', ' '], '']);
23
- assert.deepStrictEqual(inspect(parser(' \n')), [[' ', '\n'], '']);
24
- assert.deepStrictEqual(inspect(parser(' \n')), [[' ', ' ', '\n'], '']);
25
- assert.deepStrictEqual(inspect(parser(' \n')), [[' ', ' ', '\n'], '']);
23
+ //assert.deepStrictEqual(inspect(parser(' \n')), [[' ', '\n'], '']);
24
+ //assert.deepStrictEqual(inspect(parser(' \n')), [[' ', ' ', '\n'], '']);
25
+ //assert.deepStrictEqual(inspect(parser(' \n')), [[' ', ' ', '\n'], '']);
26
26
  });
27
27
 
28
28
  it('linebreak', () => {
29
- assert.deepStrictEqual(inspect(parser('\n\n')), [['\n', '\n'], '']);
29
+ //assert.deepStrictEqual(inspect(parser('\n\n')), [['\n', '\n'], '']);
30
30
  });
31
31
 
32
32
  it('\\', () => {
@@ -39,7 +39,7 @@ describe('Unit: parser/source/unescapable', () => {
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
  });
@@ -18,17 +18,22 @@ export const unescsource: UnescapableSourceParser = ({ source, context }) => {
18
18
  consume(-1, context);
19
19
  return [[], source.slice(1)];
20
20
  case Command.Escape:
21
- assert(source[0] !== Command.Escape);
21
+ assert(false);
22
22
  consume(1, context);
23
23
  return [[source.slice(1, 2)], source.slice(2)];
24
+ case '\n':
25
+ assert(false);
26
+ return [[source[0]], source.slice(1)];
27
+ default:
28
+ assert(source[0] !== '\n');
29
+ const b = source[0].trimStart() === '';
30
+ const i = b || isAlphanumeric(source[0])
31
+ ? source.search(b ? nonWhitespace : nonAlphanumeric) || 1
32
+ : 1;
33
+ assert(i > 0);
34
+ consume(i - 1, context);
35
+ return [[source.slice(0, i - +b || 1)], source.slice(i - +b || 1)];
24
36
  }
25
- const b = source[0] !== '\n' && source[0].trimStart() === '';
26
- const i = b || isAlphanumeric(source[0])
27
- ? source.search(b ? nonWhitespace : nonAlphanumeric) || 1
28
- : 1;
29
- assert(i > 0);
30
- consume(i - 1, context);
31
- return [[source.slice(0, i - +b || 1)], source.slice(i - +b || 1)];
32
37
  }
33
38
  default:
34
39
  consume(i, context);
@@ -75,6 +75,18 @@ export function repeat<T extends HTMLElement | string>(symbol: string, parser: P
75
75
  };
76
76
  }
77
77
 
78
+ export function invalid(
79
+ syntax: string,
80
+ type: string,
81
+ message: string,
82
+ ): Record<string, string> {
83
+ return {
84
+ 'data-invalid-syntax': syntax,
85
+ 'data-invalid-type': type,
86
+ 'data-invalid-message': message,
87
+ };
88
+ }
89
+
78
90
  export function markInvalid<T extends Element>(
79
91
  el: T,
80
92
  syntax: string,
@@ -5,7 +5,6 @@ import { union, some, verify, convert, fmap } from '../combinator';
5
5
  import { unsafehtmlentity } from './inline/htmlentity';
6
6
  import { linebreak, unescsource } from './source';
7
7
  import { invisibleHTMLEntityNames } from './api/normalize';
8
- import { reduce } from 'spica/memoize';
9
8
  import { push } from 'spica/array';
10
9
 
11
10
  export namespace blank {
@@ -78,7 +77,7 @@ export function tightStart<T>(parser: Parser<T>, except?: string): Parser<T> {
78
77
  ? parser(input)
79
78
  : undefined;
80
79
  }
81
- const isTightStart = reduce((input: Input<MarkdownParser.Context>, except?: string): boolean => {
80
+ function isTightStart(input: Input<MarkdownParser.Context>, except?: string): boolean {
82
81
  const { source } = input;
83
82
  if (source === '') return true;
84
83
  if (except && source.slice(0, except.length) === except) return false;
@@ -109,7 +108,7 @@ const isTightStart = reduce((input: Input<MarkdownParser.Context>, except?: stri
109
108
  default:
110
109
  return source[0].trimStart() !== '';
111
110
  }
112
- }, ({ source }, except = '') => `${source}${Command.Separator}${except}`);
111
+ }
113
112
 
114
113
  export function isLooseNodeStart(nodes: readonly (HTMLElement | string)[]): boolean {
115
114
  if (nodes.length === 0) return true;