securemark 0.294.3 → 0.294.5

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 (63) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/index.js +164 -144
  3. package/markdown.d.ts +3 -12
  4. package/package.json +3 -3
  5. package/src/combinator/control/manipulation/fence.ts +2 -0
  6. package/src/combinator/control/manipulation/indent.ts +1 -1
  7. package/src/combinator/control/manipulation/match.ts +11 -8
  8. package/src/parser/api/parse.test.ts +3 -3
  9. package/src/parser/block/blockquote.test.ts +3 -9
  10. package/src/parser/block/blockquote.ts +4 -4
  11. package/src/parser/block/dlist.ts +4 -4
  12. package/src/parser/block/extension/example.ts +1 -3
  13. package/src/parser/block/extension/fig.test.ts +0 -1
  14. package/src/parser/block/extension/fig.ts +6 -6
  15. package/src/parser/block/extension/figbase.ts +1 -1
  16. package/src/parser/block/extension/figure.test.ts +1 -1
  17. package/src/parser/block/extension/figure.ts +6 -6
  18. package/src/parser/block/extension/message.ts +1 -1
  19. package/src/parser/block/extension/table.ts +4 -4
  20. package/src/parser/block/heading.ts +4 -4
  21. package/src/parser/block/reply/cite.ts +2 -2
  22. package/src/parser/block/reply/quote.ts +2 -2
  23. package/src/parser/block/sidefence.test.ts +1 -3
  24. package/src/parser/block/sidefence.ts +4 -4
  25. package/src/parser/block/table.ts +2 -2
  26. package/src/parser/block.ts +1 -1
  27. package/src/parser/header.ts +3 -3
  28. package/src/parser/inline/autolink/account.test.ts +18 -17
  29. package/src/parser/inline/autolink/account.ts +14 -20
  30. package/src/parser/inline/autolink/anchor.test.ts +2 -1
  31. package/src/parser/inline/autolink/anchor.ts +10 -13
  32. package/src/parser/inline/autolink/channel.test.ts +6 -6
  33. package/src/parser/inline/autolink/channel.ts +28 -32
  34. package/src/parser/inline/autolink/email.test.ts +19 -19
  35. package/src/parser/inline/autolink/email.ts +7 -7
  36. package/src/parser/inline/autolink/hashnum.test.ts +20 -20
  37. package/src/parser/inline/autolink/hashnum.ts +6 -8
  38. package/src/parser/inline/autolink/hashtag.test.ts +27 -27
  39. package/src/parser/inline/autolink/hashtag.ts +15 -16
  40. package/src/parser/inline/autolink/url.test.ts +6 -6
  41. package/src/parser/inline/autolink/url.ts +5 -4
  42. package/src/parser/inline/autolink.ts +4 -5
  43. package/src/parser/inline/code.ts +12 -18
  44. package/src/parser/inline/deletion.test.ts +1 -1
  45. package/src/parser/inline/deletion.ts +3 -3
  46. package/src/parser/inline/emstrong.ts +3 -3
  47. package/src/parser/inline/extension/indexer.ts +1 -1
  48. package/src/parser/inline/html.test.ts +1 -0
  49. package/src/parser/inline/html.ts +1 -1
  50. package/src/parser/inline/insertion.test.ts +1 -1
  51. package/src/parser/inline/insertion.ts +3 -3
  52. package/src/parser/inline/italic.test.ts +2 -2
  53. package/src/parser/inline/italic.ts +3 -3
  54. package/src/parser/inline/link.test.ts +0 -1
  55. package/src/parser/inline/link.ts +3 -3
  56. package/src/parser/inline/mark.test.ts +1 -1
  57. package/src/parser/inline/mark.ts +3 -3
  58. package/src/parser/inline/remark.ts +3 -3
  59. package/src/parser/inline.test.ts +11 -11
  60. package/src/parser/inline.ts +2 -0
  61. package/src/parser/source/text.ts +8 -4
  62. package/src/parser/util.ts +1 -1
  63. package/src/parser/visibility.ts +4 -6
package/markdown.d.ts CHANGED
@@ -1096,13 +1096,10 @@ export namespace MarkdownParser {
1096
1096
  AutolinkParser.UrlParser.LineUrlParser,
1097
1097
  AutolinkParser.UrlParser,
1098
1098
  AutolinkParser.EmailParser,
1099
- SourceParser.StrParser,
1100
1099
  AutolinkParser.ChannelParser,
1101
1100
  AutolinkParser.AccountParser,
1102
- SourceParser.StrParser,
1103
1101
  AutolinkParser.HashtagParser,
1104
1102
  AutolinkParser.HashnumParser,
1105
- SourceParser.StrParser,
1106
1103
  AutolinkParser.AnchorParser,
1107
1104
  ]> {
1108
1105
  }
