securemark 0.235.1 → 0.236.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/securemark.js +179 -141
  3. package/markdown.d.ts +7 -2
  4. package/package-lock.json +43 -64
  5. package/package.json +3 -3
  6. package/src/debug.test.ts +3 -1
  7. package/src/parser/api/parse.test.ts +3 -3
  8. package/src/parser/block/blockquote.test.ts +1 -1
  9. package/src/parser/block/codeblock.ts +1 -1
  10. package/src/parser/block/extension/aside.test.ts +1 -1
  11. package/src/parser/block/extension/aside.ts +2 -2
  12. package/src/parser/block/extension/example.test.ts +2 -2
  13. package/src/parser/block/extension/example.ts +2 -2
  14. package/src/parser/block/extension/fig.test.ts +20 -20
  15. package/src/parser/block/extension/figure.test.ts +33 -31
  16. package/src/parser/block/extension/figure.ts +8 -4
  17. package/src/parser/block/extension/message.ts +2 -2
  18. package/src/parser/block/extension/placeholder.ts +1 -1
  19. package/src/parser/block/extension/table.ts +5 -5
  20. package/src/parser/block/ilist.ts +1 -1
  21. package/src/parser/block/mathblock.ts +1 -1
  22. package/src/parser/block/olist.ts +1 -1
  23. package/src/parser/block/paragraph.test.ts +1 -1
  24. package/src/parser/block/reply/quote.ts +1 -1
  25. package/src/parser/block/table.ts +1 -1
  26. package/src/parser/block/ulist.ts +1 -1
  27. package/src/parser/header.ts +1 -1
  28. package/src/parser/inline/annotation.ts +3 -3
  29. package/src/parser/inline/bracket.test.ts +10 -10
  30. package/src/parser/inline/bracket.ts +2 -5
  31. package/src/parser/inline/deletion.test.ts +4 -1
  32. package/src/parser/inline/deletion.ts +7 -4
  33. package/src/parser/inline/emphasis.ts +2 -2
  34. package/src/parser/inline/emstrong.ts +4 -4
  35. package/src/parser/inline/extension/placeholder.ts +1 -1
  36. package/src/parser/inline/html.test.ts +25 -17
  37. package/src/parser/inline/html.ts +43 -19
  38. package/src/parser/inline/htmlentity.test.ts +1 -1
  39. package/src/parser/inline/htmlentity.ts +18 -11
  40. package/src/parser/inline/insertion.test.ts +4 -1
  41. package/src/parser/inline/insertion.ts +7 -4
  42. package/src/parser/inline/link.test.ts +3 -0
  43. package/src/parser/inline/link.ts +10 -11
  44. package/src/parser/inline/mark.ts +2 -2
  45. package/src/parser/inline/math.ts +1 -1
  46. package/src/parser/inline/media.test.ts +3 -0
  47. package/src/parser/inline/media.ts +4 -4
  48. package/src/parser/inline/reference.ts +7 -7
  49. package/src/parser/inline/ruby.ts +2 -2
  50. package/src/parser/inline/strong.ts +2 -2
  51. package/src/parser/inline.test.ts +2 -2
  52. package/src/parser/processor/figure.test.ts +33 -29
  53. package/src/parser/processor/figure.ts +25 -3
  54. package/src/parser/processor/footnote.ts +3 -3
  55. package/src/parser/util.ts +38 -32
@@ -2,10 +2,10 @@ import { undefined } from 'spica/global';
2
2
  import { isFrozen, ObjectEntries, ObjectFreeze, ObjectSetPrototypeOf, ObjectValues } from 'spica/alias';
3
3
  import { MarkdownParser } from '../../../markdown';
4
4
  import { HTMLParser } from '../inline';
5
- import { union, some, validate, context, creator, surround, match, lazy } from '../../combinator';
5
+ import { union, some, validate, context, creator, surround, open, match, lazy } from '../../combinator';
6
6
  import { inline } from '../inline';
7
7
  import { str } from '../source';
8
- import { startLoose, trimNodeEndBR } from '../util';
8
+ import { startLoose, blank } from '../util';
9
9
  import { html as h, defrag } from 'typed-dom';
10
10
  import { memoize } from 'spica/memoize';
11
11
  import { Cache } from 'spica/cache';
