securemark 0.293.1 → 0.293.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +7 -10
  3. package/dist/index.js +329 -150
  4. package/package.json +1 -1
  5. package/src/combinator/control/manipulation/indent.test.ts +6 -1
  6. package/src/combinator/control/manipulation/indent.ts +1 -1
  7. package/src/combinator/control/manipulation/scope.ts +3 -4
  8. package/src/combinator/control/manipulation/surround.ts +2 -1
  9. package/src/combinator/data/parser/context/delimiter.ts +7 -12
  10. package/src/combinator/data/parser/some.ts +4 -8
  11. package/src/parser/api/parse.test.ts +2 -2
  12. package/src/parser/block/olist.test.ts +8 -6
  13. package/src/parser/block/olist.ts +2 -2
  14. package/src/parser/block/sidefence.ts +2 -2
  15. package/src/parser/block/table.ts +2 -2
  16. package/src/parser/block.ts +38 -36
  17. package/src/parser/inline/annotation.test.ts +1 -1
  18. package/src/parser/inline/autolink/url.test.ts +5 -5
  19. package/src/parser/inline/bracket.test.ts +6 -4
  20. package/src/parser/inline/bracket.ts +114 -88
  21. package/src/parser/inline/html.ts +24 -13
  22. package/src/parser/inline/italic.test.ts +9 -9
  23. package/src/parser/inline/link.ts +1 -1
  24. package/src/parser/inline/mark.test.ts +5 -5
  25. package/src/parser/inline/math.ts +2 -2
  26. package/src/parser/inline/media.ts +3 -2
  27. package/src/parser/inline/reference.test.ts +1 -1
  28. package/src/parser/inline/remark.ts +2 -2
  29. package/src/parser/inline/template.ts +1 -1
  30. package/src/parser/inline.test.ts +24 -23
  31. package/src/parser/inline.ts +46 -47
  32. package/src/parser/segment.ts +12 -12
  33. package/src/parser/source/escapable.test.ts +1 -1
  34. package/src/parser/source/escapable.ts +7 -4
  35. package/src/parser/source/text.ts +162 -25
  36. package/src/parser/source/unescapable.ts +5 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securemark",
