securemark 0.298.7 → 0.299.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 (47) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/dist/index.js +128 -359
  3. package/package.json +1 -1
  4. package/src/combinator/control/constraint/block.ts +8 -5
  5. package/src/combinator/control/constraint/line.ts +13 -7
  6. package/src/combinator/control/manipulation/convert.ts +8 -11
  7. package/src/combinator/control/manipulation/match.ts +8 -4
  8. package/src/combinator/control/manipulation/scope.ts +6 -4
  9. package/src/combinator/control/monad/bind.ts +14 -6
  10. package/src/combinator/data/parser/context.ts +1 -74
  11. package/src/combinator/data/parser.ts +10 -8
  12. package/src/parser/api/normalize.ts +2 -0
  13. package/src/parser/block/blockquote.ts +7 -7
  14. package/src/parser/block/extension/aside.ts +1 -1
  15. package/src/parser/block/extension/example.ts +1 -1
  16. package/src/parser/block/extension/fig.ts +4 -4
  17. package/src/parser/block/extension/figbase.ts +1 -1
  18. package/src/parser/block/extension/figure.ts +3 -3
  19. package/src/parser/block/extension/message.ts +1 -1
  20. package/src/parser/block/extension/table.ts +8 -8
  21. package/src/parser/block/heading.ts +1 -1
  22. package/src/parser/block/ilist.ts +3 -3
  23. package/src/parser/block/mediablock.ts +1 -1
  24. package/src/parser/block/olist.ts +2 -2
  25. package/src/parser/block/pagebreak.ts +1 -1
  26. package/src/parser/block/reply/cite.ts +1 -1
  27. package/src/parser/block/sidefence.ts +6 -6
  28. package/src/parser/block/table.ts +2 -2
  29. package/src/parser/block/ulist.ts +3 -3
  30. package/src/parser/header.ts +2 -3
  31. package/src/parser/inline/annotation.ts +1 -1
  32. package/src/parser/inline/autolink/url.ts +1 -1
  33. package/src/parser/inline/code.ts +1 -1
  34. package/src/parser/inline/extension/indexer.ts +1 -1
  35. package/src/parser/inline/html.ts +1 -1
  36. package/src/parser/inline/link.ts +1 -1
  37. package/src/parser/inline/math.ts +2 -2
  38. package/src/parser/inline/remark.ts +2 -2
  39. package/src/parser/inline/ruby.ts +1 -1
  40. package/src/parser/inline/shortmedia.ts +1 -1
  41. package/src/parser/repeat.ts +8 -5
  42. package/src/parser/segment.ts +1 -2
  43. package/src/parser/source/escapable.ts +1 -1
  44. package/src/parser/source/text.test.ts +4 -0
  45. package/src/parser/source/text.ts +2 -0
  46. package/src/parser/visibility.ts +17 -13
  47. package/src/combinator/data/parser/context.test.ts +0 -62
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securemark",
3
- "version": "0.298.7",
3
+ "version": "0.299.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",
@@ -1,10 +1,10 @@
1
- import { Parser, List, Segment, failsafe } from '../../data/parser';
1
+ import { Parser, List, Segment } from '../../data/parser';
2
2
  import { isEmptyline } from './line';
3
3
 
4
4
  export function block<P extends Parser>(parser: P, separation?: boolean, segment?: number): P;
5
5
  export function block<N>(parser: Parser<N>, separation = true, segment = 0): Parser<N> {
6
6
  assert(parser);
7
- return failsafe(input => {
7
+ return input => {
8
8
  const context = input;
9
9
  const { source, position } = context;
10
10
  if (position === source.length) return;
@@ -14,12 +14,15 @@ export function block<N>(parser: Parser<N>, separation = true, segment = 0): Par
14
14
  return new List();
15
15
  }
16
16
  const result = parser(input);
17
- if (result === undefined) return;
18
- if (separation && !isEmptyline(source, context.position)) return;
17
+ if (result === undefined ||
18
+ separation && !isEmptyline(source, context.position)) {
19
+ context.position = position;
20
+ return;
21
+ }
19
22
  assert(context.position === source.length || source[context.position - 1] === '\n');
20
23
  if (segment !== 0 && context.segment & Segment.write ^ Segment.write) {
21
24
  context.segment = segment;
22
25
  }
23
26
  return result;
24
- });
27
+ };
25
28
  }
@@ -1,23 +1,29 @@
1
- import { Parser, input, failsafe } from '../../data/parser';
1
+ import { Parser, input } from '../../data/parser';
2
2
 