@@ -26,17 +26,17 @@ export const html: HTMLParser = lazy(() => creator(validate('<', validate(/^<[a-
26
26
  memoize(
27
27
  ([, tag]) =>
28
28
  surround(
29
- `<${tag}`, some(union([attribute])), '>', true,
29
+ `<${tag}`, some(union([attribute])), /^\s*>/, true,
30
30
  ([, bs = []], rest) =>
31
31
  [[h(tag as 'span', attributes('html', [], attrspec[tag], bs))], rest]),
32
32
  ([, tag]) => tag)),
33
33
  match(
34
- /^(?=<(sup|sub|small|bdo|bdi)(?=[^\S\n]|>))/,
34
+ /^(?=<(sup|sub|small)(?=[^\S\n]|>))/,
35
35
  memoize(
36
36
  ([, tag]) =>
37
37
  validate(`<${tag}`, `</${tag}>`,
38
38
  surround<HTMLParser.TagParser, string>(surround(
39
- str(`<${tag}`), some(attribute), str('>'), true),
39
+ str(`<${tag}`), some(attribute), str(/^\s*>/), true),
40
40
  startLoose(
41
41
  context((() => {
42
42
  switch (tag) {
@@ -58,25 +58,49 @@ export const html: HTMLParser = lazy(() => creator(validate('<', validate(/^<[a-
58
58
  }},
59
59
  };
60
60
  default:
61
+ assert(false);
61
62
  return {};
62
63
  }
63
64
  })(),
64
- some(union([inline]), `</${tag}>`)), `</${tag}>`),
65
+ some(union([
66
+ some(inline, blank(/\n?/, `</${tag}>`)),
67
+ open(/^\n?/, some(inline, '</'), true),
68
+ ]), `</${tag}>`)), `</${tag}>`),
65
69
  str(`</${tag}>`), false,
66
70
  ([as, bs, cs], rest, context) =>
67
- [[elem(tag, as, trimNodeEndBR(defrag(bs)), cs, context)], rest])),
71
+ [[elem(tag, as, defrag(bs), cs, context)], rest])),
68
72
  ([, tag]) => tag)),
73
+ match(
74
+ /^(?=<(bdo|bdi)(?=[^\S\n]|>))/,
75
+ memoize(
76
+ ([, tag]) =>
77
+ validate(`<${tag}`, `</${tag}>`,
78
+ surround<HTMLParser.TagParser, string>(surround(
79
+ str(`<${tag}`), some(attribute), str(/^\s*>/), true),
80
+ startLoose(some(union([
81
+ some(inline, blank(/\n?/, `</${tag}>`)),
82
+ open(/^\n?/, some(inline, '</'), true),
83
+ ]), `</${tag}>`), `</${tag}>`),
84
+ str(`</${tag}>`), false,
85
+ ([as, bs, cs], rest) =>
86
+ [[elem(tag, as, defrag(bs), cs, {})], rest],
87
+ ([as, bs], rest) =>
88
+ as.length === 1 ? [unshift(as, bs), rest] : undefined)),
89
+ ([, tag]) => tag)),
69
90
  match(
70
91
  /^(?=<([a-z]+)(?=[^\S\n]|>))/,
71
92
  memoize(
72
93
  ([, tag]) =>
73
94
  validate(`<${tag}`, `</${tag}>`,
74
95
  surround<HTMLParser.TagParser, string>(surround(
75
- str(`<${tag}`), some(attribute), str('>'), true),
76
- startLoose(some(union([inline]), `</${tag}>`), `</${tag}>`),
96
+ str(`<${tag}`), some(attribute), str(/^\s*>/), true),
97
+ startLoose(some(union([
98
+ some(inline, blank(/\n?/, `</${tag}>`)),
99
+ open(/^\n?/, some(inline, '</'), true),
100
+ ]), `</${tag}>`), `</${tag}>`),
77
101
  str(`</${tag}>`), false,
78
102
  ([as, bs, cs], rest) =>
79
- [[elem(tag, as, trimNodeEndBR(defrag(bs)), cs, {})], rest],
103
+ [[elem(tag, as, defrag(bs), cs, {})], rest],
80
104
  ([as, bs], rest) =>
81
105
  as.length === 1 ? [unshift(as, bs), rest] : undefined)),
82
106
  ([, tag]) => tag,
@@ -89,41 +113,41 @@ export const attribute: HTMLParser.TagParser.AttributeParser = union([
89
113
 
90
114
  function elem(tag: string, as: string[], bs: (HTMLElement | string)[], cs: string[], context: MarkdownParser.Context): HTMLElement {
91
115
  assert(as.length > 0);
116
+ assert(as[0][0] === '<' && as[as.length - 1].slice(-1) === '>');
92
117
  assert(bs.length === defrag(bs).length);
93
118
  assert(cs.length === 1);
94
- if (!tags.includes(tag)) return invalid('tag', `Invalid HTML tag <${tag}>.`, as, bs, cs);
119
+ if (!tags.includes(tag)) return invalid('tag', `Invalid HTML tag <${tag}>`, as, bs, cs);
95
120
  switch (tag) {
96
121
  case 'sup':
97
122
  case 'sub':
98
123
  switch (true) {
99
124
  case context.state?.in?.supsub:
100
- return invalid('nest', `<${tag}> HTML tag cannot be used in <sup> or <sub> HTML tag.`, as, bs, cs);
125
+ return invalid('nest', `<${tag}> HTML tag cannot be used in <sup> or <sub> HTML tag`, as, bs, cs);
101
126
  }
102
127
  break;
103
128
  case 'small':
104
129
  switch (true) {
105
130
  case context.state?.in?.supsub:
106
131
  case context.state?.in?.small:
107
- return invalid('nest', `<${tag}> HTML tag cannot be used in <sup>, <sub>, or <small> HTML tag.`, as, bs, cs);
132
+ return invalid('nest', `<${tag}> HTML tag cannot be used in <sup>, <sub>, or <small> HTML tag`, as, bs, cs);
108
133
  }
109
134
  break;
110
135
  }
111
136
  let attrs: Record<string, string | undefined> | undefined;
112
137
  switch (true) {
113
- case as[as.length - 1] !== '>'
114
- || 'data-invalid-syntax' in (attrs = attributes('html', [], attrspec[tag], as.slice(1, -1) as string[])):
115
- return invalid('attribute', 'Invalid HTML attribute.', as, bs, cs);
138
+ case 'data-invalid-syntax' in (attrs = attributes('html', [], attrspec[tag], as.slice(1, -1) as string[])):
139
+ return invalid('attribute', 'Invalid HTML attribute', as, bs, cs);
116
140
  default:
117
141
  assert(attrs);
118
142
  return h(tag as 'span', attrs, bs);
119
143
  }
120
144
  }
121
- function invalid(type: string, description: string, as: (HTMLElement | string)[], bs: (HTMLElement | string)[], cs: (HTMLElement | string)[]): HTMLElement {
145
+ function invalid(type: string, message: string, as: (HTMLElement | string)[], bs: (HTMLElement | string)[], cs: (HTMLElement | string)[]): HTMLElement {
122
146
  return h('span', {
123
147
  class: 'invalid',
124
148
  'data-invalid-syntax': 'html',
125
149
  'data-invalid-type': type,
126
- 'data-invalid-description': description,
150
+ 'data-invalid-message': message,
127
151
  }, defrag(push(unshift(as, bs), cs)));
128
152
  }
129
153
 
@@ -163,7 +187,7 @@ export function attributes(
163
187
  attrs['class'] = join(classes, ' ');
164
188
  attrs['data-invalid-syntax'] = syntax;
165
189
  attrs['data-invalid-type'] = 'argument';
166
- attrs['data-invalid-description'] = 'Invalid argument.';
190
+ attrs['data-invalid-message'] = 'Invalid argument';
167
191
  }
168
192
  return attrs;
169
193
  }
@@ -14,7 +14,6 @@ describe('Unit: parser/inline/htmlentity', () => {
14
14
  assert.deepStrictEqual(inspect(parser('& ;')), undefined);
15
15
  assert.deepStrictEqual(inspect(parser('&\n;')), undefined);
16
16
  assert.deepStrictEqual(inspect(parser('&a;')), [['<span class="invalid">&amp;a;</span>'], '']);
17
- assert.deepStrictEqual(inspect(parser('&NewLine;')), undefined);
18
17
  assert.deepStrictEqual(inspect(parser('&#;')), undefined);
19
18
  assert.deepStrictEqual(inspect(parser('&#g;')), undefined);
20
19
  assert.deepStrictEqual(inspect(parser('&#x;')), undefined);
@@ -36,6 +35,7 @@ describe('Unit: parser/inline/htmlentity', () => {
36
35
  });
37
36
 
38
37
  it('entity', () => {
38
+ assert.deepStrictEqual(inspect(parser('&NewLine;')), [[' '], '']);
39
39
  assert.deepStrictEqual(inspect(parser('&nbsp;')), [['\u00A0'], '']);
40
40
  assert.deepStrictEqual(inspect(parser('&amp;')), [['&'], '']);
41
41
  assert.deepStrictEqual(inspect(parser('&copy;')), [['©'], '']);
@@ -1,24 +1,31 @@
1
+ import { undefined } from 'spica/global';
1
2
  import { HTMLEntityParser, UnsafeHTMLEntityParser } from '../inline';
2
3
  import { union, validate, focus, creator, fmap } from '../../combinator';
3
4
  import { html } from 'typed-dom';
5
+ import { reduce } from 'spica/memoize';
4
6
 
5
7
  export const unsafehtmlentity: UnsafeHTMLEntityParser = creator(validate('&', focus(
6
- /^&(?!NewLine;)[0-9A-Za-z]+;/,
7
- (parser => entity => (
8
- parser.innerHTML = entity,
9
- entity = parser.textContent!,
10
- [[`${entity[0] !== '&' || entity.length === 1 ? '' : '\0'}${entity}`], '']
11
- ))(html('b')))));
8
+ /^&[0-9A-Za-z]+;/,
9
+ entity => [[parse(entity) ?? `\0${entity}`], ''])));
12
10
 
13
11
  export const htmlentity: HTMLEntityParser = fmap(
14
12
  union([unsafehtmlentity]),
15
- ([str]) => [
16
- str[0] === '\0'
13
+ ([test]) => [
14
+ test[0] === '\0'
17
15
  ? html('span', {
18
16
  class: 'invalid',
19
17
  'data-invalid-syntax': 'htmlentity',
20
18
  'data-invalid-type': 'syntax',
21
- 'data-invalid-description': 'Invalid HTML entity.',
22
- }, str.slice(1))
23
- : str,
19
+ 'data-invalid-message': 'Invalid HTML entity',
20
+ }, test.slice(1))
21
+ : test,
24
22
  ]);
23
+
24
+ const parse = reduce((el => (entity: string): string | undefined => {
25
+ if (entity === '&NewLine;') return ' ';
26
+ el.innerHTML = entity;
27
+ const text = el.textContent!;
28
+ return entity === text
29
+ ? undefined
30
+ : text;
31
+ })(html('b')));
@@ -18,7 +18,6 @@ describe('Unit: parser/inline/insertion', () => {
18
18
  it('basic', () => {
19
19
  assert.deepStrictEqual(inspect(parser('++a++')), [['<ins>a</ins>'], '']);
20
20
  assert.deepStrictEqual(inspect(parser('++a+b++')), [['<ins>a+b</ins>'], '']);
21
- assert.deepStrictEqual(inspect(parser('++a ++')), [['<ins>a </ins>'], '']);
22
21
  assert.deepStrictEqual(inspect(parser('++ ++')), [['<ins> </ins>'], '']);
23
22
  assert.deepStrictEqual(inspect(parser('++ a++')), [['<ins> a</ins>'], '']);
24
23
  assert.deepStrictEqual(inspect(parser('++ a ++')), [['<ins> a </ins>'], '']);
@@ -26,7 +25,11 @@ describe('Unit: parser/inline/insertion', () => {
26
25
  assert.deepStrictEqual(inspect(parser('++\na++')), [['<ins><br>a</ins>'], '']);
27
26
  assert.deepStrictEqual(inspect(parser('++\\\na++')), [['<ins><span class="linebreak"> </span>a</ins>'], '']);
28
27
  assert.deepStrictEqual(inspect(parser('++<wbr>a++')), [['<ins><wbr>a</ins>'], '']);
28
+ assert.deepStrictEqual(inspect(parser('++a ++')), [['<ins>a </ins>'], '']);
29
+ assert.deepStrictEqual(inspect(parser('++a \n ++')), [['<ins>a </ins>'], '']);
29
30
  assert.deepStrictEqual(inspect(parser('++a\n++')), [['<ins>a</ins>'], '']);
31
+ assert.deepStrictEqual(inspect(parser('++a\n ++')), [['<ins>a </ins>'], '']);
32
+ assert.deepStrictEqual(inspect(parser('++a\n<wbr>++')), [['<ins>a<wbr></ins>'], '']);
30
33
  assert.deepStrictEqual(inspect(parser('++a\nb++')), [['<ins>a<br>b</ins>'], '']);
31
34
  assert.deepStrictEqual(inspect(parser('++a\\\nb++')), [['<ins>a<span class="linebreak"> </span>b</ins>'], '']);
32
35
  assert.deepStrictEqual(inspect(parser('++\\+++')), [['<ins>+</ins>'], '']);
@@ -1,14 +1,17 @@
1
1
  import { InsertionParser } from '../inline';
2
- import { union, some, creator, surround, lazy } from '../../combinator';
2
+ import { union, some, creator, surround, open, lazy } from '../../combinator';
3
3
  import { inline } from '../inline';
4
4
  import { str } from '../source';
5
- import { trimNodeEndBR } from '../util';
5
+ import { blank } from '../util';
6
6
  import { html, defrag } from 'typed-dom';
7
7
  import { unshift } from 'spica/array';
8
8
 
9
9
  export const insertion: InsertionParser = lazy(() => creator(surround(
10
10
  str('++'),
11
- union([some(inline, '++')]),
11
+ some(union([
12
+ some(inline, blank(/\n?/, '++')),
13
+ open(/^\n?/, some(inline, '+'), true),
14
+ ])),
12
15
  str('++'), false,
13
- ([, bs], rest) => [[html('ins', defrag(trimNodeEndBR(bs)))], rest],
16
+ ([, bs], rest) => [[html('ins', defrag(bs))], rest],
14
17
  ([as, bs], rest) => [unshift(as, bs), rest])));
@@ -93,6 +93,9 @@ describe('Unit: parser/inline/link', () => {
93
93
  assert.deepStrictEqual(inspect(parser('[]{b }')), [['<a href="b">b</a>'], '']);
94
94
  assert.deepStrictEqual(inspect(parser('[]{ b }')), [['<a href="b">b</a>'], '']);
95
95
  assert.deepStrictEqual(inspect(parser('[]{ b }')), [['<a href="b">b</a>'], '']);
96
+ assert.deepStrictEqual(inspect(parser('[]{ b }')), [['<a href="b">b</a>'], '']);
97
+ assert.deepStrictEqual(inspect(parser('[]{ b }')), [['<a href="b">b</a>'], '']);
98
+ assert.deepStrictEqual(inspect(parser('[]{ b }')), [['<a href="b">b</a>'], '']);
96
99
  assert.deepStrictEqual(inspect(parser('[]{\\}')), [[`<a href="\\">\\</a>`], '']);
97
100
  assert.deepStrictEqual(inspect(parser('[]{\\ }')), [[`<a href="\\">\\</a>`], '']);
98
101
  assert.deepStrictEqual(inspect(parser('[]{\\b}')), [[`<a href="\\b">\\b</a>`], '']);
@@ -7,7 +7,7 @@ import { inline, media, shortmedia } from '../inline';
7
7
  import { attributes } from './html';
8
8
  import { autolink } from '../autolink';
9
9
  import { str } from '../source';
10
- import { startLoose, trimNode, stringify } from '../util';
10
+ import { startLoose, trimSpaceStart, trimNodeEnd, stringify } from '../util';
11
11
  import { html, define, defrag } from 'typed-dom';
12
12
  import { ReadonlyURL } from 'spica/url';
13
13
 
@@ -38,11 +38,11 @@ export const link: LinkParser = lazy(() => creator(10, validate(['[', '{'], '}',
38
38
  media: false,
39
39
  autolink: false,
40
40
  }}},
41
- some(inline, ']', /^\\?\n/)), ']'),
41
+ trimSpaceStart(some(inline, ']', /^\\?\n/)))),
42
42
  ']',
43
43
  true),
44
44
  ]))),
45
- dup(surround(/^{(?![{}])/, inits([uri, some(option)]), /^[^\S\n]?}/)),
45
+ dup(surround(/^{(?![{}])/, inits([uri, some(option)]), /^[^\S\n]*}/)),
46
46
  ]))),
