securemark 0.243.0 → 0.244.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package-lock.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securemark",
3
- "version": "0.243.0",
3
+ "version": "0.244.0",
4
4
  "lockfileVersion": 1,
5
5
  "requires": true,
6
6
  "dependencies": {
@@ -1962,9 +1962,9 @@
1962
1962
  "dev": true
1963
1963
  },
1964
1964
  "caniuse-lite": {
1965
- "version": "1.0.30001332",
1966
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001332.tgz",
1967
- "integrity": "sha512-10T30NYOEQtN6C11YGg411yebhvpnC6Z102+B95eAsN0oB6KUs01ivE8u+G6FMIRtIrVlYXhL+LUwQ3/hXwDWw==",
1965
+ "version": "1.0.30001334",
1966
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001334.tgz",
1967
+ "integrity": "sha512-kbaCEBRRVSoeNs74sCuq92MJyGrMtjWVfhltoHUCW4t4pXFvGjUBrfo47weBRViHkiV3eBYyIsfl956NtHGazw==",
1968
1968
  "dev": true
1969
1969
  },
1970
1970
  "chalk": {
@@ -8028,9 +8028,9 @@
8028
8028
  }
8029
8029
  },
8030
8030
  "node-releases": {
8031
- "version": "2.0.3",
8032
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.3.tgz",
8033
- "integrity": "sha512-maHFz6OLqYxz+VQyCAtA3PTX4UP/53pa05fyDNc9CwjvJ0yEh6+xBwKsgCxMNhS8taUKBFYxfuiaD9U/55iFaw==",
8031
+ "version": "2.0.4",
8032
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.4.tgz",
8033
+ "integrity": "sha512-gbMzqQtTtDz/00jQzZ21PQzdI9PyLYqUSvD0p3naOhX4odFji0ZxYdnVwPTxmSwkmxhcFImpozceidSG+AgoPQ==",
8034
8034
  "dev": true
8035
8035
  },
8036
8036
  "nopt": {
@@ -8093,9 +8093,9 @@
8093
8093
  }
8094
8094
  },
