securemark 0.300.3 → 0.300.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 (53) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/index.js +230 -180
  3. package/index.d.ts +2 -2
  4. package/markdown.d.ts +36 -36
  5. package/package.json +1 -1
  6. package/src/api/bind.test.ts +3 -1
  7. package/src/api/bind.ts +6 -3
  8. package/src/api/header.ts +1 -1
  9. package/src/api/parse.ts +8 -2
  10. package/src/combinator/effect/backtrack.ts +19 -7
  11. package/src/combinator/parser/list.ts +13 -15
  12. package/src/combinator/parser.ts +3 -5
  13. package/src/debug.test.ts +3 -0
  14. package/src/parser/autolink.ts +2 -2
  15. package/src/parser/block/blockquote.test.ts +4 -4
  16. package/src/parser/block/blockquote.ts +2 -1
  17. package/src/parser/block/extension/aside.test.ts +3 -3
  18. package/src/parser/block/extension/aside.ts +2 -1
  19. package/src/parser/block/extension/example.test.ts +3 -3
  20. package/src/parser/block/extension/example.ts +3 -1
  21. package/src/parser/{header.test.ts → block/header.test.ts} +3 -3
  22. package/src/parser/{header.ts → block/header.ts} +7 -7
  23. package/src/parser/block/reply/cite.ts +2 -3
  24. package/src/parser/block/reply/quote.ts +1 -2
  25. package/src/parser/block/reply.ts +1 -2
  26. package/src/parser/block.ts +6 -5
  27. package/src/parser/context.ts +3 -1
  28. package/src/parser/document.ts +25 -22
  29. package/src/parser/inline/annotation.ts +11 -5
  30. package/src/parser/inline/extension/index.ts +1 -2
  31. package/src/parser/inline/extension/indexee.ts +1 -2
  32. package/src/parser/inline/extension/label.ts +10 -6
  33. package/src/parser/inline/html.ts +1 -2
  34. package/src/parser/inline/htmlentity.ts +5 -4
  35. package/src/parser/inline/media.ts +2 -3
  36. package/src/parser/inline/reference.ts +3 -3
  37. package/src/parser/inline.test.ts +1 -0
  38. package/src/parser/inline.ts +2 -2
  39. package/src/parser/node.ts +0 -5
  40. package/src/parser/repeat.ts +5 -1
  41. package/src/parser/segment.ts +2 -2
  42. package/src/parser/source/escapable.ts +5 -6
  43. package/src/parser/source/str.ts +3 -2
  44. package/src/parser/source/text.ts +3 -4
  45. package/src/parser/source/unescapable.ts +3 -4
  46. package/src/parser/source.ts +2 -2
  47. package/src/parser/util.ts +9 -0
  48. package/src/parser/visibility.ts +4 -5
  49. package/src/processor/figure.test.ts +35 -24
  50. package/src/processor/figure.ts +11 -27
  51. package/src/processor/note.test.ts +71 -25
  52. package/src/processor/note.ts +45 -35
  53. package/src/util/toc.ts +2 -3