47
47
  ([params, content = []]: [string[], (HTMLElement | string)[]], rest, context) => {
48
48
  assert(params.every(p => typeof p === 'string'));
@@ -53,7 +53,7 @@ export const link: LinkParser = lazy(() => creator(10, validate(['[', '{'], '}',
53
53
  assert(!INSECURE_URI.match(/\s/));
54
54
  const el = elem(
55
55
  INSECURE_URI,
56
- trimNode(defrag(content)),
56
+ trimNodeEnd(defrag(content)),
57
57
  new ReadonlyURL(
58
58
  resolve(INSECURE_URI, context.host ?? location, context.url ?? context.host ?? location),
59
59
  context.host?.href || location.href),
@@ -64,15 +64,14 @@ export const link: LinkParser = lazy(() => creator(10, validate(['[', '{'], '}',
64
64
  }))));
65
65
 
66
66
  export const uri: LinkParser.ParameterParser.UriParser = union([
67
- open(/^[^\S\n]/, str(/^\S+/)),
67
+ open(/^[^\S\n]+/, str(/^\S+/)),
68
68
  str(/^[^\s{}]+/),
69
69
  ]);
70
70
 
71
71
  export const option: LinkParser.ParameterParser.OptionParser = union([
72
72
  fmap(str(/^[^\S\n]+nofollow(?=[^\S\n]|})/), () => [` rel="nofollow"`]),
73
73
  str(/^[^\S\n]+[a-z]+(?:-[a-z]+)*(?:="(?:\\[^\n]|[^\\\n"])*")?(?=[^\S\n]|})/),
74
- fmap(str(/^[^\S\n]+(?=})/), () => []),
75
- fmap(str(/^[^\S\n]+[^\n{}]+/), opt => [` \\${opt.slice(1)}`]),
74
+ fmap(str(/^[^\S\n]+[^\s{}]+/), opt => [` \\${opt.slice(1)}`]),
76
75
  ]);
77
76
 
78
77
  export function resolve(uri: string, host: URL | Location, source: URL | Location): string {
@@ -104,7 +103,7 @@ function elem(
104
103
  origin: string,
105
104
  ): HTMLAnchorElement {
106
105
  let type: string;
107
- let description: string;
106
+ let message: string;
108
107
  switch (uri.protocol) {
109
108
  case 'http:':
110
109
  case 'https:':
@@ -112,7 +111,7 @@ function elem(
112
111
  if (INSECURE_URI.slice(0, 2) === '^/' &&
113
112
  /\/\.\.?(?:\/|$)/.test(INSECURE_URI.slice(0, INSECURE_URI.search(/[?#]|$/)))) {
114
113
  type = 'argument';
115
- description = 'Dot-segments cannot be used in subresource paths.';
114
+ message = 'Dot-segments cannot be used in subresource paths';
116
115
  break;
117
116
  }
118
117
  return html('a',
@@ -141,7 +140,7 @@ function elem(
141
140
  return html('a', { href: uri.source }, content);
142
141
  }
143
142
  type = 'content';
144
- description = 'Invalid phone number.';
143
+ message = 'Invalid phone number';
145
144
  break;
146
145
  }
147
146
  return html('a',
@@ -149,7 +148,7 @@ function elem(
149
148
  class: 'invalid',
150
149
  'data-invalid-syntax': 'link',
151
150
  'data-invalid-type': type ??= 'argument',
152
- 'data-invalid-description': description ??= 'Invalid protocol.',
151
+ 'data-invalid-message': message ??= 'Invalid protocol',
153
152
  },
154
153
  content.length === 0
155
154
  ? INSECURE_URI
@@ -2,14 +2,14 @@ import { MarkParser } from '../inline';
2
2
  import { union, some, creator, surround, open, lazy } from '../../combinator';
3
3
  import { inline } from '../inline';
4
4
  import { str } from '../source';
5
- import { startTight, delimiter } from '../util';
5
+ import { startTight, blank } from '../util';
6
6
  import { html, defrag } from 'typed-dom';
7
7
  import { unshift } from 'spica/array';
8
8
 
9
9
  export const mark: MarkParser = lazy(() => creator(surround(
10
10
  str('=='),
11
11
  startTight(some(union([
12
- some(inline, delimiter(/==/)),
12
+ some(inline, blank('', '==')),
13
13
  open(some(inline, '='), inline),
14
14
  ]))),
15
15
  str('=='), false,
@@ -29,7 +29,7 @@ export const math: MathParser = lazy(() => creator(validate('$', rewrite(
29
29
  translate: 'no',
30
30
  'data-invalid-syntax': 'math',
31
31
  'data-invalid-type': 'content',
32
- 'data-invalid-description': `"${source.match(disallowedCommand)![0]}" command is disallowed.`,
32
+ 'data-invalid-message': `"${source.match(disallowedCommand)![0]}" command is disallowed`,
33
33
  },
34
34
  source)
35
35
  ], '']))));
@@ -67,6 +67,9 @@ describe('Unit: parser/inline/media', () => {
67
67
  assert.deepStrictEqual(inspect(parser('![]{b }')), [['<a href="b" target="_blank"><img class="media" data-src="b" alt=""></a>'], '']);
68
68
  assert.deepStrictEqual(inspect(parser('![]{ b }')), [['<a href="b" target="_blank"><img class="media" data-src="b" alt=""></a>'], '']);
69
69
  assert.deepStrictEqual(inspect(parser('![]{ b }')), [['<a href="b" target="_blank"><img class="media" data-src="b" alt=""></a>'], '']);
70
+ assert.deepStrictEqual(inspect(parser('![]{ b }')), [['<a href="b" target="_blank"><img class="media" data-src="b" alt=""></a>'], '']);
71
+ assert.deepStrictEqual(inspect(parser('![]{ b }')), [['<a href="b" target="_blank"><img class="media" data-src="b" alt=""></a>'], '']);
72
+ assert.deepStrictEqual(inspect(parser('![]{ b }')), [['<a href="b" target="_blank"><img class="media" data-src="b" alt=""></a>'], '']);
70
73
  assert.deepStrictEqual(inspect(parser('![]{\\}')), [['<a href="\\" target="_blank"><img class="media" data-src="\\" alt=""></a>'], '']);
71
74
  assert.deepStrictEqual(inspect(parser('![]{\\ }')), [['<a href="\\" target="_blank"><img class="media" data-src="\\" alt=""></a>'], '']);
72
75
  assert.deepStrictEqual(inspect(parser('![]{\\b}')), [['<a href="\\b" target="_blank"><img class="media" data-src="\\b" alt=""></a>'], '']);
@@ -27,7 +27,7 @@ export const media: MediaParser = lazy(() => creator(10, validate(['![', '!{'],
27
27
  some(union([unsafehtmlentity, bracket, txt]), ']', /^\\?\n/),
28
28
  ']',
29
29
  true)),
30
- dup(surround(/^{(?![{}])/, inits([uri, some(option)]), /^[^\S\n]?}/)),
30
+ dup(surround(/^{(?![{}])/, inits([uri, some(option)]), /^[^\S\n]*}/)),
31
31
  ]))),
32
32
  ([as, bs]) => bs ? [[join(as).trim() || join(as)], bs] : [[''], as]),
33
33
  ([[text]]) => text === '' || text.trim() !== ''),
@@ -81,7 +81,7 @@ function sanitize(target: HTMLElement, uri: ReadonlyURL, alt: string): boolean {
81
81
  class: void target.classList.add('invalid'),
82
82
  'data-invalid-syntax': 'media',
83
83
  'data-invalid-type': 'argument',
84
- 'data-invalid-description': 'Dot-segments cannot be used in media paths; use subresource paths instead.',
84
+ 'data-invalid-message': 'Dot-segments cannot be used in media paths; use subresource paths instead',
85
85
  });
86
86
  return false;
87
87
  }
@@ -91,7 +91,7 @@ function sanitize(target: HTMLElement, uri: ReadonlyURL, alt: string): boolean {
91
91
  class: void target.classList.add('invalid'),
92
92
  'data-invalid-syntax': 'media',
93
93
  'data-invalid-type': 'argument',
94
- 'data-invalid-description': 'Invalid protocol.',
94
+ 'data-invalid-message': 'Invalid protocol',
95
95
  });
96
96
  return false;
97
97
  }
@@ -100,7 +100,7 @@ function sanitize(target: HTMLElement, uri: ReadonlyURL, alt: string): boolean {
100
100
  class: void target.classList.add('invalid'),
101
101
  'data-invalid-syntax': 'media',
102
102
  'data-invalid-type': 'content',
103
- 'data-invalid-description': `Cannot use invalid HTML entitiy "${alt.match(/&[0-9A-Za-z]+;/)![0]}".`,
103
+ 'data-invalid-message': `Cannot use invalid HTML entitiy "${alt.match(/&[0-9A-Za-z]+;/)![0]}"`,
104
104
  alt: target.getAttribute('alt')?.replace(/\0/g, ''),
105
105
  });
106
106
  return false;
@@ -3,7 +3,7 @@ import { ReferenceParser } from '../inline';
3
3
  import { union, subsequence, some, validate, verify, focus, guard, context, creator, surround, lazy, fmap } from '../../combinator';
4
4
  import { inline } from '../inline';
5
5
  import { str } from '../source';
6
- import { startLoose, isStartLoose, trimNode, stringify } from '../util';
6
+ import { startLoose, isStartLoose, trimSpaceStart, trimNodeEnd, stringify } from '../util';
7
7
  import { html, defrag } from 'typed-dom';
8
8
 
9
9
  export const reference: ReferenceParser = lazy(() => creator(validate('[[', ']]', '\n', fmap(surround(
@@ -22,16 +22,16 @@ export const reference: ReferenceParser = lazy(() => creator(validate('[[', ']]'
22
22
  }}, state: undefined },
23
23
  subsequence([
24
24
  abbr,
25
- focus('^', c => [['', c], '']),
26
- some(inline, ']', /^\\?\n/),
27
- ])), ']]')),
25
+ focus(/^\^[^\S\n]*/, source => [['', source], '']),
26
+ trimSpaceStart(some(inline, ']', /^\\?\n/)),
27
+ ])))),
28
28
  ']]'),
