securemark 0.243.1 → 0.244.1

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.
@@ -2,36 +2,46 @@ import { Parser, Ctx } from '../../data/parser';
2
2
  import { firstline, isEmpty } from '../constraint/line';
3
3
  import { unshift } from 'spica/array';
4
4
 
5
- export function fence<C extends Ctx, D extends Parser<unknown, C>[]>(opener: RegExp, limit: number, separation: boolean = true): Parser<string, C, D> {
5
+ export function fence<C extends Ctx, D extends Parser<unknown, C>[]>(opener: RegExp, limit: number, separation = true): Parser<string, C, D> {
6
6
  return source => {
7
7
  if (source === '') return;
8
8
  const matches = source.match(opener);
9
9
  if (!matches) return;
10
10
  assert(matches[0] === firstline(source));
11
11
  const delim = matches[1];
12
+ assert(delim && delim === delim.trim());
12
13
  if (matches[0].indexOf(delim, delim.length) !== -1) return;
13
14
  let rest = source.slice(matches[0].length);
14
15
  // Prevent annoying parsing in editing.
15
16
  if (isEmpty(firstline(rest)) && firstline(rest.slice(firstline(rest).length)).trimEnd() !== delim) return;
16
17
  let block = '';
17
18
  let closer = '';
18
- for (let count = 1, next = firstline(rest); ; ++count) {
19
+ let overflow = '';
20
+ for (let count = 1; ; ++count) {
19
21
  if (rest === '') break;
20
- const line = next;
21
- next = firstline(rest.slice(line.length));
22
- if (count > limit + 1 && isEmpty(line)) break;
23
- if (count <= limit + 1 &&
24
- line.slice(0, delim.length) === delim &&
25
- line.trimEnd() === delim &&
26
- (!separation || isEmpty(next))) {
27
- assert(line.trimEnd() === delim);
28
- closer = delim;
29
- rest = rest.slice(line.length);
30
- break;
22
+ const line = firstline(rest);
23
+ if ((closer || count > limit + 1) && isEmpty(line)) break;
24
+ if(closer) {
25
+ overflow += line;
26
+ }
27
+ if (!closer && count <= limit + 1 && line.slice(0, delim.length) === delim && line.trimEnd() === delim) {
28
+ closer = line;
29
+ if (isEmpty(firstline(rest.slice(line.length)))) {
30
+ rest = rest.slice(line.length);
31
+ break;
32
+ }
33
+ if (!separation) {
34
+ rest = rest.slice(line.length);
35
+ break;
36
+ }
37
+ assert(!overflow);
38
+ overflow = line;
39
+ }
40
+ if (!overflow) {
41
+ block += line;
31
42
  }
32
- block += line;
33
43
  rest = rest.slice(line.length);
34
44
  }
35
- return [unshift([block, closer], matches), rest];
45
+ return [unshift([block, overflow, closer], matches), rest];
36
46
  };
37
47
  }
@@ -1,3 +1,5 @@
1
+ import { undefined } from 'spica/global';
2
+
1
3
  export type Parser<T, C extends Ctx = Ctx, D extends Parser<unknown, C>[] = any>
2
4
  = (source: string, context: C) => Result<T, C, D>;
3
5
  export type Result<T, C extends Ctx = Ctx, D extends Parser<unknown, C>[] = any>
package/src/debug.test.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { Result, eval, exec } from './combinator/data/parser';
2
2
  import { html, define } from 'typed-dom/dom';
3
+ import { querySelector, querySelectorAll } from 'typed-dom/query';
3
4
 
4
5
  export function inspect(result: Result<HTMLElement | string>, until: number | string = Infinity): Result<string> {
5
6
  return result && [
@@ -7,14 +8,15 @@ export function inspect(result: Result<HTMLElement | string>, until: number | st
7
8
  assert(node);
8
9
  if (typeof node === 'string') return node;
9
10
  node = node.cloneNode(true);
10
- assert(!node.matches('.invalid[data-invalid-message$="."]'));
11
- assert(!node.querySelector('.invalid[data-invalid-message$="."]'));
12
- [node, ...node.querySelectorAll('.invalid')].forEach(el =>
11
+ assert(!querySelector(node, '.invalid[data-invalid-message$="."]'));
12
+ querySelectorAll(node, '.invalid').forEach(el => {
13
+ assert(el.matches('[data-invalid-syntax][data-invalid-type][data-invalid-message]'));
13
14
  define(el, {
14
15
  'data-invalid-syntax': null,
15
16
  'data-invalid-type': null,
16
17
  'data-invalid-message': null,
17
- }));
18
+ });
19
+ });
18
20
  until = typeof until === 'number'
19
21
  ? until
20
22
  : ~(~node.outerHTML.indexOf(until) || -Infinity) + until.length;
@@ -17,6 +17,7 @@ describe('Unit: parser/block/codeblock', () => {
17
17
  assert.deepStrictEqual(inspect(parser('```\na\n```\nb')), [['<pre class="invalid" translate="no">```\na\n```\nb</pre>'], '']);
18
18
  assert.deepStrictEqual(inspect(parser('```\n\n\n```')), undefined);
19
19
  assert.deepStrictEqual(inspect(parser('```a ```\n```')), undefined);
20
+ assert.deepStrictEqual(inspect(parser('```\n```\n```')), [['<pre class="invalid" translate="no">```\n```\n```</pre>'], '']);
20
21
  assert.deepStrictEqual(inspect(parser('```\n````')), [['<pre class="invalid" translate="no">```\n````</pre>'], '']);
21
22
  assert.deepStrictEqual(inspect(parser('````\n```')), [['<pre class="invalid" translate="no">````\n```</pre>'], '']);
22
23
  assert.deepStrictEqual(inspect(parser(' ```\n```')), undefined);
@@ -30,7 +31,6 @@ describe('Unit: parser/block/codeblock', () => {
30
31
  assert.deepStrictEqual(inspect(parser('```\na\nb\n```')), [['<pre class="text">a<br>b</pre>'], '']);
31
32
  assert.deepStrictEqual(inspect(parser('```\n\\\n```')), [['<pre class="text">\\</pre>'], '']);
32
33
  assert.deepStrictEqual(inspect(parser('```\n`\n```')), [['<pre class="text">`</pre>'], '']);
33
- assert.deepStrictEqual(inspect(parser('```\n```\n```')), [['<pre class="text">```</pre>'], '']);
34
34
  assert.deepStrictEqual(inspect(parser('```\n```\n\n```')), [['<pre class="text"></pre>'], '\n```']);
35
35
  assert.deepStrictEqual(inspect(parser('```\n````\n```')), [['<pre class="text">````</pre>'], '']);
36
36
  assert.deepStrictEqual(inspect(parser('```\n````\n\n```')), [['<pre class="text">````<br></pre>'], '']);
@@ -17,7 +17,7 @@ export const segment_: CodeBlockParser.SegmentParser = block(validate('```',
17
17
  export const codeblock: CodeBlockParser = block(validate('```', fmap(
18
18
  fence(opener, 300),
19
19
  // Bug: Type mismatch between outer and inner.
20
- ([body, closer, opener, delim, param]: string[], _, context) => {
20
+ ([body, overflow, closer, opener, delim, param]: string[], _, context) => {
21
21
  const params = param.match(/(?:\\.?|\S)+/g)?.reduce<{
22
22
  lang?: string;
23
23
  path?: string;
@@ -45,17 +45,20 @@ export const codeblock: CodeBlockParser = block(validate('```', fmap(
45
45
  }
46
46
  }
47
47
  name in params
48
- ? params.invalid = `Duplicate ${name} value`
48
+ ? params.invalid = `Duplicate ${name} attribute`
49
49
  : params[name] = value;
50
50
  return params;
51
51
  }, {}) ?? {};
52
- if (!closer || params.invalid) return [html('pre', {
52
+ if (!closer || overflow || params.invalid) return [html('pre', {
53
53
  class: 'invalid',
54
54
  translate: 'no',
55
55
  'data-invalid-syntax': 'codeblock',
56
- 'data-invalid-type': !closer ? 'fence' : 'argument',
57
- 'data-invalid-message': !closer ? `Missing the closing delimiter "${delim}"` : params.invalid,
58
- }, `${opener}${body}${closer}`)];
56
+ 'data-invalid-type': !closer || overflow ? 'fence' : 'argument',
57
+ 'data-invalid-message':
58
+ !closer ? `Missing the closing delimiter "${delim}"` :
59
+ overflow ? `Invalid trailing line after the closing delimiter "${delim}"` :
60
+ params.invalid,
61
+ }, `${opener}${body}${overflow || closer}`)];
59
62
  const el = html('pre',
60
63
  {
61
64
  class: params.lang ? `code language-${params.lang}` : 'text',
@@ -7,14 +7,17 @@ import { html } from 'typed-dom/dom';
7
7
  export const aside: ExtensionParser.AsideParser = creator(100, block(validate('~~~', fmap(
8
8
  fence(/^(~{3,})aside(?!\S)([^\n]*)(?:$|\n)/, 300),
9
9
  // Bug: Type mismatch between outer and inner.
10
- ([body, closer, opener, delim, param]: string[], _, context) => {
11
- if (!closer || param.trimStart()) return [html('pre', {
10
+ ([body, overflow, closer, opener, delim, param]: string[], _, context) => {
11
+ if (!closer || overflow || param.trimStart()) return [html('pre', {
12
12
  class: 'invalid',
13
13
  translate: 'no',
14
14
  'data-invalid-syntax': 'aside',
15
- 'data-invalid-type': !closer ? 'fence' : 'argument',
16
- 'data-invalid-message': !closer ? `Missing the closing delimiter "${delim}"` : 'Invalid argument',
17
- }, `${opener}${body}${closer}`)];
15
+ 'data-invalid-type': !closer || overflow ? 'fence' : 'argument',
16
+ 'data-invalid-message':
17
+ !closer ? `Missing the closing delimiter "${delim}"` :
18
+ overflow ? `Invalid trailing line after the closing delimiter "${delim}"` :
19
+ 'Invalid argument',
20
+ }, `${opener}${body}${overflow || closer}`)];
18
21
  const annotations = html('ol', { class: 'annotations' });
19
22
  const references = html('ol', { class: 'references' });
20
23
  const document = parse(body.slice(0, -1), {
@@ -10,14 +10,17 @@ const opener = /^(~{3,})(?:example\/(\S+))?(?!\S)([^\n]*)(?:$|\n)/;
10
10
  export const example: ExtensionParser.ExampleParser = creator(100, block(validate('~~~', fmap(
11
11
  fence(opener, 300),
12
12
  // Bug: Type mismatch between outer and inner.
13
- ([body, closer, opener, delim, type = 'markdown', param]: string[], _, context) => {
14
- if (!closer || param.trimStart()) return [html('pre', {
13
+ ([body, overflow, closer, opener, delim, type = 'markdown', param]: string[], _, context) => {
14
+ if (!closer || overflow || param.trimStart()) return [html('pre', {
15
15
  class: 'invalid',
16
16
  translate: 'no',
17
17
  'data-invalid-syntax': 'example',
18
- 'data-invalid-type': !closer ? 'fence' : 'argument',
19
- 'data-invalid-message': !closer ? `Missing the closing delimiter "${delim}"` : 'Invalid argument',
20
- }, `${opener}${body}${closer}`)];
18
+ 'data-invalid-type': !closer || overflow ? 'fence' : 'argument',
19
+ 'data-invalid-message':
20
+ !closer ? `Missing the closing delimiter "${delim}"` :
21
+ overflow ? `Invalid trailing line after the closing delimiter "${delim}"` :
22
+ 'Invalid argument',
23
+ }, `${opener}${body}${overflow || closer}`)];
21
24
  switch (type) {
22
25
  case 'markdown': {
23
26
  const annotations = html('ol', { class: 'annotations' });
@@ -52,6 +55,7 @@ export const example: ExtensionParser.ExampleParser = creator(100, block(validat
52
55
  class: 'invalid',
53
56
  translate: 'no',
54
57
  'data-invalid-syntax': 'example',
58
+ 'data-invalid-type': 'type',
55
59
  'data-invalid-message': 'Invalid example type',
56
60
  }, `${opener}${body}${closer}`),
57
61
  ];
@@ -31,6 +31,8 @@ describe('Unit: parser/block/extension/fig', () => {
31
31
  assert.deepStrictEqual(inspect(parser('[$group-name]\n```\n~~~\n```')), [['<figure data-type="text" data-label="group-name" data-group="group"><figcaption><span class="figindex"></span></figcaption><div><pre class="text">~~~</pre></div></figure>'], '']);
32
32
  assert.deepStrictEqual(inspect(parser('[$group-name]\n$$\n\n$$')), [['<figure data-type="math" data-label="group-name" data-group="group"><figcaption><span class="figindex"></span></figcaption><div><div class="math" translate="no">$$\n\n$$</div></div></figure>'], '']);
33
33
  assert.deepStrictEqual(inspect(parser('[$group-name]\n$$\n\n$$\n')), [['<figure data-type="math" data-label="group-name" data-group="group"><figcaption><span class="figindex"></span></figcaption><div><div class="math" translate="no">$$\n\n$$</div></div></figure>'], '']);
34
+ assert.deepStrictEqual(inspect(parser('[$group-name]\n~~~\n~~~')), [['<figure data-type="example" data-label="group-name" data-group="group"><figcaption><span class="figindex"></span></figcaption><div><aside class="example" data-type="markdown"><pre translate="no"></pre><hr><section><ol class="annotations"></ol><ol class="references"></ol></section></aside></div></figure>'], '']);
35
+ assert.deepStrictEqual(inspect(parser('[$group-name]\n~~~\n~~~\n')), [['<figure data-type="example" data-label="group-name" data-group="group"><figcaption><span class="figindex"></span></figcaption><div><aside class="example" data-type="markdown"><pre translate="no"></pre><hr><section><ol class="annotations"></ol><ol class="references"></ol></section></aside></div></figure>'], '']);
34
36
  assert.deepStrictEqual(inspect(parser('[$group-name]\n~~~example/markdown\n~~~')), [['<figure data-type="example" data-label="group-name" data-group="group"><figcaption><span class="figindex"></span></figcaption><div><aside class="example" data-type="markdown"><pre translate="no"></pre><hr><section><ol class="annotations"></ol><ol class="references"></ol></section></aside></div></figure>'], '']);
35
37
  assert.deepStrictEqual(inspect(parser('[$group-name]\n~~~example/markdown\n~~~\n')), [['<figure data-type="example" data-label="group-name" data-group="group"><figcaption><span class="figindex"></span></figcaption><div><aside class="example" data-type="markdown"><pre translate="no"></pre><hr><section><ol class="annotations"></ol><ol class="references"></ol></section></aside></div></figure>'], '']);
36
38
  assert.deepStrictEqual(inspect(parser('[$group-name]\n~~~table\n~~~')), [['<figure data-type="table" data-label="group-name" data-group="group"><figcaption><span class="figindex"></span></figcaption><div><table></table></div></figure>'], '']);
@@ -17,6 +17,7 @@ describe('Unit: parser/block/extension/figure', () => {
17
17
  assert.deepStrictEqual(inspect(parser('~~~figure [$group-name]\n!https://host\n\n\n\n~~~')), [['<pre class="invalid" translate="no">~~~figure [$group-name]\n!https://host\n\n\n\n~~~</pre>'], '']);
18
18
  assert.deepStrictEqual(inspect(parser('~~~figure [$group-name]\n !https://host\n~~~')), [['<pre class="invalid" translate="no">~~~figure [$group-name]\n !https://host\n~~~</pre>'], '']);
19
19
  assert.deepStrictEqual(inspect(parser('~~~figure [$group-name]\n!https://host\n~~~\n~~~')), [['<pre class="invalid" translate="no">~~~figure [$group-name]\n!https://host\n~~~\n~~~</pre>'], '']);
20
+ assert.deepStrictEqual(inspect(parser('~~~figure [$group-name]\n!https://host\n\ncaption\n~~~\n~~~')), [['<pre class="invalid" translate="no">~~~figure [$group-name]\n!https://host\n\ncaption\n~~~\n~~~</pre>'], '']);
20
21
  assert.deepStrictEqual(inspect(parser('~~~figure [$group-name]\n!https://host\n~~~~')), [['<pre class="invalid" translate="no">~~~figure [$group-name]\n!https://host\n~~~~</pre>'], '']);
21
22
  assert.deepStrictEqual(inspect(parser('~~~~figure [$group-name]\n!https://host\n~~~')), [['<pre class="invalid" translate="no">~~~~figure [$group-name]\n!https://host\n~~~</pre>'], '']);
22
23
  assert.deepStrictEqual(inspect(parser('~~~figure [$group-name]a\nhttps://host\n~~~')), [['<pre class="invalid" translate="no">~~~figure [$group-name]a\nhttps://host\n~~~</pre>'], '']);
@@ -33,7 +34,7 @@ describe('Unit: parser/block/extension/figure', () => {
33
34
  assert.deepStrictEqual(inspect(parser('~~~figure [$figure-name]\n> \n\n~~~')), [['<figure data-type="quote" data-label="figure-name" data-group="figure" class="invalid"><figcaption><span class="figindex"></span></figcaption><div><blockquote></blockquote></div></figure>'], '']);
34
35
  assert.deepStrictEqual(inspect(parser('~~~figure [$table-name]\n> \n\n~~~')), [['<figure data-type="quote" data-label="table-name" data-group="table" class="invalid"><figcaption><span class="figindex"></span></figcaption><div><blockquote></blockquote></div></figure>'], '']);
35
36
  assert.deepStrictEqual(inspect(parser(`~~~figure [$group-name]\n0${'\n'.repeat(301)}~~~`), '>'), [['<pre class="invalid" translate="no">'], '']);
36
- assert.deepStrictEqual(inspect(parser(`~~~figure [$group-name]\n~~~\n0${'\n'.repeat(301)}~~~\n~~~`), '>'), [['<pre class="invalid" translate="no">'], '\n~~~\n~~~']);
37
+ assert.deepStrictEqual(inspect(parser(`~~~figure [$group-name]\n~~~\n0${'\n'.repeat(301)}~~~\n~~~`), '>'), [['<pre class="invalid" translate="no">'], `${'\n'.repeat(300)}~~~\n~~~`]);
37
38
  });
38
39
 
39
40
  it('valid', () => {
@@ -52,6 +53,7 @@ describe('Unit: parser/block/extension/figure', () => {
52
53
  assert.deepStrictEqual(inspect(parser('~~~figure [$group-name]\n$$\n\n$$\n~~~')), [['<figure data-type="math" data-label="group-name" data-group="group"><figcaption><span class="figindex"></span></figcaption><div><div class="math" translate="no">$$\n\n$$</div></div></figure>'], '']);
53
54
  assert.deepStrictEqual(inspect(parser('~~~figure [$group-name]\n$$\n~~~\n$$\n~~~')), [['<figure data-type="math" data-label="group-name" data-group="group"><figcaption><span class="figindex"></span></figcaption><div><div class="math" translate="no">$$\n~~~\n$$</div></div></figure>'], '']);
54
55
  assert.deepStrictEqual(inspect(parser('~~~figure [$group-name]\n$$\n\n$$\n\ncaption\n~~~')), [['<figure data-type="math" data-label="group-name" data-group="group"><figcaption><span class="figindex"></span>caption</figcaption><div><div class="math" translate="no">$$\n\n$$</div></div></figure>'], '']);
56
+ assert.deepStrictEqual(inspect(parser('~~~figure [$group-name]\n~~~\n~~~\n\ncaption\n~~~')), [['<figure data-type="example" data-label="group-name" data-group="group"><figcaption><span class="figindex"></span>caption</figcaption><div><aside class="example" data-type="markdown"><pre translate="no"></pre><hr><section><ol class="annotations"></ol><ol class="references"></ol></section></aside></div></figure>'], '']);
55
57
  assert.deepStrictEqual(inspect(parser('~~~figure [$group-name]\n~~~example/markdown\n~~~\n\ncaption\n~~~')), [['<figure data-type="example" data-label="group-name" data-group="group"><figcaption><span class="figindex"></span>caption</figcaption><div><aside class="example" data-type="markdown"><pre translate="no"></pre><hr><section><ol class="annotations"></ol><ol class="references"></ol></section></aside></div></figure>'], '']);
56
58
  assert.deepStrictEqual(inspect(parser('~~~figure [$group-name]\n~~~table\n~~~\n\ncaption\n~~~')), [['<figure data-type="table" data-label="group-name" data-group="group"><figcaption><span class="figindex"></span>caption</figcaption><div><table></table></div></figure>'], '']);
57
59
  assert.deepStrictEqual(inspect(parser('~~~figure [$group-name]\n> \n~~~\n\n~~~')), [['<figure data-type="quote" data-label="group-name" data-group="group"><figcaption><span class="figindex"></span></figcaption><div><blockquote><pre><br>~~~</pre></blockquote></div></figure>'], '']);
@@ -48,7 +48,7 @@ export const segment: FigureParser.SegmentParser = block(match(
48
48
  closer),
49
49
  ([, fence]) => fence.length, [])));
50
50
 
51
- export const figure: FigureParser = block(fallback(rewrite(segment, fallback(fmap(
51
+ export const figure: FigureParser = block(fallback(rewrite(segment, fmap(
52
52
  convert(source => source.slice(source.match(/^~+(?:figure[^\S\n]+)?/)![0].length, source.trimEnd().lastIndexOf('\n')),
53
53
  sequence([
54
54
  line(sequence([label, str(/^(?=\s).*\n/)])),
@@ -82,30 +82,10 @@ export const figure: FigureParser = block(fallback(rewrite(segment, fallback(fma
82
82
  defrag(caption))),
83
83
  html('div', [content]),
84
84
  ])
85
- ]),
86
- (source, context) => [[
87
- html('pre', {
88
- class: 'invalid',
89
- translate: 'no',
90
- 'data-invalid-syntax': 'figure',
91
- ...
92
- !seg_label(source.match(/^~+(?:figure[^\S\n]+)?(\[?\$\S+)/)?.[1] ?? '', context) && {
93
- 'data-invalid-type': 'label',
94
- 'data-invalid-message': 'Invalid label',
95
- } ||
96
- /^~+(?:figure[^\S\n]+)?(\[?\$\S+)[^\S\n]+\S/.test(source) && {
97
- 'data-invalid-type': 'argument',
98
- 'data-invalid-message': 'Invalid argument',
99
- } ||
100
- {
101
- 'data-invalid-type': 'content',
102
- 'data-invalid-message': 'Invalid content',
103
- },
104
- }, source),
105
- ], ''])),
85
+ ])),
106
86
  fmap(
107
87
  fence(/^(~{3,})(?:figure|\[?\$\S*)(?!\S)[^\n]*(?:$|\n)/, 300),
108
- ([body, closer, opener, delim]: string[], _, context) => [
88
+ ([body, overflow, closer, opener, delim]: string[], _, context) => [
109
89
  html('pre', {
110
90
  class: 'invalid',
111
91
  translate: 'no',
@@ -115,6 +95,10 @@ export const figure: FigureParser = block(fallback(rewrite(segment, fallback(fma
115
95
  'data-invalid-type': 'fence',
116
96
  'data-invalid-message': `Missing the closing delimiter "${delim}"`,
117
97
  } ||
98
+ overflow && {
99
+ 'data-invalid-type': 'fence',
100
+ 'data-invalid-message': `Invalid trailing line after the closing delimiter "${delim}"`,
101
+ } ||
118
102
  !seg_label(opener.match(/^~+(?:figure[^\S\n]+)?(\[?\$\S+)/)?.[1] ?? '', context) && {
119
103
  'data-invalid-type': 'label',
120
104
  'data-invalid-message': 'Invalid label',
@@ -127,7 +111,7 @@ export const figure: FigureParser = block(fallback(rewrite(segment, fallback(fma
127
111
  'data-invalid-type': 'content',
128
112
  'data-invalid-message': 'Invalid content',
129
113
  },
130
- }, `${opener}${body}${closer}`),
114
+ }, `${opener}${body}${overflow || closer}`),
131
115
  ])));
132
116
 
133
117
  function attributes(label: string, param: string, content: HTMLElement, caption: readonly HTMLElement[]): Record<string, string | undefined> {
@@ -21,14 +21,17 @@ import MessageParser = ExtensionParser.MessageParser;
21
21
  export const message: MessageParser = block(validate('~~~', fmap(
22
22
  fence(/^(~{3,})message\/(\S+)([^\n]*)(?:$|\n)/, 300),
23
23
  // Bug: Type mismatch between outer and inner.
24
- ([body, closer, opener, delim, type, param]: string[], _, context) => {
25
- if (!closer || param.trimStart()) return [html('pre', {
24
+ ([body, overflow, closer, opener, delim, type, param]: string[], _, context) => {
25
+ if (!closer || overflow || param.trimStart()) return [html('pre', {
26
26
  class: 'invalid',
27
27
  translate: 'no',
28
28
  'data-invalid-syntax': 'message',
29
- 'data-invalid-type': !closer ? 'fence' : 'argument',
30
- 'data-invalid-message': !closer ? `Missing the closing delimiter "${delim}"` : 'Invalid argument',
31
- }, `${opener}${body}${closer}`)];
29
+ 'data-invalid-type': !closer || overflow ? 'fence' : 'argument',
30
+ 'data-invalid-message':
31
+ !closer ? `Missing the closing delimiter "${delim}"` :
32
+ overflow ? `Invalid trailing line after the closing delimiter "${delim}"` :
33
+ 'Invalid argument',
34
+ }, `${opener}${body}${overflow || closer}`)];
32
35
  switch (type) {
33
36
  case 'note':
34
37
  case 'caution':
@@ -39,6 +42,7 @@ export const message: MessageParser = block(validate('~~~', fmap(
39
42
  class: 'invalid',
40
43
  translate: 'no',
41
44
  'data-invalid-syntax': 'message',
45
+ 'data-invalid-type': 'type',
42
46
  'data-invalid-message': 'Invalid message type',
43
47
  }, `${opener}${body}${closer}`)];
44
48
  }
@@ -12,12 +12,13 @@ export const segment_: ExtensionParser.PlaceholderParser.SegmentParser = block(v
12
12
 
13
13
  export const placeholder: ExtensionParser.PlaceholderParser = block(validate('~~~', fmap(
14
14
  fence(opener, Infinity),
15
- ([body, closer, opener, delim]) => [
15
+ ([body, overflow, closer, opener, delim]) => [
16
16
  html('pre', {
17
17
  class: 'invalid',
18
18
  translate: 'no',
19
- 'data-invalid-syntax': 'extension',
20
- 'data-invalid-type': !closer ? 'fence' : 'syntax',
21
- 'data-invalid-message': !closer ? `Missing the closing delimiter "${delim}"` : 'Invalid extension name',
22
- }, `${opener}${body}${closer}`),
19
+ 'data-invalid-message':
20
+ !closer ? `Missing the closing delimiter "${delim}"` :
21
+ overflow ? `Invalid trailing line after the closing delimiter "${delim}"` :
22
+ 'Invalid argument',
23
+ }, `${opener}${body}${overflow || closer}`),
23
24
  ])));
@@ -26,14 +26,17 @@ export const segment_: TableParser.SegmentParser = block(validate('~~~',
26
26
  export const table: TableParser = block(validate('~~~', recover(fmap(
27
27
  fence(opener, 10000),
28
28
  // Bug: Type mismatch between outer and inner.
29
- ([body, closer, opener, delim, param]: string[], _, context) => {
30
- if (!closer || param.trimStart()) return [html('pre', {
29
+ ([body, overflow, closer, opener, delim, param]: string[], _, context) => {
30
+ if (!closer || overflow || param.trimStart()) return [html('pre', {
31
31
  class: 'invalid',
32
32
  translate: 'no',
33
33
  'data-invalid-syntax': 'table',
34
- 'data-invalid-type': !closer ? 'fence' : 'argument',
35
- 'data-invalid-message': !closer ? `Missing the closing delimiter "${delim}"` : 'Invalid argument',
36
- }, `${opener}${body}${closer}`)];
34
+ 'data-invalid-type': !closer || overflow ? 'fence' : 'argument',
35
+ 'data-invalid-message':
36
+ !closer ? `Missing the closing delimiter "${delim}"` :
37
+ overflow ? `Invalid trailing line after the closing delimiter "${delim}"` :
38
+ 'Invalid argument',
39
+ }, `${opener}${body}${overflow || closer}`)];
37
40
  return eval(parser(body, context)) ?? [html('table')];
38
41
  }),
39
42
  (source, _, reason) =>
@@ -19,10 +19,12 @@ describe('Unit: parser/block/mathblock', () => {
19
19
  assert.deepStrictEqual(inspect(parser('$$ $$\n$$')), undefined);
20
20
  assert.deepStrictEqual(inspect(parser('$$lang\n$$')), [['<pre class="invalid" translate="no">$$lang\n$$</pre>'], '']);
21
21
  assert.deepStrictEqual(inspect(parser('$$ param\n$$')), [['<pre class="invalid" translate="no">$$ param\n$$</pre>'], '']);
22
+ assert.deepStrictEqual(inspect(parser('$$\n$$\n$$')), [['<pre class="invalid" translate="no">$$\n$$\n$$</pre>'], '']);
23
+ assert.deepStrictEqual(inspect(parser('$$\n$$$')), [['<pre class="invalid" translate="no">$$\n$$$</pre>'], '']);
22
24
  assert.deepStrictEqual(inspect(parser('$$$\n$$')), [['<pre class="invalid" translate="no">$$$\n$$</pre>'], '']);
23
25
  assert.deepStrictEqual(inspect(parser('$$$\n$$$')), [['<pre class="invalid" translate="no">$$$\n$$$</pre>'], '']);
24
26
  assert.deepStrictEqual(inspect(parser(' $$\n$$')), undefined);
25
- assert.deepStrictEqual(inspect(parser(`$$\n0${'\n'.repeat(101)}$$`), '>'), [['<pre class="invalid" translate="no">'], '']);
27
+ assert.deepStrictEqual(inspect(parser(`$$\n0${'\n'.repeat(301)}$$`), '>'), [['<pre class="invalid" translate="no">'], '']);
26
28
  });
27
29
 
28
30
  it('basic', () => {
@@ -34,12 +36,10 @@ describe('Unit: parser/block/mathblock', () => {
34
36
  assert.deepStrictEqual(inspect(parser('$$\n\\\n$$')), [['<div class="math" translate="no">$$\n\\\n$$</div>'], '']);
35
37
  assert.deepStrictEqual(inspect(parser('$$\n$\n$$')), [['<div class="math" translate="no">$$\n$\n$$</div>'], '']);
36
38
  assert.deepStrictEqual(inspect(parser('$$\n$\n\n$$')), [['<div class="math" translate="no">$$\n$\n\n$$</div>'], '']);
37
- assert.deepStrictEqual(inspect(parser('$$\n$$\n$$')), [['<div class="math" translate="no">$$\n$$\n$$</div>'], '']);
38
39
  assert.deepStrictEqual(inspect(parser('$$\n$$\n\n$$')), [['<div class="math" translate="no">$$\n$$</div>'], '\n$$']);
39
40
  assert.deepStrictEqual(inspect(parser('$$\n$$$\n$$')), [['<div class="math" translate="no">$$\n$$$\n$$</div>'], '']);
40
41
  assert.deepStrictEqual(inspect(parser('$$\n$$$\n\n$$')), [['<div class="math" translate="no">$$\n$$$\n\n$$</div>'], '']);
41
- assert.deepStrictEqual(inspect(parser('$$\n$$\n$$')), [['<div class="math" translate="no">$$\n$$\n$$</div>'], '']);
42
- assert.deepStrictEqual(inspect(parser(`$$\n0${'\n'.repeat(100)}$$`), '>'), [['<div class="math" translate="no">'], '']);
42
+ assert.deepStrictEqual(inspect(parser(`$$\n0${'\n'.repeat(300)}$$`), '>'), [['<div class="math" translate="no">'], '']);
43
43
  });
44
44
 
45
45
  });
@@ -6,23 +6,26 @@ import { html } from 'typed-dom/dom';
6
6
  const opener = /^(\${2,})(?!\$)([^\n]*)(?:$|\n)/;
7
7
 
8
8
  export const segment: MathBlockParser.SegmentParser = block(validate('$$',
9
- clear(fence(opener, 100))));
9
+ clear(fence(opener, 300))));
10
10
 
11
11
  export const segment_: MathBlockParser.SegmentParser = block(validate('$$',
12
- clear(fence(opener, 100, false))), false);
12
+ clear(fence(opener, 300, false))), false);
13
13
 
14
14
  export const mathblock: MathBlockParser = block(validate('$$', fmap(
15
- fence(opener, 100),
15
+ fence(opener, 300),
16
16
  // Bug: Type mismatch between outer and inner.
17
- ([body, closer, opener, delim, param]: string[], _, { caches: { math: cache = undefined } = {} }) => [
18
- delim.length === 2 && closer && param.trimStart() === ''
17
+ ([body, overflow, closer, opener, delim, param]: string[], _, { caches: { math: cache = undefined } = {} }) => [
18
+ delim.length === 2 && closer && !overflow && param.trimStart() === ''
19
19
  ? cache?.get(`${delim}\n${body}${delim}`)?.cloneNode(true) as HTMLDivElement ||
20
20
  html('div', { class: 'math', translate: 'no' }, `${delim}\n${body}${delim}`)
21
21
  : html('pre', {
22
22
  class: 'invalid',
23
23
  translate: 'no',
24
24
  'data-invalid-syntax': 'mathblock',
25
- 'data-invalid-type': delim.length > 2 ? 'syntax' : !closer ? 'fence' : 'argument',
26
- 'data-invalid-message': delim.length > 2 ? 'Invalid syntax' : !closer ? `Missing the closing delimiter "${delim}"` : 'Invalid argument',
27
- }, `${opener}${body}${closer}`),
25
+ 'data-invalid-type': delim.length > 2 ? 'syntax' : !closer || overflow ? 'fence' : 'argument',
26
+ 'data-invalid-message': delim.length > 2 ? 'Invalid syntax' :
27
+ !closer ? `Missing the closing delimiter "${delim}"` :
28
+ overflow ? `Invalid trailing line after the closing delimiter "${delim}"` :
29
+ 'Invalid argument',
30
+ }, `${opener}${body}${overflow || closer}`),
28
31
  ])));
@@ -21,6 +21,7 @@ describe('Unit: parser/header', () => {
21
21
  assert.deepStrictEqual(inspect(parser('---\n \n---')), undefined);
22
22
  assert.deepStrictEqual(inspect(parser('---\n-\n---')), [['<pre class="invalid" translate="no">---\n-\n---</pre>'], '']);
23
23
  assert.deepStrictEqual(inspect(parser('----\na: b\n----')), [['<pre class="invalid" translate="no">----\na: b\n----</pre>'], '']);
24
+ assert.deepStrictEqual(inspect(parser(`---\n${'a: b\n'.repeat(101)}---`)), [[`<pre class="invalid" translate="no">---\n${'a: b\n'.repeat(101)}---</pre>`], '']);
24
25
  });
25
26
 
26
27
  it('basic', () => {
@@ -16,17 +16,33 @@ describe('Unit: parser/inline/bracket', () => {
16
16
  assert.deepStrictEqual(inspect(parser('(1)')), [['(', '1', ')'], '']);
17
17
  assert.deepStrictEqual(inspect(parser('(10)')), [['(', '10', ')'], '']);
18
18
  assert.deepStrictEqual(inspect(parser('(2000)')), [['(', '2000', ')'], '']);
19
- assert.deepStrictEqual(inspect(parser('(1, 2)')), [['(', '1, 2', ')'], '']);
20
19
  assert.deepStrictEqual(inspect(parser('(0-1)')), [['(', '0-1', ')'], '']);
21
20
  assert.deepStrictEqual(inspect(parser('(0)-1')), [['(', '0', ')'], '-1']);
22
21
  assert.deepStrictEqual(inspect(parser('(0.1)')), [['(', '0.1', ')'], '']);
23
22
  assert.deepStrictEqual(inspect(parser('(0.1.2)')), [['(', '0.1.2', ')'], '']);
24
23
  assert.deepStrictEqual(inspect(parser('(1.1, 1.2-1.3, 1.4)')), [['(', '1.1, 1.2-1.3, 1.4', ')'], '']);
24
+ assert.deepStrictEqual(inspect(parser('(1 2)')), [['<span class="paren">(1 2)</span>'], '']);
25
+ assert.deepStrictEqual(inspect(parser('(1, 2)')), [['(', '1, 2', ')'], '']);
26
+ assert.deepStrictEqual(inspect(parser('(1a)')), [['(', '1a', ')'], '']);
25
27
  assert.deepStrictEqual(inspect(parser('(a)')), [['(', 'a', ')'], '']);
28
+ assert.deepStrictEqual(inspect(parser('(a1)')), [['(', 'a1', ')'], '']);
29
+ assert.deepStrictEqual(inspect(parser('(a-1)')), [['(', 'a-1', ')'], '']);
30
+ assert.deepStrictEqual(inspect(parser('(a.1)')), [['(', 'a.1', ')'], '']);
31
+ assert.deepStrictEqual(inspect(parser('(a b)')), [['<span class="paren">(a b)</span>'], '']);
32
+ assert.deepStrictEqual(inspect(parser('(word)')), [['(', 'word', ')'], '']);
33
+ assert.deepStrictEqual(inspect(parser('(word word)')), [['<span class="paren">(word word)</span>'], '']);
34
+ assert.deepStrictEqual(inspect(parser('(word, word)')), [['(', 'word, word', ')'], '']);
26
35
  assert.deepStrictEqual(inspect(parser('(A)')), [['(', 'A', ')'], '']);
36
+ assert.deepStrictEqual(inspect(parser('(Name)')), [['(', 'Name', ')'], '']);
37
+ assert.deepStrictEqual(inspect(parser('(Word Word)')), [['<span class="paren">(Word Word)</span>'], '']);
38
+ assert.deepStrictEqual(inspect(parser('(Name, Name)')), [['(', 'Name, Name', ')'], '']);
39
+ assert.deepStrictEqual(inspect(parser('(ABBR)')), [['(', 'ABBR', ')'], '']);
40
+ assert.deepStrictEqual(inspect(parser('(ABBR, ABBR)')), [['(', 'ABBR, ABBR', ')'], '']);
27
41
  assert.deepStrictEqual(inspect(parser('(1,2)')), [['(', '1,2', ')'], '']);
28
42
  assert.deepStrictEqual(inspect(parser('(0-1)')), [['(', '0-1', ')'], '']);
29
- assert.deepStrictEqual(inspect(parser('(0.1)(A)')), [['(', '0.1', ')', '(', 'A', ')'], '']);
43
+ assert.deepStrictEqual(inspect(parser('(0.1)')), [['(', '0.1', ')'], '']);
44
+ assert.deepStrictEqual(inspect(parser('(a)')), [['(', 'a', ')'], '']);
45
+ assert.deepStrictEqual(inspect(parser('(A)')), [['(', 'A', ')'], '']);
30
46
  });
31
47
 
32
48
  it('[', () => {
@@ -6,7 +6,7 @@ import { str } from '../source';
6
6
  import { html, defrag } from 'typed-dom/dom';
7
7
  import { unshift, push } from 'spica/array';
8
8
 
9
- const index = /^(?:[0-9]+(?:(?:[.-]|, )[0-9]+)*|[A-Za-z])/;
9
+ const index = /^[0-9A-Za-z]+(?:(?:[.-]|, )[0-9A-Za-z]+)*/;
10
10
 
11
11
  export const bracket: BracketParser = lazy(() => creator(union([
12
12
  surround(str('('), str(index), str(')')),
@@ -93,22 +93,29 @@ describe('Unit: parser/inline/html', () => {
93
93
  assert.deepStrictEqual(inspect(parser('<small __proto__>a</small>')), undefined);
94
94
  assert.deepStrictEqual(inspect(parser('<small constructor>a</small>')), [['<span class="invalid">&lt;small constructor&gt;a&lt;/small&gt;</span>'], '']);
95
95
  assert.deepStrictEqual(inspect(parser('<small toString>a</small>')), undefined);
96
+ assert.deepStrictEqual(inspect(parser('<small X>a</small>')), undefined);
97
+ assert.deepStrictEqual(inspect(parser('<small x>a</small>')), [['<span class="invalid">&lt;small x&gt;a&lt;/small&gt;</span>'], '']);
96
98
  assert.deepStrictEqual(inspect(parser('<bdo>a</bdo>')), [['<span class="invalid">&lt;bdo&gt;a&lt;/bdo&gt;</span>'], '']);
97
99
  assert.deepStrictEqual(inspect(parser('<bdo >a</bdo>')), [['<span class="invalid">&lt;bdo &gt;a&lt;/bdo&gt;</span>'], '']);
98
100
  assert.deepStrictEqual(inspect(parser('<bdo __proto__>a</bdo>')), undefined);
99
101
  assert.deepStrictEqual(inspect(parser('<bdo constructor>a</bdo>')), [['<span class="invalid">&lt;bdo constructor&gt;a&lt;/bdo&gt;</span>'], '']);
100
102
  assert.deepStrictEqual(inspect(parser('<bdo toString>a</bdo>')), undefined);
103
+ assert.deepStrictEqual(inspect(parser('<bdo X>a</bdo>')), undefined);
104
+ assert.deepStrictEqual(inspect(parser('<bdo x>a</bdo>')), [['<span class="invalid">&lt;bdo x&gt;a&lt;/bdo&gt;</span>'], '']);
101
105
  assert.deepStrictEqual(inspect(parser('<bdo dir>a</bdo>')), [['<span class="invalid">&lt;bdo dir&gt;a&lt;/bdo&gt;</span>'], '']);
102
106
  assert.deepStrictEqual(inspect(parser('<bdo dir=>a</bdo>')), undefined);
103
107
  assert.deepStrictEqual(inspect(parser('<bdo dir=rtl>a</bdo>')), undefined);
104
108
  assert.deepStrictEqual(inspect(parser('<bdo dir=">a</bdo>')), undefined);
105
109
  assert.deepStrictEqual(inspect(parser('<bdo dir="">a</bdo>')), [['<span class="invalid">&lt;bdo dir=""&gt;a&lt;/bdo&gt;</span>'], '']);
106
110
  assert.deepStrictEqual(inspect(parser('<bdo dir="rtl" dir="rtl">a</bdo>')), [['<span class="invalid">&lt;bdo dir="rtl" dir="rtl"&gt;a&lt;/bdo&gt;</span>'], '']);
111
+ assert.deepStrictEqual(inspect(parser('<bdo diR="rtl">a</bdo>')), undefined);
107
112
  assert.deepStrictEqual(inspect(parser('<bdo dir="rtl">a</bdo>')), [['<bdo dir="rtl">a</bdo>'], '']);
108
113
  assert.deepStrictEqual(inspect(parser('<bdo dir="rtl" >a</bdo>')), [['<bdo dir="rtl">a</bdo>'], '']);
109
114
  assert.deepStrictEqual(inspect(parser('<bdo dir="rtl" >a</bdo>')), [['<bdo dir="rtl">a</bdo>'], '']);
110
115
  assert.deepStrictEqual(inspect(parser('<bdo dir="rtl">a</bdo>')), [['<bdo dir="rtl">a</bdo>'], '']);
111
116
  assert.deepStrictEqual(inspect(parser('<wbr constructor>')), [['<wbr class="invalid">'], '']);
117
+ assert.deepStrictEqual(inspect(parser('<wbr X>')), undefined);
118
+ assert.deepStrictEqual(inspect(parser('<wbr x>')), [['<wbr class="invalid">'], '']);
112
119
  });
113
120
 
114
121
  });