8095
8095
  "npm-check-updates": {
8096
- "version": "12.5.9",
8097
- "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-12.5.9.tgz",
8098
- "integrity": "sha512-l9iOvD7EsQb96gFJL45V01YG6bP8+dmobYnSguvehPuNwgdWNMrE8RC8bSfURX5iUmX4bkobN4T8XMHXN9GMHA==",
8096
+ "version": "12.5.10",
8097
+ "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-12.5.10.tgz",
8098
+ "integrity": "sha512-GKBmgC6ZDZwshsOSuse+JYDFVQcaKj9NWNWXW9oWX0TdrKJx26/iR6d4t6LG+iNm5jX+voBoGcEIaGF8CRzWpw==",
8099
8099
  "dev": true,
8100
8100
  "requires": {
8101
8101
  "chalk": "^4.1.2",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securemark",
3
- "version": "0.243.0",
3
+ "version": "0.244.0",
4
4
  "description": "Secure markdown renderer working on browsers for user input data.",
5
5
  "private": false,
6
6
  "homepage": "https://github.com/falsandtru/securemark",
@@ -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), {
@@ -5,19 +5,22 @@ import { parse } from '../../api/parse';
5
5
  import { mathblock } from '../mathblock';
6
6
  import { html } from 'typed-dom/dom';
7
7
 
8
- const opener = /^(~{3,})(?:example\/(\S+)|(?!\S))([^\n]*)(?:$|\n)/;
8
+ const opener = /^(~{3,})(?:example\/(\S+))?(?!\S)([^\n]*)(?:$|\n)/;
9
9
 
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>'], '']);
@@ -31,4 +31,4 @@ export const fig: FigParser = block(rewrite(segment, verify(convert(
31
31
  return `${fence}figure ${source}\n\n${fence}`;
32
32
  },
33
33
  union([figure])),
34
- ([el]) => el.className !== 'invalid' || el.getAttribute('data-invalid-type') !== 'content')));
34
+ ([el]) => el.tagName === 'FIGURE')));
@@ -7,25 +7,24 @@ describe('Unit: parser/block/extension/figure', () => {
7
7
  const parser = (source: string) => some(figure)(source, {});
8
8
 
9
9
  it('invalid', () => {
10
- assert.deepStrictEqual(inspect(parser('~~~figure\n!https://host\n~~~')), undefined);
11
- assert.deepStrictEqual(inspect(parser('~~~figure $group-name]\n!https://host\n~~~')), [['<figure data-type="media" data-label="group-name" data-group="group" class="invalid"><figcaption><span class="figindex"></span></figcaption><div><a href="https://host" target="_blank"><img class="media" data-src="https://host" alt=""></a></div></figure>'], '']);
12
- assert.deepStrictEqual(inspect(parser('~~~figure [$group-name\n!https://host\n~~~')), undefined);
13
- assert.deepStrictEqual(inspect(parser('~~~figure [$group-name]\nhttps://host\n~~~')), [['<figure data-type="invalid" data-label="group-name" data-group="group" class="invalid"><figcaption><span class="figindex"></span></figcaption><div><br></div></figure>'], '']);
14
- assert.deepStrictEqual(inspect(parser('~~~figure [$group-name]\n!https://host\\\n~~~')), [['<figure data-type="invalid" data-label="group-name" data-group="group" class="invalid"><figcaption><span class="figindex"></span></figcaption><div><br></div></figure>'], '']);
15
- assert.deepStrictEqual(inspect(parser('~~~figure [$group-name]\n!https://host\n\\\n~~~')), [['<figure data-type="invalid" data-label="group-name" data-group="group" class="invalid"><figcaption><span class="figindex"></span></figcaption><div><br></div></figure>'], '']);
16
- assert.deepStrictEqual(inspect(parser('~~~figure [$group-name]\n!https://host\na\n~~~')), [['<figure data-type="invalid" data-label="group-name" data-group="group" class="invalid"><figcaption><span class="figindex"></span></figcaption><div><br></div></figure>'], '']);
17
- assert.deepStrictEqual(inspect(parser('~~~figure [$group-name]\n!https://host\n\n\n\n~~~')), undefined);
18
- assert.deepStrictEqual(inspect(parser('~~~figure [$group-name]\n !https://host\n~~~')), [['<figure data-type="invalid" data-label="group-name" data-group="group" class="invalid"><figcaption><span class="figindex"></span></figcaption><div><br></div></figure>'], '']);
19
- assert.deepStrictEqual(inspect(parser('~~~figure [$group-name]\n\n!https://host\n~~~')), undefined);
20
- assert.deepStrictEqual(inspect(parser('~~~figure [$group-name]\n!https://host\n~~~\n~~~')), undefined);
21
- assert.deepStrictEqual(inspect(parser('~~~figure [$group-name]\n!https://host\n~~~~')), undefined);
22
- assert.deepStrictEqual(inspect(parser('~~~~figure [$group-name]\n!https://host\n~~~')), undefined);
23
- assert.deepStrictEqual(inspect(parser('~~~figure [$group-name]a\nhttps://host\n~~~')), [['<figure data-type="invalid" data-label="group-name" data-group="group" class="invalid"><figcaption><span class="figindex"></span></figcaption><div><br></div></figure>'], '']);
24
- assert.deepStrictEqual(inspect(parser('~~~figure [$group-name]a\n!https://host\n~~~')), [['<figure data-type="media" data-label="group-name" data-group="group" class="invalid"><figcaption><span class="figindex"></span></figcaption><div><a href="https://host" target="_blank"><img class="media" data-src="https://host" alt=""></a></div></figure>'], '']);
25
- assert.deepStrictEqual(inspect(parser('~~~figure [$group-name] a\nhttps://host\n~~~')), [['<figure data-type="invalid" data-label="group-name" data-group="group" class="invalid"><figcaption><span class="figindex"></span></figcaption><div><br></div></figure>'], '']);
10
+ assert.deepStrictEqual(inspect(parser('~~~figure\n!https://host\n~~~')), [['<pre class="invalid" translate="no">~~~figure\n!https://host\n~~~</pre>'], '']);
11
+ assert.deepStrictEqual(inspect(parser('~~~figure $group-name]\n!https://host\n~~~')), [['<pre class="invalid" translate="no">~~~figure $group-name]\n!https://host\n~~~</pre>'], '']);
12
+ assert.deepStrictEqual(inspect(parser('~~~figure [$group-name\n!https://host\n~~~')), [['<pre class="invalid" translate="no">~~~figure [$group-name\n!https://host\n~~~</pre>'], '']);
13
+ assert.deepStrictEqual(inspect(parser('~~~figure [$group-name]\nhttps://host\n~~~')), [['<pre class="invalid" translate="no">~~~figure [$group-name]\nhttps://host\n~~~</pre>'], '']);
14
+ assert.deepStrictEqual(inspect(parser('~~~figure [$group-name]\n!https://host\\\n~~~')), [['<pre class="invalid" translate="no">~~~figure [$group-name]\n!https://host\\\n~~~</pre>'], '']);
15
+ 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>'], '']);
16
+ assert.deepStrictEqual(inspect(parser('~~~figure [$group-name]\n!https://host\na\n~~~')), [['<pre class="invalid" translate="no">~~~figure [$group-name]\n!https://host\na\n~~~</pre>'], '']);
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
+ 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
+ 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~~~~')), [['<pre class="invalid" translate="no">~~~figure [$group-name]\n!https://host\n~~~~</pre>'], '']);
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>'], '']);
22
+ assert.deepStrictEqual(inspect(parser('~~~figure [$group-name]a\nhttps://host\n~~~')), [['<pre class="invalid" translate="no">~~~figure [$group-name]a\nhttps://host\n~~~</pre>'], '']);
23
+ assert.deepStrictEqual(inspect(parser('~~~figure [$group-name]a\n!https://host\n~~~')), [['<pre class="invalid" translate="no">~~~figure [$group-name]a\n!https://host\n~~~</pre>'], '']);
24
+ assert.deepStrictEqual(inspect(parser('~~~figure [$group-name] a\nhttps://host\n~~~')), [['<pre class="invalid" translate="no">~~~figure [$group-name] a\nhttps://host\n~~~</pre>'], '']);
26
25
  assert.deepStrictEqual(inspect(parser('~~~figure [$group-name] a\n!https://host\n~~~')), [['<figure data-type="media" data-label="group-name" data-group="group" class="invalid"><figcaption><span class="figindex"></span></figcaption><div><a href="https://host" target="_blank"><img class="media" data-src="https://host" alt=""></a></div></figure>'], '']);
27
- assert.deepStrictEqual(inspect(parser('~~~figure a[$group-name]\n!https://host\n~~~')), undefined);
28
- assert.deepStrictEqual(inspect(parser('~~~figure a [$group-name]\n!https://host\n~~~')), undefined);
26
+ assert.deepStrictEqual(inspect(parser('~~~figure a[$group-name]\n!https://host\n~~~')), [['<pre class="invalid" translate="no">~~~figure a[$group-name]\n!https://host\n~~~</pre>'], '']);
27
+ assert.deepStrictEqual(inspect(parser('~~~figure a [$group-name]\n!https://host\n~~~')), [['<pre class="invalid" translate="no">~~~figure a [$group-name]\n!https://host\n~~~</pre>'], '']);
29
28
  assert.deepStrictEqual(inspect(parser('~~~ [$group-name]\n!https://host\n~~~')), undefined);
30
29
  assert.deepStrictEqual(inspect(parser('~~~ $group-name\n!https://host\n~~~')), undefined);
31
30
  assert.deepStrictEqual(inspect(parser(' ~~~figure [$group-name]\n!https://host\n~~~')), undefined);
@@ -33,7 +32,8 @@ describe('Unit: parser/block/extension/figure', () => {
33
32
  assert.deepStrictEqual(inspect(parser('~~~figure [$fig-name]\n> \n\n~~~')), [['<figure data-type="quote" data-label="fig-name" data-group="fig" class="invalid"><figcaption><span class="figindex"></span></figcaption><div><blockquote></blockquote></div></figure>'], '']);
34
33
  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>'], '']);
35
34
  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>'], '']);
36
- assert(!parser('~~~figure [$group-name]\n```\n0' + '\n'.repeat(301) + '```\n~~~'));
35
+ 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'.repeat(300)}~~~\n~~~`]);
37
37
  });
38
38
 
39
39
  it('valid', () => {
@@ -52,6 +52,7 @@ describe('Unit: parser/block/extension/figure', () => {
52
52
  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
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>'], '']);
54
54
  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>'], '']);
55
+ 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
56
  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
57
  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
58
  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>'], '']);
@@ -67,8 +68,8 @@ describe('Unit: parser/block/extension/figure', () => {
67
68
  assert.deepStrictEqual(inspect(parser('~~~figure [$-0.0]\n$$\n\n$$\n~~~')), [['<figure data-type="math" data-label="$-0.0" data-group="$" class="invalid"><figcaption><span class="figindex"></span></figcaption><div><div class="math" translate="no">$$\n\n$$</div></div></figure>'], '']);
68
69
  assert.deepStrictEqual(inspect(parser('~~~figure [$-name]\n!https://host\n~~~')), [['<figure data-type="media" data-label="$-name" data-group="$" class="invalid"><figcaption><span class="figindex"></span></figcaption><div><a href="https://host" target="_blank"><img class="media" data-src="https://host" alt=""></a></div></figure>'], '']);
69
70
  assert.deepStrictEqual(inspect(parser('~~~figure [$-name]\n$$\n\n$$\n\ncaption\n~~~')), [['<figure data-type="math" data-label="$-name" data-group="$" class="invalid"><figcaption><span class="figindex"></span>caption</figcaption><div><div class="math" translate="no">$$\n\n$$</div></div></figure>'], '']);
70
- assert(parser('~~~figure [$group-name]\n```\n0' + '\n'.repeat(300) + '```\n~~~'));
71
- assert(parser('~~~figure [$group-name]\n' + '>\n'.repeat(500) + '\n~~~'));
71
+ assert.deepStrictEqual(inspect(parser(`~~~figure [$group-name]\n${'>\n'.repeat(500)}\n~~~`), '>'), [['<figure data-type="quote" data-label="group-name" data-group="group">'], '']);
72
+ assert.deepStrictEqual(inspect(parser(`~~~figure [$group-name]\n~~~\n0${'\n'.repeat(300)}~~~\n~~~`), '>'), [['<figure data-type="example" data-label="group-name" data-group="group">'], '']);
72
73
  });
73
74
 
74
75
  });
@@ -1,8 +1,8 @@
1
1
  import { undefined } from 'spica/global';
2
2
  import { ExtensionParser } from '../../block';
3
- import { union, inits, sequence, some, block, line, rewrite, context, close, match, convert, trim, fmap } from '../../../combinator';
3
+ import { union, inits, sequence, some, block, line, fence, rewrite, context, close, match, convert, trim, fallback, fmap } from '../../../combinator';
4
4
  import { str, contentline, emptyline } from '../../source';
5
- import { label } from '../../inline/extension/label';
5
+ import { label, segment as seg_label } from '../../inline/extension/label';
6
6
  import { ulist } from '../ulist';
7
7
  import { olist } from '../olist';
8
8
  import { table as styled_table } from '../table';
@@ -23,36 +23,35 @@ import { unshift } from 'spica/array';
23
23
  import FigureParser = ExtensionParser.FigureParser;
24
24
 
25
25
  export const segment: FigureParser.SegmentParser = block(match(
26
- /^(~{3,})(?:figure[^\S\n]+)?(?=\[?\$[A-Za-z-][^\n]*\n)/,
26
+ /^(~{3,})(?:figure[^\S\n]|(?=\[?\$))/,
27
27
  memoize(
28
- ([, fence], closer = new RegExp(String.raw`^${fence}[^\S\n]*(?:$|\n)`)) =>
29
- close(
30
- sequence([
31
- contentline,
32
- inits([
33
- // All parsers which can include closing terms.
34
- union([
35
- seg_code,
36
- seg_math,
37
- seg_table,
38
- seg_blockquote,
39
- seg_placeholder,
40
- some(contentline, closer),
41
- ]),
28
+ ([, fence], closer = new RegExp(String.raw`^${fence}[^\S\n]*(?:$|\n)`)) => close(
29
+ sequence([
30
+ contentline,
31
+ inits([
32
+ // All parsers which can include closing terms.
33
+ union([
34
+ seg_code,
35
+ seg_math,
36
+ seg_table,
37
+ seg_blockquote,
38
+ seg_placeholder,
39
+ some(contentline, closer),
40
+ ]),
41
+ emptyline,
42
+ union([
42
43
  emptyline,
43
- union([
44
- emptyline,
45
- some(contentline, closer),
46
- ]),
44
+ some(contentline, closer),
47
45
  ]),
48
46
  ]),
49
- closer),
47
+ ]),
48
+ closer),
50
49
  ([, fence]) => fence.length, [])));
51
50
 
52
- export const figure: FigureParser = block(rewrite(segment, fmap(
53
- convert(source => source.slice(source.search(/[[$]/), source.trimEnd().lastIndexOf('\n')),
51
+ export const figure: FigureParser = block(fallback(rewrite(segment, fmap(
52
+ convert(source => source.slice(source.match(/^~+(?:figure[^\S\n]+)?/)![0].length, source.trimEnd().lastIndexOf('\n')),
54
53
  sequence([
55
- line(sequence([label, str(/^.*\n/)])),
54
+ line(sequence([label, str(/^(?=\s).*\n/)])),
56
55
  inits([
57
56
  block(union([
58
57
  ulist,
@@ -65,9 +64,8 @@ export const figure: FigureParser = block(rewrite(segment, fmap(
65
64
  table,
66
65
  blockquote,
67
66
  placeholder,
68
- block(line(media)),
69
- block(line(shortmedia)),
70
- fmap(some(contentline), () => [html('br')]),
67
+ line(media),
68
+ line(shortmedia),
71
69
  ])),
72
70
  emptyline,
73
71
  block(localize(
@@ -84,7 +82,37 @@ export const figure: FigureParser = block(rewrite(segment, fmap(
84
82
  defrag(caption))),
85
83
  html('div', [content]),
86
84
  ])
87
- ])));
85
+ ])),
86
+ fmap(
87
+ fence(/^(~{3,})(?:figure|\[?\$\S*)(?!\S)[^\n]*(?:$|\n)/, 300),
88
+ ([body, overflow, closer, opener, delim]: string[], _, context) => [
89
+ html('pre', {
90
+ class: 'invalid',
91
+ translate: 'no',
92
+ 'data-invalid-syntax': 'figure',
93
+ ...
94
+ !closer && {
95
+ 'data-invalid-type': 'fence',
96
+ 'data-invalid-message': `Missing the closing delimiter "${delim}"`,
97
+ } ||
98
+ overflow && {
99
+ 'data-invalid-type': 'fence',
100
+ 'data-invalid-message': `Invalid trailing line after the closing delimiter "${delim}"`,
101
+ } ||
102
+ !seg_label(opener.match(/^~+(?:figure[^\S\n]+)?(\[?\$\S+)/)?.[1] ?? '', context) && {
103
+ 'data-invalid-type': 'label',
104
+ 'data-invalid-message': 'Invalid label',
105
+ } ||
106
+ /^~+(?:figure[^\S\n]+)?(\[?\$\S+)[^\S\n]+\S/.test(opener) && {
107
+ 'data-invalid-type': 'argument',
108
+ 'data-invalid-message': 'Invalid argument',
109
+ } ||
110
+ {
111
+ 'data-invalid-type': 'content',
112
+ 'data-invalid-message': 'Invalid content',
113
+ },
114
+ }, `${opener}${body}${overflow || closer}`),
115
+ ])));
88
116
 
89
117
  function attributes(label: string, param: string, content: HTMLElement, caption: readonly HTMLElement[]): Record<string, string | undefined> {
90
118
  const group = label.split('-', 1)[0];
@@ -104,9 +132,6 @@ function attributes(label: string, param: string, content: HTMLElement, caption:
104
132
  case 'A':
105
133
  type = 'media';
106
134
  break;
107
- case 'BR':
108
- type = 'invalid';
109
- break;
110
135
  case 'text':
111
136
  case 'code':
112
137
  case 'math':
@@ -117,18 +142,14 @@ function attributes(label: string, param: string, content: HTMLElement, caption:
117
142
  assert(false);
118
143
  }
119
144
  const invalid =
120
- type === 'invalid' && content.tagName === 'BR' && {
121
- 'data-invalid-type': 'content',
122
- 'data-invalid-message': 'Invalid content',
145
+ param.trimStart() !== '' && {
146
+ 'data-invalid-type': 'argument',
147
+ 'data-invalid-message': 'Invalid argument',
123
148
  } ||
124
149
  /^[^-]+-(?:[0-9]+\.)*0$/.test(label) && {
125
150
  'data-invalid-type': 'label',
126
151
  'data-invalid-message': 'The last part of the fixed label numbers must not be 0',
127
152
  } ||
128
- param.trimStart() !== '' && {
129
- 'data-invalid-type': 'argument',
130
- 'data-invalid-message': 'Invalid argument',
131
- } ||
132
153
  group === '$' && (type !== 'math' || caption.length > 0) && {
133
154
  'data-invalid-type': 'label',
134
155
  'data-invalid-message': '"$" label group must be used to math formulas with no caption',
@@ -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
  }
@@ -1,6 +1,5 @@
1
1
  import { ExtensionParser } from '../../block';
2
2
  import { block, validate, fence, clear, fmap } from '../../../combinator';
3
- import { segment as label } from '../../inline/extension/label';
4
3
  import { html } from 'typed-dom/dom';
5
4
 
6
5
  const opener = /^(~{3,})(?!~)[^\n]*(?:$|\n)/;
@@ -13,17 +12,13 @@ export const segment_: ExtensionParser.PlaceholderParser.SegmentParser = block(v
13
12
 
14
13
  export const placeholder: ExtensionParser.PlaceholderParser = block(validate('~~~', fmap(
15
14
  fence(opener, Infinity),
16
- ([body, closer, opener, delim]: string[], _, context) => [
15
+ ([body, overflow, closer, opener, delim]) => [
17
16
  html('pre', {
18
17
  class: 'invalid',
19
18
  translate: 'no',
20
- 'data-invalid-syntax': 'extension',
21
- 'data-invalid-type': !closer ? 'fence' : 'syntax',
22
- 'data-invalid-message': !closer ? `Missing the closing delimiter "${delim}"` : 'Invalid extension name',
23
- ...closer && (opener.slice(delim.length).split(/\s/, 1)[0] === 'figure' || label(opener.slice(delim.length), context)) && {
24
- 'data-invalid-syntax': 'figure',
25
- 'data-invalid-type': 'content',
26
- 'data-invalid-message': 'Invalid content',
27
- },
28
- }, `${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}`),
29
24
  ])));