29
- ns => [html('sup', attributes(ns), trimNode(defrag(ns)))]))));
29
+ ns => [html('sup', attributes(ns), trimNodeEnd(defrag(ns)))]))));
30
30
 
31
31
  const abbr: ReferenceParser.AbbrParser = creator(fmap(verify(surround(
32
32
  '^',
33
33
  union([str(/^(?![0-9]+\s?[|\]])[0-9A-Za-z]+(?:(?:-|(?=\W)(?!'\d)'?(?!\.\d)\.?(?!,\S),? ?)[0-9A-Za-z]+)*(?:-|'?\.?,? ?)?/)]),
34
- /^\|?(?=]])|^\|[^\S\n]/),
34
+ /^\|?(?=]])|^\|[^\S\n]+/),
35
35
  (_, rest, context) => isStartLoose(rest, context)),
36
36
  ([source]) => [html('abbr', source)]));
37
37
 
@@ -46,7 +46,7 @@ function attributes(ns: (string | HTMLElement)[]): Record<string, string | undef
46
46
  class: 'invalid',
47
47
  'data-invalid-syntax': 'reference',
48
48
  'data-invalid-type': 'syntax',
49
- 'data-invalid-description': 'Invalid abbr.',
49
+ 'data-invalid-message': 'Invalid abbr',
50
50
  }