package/index.d.ts CHANGED
@@ -42,8 +42,8 @@ export interface ParserSettings {
42
42
  export type Progress =
43
43
  | { readonly type: 'segment'; readonly value: string; }
44
44
  | { readonly type: 'block'; readonly value: HTMLElement; }
45
- | { readonly type: 'figure'; readonly value: HTMLAnchorElement; }
46
- | { readonly type: 'note'; readonly value: HTMLLIElement | HTMLElement; }
45
+ | { readonly type: 'figure'; }
46
+ | { readonly type: 'note'; readonly value: HTMLOListElement; }
47
47
  | { readonly type: 'break'; readonly value: 'segment' | 'block' | 'parser' | 'figure' | 'note'; }
48
48
  | { readonly type: 'cancel'; };
49
49
 
package/markdown.d.ts CHANGED
@@ -1,19 +1,18 @@
1
1
  import { Parser, List, Node } from './src/combinator/parser';
2
2
  import { Input } from './src/parser/context';
3
3
 
4
- declare abstract class Markdown<T> {
4
+ declare abstract class Document<T> {
5
5
  private parser?: T;
6
6
  }
7
- export interface MarkdownParser extends
8
- Markdown<'markdown'>,
7
+ export interface DocumentParser extends
8
+ Document<'document'>,
9
9
  Parser<DocumentFragment, Input, [
10
- MarkdownParser.HeaderParser,
11
- MarkdownParser.BlockParser,
10
+ DocumentParser.BlockParser,
12
11
  ]> {
13
12
  }
14
- export namespace MarkdownParser {
13
+ export namespace DocumentParser {
15
14
  export interface SegmentParser extends
16
- Markdown<'segment'>,
15
+ Document<'segment'>,
17
16
  Parser<string, Input, [
18
17
  SourceParser.EmptySegmentParser,
19
18
  Parser<string, Input, [
@@ -25,39 +24,19 @@ export namespace MarkdownParser {
25
24
  SourceParser.ContentLineParser,
26
25
  ]> {
27
26
  }
28
- export interface HeaderParser extends
29
- // ---
30
- // url: https://host/path
31
- // ---
32
- Markdown<'header'>,
33
- Parser<HTMLElement | HTMLPreElement, Input, [
34
- Parser<HTMLElement | HTMLPreElement, Input, [
35
- Parser<HTMLElement, Input, [
36
- HeaderParser.FieldParser,
37
- ]>,
38
- Parser<HTMLPreElement, Input, []>,
39
- ]>,
40
- Parser<never, Input, []>,
41
- ]> {
42
- }
43
- export namespace HeaderParser {
44
- export interface FieldParser extends
45
- Markdown<'header/field'>,
46
- Parser<HTMLSpanElement, Input, []> {
47
- }
48
- }
49
27
  export interface BlockParser extends
50
- Markdown<'block'>,
28
+ Document<'block'>,
51
29
  Parser<HTMLElement, Input, [
52
30
  SourceParser.EmptySegmentParser,
53
31
  Parser<HTMLElement, Input, [
54
- BlockParser.PagebreakParser,
32
+ BlockParser.HeaderParser,
55
33
  BlockParser.HeadingParser,
56
34
  BlockParser.UListParser,
57
35
  BlockParser.OListParser,
58
36
  BlockParser.IListParser,
59
37
  BlockParser.DListParser,
60
38
  BlockParser.TableParser,
39
+ BlockParser.PagebreakParser,
61
40
  BlockParser.CodeBlockParser,
62
41
  BlockParser.MathBlockParser,
63
42
  BlockParser.ExtensionParser,
@@ -70,7 +49,28 @@ export namespace MarkdownParser {
70
49
  ]> {
71
50
  }
72
51
  export namespace BlockParser {
73
- interface Block<T extends string> extends Markdown<`block/${T}`> { }
52
+ interface Block<T extends string> extends Document<`block/${T}`> { }
53
+ export interface HeaderParser extends
54
+ // ---
55
+ // url: https://host/path
56
+ // ---
57
+ Block<'header'>,
58
+ Parser<HTMLElement | HTMLPreElement, Input, [
59
+ Parser<HTMLElement | HTMLPreElement, Input, [
60
+ Parser<HTMLElement, Input, [
61
+ HeaderParser.FieldParser,
62
+ ]>,
63
+ Parser<HTMLPreElement, Input, []>,
64
+ ]>,
65
+ Parser<never, Input, []>,
66
+ ]> {
67
+ }
68
+ export namespace HeaderParser {
69
+ export interface FieldParser extends
70
+ Block<'header/field'>,
71
+ Parser<HTMLSpanElement, Input, []> {
72
+ }
73
+ }
74
74
  export interface PagebreakParser extends
75
75
  // ===
76
76
  Block<'pagebreak'>,
@@ -520,7 +520,7 @@ export namespace MarkdownParser {
520
520
  Parser<HTMLElement, Input, [
521
521
  Parser<string, Input, []>,
522
522
  Parser<HTMLElement, Input, []>,
523
- MarkdownParser,
523
+ DocumentParser,
524
524
  Parser<HTMLElement, Input, []>,
525
525
  ]> {
526
526
  }
@@ -635,7 +635,7 @@ export namespace MarkdownParser {
635
635
  }
636
636
  }
637
637
  export interface InlineParser extends
638
- Markdown<'inline'>,
638
+ Document<'inline'>,
639
639
  Parser<HTMLElement | string, Input, [
640
640
  Parser<HTMLElement | string, Input, [
641
641
  InlineParser.AnnotationParser,
@@ -663,7 +663,7 @@ export namespace MarkdownParser {
663
663
  ]> {
664
664
  }
665
665
  export namespace InlineParser {
666
- interface Inline<T extends string> extends Markdown<`inline/${T}`> { }
666
+ interface Inline<T extends string> extends Document<`inline/${T}`> { }
667
667
  export interface AnnotationParser extends
668
668
  // ((abc))
669
669
  Inline<'annotation'>,
@@ -1173,14 +1173,14 @@ export namespace MarkdownParser {
1173
1173
  }
1174
1174
  }
1175
1175
  export interface AutolinkParser extends
1176
- Markdown<'autolink'>,
1176
+ Document<'autolink'>,
1177
1177
  Parser<string | HTMLElement, Input, [
1178
1178
  InlineParser.AutolinkParser,
1179
1179
  SourceParser.UnescapableSourceParser,
1180
1180
  ]> {
1181
1181
  }
1182
1182
  export namespace SourceParser {
1183
- interface Source<T extends string> extends Markdown<`source/${T}`> { }
1183
+ interface Source<T extends string> extends Document<`source/${T}`> { }
1184
1184
  export interface TextParser extends
1185
1185
  // abc
1186
1186
  Source<'text'>,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securemark",
3
- "version": "0.300.3",
3
+ "version": "0.300.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",
@@ -10,8 +10,10 @@ describe('Unit: api/bind', () => {
10
10
  const result = iter.next();
11
11
  if (result.done) break;
12
12
  switch (result.value.type) {
13
- case 'break':
14
13
  case 'segment':
14
+ case 'figure':
15
+ case 'note':
16
+ case 'break':
15
17
  --i;
16
18
  continue;
17
19
  case 'block':
package/src/api/bind.ts CHANGED
@@ -126,14 +126,17 @@ export function bind(target: DocumentFragment | HTMLElement | ShadowRoot, settin
126
126
  }
127
127
  yield { type: 'break', value: 'parser' };
128
128
  if (rev !== revision) return yield { type: 'cancel' };
129
- for (const el of figure(next(0)?.parentNode ?? target, settings.notes, options)) {
129
+ for (const el of figure(next(0)?.parentNode ?? target, output.labels.pop()!, settings.notes, options)) {
130
130
  assert(rev === revision);
131
131
  el
132
- ? yield { type: 'figure', value: el }
132
+ ? yield { type: 'figure' }
133
133
  : yield { type: 'break', value: 'figure' };
134
134
  if (rev !== revision) return yield { type: 'cancel' };
135
135
  }
136
- for (const el of note(next(0)?.parentNode ?? target, settings.notes, options, bottom)) {
136
+ for (const el of note(next(0)?.parentNode ?? target, {
137
+ annotations: output.annotations.pop()!,
138
+ references: output.references.pop()!,
139
+ }, settings.notes, options, bottom)) {
137
140
  assert(rev === revision);
138
141
  el
139
142
  ? yield { type: 'note', value: el }
package/src/api/header.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Input } from '../parser/context';
2
2
  import { Output, run } from '../combinator/parser';
3
- import { header as h } from '../parser/header';
3
+ import { header as h } from '../parser/block/header';
4
4
 
5
5
  export function header(source: string): string {
6
6
  const [, pos = 0] = parse(source);
package/src/api/parse.ts CHANGED
@@ -1,12 +1,15 @@
1
1
  import { ParserOptions } from '../..';
2
2
  import { Input, Options } from '../parser/context';
3
- import { Output, run } from '../combinator/parser';
3
+ import { Output, List, Node, run } from '../combinator/parser';
4
4
  import { document } from '../parser/document';
5
5
  import { ReadonlyURL } from 'spica/url';
6
6
 
7
- interface Opts extends ParserOptions {
7
+ export interface Opts extends ParserOptions {
8
8
  readonly local?: boolean;
9
9
  readonly test?: boolean;
10
+ readonly labels?: List<Node<HTMLAnchorElement>>;
11
+ readonly annotations?: List<Node<HTMLElement>>;
12
+ readonly references?: List<Node<HTMLElement>>;
10
13
  }
11
14
 
12
15
  export function* parse(source: string, opts: Opts = {}, options?: Options): Generator<void, DocumentFragment, void> {
@@ -23,6 +26,9 @@ export function* parse(source: string, opts: Opts = {}, options?: Options): Gene
23
26
  if (options.id?.match(/[^0-9a-z/-]/i)) throw new Error('Invalid ID: ID must be alphanumeric');
24
27
  if (options.host?.origin === 'null') throw new Error(`Invalid host: ${options.host.href}`);
25
28
  const output = new Output<DocumentFragment>();
29
+ assert(output.labels[0] = opts.labels ?? output.labels[0]);
30
+ assert(output.annotations[0] = opts.annotations ?? output.annotations[0]);
31
+ assert(output.references[0] = opts.references ?? output.references[0]);
26
32
  for (const _ of run(document, new Input(options, source), output)) yield;
27
33
  assert(output.data.length === 1);
28
34
  assert(output.peek().length === 1);
@@ -1,5 +1,5 @@
1
1
  import { always } from '../control/state';
2
- import { Parser, Input } from '../parser';
2
+ import { Parser, Input, Output, List } from '../parser';
3
3
  import { Scope } from './scope';
4
4
 
5
5
  interface Data {
@@ -30,8 +30,8 @@ export class Backtrack {
30
30
  input.range = range;
31
31
  input.linebreak = linebreak;
32
32
  }
33
- public handle(state: boolean): void {
34
- state
33
+ public handle(output: Output<unknown>): void {
34
+ output.state
35
35
  ? this.unmemory()
36
36
  : this.backtrack();
37
37
  }
@@ -50,14 +50,26 @@ export function backtrack<T>(parser: Parser<T>): Parser<T> {
50
50
  linebreak,
51
51
  });
52
52
  output.push();
53
+ output.labels.push(new List());
54
+ output.annotations.push(new List());
55
+ output.references.push(new List());
53
56
  return output.context;
54
57
  },
55
58
  parser,
56
59
  ({ backtrack }, output) => {
57
- output.state
58
- ? output.flat()
59
- : output.pop();
60
- backtrack.handle(output.state);
60
+ backtrack.handle(output);
61
+ if (output.state) {
62
+ output.import(output.pop());
63
+ output.labels.at(-2)!.import(output.labels.pop()!);
64
+ output.annotations.at(-2)!.import(output.annotations.pop()!);
65
+ output.references.at(-2)!.import(output.references.pop()!);
66
+ }
67
+ else {
68
+ output.pop();
69
+ output.labels.pop();
70
+ output.annotations.pop();
71
+ output.references.pop();
72
+ }
61
73
  return output.context;
62
74
  },
63
75
  ]);
@@ -8,6 +8,9 @@ export class List<N extends List.Node = List.Node, I extends Input = Input, S ex
8
8
  }
9
9
  }
10
10
  public length = 0;
11
+ public isEmpty(): boolean {
12
+ return this.head === undefined;
13
+ }
11
14
  public head?: N = undefined;
12
15
  public last?: N = undefined;
13
16
  public get tail(): N | undefined {
@@ -93,18 +96,6 @@ export class List<N extends List.Node = List.Node, I extends Input = Input, S ex
93
96
  list.clear();
94
97
  return this;
95
98
  }
96
- public truncateBefore(node: N): void {
97
- assert(node.next || node.prev || this.head === this.last);
98
- if (node.prev === undefined) return;
99
- this.delete(node.prev);
100
- this.head = node;
101
- }
102
- public truncateAfter(node: N): void {
103
- assert(node.next || node.prev || this.head === this.last);
104
- if (node.next === undefined) return;
105
- this.delete(node.next);
106
- this.last = node;
107
- }
108
99
  public clear(): void {
109
100
  this.length = 0;
110
101
  this.head = this.last = undefined;
@@ -155,12 +146,19 @@ export namespace List {
155
146
  prev?: this;
156
147
  }
157
148
  }
158
- export class Node<N> implements List.Node {
149
+ export class Node<T> implements List.Node {
159
150
  constructor(
160
- public value: N,
161
- public flags: number = 0,
151
+ public value: T,
152
+ public position: number = 0,
153
+ public flags: Node.Flag = 0,
162
154
  ) {
163
155
  }
164
156
  public next?: this = undefined;
165
157
  public prev?: this = undefined;
166
158
  }
159
+ export namespace Node {
160
+ export const enum Flag {
161
+ none,
162
+ blank,
163
+ }
164
+ }
@@ -142,6 +142,9 @@ export class Output<T> {
142
142
  public state: boolean = true;
143
143
  public readonly context: Result.Succ | Result.Fail = Result.succ;
144
144
  public error?: Error = undefined;
145
+ public readonly labels: List<Node<HTMLAnchorElement>>[] = [new List()];
146
+ public readonly annotations: List<Node<HTMLElement>>[] = [new List()];
147
+ public readonly references: List<Node<HTMLElement>>[] = [new List()];
145
148
  public peek(): List<Node<T>> {
146
149
  assert(this.data.length > 0);
147
150
  return this.data.at(-1)!;
@@ -163,11 +166,6 @@ export class Output<T> {
163
166
  this.data[this.data.length - 1] = list;
164
167
  return Result.succ;
165
168
  }
166
- public flat(): Result.Succ | Result.Fail {
167
- assert(this.data.length > 1);
168
- this.import(this.data.pop()!);
169
- return Result.succ;
170
- }
171
169
  public push(list: List<Node<T>> = new List()): void {
172
170
  this.data.push(list);
173
171
  }
package/src/debug.test.ts CHANGED
@@ -6,6 +6,9 @@ export function inspect(parser: Parser<DocumentFragment | HTMLElement | string>,
6
6
  const output = new Output<DocumentFragment | HTMLElement | string>();
7
7
  for (const _ of run(parser, input, output));
8
8
  assert.deepStrictEqual(output.data, [output.data[0]]);
9
+ assert(output.labels.length === 1);
10
+ assert(output.annotations.length === 1);
11
+ assert(output.references.length === 1);
9
12
  assert(output.state || output.peek().length === 0);
10
13
  assert(!output.error);
11
14
  return !output.state ? undefined : [
@@ -1,9 +1,9 @@
1
- import { MarkdownParser } from '../../markdown';
1
+ import { DocumentParser } from '../../markdown';
2
2
  import { union, some, lazy } from '../combinator';
3
3
  import { autolink as autolink_ } from './inline/autolink';
4
4
  import { unescsource } from './source';
5
5
 
6
- export import AutolinkParser = MarkdownParser.AutolinkParser;
6
+ export import AutolinkParser = DocumentParser.AutolinkParser;
7
7
 
8
8
  export const autolink: AutolinkParser = lazy(() =>
9
9
  some(union([
@@ -77,7 +77,7 @@ describe('Unit: parser/block/blockquote', () => {
77
77
  assert.deepStrictEqual(inspect(parser, input('!> a \n b c ')), [['<blockquote><section><p> a<br> b c</p><h2>References</h2><ol class="references"></ol></section></blockquote>'], '']);
78
78
  assert.deepStrictEqual(inspect(parser, input('!>> a')), [['<blockquote><blockquote><section><p>a</p><h2>References</h2><ol class="references"></ol></section></blockquote></blockquote>'], '']);
79
79
  assert.deepStrictEqual(inspect(parser, input('!>> a\n> b')), [['<blockquote><blockquote><section><p>a</p><h2>References</h2><ol class="references"></ol></section></blockquote><section><p>b</p><h2>References</h2><ol class="references"></ol></section></blockquote>'], '']);
80
- assert.deepStrictEqual(inspect(parser, input('!> - a')), [['<blockquote><section><ul><li id="index:random:a" class="local">a</li></ul><h2>References</h2><ol class="references"></ol></section></blockquote>'], '']);
80
+ assert.deepStrictEqual(inspect(parser, input('!> - a')), [['<blockquote><section><ul><li id="index:random:a">a</li></ul><h2>References</h2><ol class="references"></ol></section></blockquote>'], '']);
81
81
  assert.deepStrictEqual(inspect(parser, input('!> ```\na\n```')), [['<blockquote><section><pre class="text">a</pre><h2>References</h2><ol class="references"></ol></section></blockquote>'], '']);
82
82
  assert.deepStrictEqual(inspect(parser, input('!> ```\n> a\n```')), [['<blockquote><section><pre class="text">a</pre><h2>References</h2><ol class="references"></ol></section></blockquote>'], '']);
83
83
  assert.deepStrictEqual(inspect(parser, input('!> ```\n> a\n> ```')), [['<blockquote><section><pre class="text">a</pre><h2>References</h2><ol class="references"></ol></section></blockquote>'], '']);
@@ -90,10 +90,10 @@ describe('Unit: parser/block/blockquote', () => {
90
90
  assert.deepStrictEqual(inspect(parser, input('!>> > a\n> b')), [['<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>'], '']);
91
91
  assert.deepStrictEqual(inspect(parser, input('!> !> a')), [['<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>'], '']);
92
92
  assert.deepStrictEqual(inspect(parser, input('!> \na')), [['<blockquote><section><p>a</p><h2>References</h2><ol class="references"></ol></section></blockquote>'], '']);
93
- assert.deepStrictEqual(inspect(parser, input('!>> ## a\n> ## a')), [['<blockquote><blockquote><section><h2 id="index:random:a" class="local">a</h2><h2>References</h2><ol class="references"></ol></section></blockquote><section><h2 id="index:random:a" class="local">a</h2><h2>References</h2><ol class="references"></ol></section></blockquote>'], '']);
94
- assert.deepStrictEqual(inspect(parser, input('!>> ~ a\n> ~ a')), [['<blockquote><blockquote><section><dl><dt id="index:random:a" class="local">a</dt><dd></dd></dl><h2>References</h2><ol class="references"></ol></section></blockquote><section><dl><dt id="index:random:a" class="local">a</dt><dd></dd></dl><h2>References</h2><ol class="references"></ol></section></blockquote>'], '']);
93
+ assert.deepStrictEqual(inspect(parser, input('!>> ## a\n> ## a')), [['<blockquote><blockquote><section><h2 id="index:random:a">a</h2><h2>References</h2><ol class="references"></ol></section></blockquote><section><h2 id="index:random:a">a</h2><h2>References</h2><ol class="references"></ol></section></blockquote>'], '']);
94
+ assert.deepStrictEqual(inspect(parser, input('!>> ~ a\n> ~ a')), [['<blockquote><blockquote><section><dl><dt id="index:random:a">a</dt><dd></dd></dl><h2>References</h2><ol class="references"></ol></section></blockquote><section><dl><dt id="index:random:a">a</dt><dd></dd></dl><h2>References</h2><ol class="references"></ol></section></blockquote>'], '']);
95
95
  assert.deepStrictEqual(inspect(parser, input('!>> ~~~figure $test-a\n>> > \n>>\n~~~\n> ~~~figure $test-a\n> > \n>\n[#a]\n~~~')), [['<blockquote><blockquote><section><figure data-type="quote" data-label="test-a" data-group="test" data-number="1" id="label:random:test-a"><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" id="label:random:test-a"><figcaption><span class="figindex">Test 1. </span><span class="figtext"><a class="index" href="#index:random:a">a</a></span></figcaption><div><blockquote></blockquote></div></figure><h2>References</h2><ol class="references"></ol></section></blockquote>'], '']);
96
- assert.deepStrictEqual(inspect(parser, input('!>> ((a))\n> ((a))')), [['<blockquote><blockquote><section><p><sup class="annotation local" id="annotation:random:ref:a:1" title="a"><a href="#annotation:random:def:a:1">*1</a></sup></p><ol class="annotations"><li id="annotation:random:def:a:1" data-marker="*1"><span>a</span><sup><a href="#annotation:random:ref:a:1">^1</a></sup></li></ol><h2>References</h2><ol class="references"></ol></section></blockquote><section><p><sup class="annotation local" id="annotation:random:ref:a:1" title="a"><a href="#annotation:random:def:a:1">*1</a></sup></p><ol class="annotations"><li id="annotation:random:def:a:1" data-marker="*1"><span>a</span><sup><a href="#annotation:random:ref:a:1">^1</a></sup></li></ol><h2>References</h2><ol class="references"></ol></section></blockquote>'], '']);
96
+ assert.deepStrictEqual(inspect(parser, input('!>> ((a))\n> ((a))')), [['<blockquote><blockquote><section><p><sup class="annotation" id="annotation:random:ref:a:1" title="a"><a href="#annotation:random:def:a:1">*1</a></sup></p><ol class="annotations"><li id="annotation:random:def:a:1" data-marker="*1"><span>a</span><sup><a href="#annotation:random:ref:a:1">^1</a></sup></li></ol><h2>References</h2><ol class="references"></ol></section></blockquote><section><p><sup class="annotation" id="annotation:random:ref:a:1" title="a"><a href="#annotation:random:def:a:1">*1</a></sup></p><ol class="annotations"><li id="annotation:random:def:a:1" data-marker="*1"><span>a</span><sup><a href="#annotation:random:ref:a:1">^1</a></sup></li></ol><h2>References</h2><ol class="references"></ol></section></blockquote>'], '']);
97
97
  });
98
98
 
99
99
  });
@@ -54,7 +54,8 @@ const markdown: BlockquoteParser.MarkdownParser = lazy(() => fmap(
54
54
  const doc = output.pop().head!.value;
55
55
  if (!doc.firstChild) return output.context;
56
56
  return output.append(new Node(html('section', [
57
- doc,
57
+ // DocumentFragmentを追加すると異常に重くなるので避ける
58
+ ...doc.children,
58
59
  html('h2', 'References'),
59
60
  notes!.references,
60
61
  ])));
@@ -14,9 +14,9 @@ describe('Unit: parser/block/extension/aside', () => {
14
14
  });
15
15
 
16
16
  it('valid', () => {
17
- assert.deepStrictEqual(inspect(parser, input('~~~aside\n# 0\n~~~')), [['<aside id="index::0" class="aside"><h1 id="index:random:0" class="local">0</h1><h2>References</h2><ol class="references"></ol></aside>'], '']);
18
- assert.deepStrictEqual(inspect(parser, input('~~~aside\n## 0\n~~~')), [['<aside id="index::0" class="aside"><h2 id="index:random:0" class="local">0</h2><h2>References</h2><ol class="references"></ol></aside>'], '']);
19
- assert.deepStrictEqual(inspect(parser, input('~~~aside\n# 0\n\n$-0.0\n\n## 1\n\n$test-a\n> \n~~~')), [['<aside id="index::0" class="aside"><h1 id="index:random:0" class="local">0</h1><figure data-label="$-0.0" data-group="$" hidden="" data-number="0.0"></figure><h2 id="index:random:1" class="local">1</h2><figure data-type="quote" data-label="test-a" data-group="test" data-number="1.1" id="label:random:test-a"><figcaption><span class="figindex">Test 1.1. </span><span class="figtext"></span></figcaption><div><blockquote></blockquote></div></figure><h2>References</h2><ol class="references"></ol></aside>'], '']);
17
+ assert.deepStrictEqual(inspect(parser, input('~~~aside\n# 0\n~~~')), [['<aside id="index::0" class="aside"><h1 id="index:random:0">0</h1><h2>References</h2><ol class="references"></ol></aside>'], '']);
18
+ assert.deepStrictEqual(inspect(parser, input('~~~aside\n## 0\n~~~')), [['<aside id="index::0" class="aside"><h2 id="index:random:0">0</h2><h2>References</h2><ol class="references"></ol></aside>'], '']);
19
+ assert.deepStrictEqual(inspect(parser, input('~~~aside\n# 0\n\n$-0.0\n\n## 1\n\n$test-a\n> \n~~~')), [['<aside id="index::0" class="aside"><h1 id="index:random:0">0</h1><figure data-label="$-0.0" data-group="$" hidden="" data-number="0.0"></figure><h2 id="index:random:1">1</h2><figure data-type="quote" data-label="test-a" data-group="test" data-number="1.1" id="label:random:test-a"><figcaption><span class="figindex">Test 1.1. </span><span class="figtext"></span></figcaption><div><blockquote></blockquote></div></figure><h2>References</h2><ol class="references"></ol></aside>'], '']);
20
20
  });
21
21
 
22
22
  });
@@ -72,7 +72,8 @@ export const aside: ExtensionParser.AsideParser = block(recursion(Recursion.bloc
72
72
  class: 'aside',
73
73
  },
74
74
  [
75
- doc,
75
+ // DocumentFragmentを追加すると異常に重くなるので避ける
76
+ ...doc.children,
76
77
  html('h2', 'References'),
77
78
  notes!.references,
78
79
  ])));
@@ -21,9 +21,9 @@ describe('Unit: parser/block/extension/example', () => {
21
21
  assert.deepStrictEqual(inspect(parser, input('~~~example/markdown\n++a\nb++\n~~~')), [['<aside class="example" data-type="markdown"><pre translate="no">++a\nb++</pre><hr><section><p><ins>a<br>b</ins></p><h2>References</h2><ol class="references"></ol></section></aside>'], '']);
22
22
  assert.deepStrictEqual(inspect(parser, input('~~~example/markdown\n$fig-a\n!https://host\n~~~')), [['<aside class="example" data-type="markdown"><pre translate="no">$fig-a\n!https://host</pre><hr><section><figure data-type="media" data-label="fig-a" data-group="fig" data-number="1" id="label:random:fig-a"><figcaption><span class="figindex">Fig. 1. </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><h2>References</h2><ol class="references"></ol></section></aside>'], '']);
23
23
  assert.deepStrictEqual(inspect(parser, input('~~~example/markdown\n[$fig-a]\n!https://host\n~~~')), [['<aside class="example" data-type="markdown"><pre translate="no">[$fig-a]\n!https://host</pre><hr><section><figure data-type="media" data-label="fig-a" data-group="fig" data-number="1" id="label:random:fig-a"><figcaption><span class="figindex">Fig. 1. </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><h2>References</h2><ol class="references"></ol></section></aside>'], '']);
24
- assert.deepStrictEqual(inspect(parser, input('~~~example/markdown\n## a\n~~~')), [['<aside class="example" data-type="markdown"><pre translate="no">## a</pre><hr><section><h2 id="index:random:a" class="local">a</h2><h2>References</h2><ol class="references"></ol></section></aside>'], '']);
25
- assert.deepStrictEqual(inspect(parser, input('~~~example/markdown\n~ a\n~~~')), [['<aside class="example" data-type="markdown"><pre translate="no">~ a</pre><hr><section><dl><dt id="index:random:a" class="local">a</dt><dd></dd></dl><h2>References</h2><ol class="references"></ol></section></aside>'], '']);
26
- assert.deepStrictEqual(inspect(parser, input('~~~example/markdown\n((a))[[b]]\n~~~')), [['<aside class="example" data-type="markdown"><pre translate="no">((a))[[b]]</pre><hr><section><p><sup class="annotation local" id="annotation:random:ref:a:1" title="a"><a href="#annotation:random:def:a:1">*1</a></sup><sup class="reference local" id="reference:random:ref:b:1" title="b"><a href="#reference:random:def:b">[1]</a></sup></p><ol class="annotations"><li id="annotation:random:def:a:1" data-marker="*1"><span>a</span><sup><a href="#annotation:random:ref:a:1">^1</a></sup></li></ol><h2>References</h2><ol class="references"><li id="reference:random:def:b"><span>b</span><sup><a href="#reference:random:ref:b:1">^1</a></sup></li></ol></section></aside>'], '']);
24
+ assert.deepStrictEqual(inspect(parser, input('~~~example/markdown\n## a\n~~~')), [['<aside class="example" data-type="markdown"><pre translate="no">## a</pre><hr><section><h2 id="index:random:a">a</h2><h2>References</h2><ol class="references"></ol></section></aside>'], '']);
25
+ assert.deepStrictEqual(inspect(parser, input('~~~example/markdown\n~ a\n~~~')), [['<aside class="example" data-type="markdown"><pre translate="no">~ a</pre><hr><section><dl><dt id="index:random:a">a</dt><dd></dd></dl><h2>References</h2><ol class="references"></ol></section></aside>'], '']);
26
+ assert.deepStrictEqual(inspect(parser, input('~~~example/markdown\n((a))[[b]]\n~~~')), [['<aside class="example" data-type="markdown"><pre translate="no">((a))[[b]]</pre><hr><section><p><sup class="annotation" id="annotation:random:ref:a:1" title="a"><a href="#annotation:random:def:a:1">*1</a></sup><sup class="reference" id="reference:random:ref:b:1" title="b"><a href="#reference:random:def:b">[1]</a></sup></p><ol class="annotations"><li id="annotation:random:def:a:1" data-marker="*1"><span>a</span><sup><a href="#annotation:random:ref:a:1">^1</a></sup></li></ol><h2>References</h2><ol class="references"><li id="reference:random:def:b"><span>b</span><sup><a href="#reference:random:ref:b:1">^1</a></sup></li></ol></section></aside>'], '']);
27
27
  assert.deepStrictEqual(inspect(parser, input('~~~~example/markdown\na\n~~~~')), [['<aside class="example" data-type="markdown"><pre translate="no">a</pre><hr><section><p>a</p><h2>References</h2><ol class="references"></ol></section></aside>'], '']);
28
28
  assert.deepStrictEqual(inspect(parser, input('~~~example/math\na\n~~~')), [['<aside class="example" data-type="math"><pre translate="no">a</pre><hr><div class="math" translate="no">$$\na\n$$</div></aside>'], '']);
29
29
  });
@@ -66,6 +66,7 @@ const contMD: Result<DocumentFragment | HTMLElement, Input<Memory>> = [
66
66
  (input, output) => {
67
67
  const { notes } = input;
68
68
  input = input.scope.pop();
69
+ const doc = output.pop().head!.value;
69
70
  return output.append(
70
71
  new Node(html('aside',
71
72
  { class: 'example', 'data-type': 'markdown' },
@@ -73,7 +74,8 @@ const contMD: Result<DocumentFragment | HTMLElement, Input<Memory>> = [
73
74
  html('pre', { translate: 'no' }, input.memory.body.slice(0, input.memory.body.at(-2) === '\r' ? -2 : -1)),
74
75
  html('hr'),
75
76
  html('section', [
76
- output.pop().head!.value,
77
+ // DocumentFragmentを追加すると異常に重くなるので避ける
78
+ ...doc.children,
77
79
  html('h2', 'References'),
78
80
  notes!.references,
79
81
  ]),
@@ -1,8 +1,8 @@
1
1
  import { header } from './header';
2
- import { input } from './context';
3
- import { inspect } from '../debug.test';
2
+ import { input } from '../context';
3
+ import { inspect } from '../../debug.test';
4
4
 
5
- describe('Unit: parser/header', () => {
5
+ describe('Unit: parser/block/header', () => {
6
6
  describe('header', () => {
7
7
  const parser = header;
8
8
 
@@ -1,12 +1,12 @@
1
- import { MarkdownParser } from '../../markdown';
2
- import { List, Node } from '../combinator/parser';
3
- import { union, inits, some, scope, block, line, validate, focus, clear, lazy, fmap } from '../combinator';
4
- import { str } from './source';
5
- import { unwrap, invalid } from './util';
1
+ import { HeaderParser } from '../block';
2
+ import { List, Node } from '../../combinator/parser';
3
+ import { union, inits, some, scope, block, line, validate, focus, clear, lazy, fmap } from '../../combinator';
4
+ import { str } from '../source';
5
+ import { unwrap, invalid } from '../util';
6
6
  import { ReadonlyURL } from 'spica/url';
7
7
  import { html, defrag } from 'typed-dom/dom';
8
8
 
9
- export const header: MarkdownParser.HeaderParser = lazy(() => validate(
9
+ export const header: HeaderParser = lazy(() => validate(
10
10
  /---+[^\S\r\n]*\r?\n(?=\S)/y,
11
11
  inits([
12
12
  block(
@@ -40,7 +40,7 @@ export const header: MarkdownParser.HeaderParser = lazy(() => validate(
40
40
  clear(str(/[^\S\r\n]*\r?\n/y)),
41
41
  ])));
42
42
 
43
- const field: MarkdownParser.HeaderParser.FieldParser = line((input, output) => {
43
+ const field: HeaderParser.FieldParser = line((input, output) => {
44
44
  const { source, position } = input;
45
45
  const name = source.slice(position, source.indexOf(':', position));
46
46
  const value = source.slice(position + name.length + 1).trim();
@@ -1,6 +1,5 @@
1
1
  import { ReplyParser } from '../../block';
2
2
  import { List, Node } from '../../../combinator/parser';
3
- import { Flag } from '../../node';
4
3
  import { union, line, focus, open, fmap } from '../../../combinator';
5
4
  import { anchor } from '../../inline/autolink/anchor';
6
5
  import { str } from '../../source';
@@ -20,7 +19,7 @@ export const cite: ReplyParser.CiteParser = line(fmap(
20
19
  focus(/>>https?:\/\/\S+(?=\s*$)/y, ({ source }, output) => output.append(new Node(html('a', { class: 'anchor', href: source.slice(2).trimEnd(), target: '_blank' }, source)))),
21
20
  focus(/>>\S+(?=\s*$)/y, ({ source }, output) => output.append(new Node(source))),
22
21
  ])),
23
- nodes => {
22
+ (nodes, { position }) => {
24
23
  const quotes = nodes.head!.value as string;
25
24
  const node = nodes.last!.value;
26
25
  return new List([
@@ -34,6 +33,6 @@ export const cite: ReplyParser.CiteParser = line(fmap(
34
33
  ? define(node, { 'data-depth': `${quotes.length + 1}` }, node.innerText.slice(1))
35
34
  : node.slice(1),
36
35
  ]))),
37
- new Node(html('br'), Flag.blank),
36
+ new Node(html('br'), position, Node.Flag.blank),
38
37
  ]);
39
38
  }));
@@ -1,6 +1,5 @@
1
1
  import { ReplyParser } from '../../block';
2
2
  import { List, Node } from '../../../combinator/parser';
3
- import { Flag } from '../../node';
4
3
  import { union, some, scope, block, validate, rewrite, fmap } from '../../../combinator';
5
4
  import { math } from '../../inline/math';
6
5
  import { autolink } from '../../inline/autolink';
@@ -23,7 +22,7 @@ export const quote: ReplyParser.QuoteParser = block(fmap(
23
22
  ])),
24
23
  true)),
25
24
  (ns, { source, position }) => new List([
26
- new Node(source[position - 1] === '\n' ? ns.pop()!.value as HTMLBRElement : html('br'), Flag.blank),
25
+ new Node(source[position - 1] === '\n' ? ns.pop()!.value as HTMLBRElement : html('br'), position, Node.Flag.blank),
27
26
  new Node(html('span', { class: 'quote' }, defrag(unwrap(ns)))),
28
27
  ].reverse())),
29
28
  false);
@@ -1,6 +1,5 @@
1
1
  import { ReplyParser } from '../block';
2
2
  import { List, Node } from '../../combinator/parser';
3
- import { Flag } from '../node';
4
3
  import { union, some, block, validate, rewrite, fmap } from '../../combinator';
5
4
  import { cite, syntax as csyntax } from './reply/cite';
6
5
  import { quote, syntax as qsyntax } from './reply/quote';
@@ -21,6 +20,6 @@ export const reply: ReplyParser = block(validate(csyntax, fmap(
21
20
  visualize(fmap(some(inline), (ns, { source, position }) =>
22
21
  source[position - 1] === '\n'
23
22
  ? ns
24
- : ns.push(new Node(html('br'), Flag.blank)))))
23
+ : ns.push(new Node(html('br'), position, Node.Flag.blank)))))
25
24
  ])),
26
25
  ns => new List([new Node(html('p', defrag(unwrap(trimBlankNodeEnd(ns)))))]))));