@@ -1110,9 +1107,9 @@ export namespace MarkdownParser {
1110
1107
  export interface UrlParser extends
1111
1108
  // https://host
1112
1109
  Inline<'url'>,
1113
- Parser<string | HTMLAnchorElement, Context, [
1110
+ Parser<string | HTMLElement, Context, [
1114
1111
  LinkParser.UnsafeLinkParser,
1115
- Parser<string, Context, []>,
1112
+ InlineParser,
1116
1113
  ]> {
1117
1114
  }
1118
1115
  export namespace UrlParser {
@@ -1122,7 +1119,7 @@ export namespace MarkdownParser {
1122
1119
  SourceParser.StrParser,
1123
1120
  Parser<string | HTMLElement, Context, [
1124
1121
  InlineParser.LinkParser.UnsafeLinkParser,
1125
- Parser<string, Context, []>,
1122
+ InlineParser,
1126
1123
  ]>,
1127
1124
  ]> {
1128
1125
  }
@@ -1150,7 +1147,6 @@ export namespace MarkdownParser {
1150
1147
  Inline<'email'>,
1151
1148
  Parser<string | HTMLAnchorElement, Context, [
1152
1149
  Parser<HTMLAnchorElement, Context, []>,
1153
- Parser<string, Context, []>,
1154
1150
  ]> {
1155
1151
  }
1156
1152
  export interface ChannelParser extends
@@ -1158,7 +1154,6 @@ export namespace MarkdownParser {
1158
1154
  Inline<'channel'>,
1159
1155
  Parser<string | HTMLAnchorElement, Context, [
1160
1156
  LinkParser.UnsafeLinkParser,
1161
- Parser<string, Context, []>,
1162
1157
  ]> {
1163
1158
  }
1164
1159
  export interface AccountParser extends
@@ -1166,7 +1161,6 @@ export namespace MarkdownParser {
1166
1161
  Inline<'account'>,
1167
1162
  Parser<string | HTMLAnchorElement, Context, [
1168
1163
  LinkParser.UnsafeLinkParser,
1169
- Parser<string, Context, []>,
1170
1164
  ]> {
1171
1165
  }
1172
1166
  export interface HashtagParser extends
@@ -1174,7 +1168,6 @@ export namespace MarkdownParser {
1174
1168
  Inline<'hashtag'>,
1175
1169
  Parser<string | HTMLAnchorElement, Context, [
1176
1170
  LinkParser.UnsafeLinkParser,
1177
- Parser<string, Context, []>,
1178
1171
  ]> {
1179
1172
  }
1180
1173
  export interface HashnumParser extends
@@ -1182,7 +1175,6 @@ export namespace MarkdownParser {
1182
1175
  Inline<'hashnum'>,
1183
1176
  Parser<string | HTMLAnchorElement, Context, [
1184
1177
  LinkParser.UnsafeLinkParser,
1185
- Parser<string, Context, []>,
1186
1178
  ]> {
1187
1179
  }
1188
1180
  export interface AnchorParser extends
@@ -1190,7 +1182,6 @@ export namespace MarkdownParser {
1190
1182
  Inline<'anchor'>,
1191
1183
  Parser<string | HTMLAnchorElement, Context, [
1192
1184
  LinkParser.UnsafeLinkParser,
1193
- Parser<string, Context, []>,
1194
1185
  ]> {
1195
1186
  }
1196
1187
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securemark",
3
- "version": "0.294.3",
3
+ "version": "0.294.5",
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",
@@ -28,7 +28,7 @@
28
28
  "LICENSE"
29
29
  ],
30
30
  "dependencies": {
31
- "spica": "0.0.809"
31
+ "spica": "0.0.810"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@types/dompurify": "3.0.5",
@@ -42,7 +42,7 @@
42
42
  "babel-plugin-unassert": "^3.2.0",
43
43
  "concurrently": "^8.2.2",
44
44
  "eslint": "^9.8.0",
45
- "eslint-plugin-redos": "^4.4.5",
45
+ "eslint-plugin-redos": "^4.5.0",
46
46
  "eslint-webpack-plugin": "^4.2.0",
47
47
  "glob": "^11.0.0",
48
48
  "karma": "^6.4.4",
@@ -1,4 +1,5 @@
1
1
  import { Parser, List, Data, Ctx, failsafe } from '../../data/parser';
2
+ import { consume } from '../../../combinator';
2
3
  import { firstline, isBlank } from '../constraint/line';
3
4
  import { push } from 'spica/array';
4
5
 
@@ -12,6 +13,7 @@ export function fence<C extends Ctx, D extends Parser<unknown, C>[]>(opener: Reg
12
13
  const matches = opener.exec(source);
13
14
  if (!matches) return;
14
15
  assert(matches[0] === firstline(source, position));
16
+ consume(matches[0].length, context);
15
17
  const delim = matches[1];
16
18
  assert(delim && delim === delim.trim());
17
19
  if (matches[0].includes(delim, delim.length)) return;
@@ -26,7 +26,7 @@ export function indent<N>(opener: RegExp | Parser<N>, parser: Parser<N> | boolea
26
26
  context.position = source.length;
27
27
  return new List([new Data(source.slice(position))]);
28
28
  }))),
29
- ([indent]) => indent.length <= 16 ? indent.length * 2 + +(indent[0] === ' ') : -1, [])), separation),
29
+ ([indent]) => indent.length * 2 + -(indent[0] === ' '), [], 2 ** 4 - 1)), separation),
30
30
  (lines, context) => {
31
31
  assert(parser = parser as Parser<N>);
32
32
  return parser(subinput(trimBlockEnd(lines.foldl((acc, node) => acc + node.value, '')), context));
@@ -1,20 +1,23 @@
1
1
  import { Parser, failsafe } from '../../data/parser';
2
2
  import { consume } from '../../../combinator';
3
3
 
4
- export function match<P extends Parser<unknown>>(pattern: RegExp, f: (matched: RegExpMatchArray) => P, cost?: boolean): P;
5
- export function match<N>(pattern: RegExp, f: (matched: RegExpMatchArray) => Parser<N>, cost = false): Parser<N> {
4
+ export function match<P extends Parser<unknown>>(pattern: RegExp, f: (matched: RegExpMatchArray) => P): P;
5
+ export function match<N>(pattern: RegExp, f: (matched: RegExpMatchArray) => Parser<N>): Parser<N> {
6
6
  assert(!pattern.flags.match(/[gm]/) && pattern.sticky && !pattern.source.startsWith('^'));
7
+ const count = typeof pattern === 'object'
8
+ ? /[^^\\*+][*+]/.test(pattern.source)
9
+ : false;
7
10
  return failsafe(input => {
8
11
  const { context } = input;
9
12
  const { source, position } = context;
10
13
  if (position === source.length) return;
11
14
  pattern.lastIndex = position;
12
- const param = pattern.exec(source);
13
- if (!param) return;
14
- assert(source.startsWith(param[0], position));
15
- cost && consume(param[0].length, context);
16
- const result = f(param)(input);
17
- context.position += result && context.position === position ? param[0].length : 0;
15
+ const params = pattern.exec(source);
16
+ if (!params) return;
17
+ assert(source.startsWith(params[0], position));
18
+ count && consume(params[0].length, context);
19
+ const result = f(params)(input);
20
+ context.position += result && context.position === position ? params[0].length : 0;
18
21
  assert(context.position > position || !result);
19
22
  return context.position > position
20
23
  ? result
@@ -361,9 +361,9 @@ describe('Unit: parser/api/parse', () => {
361
361
 
362
362
  it('backtrack', function () {
363
363
  this.timeout(5000);
364
- // 最悪計算量での実行速度はCommonMarkの公式JS実装の32nに対して3倍遅い程度。
364
+ // 最悪計算量での実行速度はCommonMarkの公式JS実装の32nより速い。
365
365
  // 5n = annotation/reference + link + url/math + ruby + text
366
- const source = `${'.'.repeat(0 + 0)}((([[[[#$[${'&'.repeat(19998)}`;
366
+ const source = `((([[[[#$[${'.'.repeat(19997)}`;
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(19997 + 1)}`;
376
376
  assert.deepStrictEqual(
377
377
  [...parse(source, {}, { resources: { clock: 100000, recursions: [100] } }).children]
378
378
  .map(el => el.tagName),
@@ -14,6 +14,9 @@ describe('Unit: parser/block/blockquote', () => {
14
14
  assert.deepStrictEqual(inspect(parser('>'), ctx), undefined);
15
15
  assert.deepStrictEqual(inspect(parser('>a'), ctx), undefined);
16
16
  assert.deepStrictEqual(inspect(parser('>\n'), ctx), undefined);
17
+ assert.deepStrictEqual(inspect(parser('>\na'), ctx), undefined);
18
+ assert.deepStrictEqual(inspect(parser('!>\n'), ctx), undefined);
19
+ assert.deepStrictEqual(inspect(parser('!>\na'), ctx), undefined);
17
20
  assert.deepStrictEqual(inspect(parser(' > '), ctx), undefined);
18
21
  assert.deepStrictEqual(inspect(parser('>>'), ctx), undefined);
19
22
  });
@@ -38,10 +41,6 @@ describe('Unit: parser/block/blockquote', () => {
38
41
  assert.deepStrictEqual(inspect(parser('> a\\\nb'), ctx), [['<blockquote><pre>a\\<br>b</pre></blockquote>'], '']);
39
42
  assert.deepStrictEqual(inspect(parser('> a '), ctx), [['<blockquote><pre> a </pre></blockquote>'], '']);
40
43
  assert.deepStrictEqual(inspect(parser('> \na'), ctx), [['<blockquote><pre><br>a</pre></blockquote>'], '']);
41
- assert.deepStrictEqual(inspect(parser('>\na'), ctx), [['<blockquote><pre><br>a</pre></blockquote>'], '']);
42
- assert.deepStrictEqual(inspect(parser('>\n a'), ctx), [['<blockquote><pre><br> a</pre></blockquote>'], '']);
43
- assert.deepStrictEqual(inspect(parser('>\n>'), ctx), [['<blockquote><pre><br></pre></blockquote>'], '']);
44
- assert.deepStrictEqual(inspect(parser('>\n> a'), ctx), [['<blockquote><pre><br>a</pre></blockquote>'], '']);
45
44
  assert.deepStrictEqual(inspect(parser('> http://host'), ctx), [['<blockquote><pre><a class="url" href="http://host" target="_blank">http://host</a></pre></blockquote>'], '']);
46
45
  assert.deepStrictEqual(inspect(parser('> http://host)'), ctx), [['<blockquote><pre><a class="url" href="http://host)" target="_blank">http://host)</a></pre></blockquote>'], '']);
47
46
  assert.deepStrictEqual(inspect(parser('> !http://host'), ctx), [['<blockquote><pre>!<a class="url" href="http://host" target="_blank">http://host</a></pre></blockquote>'], '']);
@@ -92,11 +91,6 @@ describe('Unit: parser/block/blockquote', () => {
92
91
  assert.deepStrictEqual(inspect(parser('!>> > a\n> b'), ctx), [['<blockquote><blockquote><section><blockquote><pre>a</pre></blockquote><h2>References</h2><ol class="references"></ol></section></blockquote><section><p>b</p><h2>References</h2><ol class="references"></ol></section></blockquote>'], '']);
93
92
  assert.deepStrictEqual(inspect(parser('!> !> a'), ctx), [['<blockquote><section><blockquote><section><p>a</p><h2>References</h2><ol class="references"></ol></section></blockquote><h2>References</h2><ol class="references"></ol></section></blockquote>'], '']);
94
93
  assert.deepStrictEqual(inspect(parser('!> \na'), ctx), [['<blockquote><section><p>a</p><h2>References</h2><ol class="references"></ol></section></blockquote>'], '']);
95
- assert.deepStrictEqual(inspect(parser('!>\n'), ctx), undefined);
96
- assert.deepStrictEqual(inspect(parser('!>\na'), ctx), [['<blockquote><section><p>a</p><h2>References</h2><ol class="references"></ol></section></blockquote>'], '']);
97
- assert.deepStrictEqual(inspect(parser('!>\n a'), ctx), [['<blockquote><section><p> a</p><h2>References</h2><ol class="references"></ol></section></blockquote>'], '']);
98
- assert.deepStrictEqual(inspect(parser('!>\n>'), ctx), [['<blockquote><section><h2>References</h2><ol class="references"></ol></section></blockquote>'], '']);
99
- assert.deepStrictEqual(inspect(parser('!>\n> a'), ctx), [['<blockquote><section><p>a</p><h2>References</h2><ol class="references"></ol></section></blockquote>'], '']);
100
94
  assert.deepStrictEqual(inspect(parser('!>> ## a\n> ## a'), ctx), [['<blockquote><blockquote><section><h2>a</h2><h2>References</h2><ol class="references"></ol></section></blockquote><section><h2>a</h2><h2>References</h2><ol class="references"></ol></section></blockquote>'], '']);
101
95
  assert.deepStrictEqual(inspect(parser('!>> ~ a\n> ~ a'), ctx), [['<blockquote><blockquote><section><dl><dt>a</dt><dd></dd></dl><h2>References</h2><ol class="references"></ol></section></blockquote><section><dl><dt>a</dt><dd></dd></dl><h2>References</h2><ol class="references"></ol></section></blockquote>'], '']);
102
96
  assert.deepStrictEqual(inspect(parser('!>> ~~~figure $test-a\n>> > \n>>\n~~~\n> ~~~figure $test-a\n> > \n>\n[#a]\n~~~'), ctx), [['<blockquote><blockquote><section><figure data-type="quote" data-label="test-a" data-group="test" data-number="1"><figcaption><span class="figindex">Test 1. </span><span class="figtext"></span></figcaption><div><blockquote></blockquote></div></figure><h2>References</h2><ol class="references"></ol></section></blockquote><section><figure data-type="quote" data-label="test-a" data-group="test" data-number="1"><figcaption><span class="figindex">Test 1. </span><span class="figtext"><a class="index">a</a></span></figcaption><div><blockquote></blockquote></div></figure><h2>References</h2><ol class="references"></ol></section></blockquote>'], '']);
@@ -9,7 +9,7 @@ import { parse } from '../api/parse';
9
9
  import { html, defrag } from 'typed-dom/dom';
10
10
 
11
11
  export const segment: BlockquoteParser.SegmentParser = block(union([
12
- validate(/!?>+(?=[^\S\n]|\n[^\S\n]*\S)/y, some(contentline)),
12
+ validate(/!?>+ /y, some(contentline)),
13
13
  ]));
14
14
 
15
15
  export const blockquote: BlockquoteParser = lazy(() => block(rewrite(segment, union([
@@ -17,9 +17,9 @@ export const blockquote: BlockquoteParser = lazy(() => block(rewrite(segment, un
17
17
  open(/!(?=>)/y, markdown),
18
18
  ]))));
19
19
 
20
- const opener = /(?=>>+(?:$|\s))/y;
21
- const indent = block(open(opener, some(contentline, />(?:$|\s)/y)), false);
22
- const unindent = (source: string) => source.replace(/(?<=^|\n)>(?:[^\S\n]|(?=>*(?:$|\s)))|\n$/g, '');
20
+ const opener = /(?=>>+(?:$|[ \n]))/y;
21
+ const indent = block(open(opener, some(contentline, />(?:$|[ \n])/y)), false);
22
+ const unindent = (source: string) => source.replace(/(?<=^|\n)>(?: |(?=>*(?:$|[ \n])))|\n$/g, '');
23
23
 
24
24
  const source: BlockquoteParser.SourceParser = lazy(() => fmap(
25
25
  some(recursion(Recursion.blockquote, union([
@@ -9,7 +9,7 @@ import { unwrap } from '../util';
9
9
  import { html, defrag } from 'typed-dom/dom';
10
10
 
11
11
  export const dlist: DListParser = lazy(() => block(fmap(validate(
12
- /~[^\S\n]+(?=\S)/y,
12
+ /~ +(?=\S)/y,
13
13
  some(inits([
14
14
  state(State.annotation | State.reference | State.index | State.label | State.link,
15
15
  some(term)),
@@ -18,15 +18,15 @@ export const dlist: DListParser = lazy(() => block(fmap(validate(
18
18
  ns => new List([new Data(html('dl', unwrap(fillTrailingDescription(ns))))]))));
19
19
 
20
20
  const term: DListParser.TermParser = line(indexee(fmap(open(
21
- /~[^\S\n]+(?=\S)/y,
21
+ /~ +(?=\S)/y,
22
22
  visualize(trimBlank(some(union([indexer, inline])))),
23
23
  true),
24
24
  ns => new List([new Data(html('dt', { 'data-index': dataindex(ns) }, defrag(unwrap(ns))))]))));
25
25
 
26
26
  const desc: DListParser.DescriptionParser = block(fmap(open(
27
- /:[^\S\n]+(?=\S)|/y,
27
+ /: +(?=\S)|/y,
28
28
  rewrite(
29
- some(anyline, /[~:][^\S\n]+\S/y),
29
+ some(anyline, /[~:] +(?=\S)/y),
30
30
  visualize(trimBlankEnd(some(union([inline]))))),
31
31
  true),
32
32
  ns => new List([new Data(html('dd', defrag(unwrap(ns))))])),
@@ -7,10 +7,8 @@ import { unwrap, invalid } from '../../util';
7
7
  import { parse } from '../../api/parse';
8
8
  import { html } from 'typed-dom/dom';
9
9
 
10
- const opener = /(~{3,})(?:example\/(\S+))?(?!\S)([^\n]*)(?:$|\n)/y;
11
-
12
10
  export const example: ExtensionParser.ExampleParser = recursion(Recursion.block, block(fmap(
13
- fence(opener, 300),
11
+ fence(/(~{3,})(?:example\/(\S+))?(?!\S)([^\n]*)(?:$|\n)/y, 300),
14
12
  // Bug: Type mismatch between outer and inner.
15
13
  (nodes: List<Data<string>>, context) => {
16
14
  const [body, overflow, closer, opener, delim, type = 'markdown', param] = unwrap(nodes);
@@ -40,7 +40,6 @@ describe('Unit: parser/block/extension/fig', () => {
40
40
  assert.deepStrictEqual(inspect(parser('[$group-name]\n~~~table\n~~~\n'), ctx), [['<figure data-type="table" data-label="group-name" data-group="group"><figcaption><span class="figindex"></span><span class="figtext"></span></figcaption><div><table></table></div></figure>'], '']);
41
41
  assert.deepStrictEqual(inspect(parser('[$group-name]\n> '), ctx), [['<figure data-type="quote" data-label="group-name" data-group="group"><figcaption><span class="figindex"></span><span class="figtext"></span></figcaption><div><blockquote></blockquote></div></figure>'], '']);
42
42
  assert.deepStrictEqual(inspect(parser('[$group-name]\n> \n'), ctx), [['<figure data-type="quote" data-label="group-name" data-group="group"><figcaption><span class="figindex"></span><span class="figtext"></span></figcaption><div><blockquote></blockquote></div></figure>'], '']);
43
- assert.deepStrictEqual(inspect(parser('[$group-name]\n>\n~~~'), ctx), [['<figure data-type="quote" data-label="group-name" data-group="group"><figcaption><span class="figindex"></span><span class="figtext"></span></figcaption><div><blockquote><pre><br>~~~</pre></blockquote></div></figure>'], '']);
44
43
  assert.deepStrictEqual(inspect(parser('[$group-name]\n!> *a*'), ctx), [['<figure data-type="quote" data-label="group-name" data-group="group"><figcaption><span class="figindex"></span><span class="figtext"></span></figcaption><div><blockquote><section><p><em>a</em></p><h2>References</h2><ol class="references"></ol></section></blockquote></div></figure>'], '']);
45
44
  assert.deepStrictEqual(inspect(parser('[$group-name]\n![]{https://host}'), ctx), [['<figure data-type="media" data-label="group-name" data-group="group"><figcaption><span class="figindex"></span><span class="figtext"></span></figcaption><div><a href="https://host" target="_blank"><img class="media" data-src="https://host" alt="https://host"></a></div></figure>'], '']);
46
45
  assert.deepStrictEqual(inspect(parser('[$group-name]\n![]{https://host}\n'), ctx), [['<figure data-type="media" data-label="group-name" data-group="group"><figcaption><span class="figindex"></span><span class="figtext"></span></figcaption><div><a href="https://host" target="_blank"><img class="media" data-src="https://host" alt="https://host"></a></div></figure>'], '']);
@@ -1,5 +1,5 @@
1
1
  import { ExtensionParser } from '../../block';
2
- import { union, sequence, some, block, line, validate, verify, rewrite, close, convert } from '../../../combinator';
2
+ import { union, sequence, some, block, line, verify, rewrite, close, convert } from '../../../combinator';
3
3
  import { contentline } from '../../source';
4
4
  import { figure } from './figure';
5
5
  import { segment as seg_label } from '../../inline/extension/label';
@@ -12,9 +12,9 @@ import { media, lineshortmedia } from '../../inline';
12
12
 
13
13
  import FigParser = ExtensionParser.FigParser;
14
14
 
15
- export const segment: FigParser.SegmentParser = block(validate(/\[?\$/y,
15
+ export const segment: FigParser.SegmentParser = block(
16
16
  sequence([
17
- line(close(seg_label, /(?=\s).*\n/y)),
17
+ line(close(seg_label, /(?!\S).*\n/y)),
18
18
  union([
19
19
  seg_code,
20
20
  seg_math,
@@ -23,12 +23,12 @@ export const segment: FigParser.SegmentParser = block(validate(/\[?\$/y,
23
23
  seg_placeholder,
24
24
  some(contentline),
25
25
  ]),
26
- ])));
26
+ ]));
27
27
 
28
28
  export const fig: FigParser = block(rewrite(segment, verify(convert(
29
29
  (source, context) => {
30
30
  // Bug: TypeScript
31
- const fence = (/^[^\n]*\n!?>+\s/.test(source) && source.match(/^~{3,}(?=[^\S\n]*$)/mg) as string[] || [])
31
+ const fence = (/^[^\n]*\n!?>+ /.test(source) && source.match(/^~{3,}(?=[^\S\n]*$)/mg) as string[] || [])
32
32
  .reduce((max, fence) => fence > max ? fence : max, '~~') + '~';
33
33
  return parser({ context })
34
34
  ? `${fence}figure ${source.replace(/^(.+\n.+\n)([\S\s]+?)\n?$/, '$1\n$2')}\n${fence}`
@@ -39,7 +39,7 @@ export const fig: FigParser = block(rewrite(segment, verify(convert(
39
39
  ([{ value: el }]) => el.tagName === 'FIGURE')));
40
40
 
41
41
  const parser = sequence([
42
- line(close(seg_label, /(?=\s).*\n/y)),
42
+ line(close(seg_label, /(?!\S).*\n/y)),
43
43
  line(union([
44
44
  media,
45
45
  lineshortmedia,
@@ -5,7 +5,7 @@ import { label } from '../../inline/extension/label';
5
5
  import { html } from 'typed-dom/dom';
6
6
 
7
7
  export const figbase: ExtensionParser.FigbaseParser = block(fmap(
8
- validate(/\[?\$-(?:[0-9]+\.)*0\]?[^\S\n]*(?!\S|\n[^\S\n]*\S)/y,
8
+ validate(/\[?\$-(?:[0-9]+\.)*0\]?(?:$|[ \n])/y,
9
9
  line(union([label]))),
10
10
  ([{ value: el }]) => {
11
11
  const label = el.getAttribute('data-label')!;
@@ -71,7 +71,7 @@ describe('Unit: parser/block/extension/figure', () => {
71
71
  assert.deepStrictEqual(inspect(parser('~~~figure [$-0.0]\n$$\n\n$$\n~~~'), ctx), [['<figure data-type="math" data-label="$-0.0" data-group="$" class="invalid"><figcaption><span class="figindex"></span><span class="figtext"></span></figcaption><div><div class="math" translate="no">$$\n\n$$</div></div></figure>'], '']);
72
72
  assert.deepStrictEqual(inspect(parser('~~~figure [$-name]\n!https://host\n~~~'), ctx), [['<figure data-type="media" data-label="$-name" data-group="$" class="invalid"><figcaption><span class="figindex"></span><span class="figtext"></span></figcaption><div><a href="https://host" target="_blank"><img class="media" data-src="https://host" alt="https://host"></a></div></figure>'], '']);
73
73
  assert.deepStrictEqual(inspect(parser('~~~figure [$-name]\n$$\n\n$$\n\ncaption\n~~~'), ctx), [['<figure data-type="math" data-label="$-name" data-group="$" class="invalid"><figcaption><span class="figindex"></span><span class="figtext">caption</span></figcaption><div><div class="math" translate="no">$$\n\n$$</div></div></figure>'], '']);
74
- assert.deepStrictEqual(inspect(parser(`~~~figure [$group-name]\n${'>\n'.repeat(500)}\n~~~`), ctx, '>'), [['<figure data-type="quote" data-label="group-name" data-group="group">'], '']);
74
+ assert.deepStrictEqual(inspect(parser(`~~~figure [$group-name]\n${'> \n'.repeat(500)}\n~~~`), ctx, '>'), [['<figure data-type="quote" data-label="group-name" data-group="group">'], '']);
75
75
  assert.deepStrictEqual(inspect(parser(`~~~figure [$group-name]\n~~~\n0${'\n'.repeat(300)}~~~\n~~~`), ctx, '>'), [['<figure data-type="example" data-label="group-name" data-group="group">'], '']);
76
76
  });
77
77
 
@@ -21,7 +21,7 @@ import { html, defrag } from 'typed-dom/dom';
21
21
  import FigureParser = ExtensionParser.FigureParser;
22
22
 
23
23
  export const segment: FigureParser.SegmentParser = block(match(
24
- /(~{3,})(?:figure[^\S\n])?(?=\[?\$)/y,
24
+ /(~{3,})(?:figure )?(?=\[?\$)/y,
25
25
  memoize(
26
26
  ([, fence], closer = new RegExp(String.raw`${fence}[^\S\n]*(?:$|\n)`, 'y')) => close(
27
27
  sequence([
@@ -44,12 +44,12 @@ export const segment: FigureParser.SegmentParser = block(match(
44
44
  ]),
45
45
  ]),
46
46
  closer),
47
- ([, fence]) => fence.length <= 16 ? fence.length : -1, [])));
47
+ ([, fence]) => fence.length - 1, [], 2 ** 4 - 1)));
48
48
 
49
49
  export const figure: FigureParser = block(fallback(rewrite(segment, fmap(
50
50
  convert(source => source.slice(source.match(/^~+(?:\w+\s+)?/)![0].length, source.trimEnd().lastIndexOf('\n')),
51
51
  sequence([
52
- line(sequence([label, str(/(?=\s).*\n/y)])),
52
+ line(sequence([label, str(/(?!\S).*\n/y)])),
53
53
  inits([
54
54
  block(union([
55
55
  ulist,
@@ -83,7 +83,7 @@ export const figure: FigureParser = block(fallback(rewrite(segment, fmap(
83
83
  ]);
84
84
  })),
85
85
  fmap(
86
- fence(/(~{3,})(?:figure|\[?\$\S*)(?!\S)[^\n]*(?:$|\n)/y, 300),
86
+ fence(/(~{3,})(?:figure(?=$|[ \n])|\[?\$)[^\n]*(?:$|\n)/y, 300),
87
87
  (nodes, context) => {
88
88
  const [body, overflow, closer, opener, delim] = unwrap<string>(nodes);
89
89
  const violation =
@@ -95,11 +95,11 @@ export const figure: FigureParser = block(fallback(rewrite(segment, fmap(
95
95
  'fence',
96
96
  `Invalid trailing line after the closing delimiter "${delim}"`,
97
97
  ] ||
98
- !seg_label(subinput(opener.match(/^~+(?:figure[^\S\n]+)?(\[?\$\S+)/)?.[1] ?? '', context)) && [
98
+ !seg_label(subinput(opener.match(/^~+(?:figure )?(\[?\$\S+)/)?.[1] ?? '', context)) && [
99
99
  'label',
100
100
  'Invalid label',
101
101
  ] ||
102
- /^~+(?:figure[^\S\n]+)?(\[?\$\S+)[^\S\n]+\S/.test(opener) && [
102
+ /^~+(?:figure )?(\[?\$\S+)[^\S\n]+\S/.test(opener) && [
103
103
  'argument',
104
104
  'Invalid argument',
105
105
  ] ||
@@ -20,7 +20,7 @@ import { html } from 'typed-dom/dom';
20
20
  import MessageParser = ExtensionParser.MessageParser;
21
21
 
22
22
  export const message: MessageParser = block(fmap(
23
- fence(/(~{3,})message\/(\S+)([^\n]*)(?:$|\n)/y, 300),
23
+ fence(/(~{3,})message\/(\S+)(?!\S)([^\n]*)(?:$|\n)/y, 300),
24
24
  // Bug: Type mismatch between outer and inner.
25
25
  (nodes: List<Data<string>>, context) => {
26
26
  const [body, overflow, closer, opener, delim, type, param] = unwrap(nodes);
@@ -83,10 +83,10 @@ const align: AlignParser = line(fmap(
83
83
  union([str(alignment)]),
84
84
  ([{ value }]) => new List([new Data(value.split('/').map(s => s.split('')) as [string[], string[]?])])));
85
85
 
86
- const delimiter = /[-=<>]+(?:\/[-=^v]*)?(?=[^\S\n]*\n)|[#:](?:(?!:\D|0)\d*:(?!0)\d*)?(?:!+[+]?)?(?=\s)/y;
86
+ const delimiter = /[-=<>]+(?:\/[-=^v]*)?(?=[^\S\n]*\n)|[#:](?:(?!:\D|0)\d*:(?!0)\d*)?(?:!+[+]?)?(?=[ \n])/y;
87
87
 
88
88
  const head: CellParser.HeadParser = block(fmap(open(
89
- str(/#(?:(?!:\D|0)\d*:(?!0)\d*)?(?:!+[+]?)?(?=\s)/y),
89
+ str(/#(?:(?!:\D|0)\d*:(?!0)\d*)?(?:!+[+]?)?(?=[ \n])/y),
90
90
  rewrite(
91
91
  inits([
92
92
  anyline,
@@ -108,7 +108,7 @@ const head: CellParser.HeadParser = block(fmap(open(
108
108
  false);
109
109
 
110
110
  const data: CellParser.DataParser = block(fmap(open(
111
- str(/:(?:(?!:\D|0)\d*:(?!0)\d*)?(?:!+[+]?)?(?=\s)/y),
111
+ str(/:(?:(?!:\D|0)\d*:(?!0)\d*)?(?:!+[+]?)?(?=[ \n])/y),
112
112
  rewrite(
113
113
  inits([
114
114
  anyline,
@@ -133,7 +133,7 @@ const dataline: CellParser.DatalineParser = line(
133
133
  rewrite(
134
134
  contentline,
135
135
  union([
136
- validate(/!+\s/y, convert(source => `:${source}`, data, false)),
136
+ validate(/!+ /y, convert(source => `:${source}`, data, false)),
137
137
  convert(source => `: ${source}`, data, false),
138
138
  ])));
139
139
 
@@ -1,16 +1,16 @@
1
1
  import { HeadingParser } from '../block';
2
2
  import { State } from '../context';
3
3
  import { List, Data } from '../../combinator/data/parser';
4
- import { union, some, state, block, line, validate, focus, rewrite, open, fmap } from '../../combinator';
4
+ import { union, some, state, block, line, focus, rewrite, open, fmap } from '../../combinator';
5
5
  import { inline, indexee, indexer, dataindex } from '../inline';
6
6
  import { str } from '../source';
7
7
  import { visualize, trimBlank } from '../visibility';
8
8
  import { unwrap, invalid } from '../util';
9
9
  import { html, defrag } from 'typed-dom/dom';
10
10
 
11
- export const segment: HeadingParser.SegmentParser = block(validate('#', focus(
12
- /#+[^\S\n]+\S[^\n]*(?:\n#+(?!\S)[^\n]*)*(?:$|\n)/y,
13
- some(line(({ context: { source } }) => new List([new Data(source)]))))));
11
+ export const segment: HeadingParser.SegmentParser = block(focus(
12
+ /#+ +\S[^\n]*(?:\n#+(?=$|[ \n])[^\n]*)*(?:$|\n)/y,
13
+ some(line(({ context: { source } }) => new List([new Data(source)])))));
14
14
 
15
15
  export const heading: HeadingParser = block(rewrite(segment,
16
16
  // その他の表示制御は各所のCSSで行う。
@@ -12,12 +12,12 @@ export const cite: ReplyParser.CiteParser = line(fmap(
12
12
  open(
13
13
  str(syntax),
14
14
  union([
15
- anchor,
15
+ line(anchor),
16
16
  // Subject page representation.
17
17
  // リンクの実装は後で検討
18
18
  focus(/>>#\S*(?=\s*$)/y, ({ context: { source } }) => new List([new Data(html('a', { class: 'anchor' }, source))])),
19
19
  focus(/>>https?:\/\/\S+(?=\s*$)/y, ({ context: { source } }) => new List([new Data(html('a', { class: 'anchor', href: source.slice(2).trimEnd(), target: '_blank' }, source))])),
20
- focus(/>>.+(?=\s*$)/y, ({ context: { source } }) => new List([new Data(source)])),
20
+ focus(/>>\S+(?=\s*$)/y, ({ context: { source } }) => new List([new Data(source)])),
21
21
  ])),
22
22
  nodes => {
23
23
  const quotes = nodes.head!.value as string;
@@ -7,14 +7,14 @@ import { linebreak, unescsource, anyline } from '../../source';
7
7
  import { unwrap } from '../../util';
8
8
  import { html, defrag } from 'typed-dom/dom';
9
9
 
10
- export const syntax = />+[^\S\n]/y;
10
+ export const syntax = />+ /y;
11
11
 
12
12
  export const quote: ReplyParser.QuoteParser = lazy(() => block(fmap(
13
13
  rewrite(
14
14
  some(validate(syntax, anyline)),
15
15
  convert(
16
16
  // TODO: インデント数を渡してインデント数前の行頭確認を行う実装に置き換える
17
- source => source.replace(/(?<=^>+[^\S\n])/mg, '\r'),
17
+ source => source.replace(/(?<=^>+ )/mg, '\r'),
18
18
  some(union([
19
19
  // quote補助関数が残した数式をパースする。
20
20
  math,
@@ -39,9 +39,7 @@ describe('Unit: parser/block/sidefence', () => {
39
39
  assert.deepStrictEqual(inspect(parser('| a '), ctx), [['<blockquote class="invalid"><pre> a </pre></blockquote>'], '']);
40
40
  assert.deepStrictEqual(inspect(parser('| \na'), ctx), undefined);
41
41
  assert.deepStrictEqual(inspect(parser('|\na'), ctx), undefined);
42
- assert.deepStrictEqual(inspect(parser('|\n a'), ctx), undefined);
43
- assert.deepStrictEqual(inspect(parser('|\n|'), ctx), [['<blockquote class="invalid"><pre><br></pre></blockquote>'], '']);
44
- assert.deepStrictEqual(inspect(parser('|\n| a'), ctx), [['<blockquote class="invalid"><pre><br>a</pre></blockquote>'], '']);
42
+ assert.deepStrictEqual(inspect(parser('|\n| a'), ctx), undefined);
45
43
  assert.deepStrictEqual(inspect(parser('| http://host'), ctx), [['<blockquote class="invalid"><pre><a class="url" href="http://host" target="_blank">http://host</a></pre></blockquote>'], '']);
46
44
  assert.deepStrictEqual(inspect(parser('| http://host)'), ctx), [['<blockquote class="invalid"><pre><a class="url" href="http://host)" target="_blank">http://host)</a></pre></blockquote>'], '']);
47
45
  assert.deepStrictEqual(inspect(parser('| !http://host'), ctx), [['<blockquote class="invalid"><pre>!<a class="url" href="http://host" target="_blank">http://host</a></pre></blockquote>'], '']);
@@ -8,7 +8,7 @@ import { unwrap, invalid } from '../util';
8
8
  import { html, define, defrag } from 'typed-dom/dom';
9
9
 
10
10
  export const sidefence: SidefenceParser = lazy(() => block(fmap(focus(
11
- /(?=\|+(?:[^\S\n]|\n\|))(?:\|+(?:[^\S\n][^\n]*)?(?:$|\n))+$/y,
11
+ /\|+ [^\n]*(?:\n\|+(?=$|[ \n])[^\n]*)*(?:$|\n)/y,
12
12
  union([source])),
13
13
  ([{ value }]) => new List([
14
14
  new Data(define(value, {
@@ -17,13 +17,13 @@ export const sidefence: SidefenceParser = lazy(() => block(fmap(focus(
17
17
  })),
18
18
  ]))));
19
19
 
20
- const opener = /(?=\|\|+(?:$|\s))/y;
21
- const unindent = (source: string) => source.replace(/(?<=^|\n)\|(?:[^\S\n]|(?=\|*(?:$|\s)))|\n$/g, '');
20
+ const opener = /(?=\|\|+(?:$|[ \n]))/y;
21
+ const unindent = (source: string) => source.replace(/(?<=^|\n)\|(?: |(?=\|*(?:$|[ \n])))|\n$/g, '');
22
22
 
23
23
  const source: SidefenceParser.SourceParser = lazy(() => fmap(
24
24
  some(recursion(Recursion.block, union([
25
25
  focus(
26
- /(?:\|\|+(?:[^\S\n][^\n]*)?(?:$|\n))+/y,
26
+ /(?:\|\|+(?=$|[ \n])[^\n]*(?:$|\n))+/y,
27
27
  convert(unindent, source, false, true)),
28
28
  rewrite(
29
29
  some(contentline, opener),
@@ -28,7 +28,7 @@ export const table: TableParser = lazy(() => block(fmap(validate(
28
28
  ]))));
29
29
 
30
30
  const row = <P extends CellParser | AlignParser>(parser: P, optional: boolean): RowParser<P> => fallback(fmap(
31
- line(surround(/(?=\|)/y, some(union([parser])), /[|\\]?\s*$/y, optional)),
31
+ line(surround(/(?=\|)/y, some(union([parser])), /\|?\s*$/y, optional)),
32
32
  ns => new List([new Data(html('tr', unwrap(ns)))])),
33
33
  rewrite(contentline, ({ context: { source } }) => new List([
34
34
  new Data(html('tr', {
@@ -53,7 +53,7 @@ const cell: CellParser = surround(
53
53
  close(medialink, /\s*(?=\||$)/y),
54
54
  close(media, /\s*(?=\||$)/y),
55
55
  close(shortmedia, /\s*(?=\||$)/y),
56
- trimBlank(some(inline, /\|/y, [[/[|\\]?\s*$/y, 9]])),
56
+ trimBlank(some(inline, /\|/y, [[/\|?\s*$/y, 9]])),
57
57
  ]),
58
58
  /[^|]*/y, true);
59
59
 
@@ -114,7 +114,7 @@ export const block: BlockParser = reset(
114
114
  ]) as any));
115
115
 
116
116
  function error(parser: BlockParser): BlockParser {
117
- const reg = new RegExp(String.raw`^${Command.Error}.*\n`)
117
+ const reg = new RegExp(String.raw`^${Command.Error}[^\n]*\n`)
118
118
  return recover<BlockParser>(fallback(
119
119
  open(Command.Error, ({ context: { source, position } }) => { throw new Error(source.slice(position).split('\n', 1)[0]); }),
120
120
  parser),
@@ -8,7 +8,7 @@ import { normalize } from './api/normalize';
8
8
  import { html, defrag } from 'typed-dom/dom';
9
9
 
10
10
  export const header: MarkdownParser.HeaderParser = lazy(() => validate(
11
- /---+[^\S\v\f\r\n]*\r?\n(?=\S)/y,
11
+ /---+ *\r?\n(?=\S)/y,
12
12
  inits([
13
13
  rewrite(
14
14
  ({ context }) => {
@@ -24,7 +24,7 @@ export const header: MarkdownParser.HeaderParser = lazy(() => validate(
24
24
  block(
25
25
  union([
26
26
  validate(({ context }) => context.header ?? true,
27
- focus(/(---+)[^\S\v\f\r\n]*\r?\n(?:[A-Za-z][0-9A-Za-z]*(?:-[A-Za-z][0-9A-Za-z]*)*:[ \t]+\S[^\v\f\r\n]*\r?\n){1,100}\1[^\S\v\f\r\n]*(?:$|\r?\n)/y,
27
+ focus(/(---+) *\r?\n(?:[A-Za-z][0-9A-Za-z]*(?:-[0-9A-Za-z]+)*:[ \t]+\S[^\r\n]*\r?\n){1,100}\1 *(?:$|\r?\n)/y,
28
28
  convert(source =>
29
29
  normalize(source.slice(source.indexOf('\n') + 1, source.trimEnd().lastIndexOf('\n'))).replace(/(\S)\s+$/mg, '$1'),
30
30
  fmap(
@@ -48,7 +48,7 @@ export const header: MarkdownParser.HeaderParser = lazy(() => validate(
48
48
  ]);
49
49
  },
50
50
  ]))),
51
- clear(str(/[^\S\v\f\r\n]*\r?\n/y)),
51
+ clear(str(/ *\r?\n/y)),
52
52
  ])));
53
53
 
54
54
  const field: MarkdownParser.HeaderParser.FieldParser = line(({ context: { source, position } }) => {