51
51
  : { class: 'reference' };
52
52
  }
@@ -83,13 +83,13 @@ function attributes(texts: string[], rubies: string[]): Record<string, string> {
83
83
  let attrs: Record<string, string> | undefined;
84
84
  for (const ss of [texts, rubies]) {
85
85
  for (let i = 0; i < ss.length; ++i) {
86
- if (!ss[i].includes('\0')) continue;
86
+ if (ss[i].indexOf('\0') === -1) continue;
87
87
  ss[i] = ss[i].replace(/\0/g, '');
88
88
  attrs ??= {
89
89
  class: 'invalid',
90
90
  'data-invalid-syntax': 'ruby',
91
91
  'data-invalid-type': ss === texts ? 'content' : 'argument',
92
- 'data-invalid-description': 'Invalid HTML entity.',
92
+ 'data-invalid-message': 'Invalid HTML entity',
93
93
  };
94
94
  }
95
95
  }
@@ -2,14 +2,14 @@ import { StrongParser } from '../inline';
2
2
  import { union, some, creator, surround, open, lazy } from '../../combinator';
3
3
  import { inline } from '../inline';
4
4
  import { str } from '../source';
5
- import { startTight, delimiter } from '../util';
5
+ import { startTight, blank } from '../util';
6
6
  import { html, defrag } from 'typed-dom';
7
7
  import { unshift } from 'spica/array';
8
8
 
9
9
  export const strong: StrongParser = lazy(() => creator(surround(
10
10
  str('**'),
11
11
  startTight(some(union([
12
- some(inline, delimiter(/\*\*/)),
12
+ some(inline, blank('', '**')),
13
13
  open(some(inline, '*'), inline),
14
14
  ])), '*'),
15
15
  str('**'), false,
@@ -117,9 +117,9 @@ describe('Unit: parser/inline', () => {
117
117
  assert.deepStrictEqual(inspect(parser('[[#a]]')), [['<sup class="reference"><a href="/hashtags/a" class="hashtag">#a</a></sup>'], '']);
118
118
  assert.deepStrictEqual(inspect(parser('[[$-1]]')), [['<sup class="reference"><a class="label" data-label="$-1">$-1</a></sup>'], '']);
119
119
  assert.deepStrictEqual(inspect(parser('[[#-1]]{b}')), [['<sup class="reference">#-1</sup>', '<a href="b">b</a>'], '']);
120
- assert.deepStrictEqual(inspect(parser('[[#-1]](b)')), [['<sup class="reference">#-1</sup>', '(b)'], '']);
120
+ assert.deepStrictEqual(inspect(parser('[[#-1]](b)')), [['<sup class="reference">#-1</sup>', '(', 'b', ')'], '']);
121
121
  assert.deepStrictEqual(inspect(parser('[[#-1]a]{b}')), [['<a href="b">[#-1]a</a>'], '']);
122
- assert.deepStrictEqual(inspect(parser('[[#-1]a](b)')), [['[', '<a class="index" href="#index:-1">-1</a>', 'a', ']', '(b)'], '']);
122
+ assert.deepStrictEqual(inspect(parser('[[#-1]a](b)')), [['[', '<a class="index" href="#index:-1">-1</a>', 'a', ']', '(', 'b', ')'], '']);
123
123
  assert.deepStrictEqual(inspect(parser('[#a]{b}')), [['<a class="index" href="#index:a">a</a>', '<a href="b">b</a>'], '']);
124
124
  assert.deepStrictEqual(inspect(parser('[@a]{b}')), [['[', '<a href="/@a" class="account">@a</a>', ']', '<a href="b">b</a>'], '']);
125
125
  assert.deepStrictEqual(inspect(parser('[http://host]{http://evil}')), [['[', '<a href="http://host" target="_blank">http://host</a>', ']', '<a href="http://evil" target="_blank">http://evil</a>'], '']);