3
3
  export function line<P extends Parser>(parser: P): P;
4
4
  export function line<N>(parser: Parser<N>): Parser<N> {
5
5
  assert(parser);
6
- return failsafe(context => {
6
+ return context => {
7
7
  const { source, position } = context;
8
8
  if (position === source.length) return;
9
9
  const line = firstline(source, position);
10
10
  context.offset += position;
11
11
  const result = parser(input(line, context));
12
- context.position += position;
13
- context.position += result && context.position === position ? line.length : 0;
14
12
  context.source = source;
13
+ context.position = result
14
+ ? context.position === 0
15
+ ? position + line.length
16
+ : position + context.position
17
+ : position;
15
18
  context.offset -= position;
16
- if (result === undefined) return;
17
- if (context.position < position + line.length && !isEmptyline(source, context.position)) return;
19
+ if (result === undefined ||
20
+ context.position < position + line.length && !isEmptyline(source, context.position)) {
21
+ context.position = position;
22
+ return;
23
+ }
18
24
  context.position = position + line.length;
19
25
  return result;
20
- });
26
+ };
21
27
  }
22
28
 
23
29
  export function firstline(source: string, position: number): string {
@@ -1,25 +1,22 @@
1
- import { Parser, List, Context, subinput, failsafe } from '../../data/parser';
1
+ import { Parser, List, Context, subinput } from '../../data/parser';
2
2
 
3
- export function convert<P extends Parser>(conv: (source: string, context: Parser.Context<P>) => string, parser: P, empty?: boolean): P;
4
- export function convert<N>(conv: (source: string, context: Context) => string, parser: Parser<N>, empty = false): Parser<N> {
3
+ export function convert<P extends Parser>(conv: (source: string, context: Parser.Context<P>) => string, parser: P): P;
4
+ export function convert<N>(conv: (source: string, context: Context) => string, parser: Parser<N>): Parser<N> {
5
5
  assert(parser);
6
- return failsafe(input => {
6
+ return input => {
7
7
  const context = input;
8
- const { source, position } = context;
8
+ const { source, position, offset } = context;
9
9
  if (position === source.length) return;
10
10
  const src = conv(source.slice(position), context);
11
11
  assert(context.position === position);
12
12
  if (src === '') {
13
- if (!empty) return;
14
13
  context.position = source.length;
15
14
  return new List();
16
15
  }
17
- const { offset, backtracks } = context;
18
16
  const result = parser(subinput(src, context));
19
- context.position = context.source.length
20
- assert(context.offset === offset);
17
+ context.position = result ? context.source.length : position;
21
18
  assert(context.source === source);
22
- assert(context.backtracks === backtracks);
19
+ assert(context.offset === offset);
23
20
  return result;
24
- });
21
+ };
25
22
  }
@@ -1,4 +1,4 @@
1
- import { Parser, failsafe } from '../../data/parser';
1
+ import { Parser } from '../../data/parser';
2
2
  import { consume } from '../../../combinator';
3
3
 
4
4
  export function match<P extends Parser>(pattern: RegExp, f: (matched: RegExpMatchArray) => P): P;
@@ -7,7 +7,7 @@ export function match<N>(pattern: RegExp, f: (matched: RegExpMatchArray) => Pars
7
7
  const count = typeof pattern === 'object'
8
8
  ? /[^^\\*+][*+]|{\d+,}/.test(pattern.source)
9
9
  : false;
10
- return failsafe(input => {
10
+ return input => {
11
11
  const context = input;
12
12
  const { source, position } = context;
13
13
  if (position === source.length) return;
@@ -17,7 +17,11 @@ export function match<N>(pattern: RegExp, f: (matched: RegExpMatchArray) => Pars
17
17
  assert(source.startsWith(params[0], position));
18
18
  count && consume(params[0].length, context);
19
19
  const result = f(params)(input);
20
- context.position += result && context.position === position ? params[0].length : 0;
20
+ context.position += result
21
+ ? context.position === position
22
+ ? params[0].length
23
+ : 0
24
+ : context.position - position;
21
25
  return result;
22
- });
26
+ };
23
27
  }
@@ -6,7 +6,7 @@ export function focus<N>(scope: string | RegExp, parser: Parser<N>, slice = true
6
6
  assert(parser);
7
7
  const match = matcher(scope, false);
8
8
  return failsafe(context => {
9
- const { source, position } = context;
9
+ const { SID, source, position } = context;
10
10
  if (position === source.length) return;
11
11
  const src = match(context)?.head?.value ?? '';
12
12
  assert(source.startsWith(src, position));
@@ -19,9 +19,10 @@ export function focus<N>(scope: string | RegExp, parser: Parser<N>, slice = true
19
19
  }
20
20
  context.offset += position;
21
21
  const result = parser(input(src, context));
22
+ context.source = source;
23
+ context.SID = SID;
22
24
  context.position += position;
23
25
  context.position += result && context.position === position ? src.length : 0;
24
- context.source = source;
25
26
  context.offset -= position;
26
27
  return result;
27
28
  });
@@ -33,7 +34,7 @@ export function rewrite<N>(scope: Parser, parser: Parser<N>, slice = true): Pars
33
34
  assert(scope);
34
35
  assert(parser);
35
36
  return failsafe(context => {
36
- const { source, position } = context;
37
+ const { SID, source, position } = context;
37
38
  if (position === source.length) return;
38
39
  const res1 = scope(context);
39
40
  assert(context.position > position || !res1);
@@ -50,9 +51,10 @@ export function rewrite<N>(scope: Parser, parser: Parser<N>, slice = true): Pars
50
51
  assert(source.startsWith(src, position));
51
52
  context.offset += position;
52
53
  const res2 = parser(input(src, context));
54
+ context.SID = SID;
55
+ context.source = source;
53
56
  context.position += position;
54
57
  context.position += res2 && context.position === position ? src.length : 0;
55
- context.source = source;
56
58
  context.offset -= position;
57
59
  return res2;
58
60
  });
@@ -1,4 +1,4 @@
1
- import { Parser, Result, List, Node, Context, failsafe } from '../../data/parser';
1
+ import { Parser, Result, List, Node, Context } from '../../data/parser';
2
2
 
3
3
  export function bind<P extends Parser>(parser: Parser.IntermediateParser<P>, f: (nodes: List<Node<Parser.SubNode<P>>>, context: Parser.Context<P>) => Result<Parser.Node<P>, Parser.Context<P>, Parser.SubParsers<P>>): P;
4
4
  export function bind<P extends Parser>(parser: P, f: (nodes: List<Node<Parser.Node<P>>>, context: Parser.Context<P>) => Result<Parser.Node<P>, Parser.Context<P>, Parser.SubParsers<P>>): P;
@@ -6,13 +6,21 @@ export function bind<N, P extends Parser>(parser: Parser<N, Parser.Context<P>, P
6
6
  export function bind<U, P extends Parser>(parser: P, f: (nodes: List<Node<Parser.Node<P>>>, context: Parser.Context<P>) => Result<U, Parser.Context<P>, Parser.SubParsers<P>>): Parser<U, Parser.Context<P>, Parser.SubParsers<P>>;
7
7
  export function bind<N, U>(parser: Parser<N>, f: (nodes: List<Node<N>>, context: Context) => Result<U>): Parser<U> {
8
8
  assert(parser);
9
- return failsafe(input => {
9
+ return input => {
10
10
  const context = input;
11
11
  const { source, position } = context;
12
12
  if (position === source.length) return;
13
- const result = parser(input);
14
- if (result === undefined) return;
13
+ const res1 = parser(input);
14
+ if (res1 === undefined) {
15
+ context.position = position;
16
+ return;
17
+ }
15
18
  context.range = context.position - position;
16
- return f(result, context);
17
- });
19
+ const res2 = f(res1, context);
20
+ if (res2 === undefined) {
21
+ context.position = position;
22
+ return;
23
+ }
24
+ return res2;
25
+ };
18
26
  }
@@ -1,78 +1,5 @@
1
- import { Parser, Result, Context, Options } from '../../data/parser';
1
+ import { Parser, Context } from '../../data/parser';
2
2
  import { min } from 'spica/alias';
3
- import { clone } from 'spica/assign';
4
-
5
- export function reset<P extends Parser>(base: Options, parser: P): P;
6
- export function reset<N>(base: Context, parser: Parser<N>): Parser<N> {
7
- const clock = base.resources?.clock;
8
- const recursions = base.resources?.recursions;
9
- return input => {
10
- const context = input;
11
- // @ts-expect-error
12
- context.resources ??= {
13
- clock,
14
- recursions: recursions?.slice(),
15
- };
16
- context.backtracks = {};
17
- return parser(input);
18
- };
19
- assert(Object.getPrototypeOf(base) === Object.prototype);
20
- assert(Object.freeze(base));
21
- const changes = Object.entries(base);
22
- const values = Array(changes.length);
23
- return context =>
24
- apply(parser, context, changes, values, true);
25
- }
26
-
27
- export function context<P extends Parser>(base: Options, parser: P): P;
28
- export function context<N>(base: Context, parser: Parser<N>): Parser<N> {
29
- assert(Object.getPrototypeOf(base) === Object.prototype);
30
- assert(Object.freeze(base));
31
- const changes = Object.entries(base);
32
- const values = Array(changes.length);
33
- return context =>
34
- apply(parser, context, changes, values);
35
- }
36
-
37
- function apply<P extends Parser>(parser: P, context: Parser.Context<P>, changes: readonly [string, unknown][], values: unknown[], reset?: boolean): Result<Parser.Node<P>>;
38
- function apply<N>(parser: Parser<N>, context: Context, changes: readonly [string, unknown][], values: unknown[], reset = false): Result<N> {
39
- for (let i = 0; i < changes.length; ++i) {
40
- const change = changes[i];
41
- const prop = change[0];
42
- switch (prop) {
43
- case 'source':
44
- case 'position':
45
- continue;
46
- case 'resources':
47
- assert(reset);
48
- assert(!context.offset);
49
- assert(!context.precedence);
50
- assert(!context.state);
51
- values[i] = context[prop];
52
- context[prop as string] ??= clone({}, change[1] as object);
53
- continue;
54
- case 'backtracks':
55
- change[1] = {};
56
- }
57
- values[i] = context[prop];
58
- context[prop] = change[1];
59
- }
60
- const result = parser(context);
61
- for (let i = 0; i < changes.length; ++i) {
62
- const change = changes[i];
63
- const prop = change[0];
64
- switch (prop) {
65
- case 'source':
66
- case 'position':
67
- continue;
68
- case 'resources':
69
- assert(reset);
70
- }
71
- context[prop] = values[i];
72
- values[i] = undefined;
73
- }
74
- return result;
75
- }
76
3
 
77
4
  export function creation<P extends Parser>(cost: number, parser: P): P;
78
5
  export function creation(cost: number, parser: Parser): Parser {
@@ -107,6 +107,10 @@ export class Context {
107
107
  // 入力内の最大セグメントサイズの10%前後である。
108
108
  //
109
109
  public backtracks: Record<number, number>;
110
+ public clone(): this {
111
+ // @ts-ignore
112
+ return new this.constructor(this);
113
+ }
110
114
  }
111
115
  export type Options = Partial<Context>;
112
116
  export const enum Segment {
@@ -122,14 +126,12 @@ export function input<C extends Context>(source: string, context: C): Input<C> {
122
126
  }
123
127
 
124
128
  export function subinput<C extends Context>(source: string, context: C): Input<C> {
125
- return {
126
- ...context,
127
- SID: sid(),
128
- source,
129
- position: 0,
130
- offset: 0,
131
- backtracks: {},
132
- };
129
+ const c = context.clone();
130
+ c.source = source;
131
+ c.position = 0;
132
+ c.offset = 0;
133
+ c.backtracks = {};
134
+ return c;
133
135
  }
134
136
 
135
137
  export function failsafe<P extends Parser>(parser: P): P;
@@ -65,6 +65,8 @@ const parser = (el => (entity: string): string => {
65
65
  })(html('span'));
66
66
  export const invisibleBlankHTMLEntityNames: readonly string[] = invisibleHTMLEntityNames
67
67
  .filter(name => parser(`&${name};`).trimStart() === '');
68
+ export const invisibleBlankCharacters: readonly string[] = invisibleBlankHTMLEntityNames
69
+ .map(name => parser(`&${name};`));
68
70
  export const invisibleGraphHTMLEntityNames: readonly string[] = invisibleHTMLEntityNames
69
71
  .filter(name => parser(`&${name};`).trimStart() !== '');
70
72
  const unreadableEscapeHTMLEntityNames = invisibleHTMLEntityNames.filter(name => ![
@@ -17,18 +17,18 @@ export const blockquote: BlockquoteParser = lazy(() => block(rewrite(segment, un
17
17
  open(/!(?=>)/y, markdown),
18
18
  ]))));
19
19
 
20
- const opener = /(?=>>+(?:$|[ \n]))/y;
21
- const indent = open(opener, some(contentline, />(?:$|[ \n])/y));
22
- const unindent = (source: string) => source.replace(/(?<=^|\n)>(?: |(?=>*(?:$|[ \n])))|\n$/g, '');
20
+ const opener = /(?=>>+(?:$|[ \r\n]))/y;
21
+ const indent = open(opener, some(contentline, />(?:$|[ \r\n])/y));
22
+ const unindent = (source: string) => source.replace(/(?<=^|\n)>(?: |(?=>*(?:$|[ \r\n])))|\r?\n$/g, '');
23
23
 
24
24
  const source: BlockquoteParser.SourceParser = lazy(() => fmap(
25
25
  recursion(Recursion.blockquote, some(union([
26
26
  rewrite(
27
27
  indent,
28
- convert(unindent, source, true)),
28
+ convert(unindent, source)),
29
29
  rewrite(
30
30
  some(contentline, opener),
31
- convert(unindent, fmap(autolink, ns => new List([new Node(html('pre', defrag(unwrap(ns))))])), true)),
31
+ convert(unindent, fmap(autolink, ns => new List([new Node(html('pre', defrag(unwrap(ns))))])))),
32
32
  ]))),
33
33
  ns => new List([new Node(html('blockquote', unwrap(ns)))])));
34
34
 
@@ -36,7 +36,7 @@ const markdown: BlockquoteParser.MarkdownParser = lazy(() => fmap(
36
36
  recursion(Recursion.blockquote, some(union([
37
37
  rewrite(
38
38
  indent,
39
- convert(unindent, markdown, true)),
39
+ convert(unindent, markdown)),
40
40
  rewrite(
41
41
  some(contentline, opener),
42
42
  convert(unindent, context => {
@@ -52,6 +52,6 @@ const markdown: BlockquoteParser.MarkdownParser = lazy(() => fmap(
52
52
  }, context);
53
53
  context.position = source.length;
54
54
  return new List([new Node(html('section', [document, html('h2', 'References'), references]))]);
55
- }, true)),
55
+ })),
56
56
  ]))),
57
57
  ns => new List([new Node(html('blockquote', unwrap(ns)))])));
@@ -8,7 +8,7 @@ import { parse } from '../../api/parse';
8
8
  import { html } from 'typed-dom/dom';
9
9
 
10
10
  export const aside: ExtensionParser.AsideParser = block(recursion(Recursion.block, fmap(
11
- fence(/(~{3,})aside(?!\S)([^\n]*)(?:$|\n)/y, 300),
11
+ fence(/(~{3,})aside(?!\S)([^\r\n]*)(?:$|\r?\n)/y, 300),
12
12
  // Bug: Type mismatch between outer and inner.
13
13
  (nodes: List<Node<string>>, context) => {
14
14
  const [body, overflow, closer, opener, delim, param] = unwrap(nodes);
@@ -8,7 +8,7 @@ import { parse } from '../../api/parse';
8
8
  import { html } from 'typed-dom/dom';
9
9
 
10
10
  export const example: ExtensionParser.ExampleParser = block(recursion(Recursion.block, fmap(
11
- fence(/(~{3,})(?:example\/(\S+))?(?!\S)([^\n]*)(?:$|\n)/y, 300),
11
+ fence(/(~{3,})(?:example\/(\S+))?(?!\S)([^\r\n]*)(?:$|\r?\n)/y, 300),
12
12
  // Bug: Type mismatch between outer and inner.
13
13
  (nodes: List<Node<string>>, context) => {
14
14
  const [body, overflow, closer, opener, delim, type = 'markdown', param] = unwrap(nodes);
@@ -15,7 +15,7 @@ import FigParser = ExtensionParser.FigParser;
15
15
 
16
16
  export const segment: FigParser.SegmentParser = block(
17
17
  sequence([
18
- line(close(seg_label, /(?!\S).*\r?\n/y)),
18
+ line(close(seg_label, /(?!\S)[^\r\n]*\r?\n/y)),
19
19
  union([
20
20
  seg_code,
21
21
  seg_math,
@@ -29,21 +29,21 @@ export const segment: FigParser.SegmentParser = block(
29
29
  export const fig: FigParser = block(rewrite(segment, verify(convert(
30
30
  (source, context) => {
31
31
  // Bug: TypeScript
32
- const fence = (/^[^\n]*\n!?>+ /.test(source) && source.match(/^~{3,}(?=[^\S\n]*$)/gm) as string[] || [])
32
+ const fence = (/^[^\r\n]*\r?\n!?>+ /.test(source) && source.match(/^~{3,}(?=[^\S\r\n]*$)/gm) as string[] || [])
33
33
  .reduce((max, fence) => fence > max ? fence : max, '~~') + '~';
34
34
  const { position } = context;
35
35
  const result = parser(context);
36
36
  context.position = position;
37
37
  context.segment = Segment.figure | Segment.write;
38
38
  return result
39
- ? `${fence}figure ${source.replace(/^(.+\n.+\n)([\S\s]+?)\n?$/, '$1\n$2')}\n${fence}`
39
+ ? `${fence}figure ${source.replace(/^([^\r\n]+\r?\n[^\r\n]+\r?\n)(.+?)\r?\n?$/s, '$1\n$2')}\n${fence}`
40
40
  : `${fence}figure ${source}\n\n${fence}`;
41
41
  },
42
42
  union([figure])),
43
43
  ([{ value: el }]) => el.tagName === 'FIGURE')));
44
44
 
45
45
  const parser = sequence([
46
- line(close(seg_label, /(?!\S).*\n/y)),
46
+ line(close(seg_label, /(?!\S)[^\r\n]*\r?\n/y)),
47
47
  line(union([
48
48
  media,
49
49
  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]*(?:$|\n)/y,
8
+ validate(/\[?\$-(?:[0-9]+\.)*0\]?[^\S\r\n]*(?:$|\r?\n)/y,
9
9
  line(union([label]))),
10
10
  ([{ value: el }]) => {
11
11
  const label = el.getAttribute('data-label')!;
@@ -50,7 +50,7 @@ export const segment: FigureParser.SegmentParser = block(match(
50
50
  export const figure: FigureParser = block(fallback(rewrite(segment, fmap(
51
51
  convert(source => source.slice(source.match(/^~+(?:\w+\s+)?/)![0].length, source.trimEnd().lastIndexOf('\n')),
52
52
  sequence([
53
- line(sequence([label, str(/(?!\S).*\n/y)])),
53
+ line(sequence([label, str(/(?!\S)[^\r\n]*\r?\n/y)])),
54
54
  inits([
55
55
  block(union([
56
56
  ulist,
@@ -84,7 +84,7 @@ export const figure: FigureParser = block(fallback(rewrite(segment, fmap(
84
84
  ]);
85
85
  })),
86
86
  fmap(
87
- fence(/(~{3,})(?:figure(?=$|[ \n])|\[?\$)[^\n]*(?:$|\n)/y, 300),
87
+ fence(/(~{3,})(?:figure(?=$|[ \r\n])|\[?\$)[^\r\n]*(?:$|\r?\n)/y, 300),
88
88
  (nodes, context) => {
89
89
  const [body, overflow, closer, opener, delim] = unwrap<string>(nodes);
90
90
  const violation =
@@ -100,7 +100,7 @@ export const figure: FigureParser = block(fallback(rewrite(segment, fmap(
100
100
  'label',
101
101
  'Invalid label',
102
102
  ] ||
103
- /^~+(?:figure )?(\[?\$\S+)[^\S\n]+\S/.test(opener) && [
103
+ /^~+(?:figure )?(\[?\$\S+)[^\S\r\n]+\S/.test(opener) && [
104
104
  'argument',
105
105
  'Invalid argument',
106
106
  ] ||
@@ -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+)(?!\S)([^\n]*)(?:$|\n)/y, 300),
23
+ fence(/(~{3,})message\/(\S+)(?!\S)([^\r\n]*)(?:$|\r?\n)/y, 300),
24
24
  // Bug: Type mismatch between outer and inner.
25
25
  (nodes: List<Node<string>>, context) => {
26
26
  const [body, overflow, closer, opener, delim, type, param] = unwrap(nodes);
@@ -77,16 +77,16 @@ const row: RowParser = lazy(() => dup(fmap(
77
77
  ]),
78
78
  ns => Array.isArray(ns.head?.value) ? ns : ns.unshift(new Node([[]])) && ns)));
79
79
 
80
- const alignment = /[-=<>]+(?:\/[-=^v]*)?(?=[^\S\n]*\n)/y;
80
+ const alignment = /[-=<>]+(?:\/[-=^v]*)?(?=[^\S\r\n]*\r?\n)/y;
81
81
 
82
82
  const align: AlignParser = line(fmap(
83
83
  union([str(alignment)]),
84
84
  ([{ value }]) => new List([new Node(value.split('/').map(s => s.split('')) as [string[], string[]?])])));
85
85
 
86
- const delimiter = /[-=<>]+(?:\/[-=^v]*)?(?=[^\S\n]*\n)|[#:](?:(?!:\D|0)\d*:(?!0)\d*)?(?:!+[+]?)?(?=[ \n])/y;
86
+ const delimiter = /[-=<>]+(?:\/[-=^v]*)?(?=[^\S\r\n]*\r?\n)|[#:](?:(?!:\D|0)\d*:(?!0)\d*)?(?:!+[+]?)?(?=[ \r\n])/y;
87
87
 
88
88
  const head: CellParser.HeadParser = block(fmap(open(
89
- str(/#(?:(?!:\D|0)\d*:(?!0)\d*)?(?:!+[+]?)?(?=[ \n])/y),
89
+ str(/#(?:(?!:\D|0)\d*:(?!0)\d*)?(?:!+[+]?)?(?=[ \r\n])/y),
90
90
  rewrite(
91
91
  inits([
92
92
  anyline,
@@ -100,15 +100,15 @@ const head: CellParser.HeadParser = block(fmap(open(
100
100
  media,
101
101
  lineshortmedia,
102
102
  ]),
103
- /[^\S\n]*(?:$|\n)/y)),
104
- open(/(?:[^\S\n]*\n|\s)/y, visualize(trimBlank(some(inline))), true),
103
+ /[^\S\r\n]*(?:$|\r?\n)/y)),
104
+ open(/(?:[^\S\r\n]*\r?\n|\s)/y, visualize(trimBlank(some(inline))), true),
105
105
  ])),
106
106
  true),
107
107
  ns => new List([new Node(html('th', attributes(ns.shift()!.value as string), defrag(unwrap(ns))))])),
108
108
  false);
109
109
 
110
110
  const data: CellParser.DataParser = block(fmap(open(
111
- str(/:(?:(?!:\D|0)\d*:(?!0)\d*)?(?:!+[+]?)?(?=[ \n])/y),
111
+ str(/:(?:(?!:\D|0)\d*:(?!0)\d*)?(?:!+[+]?)?(?=[ \r\n])/y),
112
112
  rewrite(
113
113
  inits([
114
114
  anyline,
@@ -122,8 +122,8 @@ const data: CellParser.DataParser = block(fmap(open(
122
122
  media,
123
123
  lineshortmedia,
124
124
  ]),
125
- /[^\S\n]*(?:$|\n)/y)),
126
- open(/(?:[^\S\n]*\n|\s)/y, visualize(trimBlankEnd(some(inline))), true),
125
+ /[^\S\r\n]*(?:$|\r?\n)/y)),
126
+ open(/(?:[^\S\r\n]*\r?\n|\s)/y, visualize(trimBlankEnd(some(inline))), true),
127
127
  ])),
128
128
  true),
129
129
  ns => new List([new Node(html('td', attributes(ns.shift()!.value as string), defrag(unwrap(ns))))])),
@@ -9,7 +9,7 @@ import { unwrap, invalid } from '../util';
9
9
  import { html, defrag } from 'typed-dom/dom';
10
10
 
11
11
  export const segment: HeadingParser.SegmentParser = block(focus(
12
- /#+ +\S[^\r\n]*(?:\r?\n#+(?=$| |\r?\n)[^\r\n]*)*(?:$|\r?\n)/y,
12
+ /#+ +\S[^\r\n]*(?:\r?\n#+(?=$|[ \r\n])[^\r\n]*)*(?:$|\r?\n)/y,
13
13
  input => {
14
14
  const context = input;
15
15
  const { source, range } = context;
@@ -16,11 +16,11 @@ export const ilist: IListParser = lazy(() => block(validate(
16
16
  ilist_)));
17
17
 
18
18
  export const ilist_: IListParser = lazy(() => block(fmap(validate(
19
- /[-+*](?:$|[ \n])/y,
19
+ /[-+*](?:$|[ \r\n])/y,
20
20
  recursion(Recursion.listitem, some(union([
21
21
  fmap(fallback(
22
22
  inits([
23
- line(open(/[-+*](?:$|[ \n])/y, visualize(trimBlank(some(inline))), true)),
23
+ line(open(/[-+*](?:$|[ \r\n])/y, visualize(trimBlank(some(inline))), true)),
24
24
  indent(union([ulist_, olist_, ilist_])),
25
25
  ]),
26
26
  ilistitem),
@@ -40,5 +40,5 @@ export const ilistitem = rewrite(
40
40
  new Node(html('span', {
41
41
  class: 'invalid',
42
42
  ...invalid('list', 'syntax', 'Fix the indent or the head of the list item'),
43
- }, source.replace('\n', '')))
43
+ }, source.replace(/\r?\n/, '')))
44
44
  ]));
@@ -20,6 +20,6 @@ export const mediablock: MediaBlockParser = block(fmap(
20
20
  new Node(html('span', {
21
21
  class: 'invalid',
22
22
  ...invalid('mediablock', 'syntax', 'Not media syntax'),
23
- }, source.replace('\n', '')))
23
+ }, source.replace(/\r?\n/, '')))
24
24
  ]))))]),
25
25
  ns => new List([new Node(html('div', unwrap(ns)))])));
@@ -11,8 +11,8 @@ import { memoize } from 'spica/memoize';
11
11
  import { html, define, defrag } from 'typed-dom/dom';
12
12
 
13
13
  const openers = {
14
- '.': /([0-9]+|[a-z]+|[A-Z]+)(?:-(?=$|[0-9\n])[0-9]*)*(?:\.?(?:$|[\n])|\. )/y,
15
- '(': /\((?=$|[0-9a-z\n])([0-9]*|[a-z]*)(?=$|[)\n])\)?(?:-(?=$|[0-9\n])[0-9]*)*(?:$|[ \n])/y,
14
+ '.': /([0-9]+|[a-z]+|[A-Z]+)(?:-(?=$|[0-9\r\n])[0-9]*)*(?:\.?(?:$|[\r\n])|\. )/y,
15
+ '(': /\((?=$|[0-9a-z\r\n])([0-9]*|[a-z]*)(?=$|[)\r\n])\)?(?:-(?=$|[0-9\r\n])[0-9]*)*(?:$|[ \r\n])/y,
16
16
  } as const;
17
17
 
18
18
  export const olist: OListParser = lazy(() => block(validate(
@@ -4,5 +4,5 @@ import { block, line, focus } from '../../combinator';
4
4
  import { html } from 'typed-dom/dom';
5
5
 
6
6
  export const pagebreak: PagebreakParser = block(line(focus(
7
- /={3,}[^\S\n]*(?:$|\n)/y,
7
+ /={3,}[^\S\r\n]*(?:$|\r?\n)/y,
8
8
  () => new List([new Node(html('hr'))]))));
@@ -7,7 +7,7 @@ import { str } from '../../source';
7
7
  import { invalid } from '../../util';
8
8
  import { html, define, defrag } from 'typed-dom/dom';
9
9
 
10
- export const syntax = />*(?=>>[^>\s]\S*[^\S\n]*(?:$|\n))/y;
10
+ export const syntax = />*(?=>>[^>\s]\S*[^\S\r\n]*(?:$|\r?\n))/y;
11
11
 
12
12
  export const cite: ReplyParser.CiteParser = line(fmap(
13
13
  open(
@@ -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
- /\|+ [^\n]*(?:\n\|+(?=$|[ \n])[^\n]*)*(?:$|\n)/y,
11
+ /\|+ [^\r\n]*(?:\r?\n\|+(?=$|[ \r\n])[^\r\n]*)*(?:$|\r?\n)/y,
12
12
  union([source])),
13
13
  ([{ value }]) => new List([
14
14
  new Node(define(value, {
@@ -17,17 +17,17 @@ export const sidefence: SidefenceParser = lazy(() => block(fmap(focus(
17
17
  })),
18
18
  ]))));
19
19
 
20
- const opener = /(?=\|\|+(?:$|[ \n]))/y;
21
- const indent = open(opener, some(contentline, /\|(?:$|[ \n])/y));
22
- const unindent = (source: string) => source.replace(/(?<=^|\n)\|(?: |(?=\|*(?:$|[ \n])))|\n$/g, '');
20
+ const opener = /(?=\|\|+(?:$|[ \r\n]))/y;
21
+ const indent = open(opener, some(contentline, /\|(?:$|[ \r\n])/y));
22
+ const unindent = (source: string) => source.replace(/(?<=^|\n)\|(?: |(?=\|*(?:$|[ \r\n])))|\r?\n$/g, '');
23
23
 
24
24
  const source: SidefenceParser.SourceParser = lazy(() => fmap(
25
25
  recursion(Recursion.block, some(union([
26
26
  rewrite(
27
27
  indent,
28
- convert(unindent, source, true)),
28
+ convert(unindent, source)),
29
29
  rewrite(
30
30
  some(contentline, opener),
31
- convert(unindent, fmap(autolink, ns => new List([new Node(html('pre', defrag(unwrap(ns))))])), true)),
31
+ convert(unindent, fmap(autolink, ns => new List([new Node(html('pre', defrag(unwrap(ns))))])))),
32
32
  ]))),
33
33
  ns => new List([new Node(html('blockquote', unwrap(ns)))])));