3
- "version": "0.293.1",
3
+ "version": "0.293.3",
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",
@@ -12,12 +12,17 @@ describe('Unit: combinator/indent', () => {
12
12
  assert.deepStrictEqual(inspect(parser(input(' ', ctx)), ctx), undefined);
13
13
  assert.deepStrictEqual(inspect(parser(input(' ', ctx)), ctx), undefined);
14
14
  assert.deepStrictEqual(inspect(parser(input('a ', ctx)), ctx), undefined);
15
- assert.deepStrictEqual(inspect(parser(input(' a\n', ctx)), ctx), [['a'], '']);
15
+ assert.deepStrictEqual(inspect(parser(input(' a', ctx)), ctx), [['a'], '']);
16
16
  assert.deepStrictEqual(inspect(parser(input(' a ', ctx)), ctx), [['a '], '']);
17
+ assert.deepStrictEqual(inspect(parser(input(' a\n', ctx)), ctx), [['a'], '']);
17
18
  assert.deepStrictEqual(inspect(parser(input(' a \n', ctx)), ctx), [['a '], '']);
18
19
  assert.deepStrictEqual(inspect(parser(input(' a', ctx)), ctx), [['a'], '']);
20
+ assert.deepStrictEqual(inspect(parser(input(' a', ctx)), ctx), [['a'], '']);
21
+ assert.deepStrictEqual(inspect(parser(input(' a', ctx)), ctx), [['a'], '']);
22
+ assert.deepStrictEqual(inspect(parser(input(' a', ctx)), ctx), [[' a'], '']);
19
23
  assert.deepStrictEqual(inspect(parser(input(' a\n a', ctx)), ctx), [['a\na'], '']);
20
24
  assert.deepStrictEqual(inspect(parser(input(' a\n a', ctx)), ctx), [['a\n a'], '']);
25
+ assert.deepStrictEqual(inspect(parser(input(' a\n a', ctx)), ctx), [['a\n a'], '']);
21
26
  assert.deepStrictEqual(inspect(parser(input(' a\n a', ctx)), ctx), [['a'], ' a']);
22
27
  assert.deepStrictEqual(inspect(parser(input(' \ta', ctx)), ctx), [['\ta'], '']);
23
28
  assert.deepStrictEqual(inspect(parser(input('\ta', ctx)), ctx), [['a'], '']);
@@ -13,7 +13,7 @@ export function indent<N>(opener: RegExp | Parser<N>, parser: Parser<N> | boolea
13
13
  if (typeof opener === 'function') {
14
14
  separation = parser as boolean;
15
15
  parser = opener;
16
- opener = /([ \t])\1*/y;
16
+ opener = / {1,4}|\t{1,2}/y;
17
17
  }
18
18
  assert(!opener.flags.match(/[gm]/) && opener.sticky && !opener.source.startsWith('^'));
19
19
  assert(parser);
@@ -1,8 +1,8 @@
1
1
  import { Parser, Context, input, eval, failsafe } from '../../data/parser';
2
- import { consume, matcher } from '../../../combinator';
2
+ import { matcher } from '../../../combinator';
3
3
 
4
- export function focus<P extends Parser<unknown>>(scope: string | RegExp, parser: P, cost?: boolean): P;
5
- export function focus<N>(scope: string | RegExp, parser: Parser<N>, cost = true): Parser<N> {
4
+ export function focus<P extends Parser<unknown>>(scope: string | RegExp, parser: P): P;
5
+ export function focus<N>(scope: string | RegExp, parser: Parser<N>): Parser<N> {
6
6
  assert(parser);
7
7
  const match = matcher(scope, false);
8
8
  return failsafe(({ context }) => {
@@ -11,7 +11,6 @@ export function focus<N>(scope: string | RegExp, parser: Parser<N>, cost = true)
11
11
  const src = eval(match({ context }))?.[0] ?? '';
12
12
  assert(source.startsWith(src, position));
13
13
  if (src === '') return;
14
- cost && consume(src.length, context);
15
14
  context.range = src.length;
16
15
  context.offset ??= 0;
17
16
  context.offset += position;
@@ -157,7 +157,8 @@ export function setBacktrack(
157
157
  position: number,
158
158
  length: number = 1,
159
159
  ): void {
160
- const { source } = context;
160
+ const { source, state = 0 } = context;
161
+ if (state === 0) return;
161
162
  if (position === source.length) return;
162
163
  if (length === 0) return;
163
164
  for (const backtrack of backtracks) {
@@ -7,26 +7,24 @@ interface Delimiter {
7
7
  readonly signature: number | string;
8
8
  readonly matcher: (input: Input) => boolean | undefined;
9
9
  readonly precedence: number;
10
- readonly linebreakable: boolean;
11
10
  state: boolean;
12
11
  }
13
12
 
14
13
  export class Delimiters {
15
14
  // 手間を惜しまなければ規定のパターンはすべて配列のインデクスに変換可能。
16
- public static signature(pattern: string | RegExp | undefined, linebreakable: boolean): number | string {
15
+ public static signature(pattern: string | RegExp | undefined): number | string {
17
16
  switch (typeof pattern) {
18
17
  case 'undefined':
19
- return +linebreakable;
18
+ return 1 << 7;
20
19
  case 'string':
21
20
  assert(pattern !== '\x00');
22
21
  if (pattern.length === 1) {
23
22
  const code = pattern.charCodeAt(0);
24
- // 使用中のパターンの8ビット目が空いてるのでひとまずこうしとく
25
- if ((code & 1 << 7) === 0) return code | +linebreakable << 7;
23
+ return code;
26
24
  }
27
- return `s:${pattern}:${+linebreakable}`;
25
+ return `s:${pattern}`;
28
26
  case 'object':
29
- return `r/${pattern.source}/${+linebreakable}`;
27
+ return `r/${pattern.source}`;
30
28
  }
31
29
  }
32
30
  public static matcher(pattern: string | RegExp | undefined): (input: Input<Ctx>) => true | undefined {
@@ -61,14 +59,13 @@ export class Delimiters {
61
59
  readonly signature: number | string;
62
60
  readonly matcher: (input: Input) => boolean | undefined;
63
61
  readonly precedence: number;
64
- readonly linebreakable: boolean;
65
62
  }[]
66
63
  ): void {
67
64
  const { delimiters, stack } = this;
68
65
  // シグネチャ数以下
69
66
  assert(delimiters.length < 100);
70
67
  for (let i = 0; i < delims.length; ++i) {
71
- const { signature, matcher, precedence, linebreakable } = delims[i];
68
+ const { signature, matcher, precedence } = delims[i];
72
69
  const memory = this.registry(signature);
73
70
  const index = memory[0]?.index ?? delimiters.length;
74
71
  assert(memory.length === 0 || precedence === delimiters[index].precedence);
@@ -79,7 +76,6 @@ export class Delimiters {
79
76
  signature,
80
77
  matcher,
81
78
  precedence,
82
- linebreakable,
83
79
  state: true,
84
80
  };
85
81
  delimiters[index] = delimiter;
@@ -134,14 +130,13 @@ export class Delimiters {
134
130
  }
135
131
  }
136
132
  public match(input: Input): boolean {
137
- const { precedence = 0, linebreak = 0 } = input.context;
133
+ const { precedence = 0 } = input.context;
138
134
  const { delimiters } = this;
139
135
  for (let i = delimiters.length; i--;) {
140
136
  const delimiter = delimiters[i];
141
137
  if (delimiter.precedence <= precedence || !delimiter.state) continue;
142
138
  switch (delimiter.matcher(input)) {
143
139
  case true:
144
- if (!delimiter.linebreakable && linebreak > 0) return false;
145
140
  return true;
146
141
  case false:
147
142
  return false;
@@ -1,8 +1,7 @@
1
1
  import { Parser, eval } from '../parser';
2
2
  import { Delimiters } from './context/delimiter';
3
- import { unshift, push } from 'spica/array';
4
3
 
5
- type DelimiterOption = readonly [delimiter: string | RegExp, precedence: number, linebreak?: boolean];
4
+ type DelimiterOption = readonly [delimiter: string | RegExp, precedence: number];
6
5
 
7
6
  export function some<P extends Parser<unknown>>(parser: P, limit?: number): P;
8
7
  export function some<P extends Parser<unknown>>(parser: P, end?: string | RegExp, delimiters?: readonly DelimiterOption[], limit?: number): P;
@@ -10,11 +9,10 @@ export function some<N>(parser: Parser<N>, end?: string | RegExp | number, delim
10
9
  if (typeof end === 'number') return some(parser, undefined, delimiters, end);
11
10
  assert(parser);
12
11
  const match = Delimiters.matcher(end);
13
- const delims = delimiters.map(([delimiter, precedence, linebreakable = true]) => ({
14
- signature: Delimiters.signature(delimiter, linebreakable),
12
+ const delims = delimiters.map(([delimiter, precedence]) => ({
13
+ signature: Delimiters.signature(delimiter),
15
14
  matcher: Delimiters.matcher(delimiter),
16
15
  precedence,
17
- linebreakable,
18
16
  }));
19
17
  return input => {
20
18
  const { context } = input;
@@ -32,9 +30,7 @@ export function some<N>(parser: Parser<N>, end?: string | RegExp | number, delim
32
30
  const result = parser(input);
33
31
  if (result === undefined) break;
34
32
  nodes = nodes
35
- ? nodes.length < eval(result).length / 8
36
- ? unshift(nodes, eval(result))
37
- : push(nodes, eval(result))
33
+ ? (nodes.push(...eval(result)), nodes)
38
34
  : eval(result);
39
35
  if (limit >= 0 && context.position - position > limit) break;
40
36
  }
@@ -363,7 +363,7 @@ describe('Unit: parser/api/parse', () => {
363
363
  this.timeout(5000);
364
364
  // 最悪計算量での実行速度はCommonMarkの公式JS実装の32nに対して3倍遅い程度。
365
365
  // 5n = annotation/reference + link + url/math + ruby + text
366
- const source = `${'.'.repeat(0 + 0)}((([[[[#$[${'.'.repeat(19998)}`;
366
+ const source = `${'.'.repeat(0 + 0)}((([[[[#$[${'&'.repeat(19998)}`;
367
367
  assert.deepStrictEqual(
368
368
  [...parse(source, {}, { resources: { clock: 100000, recursions: [100] } }).children]
369
369
  .map(el => el.tagName),
@@ -372,7 +372,7 @@ describe('Unit: parser/api/parse', () => {
372
372
 
373
373
  it('backtrack error', function () {
374
374
  this.timeout(5000);
375
- const source = `${'.'.repeat(0 + 1)}((([[[[#$[${'.'.repeat(19998)}`;
375
+ const source = `${'.'.repeat(0 + 1)}((([[[[#$[${'&'.repeat(19998)}`;
376
376
  assert.deepStrictEqual(
377
377
  [...parse(source, {}, { resources: { clock: 100000, recursions: [100] } }).children]
378
378
  .map(el => el.tagName),
@@ -32,6 +32,8 @@ describe('Unit: parser/block/olist', () => {
32
32
  assert.deepStrictEqual(inspect(parser('(1)'), ctx), undefined);
33
33
  assert.deepStrictEqual(inspect(parser('(1)\n'), ctx), undefined);
34
34
  assert.deepStrictEqual(inspect(parser('(1)\n(1) a'), ctx), undefined);
35
+ assert.deepStrictEqual(inspect(parser('I. '), ctx), undefined);
36
+ assert.deepStrictEqual(inspect(parser('A. '), ctx), undefined);
35
37
  assert.deepStrictEqual(inspect(parser('(I) '), ctx), undefined);
36
38
  assert.deepStrictEqual(inspect(parser('(A) '), ctx), undefined);
37
39
  assert.deepStrictEqual(inspect(parser(' 1. '), ctx), undefined);
@@ -117,12 +119,12 @@ describe('Unit: parser/block/olist', () => {
117
119
  });
118
120
 
119
121
  it('type', () => {
120
- assert.deepStrictEqual(inspect(parser('i. '), ctx), [['<ol type="i" data-type="lower-roman"><li></li></ol>'], '']);
121
- assert.deepStrictEqual(inspect(parser('a. '), ctx), [['<ol type="a" data-type="lower-alpha"><li></li></ol>'], '']);
122
- assert.deepStrictEqual(inspect(parser('I. '), ctx), [['<ol type="I" data-type="upper-roman"><li></li></ol>'], '']);
123
- assert.deepStrictEqual(inspect(parser('A. '), ctx), [['<ol type="A" data-type="upper-alpha"><li></li></ol>'], '']);
124
- assert.deepStrictEqual(inspect(parser('a. \n1.\nc'), ctx), [['<ol type="a" data-type="lower-alpha"><li></li><li data-marker="1."></li><li data-marker="c."></li></ol>'], '']);
125
- assert.deepStrictEqual(inspect(parser('i. 1'), ctx), [['<ol type="i" data-type="lower-roman"><li id="index::1">1</li></ol>'], '']);
122
+ assert.deepStrictEqual(inspect(parser('1. \n i. '), ctx), [['<ol><li><br><ol type="i" data-type="lower-roman"><li></li></ol></li></ol>'], '']);
123
+ assert.deepStrictEqual(inspect(parser('1. \n a. '), ctx), [['<ol><li><br><ol type="a" data-type="lower-alpha"><li></li></ol></li></ol>'], '']);
124
+ assert.deepStrictEqual(inspect(parser('1. \n I. '), ctx), [['<ol><li><br><ol type="I" data-type="upper-roman"><li></li></ol></li></ol>'], '']);
125
+ assert.deepStrictEqual(inspect(parser('1. \n A. '), ctx), [['<ol><li><br><ol type="A" data-type="upper-alpha"><li></li></ol></li></ol>'], '']);
126
+ assert.deepStrictEqual(inspect(parser('1. \n a. \n 1.\n c'), ctx), [['<ol><li><br><ol type="a" data-type="lower-alpha"><li></li><li data-marker="1."></li><li data-marker="c."></li></ol></li></ol>'], '']);
127
+ assert.deepStrictEqual(inspect(parser('1. \n i. 1'), ctx), [['<ol><li><br><ol type="i" data-type="lower-roman"><li id="index::1">1</li></ol></li></ol>'], '']);
126
128
  });
127
129
 
128
130
  it('checkbox', () => {
@@ -16,8 +16,8 @@ const openers = {
16
16
 
17
17
  export const olist: OListParser = lazy(() => block(validate(
18
18
  new RegExp([
19
- /([0-9]+|[a-z]+|[A-Z]+)(?:-[0-9]+)*\. /y.source,
20
- /\(([0-9]+|[a-z]+)\)(?:-[0-9]+)* /y.source,
19
+ /(?:[0-9]+)(?:-[0-9]+)*\. /y.source,
20
+ /\((?:[0-9]+)\)(?:-[0-9]+)* /y.source,
21
21
  ].join('|'), 'y'),
22
22
  olist_)));
23
23
 
@@ -8,7 +8,7 @@ import { html, define, defrag } from 'typed-dom/dom';
8
8
 
9
9
  export const sidefence: SidefenceParser = lazy(() => block(fmap(focus(
10
10
  /(?=\|+(?:[^\S\n]|\n\|))(?:\|+(?:[^\S\n][^\n]*)?(?:$|\n))+$/y,
11
- union([source]), false),
11
+ union([source])),
12
12
  ([el]) => [
13
13
  define(el, {
14
14
  class: 'invalid',
@@ -23,7 +23,7 @@ const source: SidefenceParser.SourceParser = lazy(() => fmap(
23
23
  some(recursion(Recursion.block, union([
24
24
  focus(
25
25
  /(?:\|\|+(?:[^\S\n][^\n]*)?(?:$|\n))+/y,
26
- convert(unindent, source, false, true), false),
26
+ convert(unindent, source, false, true)),
27
27
  rewrite(
28
28
  some(contentline, opener),
29
29
  convert(unindent, fmap(autolink, ns => [html('pre', defrag(ns))]), false, true)),
@@ -40,9 +40,9 @@ const align: AlignParser = fmap(open(
40
40
  '|',
41
41
  union([
42
42
  focus(/:-+:?/y, ({ context: { source } }) =>
43
- [[source.at(-1) === ':' ? 'center' : 'start']], false),
43
+ [[source.at(-1) === ':' ? 'center' : 'start']]),
44
44
  focus(/-+:?/y, ({ context: { source } }) =>
45
- [[source.at(-1) === ':' ? 'end' : '']], false),
45
+ [[source.at(-1) === ':' ? 'end' : '']]),
46
46
  ])),
47
47
  ns => [html('td', defrag(ns))]);
48
48
 
@@ -58,51 +58,53 @@ export const block: BlockParser = reset(
58
58
  input => {
59
59
  const { context: { source, position } } = input;
60
60
  if (position === source.length) return;
61
- switch (source.slice(position, position + 3)) {
62
- case '===':
63
- return pagebreak(input);
64
- case '~~~':
65
- return extension(input);
66
- case '```':
67
- return codeblock(input);
68
- }
69
- switch (source.slice(position, position + 2)) {
70
- case '$$':
71
- return mathblock(input);
72
- case '[$':
73
- return extension(input);
74
- case '[!':
61
+ const fst = source[position];
62
+ switch (fst) {
63
+ case '=':
64
+ if (source.startsWith('===', position)) return pagebreak(input);
65
+ break;
66
+ case '`':
67
+ if (source.startsWith('```', position)) return codeblock(input);
68
+ break;
69
+ case '~':
70
+ if (source.startsWith('~~~', position)) return extension(input);
71
+ if (source[position + 1] === ' ') return dlist(input);
72
+ break;
73
+ case '-':
74
+ if (source[position + 1] === ' ') return ulist(input) || ilist(input);
75
+ break;
76
+ case '+':
77
+ case '*':
78
+ if (source[position + 1] === ' ') return ilist(input);
79
+ break;
80
+ case '[':
81
+ switch (source[position + 1]) {
82
+ case '$':
83
+ return extension(input);
84
+ case '!':
85
+ return mediablock(input);
86
+ }
87
+ break;
88
+ case '!':
89
+ if (source[position + 1] === '>') return blockquote(input);
75
90
  return mediablock(input);
76
- case '!>':
91
+ case '>':
92
+ if (source[position + 1] === '>') return blockquote(input) || reply(input);
77
93
  return blockquote(input);
78
- case '>>':
79
- return blockquote(input)
80
- || reply(input);
81
- case '- ':
82
- return ulist(input)
83
- || ilist(input);
84
- case '+ ':
85
- case '* ':
86
- return ilist(input);
87
- case '~ ':
88
- return dlist(input);
89
- }
90
- switch (source[position]) {
91
94
  case '#':
92
95
  return heading(input);
93
- case '|':
94
- return table(input)
95
- || sidefence(input);
96
96
  case '$':
97
+ if (source[position + 1] === '$') return mathblock(input);
97
98
  return extension(input);
98
- case '>':
99
- return blockquote(input);
100
- case '!':
101
- return mediablock(input);
99
+ case '|':
100
+ return table(input) || sidefence(input);
101
+ case '(':
102
+ return olist(input);
103
+ default:
104
+ if ('0' <= fst && fst <= '9') return olist(input);
102
105
  }
103
106
  },
104
107
  emptyline,
105
- olist,
106
108
  paragraph
107
109
  ]) as any));
108
110
 
@@ -17,7 +17,7 @@ describe('Unit: parser/inline/annotation', () => {
17
17
  assert.deepStrictEqual(inspect(parser('(()))'), ctx), undefined);
18
18
  assert.deepStrictEqual(inspect(parser('(("))'), ctx), undefined);
19
19
  assert.deepStrictEqual(inspect(parser('(([))'), ctx), undefined);
20
- assert.deepStrictEqual(inspect(parser('((<bdi>))'), ctx), undefined);
20
+ assert.deepStrictEqual(inspect(parser('(([%))'), ctx), undefined);
21
21
  assert.deepStrictEqual(inspect(parser('(( ))'), ctx), undefined);
22
22
  assert.deepStrictEqual(inspect(parser('(( (a'), ctx), undefined);
23
23
  assert.deepStrictEqual(inspect(parser('((\n))'), ctx), undefined);
@@ -12,10 +12,10 @@ describe('Unit: parser/inline/autolink/url', () => {
12
12
  assert.deepStrictEqual(inspect(parser(''), ctx), undefined);
13
13
  assert.deepStrictEqual(inspect(parser(' http'), ctx), [[' http'], '']);
14
14
  assert.deepStrictEqual(inspect(parser(' ttp'), ctx), [[' ttp'], '']);
15
- assert.deepStrictEqual(inspect(parser(' http://'), ctx), [[' ', 'http:', '/', '/'], '']);
16
- assert.deepStrictEqual(inspect(parser(' http://['), ctx), [[' ', 'http:', '/', '/', '['], '']);
17
- assert.deepStrictEqual(inspect(parser(' http://]'), ctx), [[' ', 'http:', '/', '/', ']'], '']);
18
- assert.deepStrictEqual(inspect(parser(' Http://host'), ctx), [[' ', 'Http:', '/', '/host'], '']);
15
+ assert.deepStrictEqual(inspect(parser(' http://'), ctx), [[' ', 'http:', '//'], '']);
16
+ assert.deepStrictEqual(inspect(parser(' http://['), ctx), [[' ', 'http:', '//', '['], '']);
17
+ assert.deepStrictEqual(inspect(parser(' http://]'), ctx), [[' ', 'http:', '//', ']'], '']);
18
+ assert.deepStrictEqual(inspect(parser(' Http://host'), ctx), [[' ', 'Http:', '//host'], '']);
19
19
  assert.deepStrictEqual(inspect(parser(' http://[::ffff:0:0%1]'), ctx), [[' ', '<a class="invalid">http://[::ffff:0:0%1]</a>'], '']);
20
20
  assert.deepStrictEqual(inspect(parser(' http://[::ffff:0:0/96]'), ctx), [[' ', '<a class="invalid">http://[::ffff:0:0/96]</a>'], '']);
21
21
  });
@@ -59,7 +59,7 @@ describe('Unit: parser/inline/autolink/url', () => {
59
59
  assert.deepStrictEqual(inspect(parser(' http://host^'), ctx), [[' ', '<a class="url" href="http://host" target="_blank">http://host</a>', '^'], '']);
60
60
  assert.deepStrictEqual(inspect(parser(' http://host_'), ctx), [[' ', '<a class="url" href="http://host" target="_blank">http://host</a>', '_'], '']);
61
61
  assert.deepStrictEqual(inspect(parser(' http://host/'), ctx), [[' ', '<a class="url" href="http://host/" target="_blank">http://host/</a>'], '']);
62
- assert.deepStrictEqual(inspect(parser(' http://host//'), ctx), [[' ', '<a class="url" href="http://host" target="_blank">http://host</a>', '/', '/'], '']);
62
+ assert.deepStrictEqual(inspect(parser(' http://host//'), ctx), [[' ', '<a class="url" href="http://host" target="_blank">http://host</a>', '//'], '']);
63
63
  assert.deepStrictEqual(inspect(parser(` http://host'`), ctx), [[' ', '<a class="url" href="http://host\'" target="_blank">http://host\'</a>'], '']);
64
64
  assert.deepStrictEqual(inspect(parser(' http://host"'), ctx), [[' ', '<a class="url" href="http://host" target="_blank">http://host</a>', '"'], '']);
65
65
  assert.deepStrictEqual(inspect(parser(' http://host`'), ctx), [[' ', '<a class="url" href="http://host" target="_blank">http://host</a>', '`'], '']);
@@ -41,6 +41,7 @@ describe('Unit: parser/inline/bracket', () => {
41
41
  assert.deepStrictEqual(inspect(parser('(ABBR, ABBR)'), ctx), [['(', 'ABBR, ABBR', ')'], '']);
42
42
  assert.deepStrictEqual(inspect(parser('(\\a)'), ctx), [['<span class="paren">(a)</span>'], '']);
43
43
  assert.deepStrictEqual(inspect(parser('(==)'), ctx), [['<span class="paren">(==)</span>'], '']);
44
+ assert.deepStrictEqual(inspect(parser('("(\n))"(")'), ctx), [['<span class="paren">("(<br>)</span>'], ')"(")']);
44
45
  assert.deepStrictEqual(inspect(parser('($)$'), ctx), [['(', '<span class="math" translate="no" data-src="$)$">$)$</span>'], '']);
45
46
  assert.deepStrictEqual(inspect(parser(')'), ctx), undefined);
46
47
  assert.deepStrictEqual(inspect(parser('(1,2)'), ctx), [['(', '1,2', ')'], '']);
@@ -60,7 +61,7 @@ describe('Unit: parser/inline/bracket', () => {
60
61
  assert.deepStrictEqual(inspect(parser('[]'), ctx), [['[', ']'], '']);
61
62
  assert.deepStrictEqual(inspect(parser('[a'), ctx), [['[', 'a'], '']);
62
63
  assert.deepStrictEqual(inspect(parser('[a]'), ctx), [['[', 'a', ']'], '']);
63
- assert.deepStrictEqual(inspect(parser('[==]'), ctx), [['[', '=', '=', ']'], '']);
64
+ assert.deepStrictEqual(inspect(parser('[==]'), ctx), [['[', '==', ']'], '']);
64
65
  assert.deepStrictEqual(inspect(parser('[$]$'), ctx), [['[', '<span class="math" translate="no" data-src="$]$">$]$</span>'], '']);
65
66
  assert.deepStrictEqual(inspect(parser(']'), ctx), undefined);
66
67
  });
@@ -70,7 +71,7 @@ describe('Unit: parser/inline/bracket', () => {
70
71
  assert.deepStrictEqual(inspect(parser('{}'), ctx), [['{', '}'], '']);
71
72
  assert.deepStrictEqual(inspect(parser('{a'), ctx), [['{', 'a'], '']);
72
73
  assert.deepStrictEqual(inspect(parser('{a}'), ctx), [['{', 'a', '}'], '']);
73
- assert.deepStrictEqual(inspect(parser('{==}'), ctx), [['{', '=', '=', '}'], '']);
74
+ assert.deepStrictEqual(inspect(parser('{==}'), ctx), [['{', '==', '}'], '']);
74
75
  assert.deepStrictEqual(inspect(parser('}'), ctx), undefined);
75
76
  });
76
77
 
@@ -82,8 +83,9 @@ describe('Unit: parser/inline/bracket', () => {
82
83
  assert.deepStrictEqual(inspect(parser('"(")"'), ctx), [['"', '(', '"'], ')"']);
83
84
  assert.deepStrictEqual(inspect(parser('"(("'), ctx), [['"', '(', '(', '"'], '']);
84
85
  assert.deepStrictEqual(inspect(parser('"(\\")"'), ctx), [['"', '<span class="paren">(")</span>', '"'], '']);
85
- assert.deepStrictEqual(inspect(parser('"\n"'), ctx), [['"', '<br>', '"'], '']);
86
- assert.deepStrictEqual(inspect(parser('"\n"(")'), ctx), [['"', '<br>', '"', '(', '"'], ')']);
86
+ assert.deepStrictEqual(inspect(parser('"(\n)"(")'), ctx), [['"', '('], '\n)"(")']);
87
+ assert.deepStrictEqual(inspect(parser('"\n"'), ctx), [['"'], '\n"']);
88
+ assert.deepStrictEqual(inspect(parser('"\n"(")'), ctx), [['"'], '\n"(")']);
87
89
  });
88
90
 
89
91
  });