securemark 0.283.3 → 0.283.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 (62) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/index.js +142 -107
  3. package/markdown.d.ts +16 -8
  4. package/package.json +1 -1
  5. package/src/combinator/control/manipulation/surround.ts +4 -4
  6. package/src/combinator/data/parser/context/delimiter.ts +20 -20
  7. package/src/combinator/data/parser/context.ts +4 -3
  8. package/src/parser/block/blockquote.ts +1 -1
  9. package/src/parser/block/dlist.ts +6 -6
  10. package/src/parser/block/extension/aside.ts +1 -1
  11. package/src/parser/block/extension/example.ts +2 -2
  12. package/src/parser/block/extension/figure.ts +3 -3
  13. package/src/parser/block/extension/table.ts +5 -5
  14. package/src/parser/block/heading.ts +6 -6
  15. package/src/parser/block/ilist.ts +1 -1
  16. package/src/parser/block/olist.ts +1 -1
  17. package/src/parser/block/paragraph.ts +3 -3
  18. package/src/parser/block/reply.ts +2 -2
  19. package/src/parser/block/sidefence.ts +1 -1
  20. package/src/parser/block/table.ts +4 -4
  21. package/src/parser/block/ulist.ts +1 -1
  22. package/src/parser/block.ts +4 -3
  23. package/src/parser/context.ts +18 -9
  24. package/src/parser/inline/annotation.ts +7 -4
  25. package/src/parser/inline/autolink/account.ts +15 -14
  26. package/src/parser/inline/autolink/anchor.ts +14 -13
  27. package/src/parser/inline/autolink/channel.ts +6 -3
  28. package/src/parser/inline/autolink/email.ts +4 -3
  29. package/src/parser/inline/autolink/hashnum.ts +11 -9
  30. package/src/parser/inline/autolink/hashtag.ts +11 -9
  31. package/src/parser/inline/autolink/url.ts +24 -18
  32. package/src/parser/inline/autolink.ts +4 -5
  33. package/src/parser/inline/bracket.ts +6 -6
  34. package/src/parser/inline/code.ts +1 -1
  35. package/src/parser/inline/deletion.ts +1 -1
  36. package/src/parser/inline/emphasis.ts +1 -1
  37. package/src/parser/inline/emstrong.ts +1 -1
  38. package/src/parser/inline/extension/index.ts +9 -6
  39. package/src/parser/inline/extension/indexer.ts +1 -1
  40. package/src/parser/inline/extension/label.ts +1 -1
  41. package/src/parser/inline/extension/placeholder.ts +1 -1
  42. package/src/parser/inline/html.ts +45 -43
  43. package/src/parser/inline/htmlentity.ts +3 -3
  44. package/src/parser/inline/insertion.ts +1 -1
  45. package/src/parser/inline/link.test.ts +7 -2
  46. package/src/parser/inline/link.ts +11 -15
  47. package/src/parser/inline/mark.ts +1 -1
  48. package/src/parser/inline/math.ts +1 -1
  49. package/src/parser/inline/media.ts +4 -4
  50. package/src/parser/inline/reference.ts +7 -4
  51. package/src/parser/inline/remark.ts +4 -4
  52. package/src/parser/inline/ruby.ts +7 -6
  53. package/src/parser/inline/shortmedia.ts +1 -1
  54. package/src/parser/inline/strong.ts +1 -1
  55. package/src/parser/inline/template.ts +2 -2
  56. package/src/parser/inline.test.ts +1 -0
  57. package/src/parser/segment.ts +3 -2
  58. package/src/parser/source/escapable.ts +2 -2
  59. package/src/parser/source/str.ts +1 -1
  60. package/src/parser/source/text.ts +3 -3
  61. package/src/parser/source/unescapable.ts +4 -2
  62. package/src/parser/visibility.ts +14 -13
@@ -1,7 +1,7 @@
1
1
  import { AutolinkParser } from '../../inline';
2
- import { union, constraint, validate, focus, convert, fmap, lazy } from '../../../combinator';
3
- import { unsafelink } from '../link';
4
2
  import { State } from '../../context';
3
+ import { union, syntax, constraint, validate, focus, convert, fmap, lazy } from '../../../combinator';
4
+ import { unsafelink } from '../link';
5
5
  import { define } from 'typed-dom/dom';
6
6
 
7
7
  // Timeline(pseudonym): user/tid
@@ -14,16 +14,17 @@ import { define } from 'typed-dom/dom';
14
14
  // 内部表現はUnixTimeに統一する(時系列順)
15
15
  // 外部表現は投稿ごとに投稿者の投稿時のタイムゾーンに統一する(非時系列順)
16
16
 
17
- export const anchor: AutolinkParser.AnchorParser = lazy(() => validate('>>', fmap(
18
- constraint(State.shortcut, false,
17
+ export const anchor: AutolinkParser.AnchorParser = lazy(() => validate('>>',
19
18
  focus(
20
19
  /^>>(?:[a-z][0-9a-z]*(?:-[0-9a-z]+)*\/)?[0-9a-z]+(?:-[0-9a-z]+)*(?![0-9a-z@#:])/i,
21
- convert(
22
- source =>
23
- `[${source}]{ ${
24
- source.includes('/')
25
- ? `/@${source.slice(2).replace('/', '/timeline?at=')}`
26
- : `?at=${source.slice(2)}`
27
- } }`,
28
- union([unsafelink])))),
29
- ([el]) => [define(el, { class: 'anchor' })])));
20
+ union([
21
+ constraint(State.autolink, false, syntax(0, State.autolink, fmap(convert(
22
+ source =>
23
+ `[${source}]{ ${source.includes('/')
24
+ ? `/@${source.slice(2).replace('/', '/timeline?at=')}`
25
+ : `?at=${source.slice(2)}`
26
+ } }`,
27
+ unsafelink),
28
+ ([el]) => [define(el, { class: 'anchor' })]))),
29
+ ({ source }) => [[source], ''],
30
+ ]))));
@@ -1,5 +1,6 @@
1
1
  import { AutolinkParser } from '../../inline';
2
- import { sequence, some, validate, bind } from '../../../combinator';
2
+ import { State } from '../../context';
3
+ import { sequence, some, constraint, validate, bind } from '../../../combinator';
3
4
  import { account } from './account';
4
5
  import { hashtag } from './hashtag';
5
6
  import { stringify } from '../../util';
@@ -7,7 +8,8 @@ import { define } from 'typed-dom/dom';
7
8
 
8
9
  // https://example/@user?ch=a+b must be a user channel page or a redirect page going there.
9
10
 
10
- export const channel: AutolinkParser.ChannelParser = validate('@', bind(
11
+ export const channel: AutolinkParser.ChannelParser = validate('@',
12
+ constraint(State.autolink, false, bind(
11
13
  sequence([
12
14
  account,
13
15
  some(hashtag),
@@ -15,6 +17,7 @@ export const channel: AutolinkParser.ChannelParser = validate('@', bind(
15
17
  (es, rest) => {
16
18
  const source = stringify(es);
17
19
  const el = es[0];
20
+ if (typeof el === 'string') return [es, rest];
18
21
  const url = `${el.getAttribute('href')}?ch=${source.slice(source.indexOf('#') + 1).replace(/#/g, '+')}`;
19
22
  return [[define(el, { class: 'channel', href: url }, source)], rest];
20
- }));
23
+ })));
@@ -1,7 +1,7 @@
1
1
  import { AutolinkParser } from '../../inline';
2
- import { creation, verify, rewrite } from '../../../combinator';
2
+ import { State, Recursion } from '../../context';
3
+ import { syntax, creation, constraint, verify, rewrite } from '../../../combinator';
3
4
  import { str } from '../../source';
4
- import { Recursion } from '../../context';
5
5
  import { html } from 'typed-dom/dom';
6
6
 
7
7
  // https://html.spec.whatwg.org/multipage/input.html
@@ -9,4 +9,5 @@ import { html } from 'typed-dom/dom';
9
9
  export const email: AutolinkParser.EmailParser = creation(1, Recursion.ignore, rewrite(verify(
10
10
  str(/^[0-9a-z](?:[_.+-](?=[0-9a-z])|[0-9a-z]){0,255}@[0-9a-z](?:(?:[0-9a-z]|-(?=[0-9a-z])){0,61}[0-9a-z])?(?:\.[0-9a-z](?:(?:[0-9a-z]|-(?=[0-9a-z])){0,61}[0-9a-z])?)*(?![0-9a-z])/i),
11
11
  ([source]) => source.length <= 255),
12
- ({ source }) => [[html('a', { class: 'email', href: `mailto:${source}` }, source)], '']));
12
+ constraint(State.autolink, false, syntax(0, State.autolink,
13
+ ({ source }) => [[html('a', { class: 'email', href: `mailto:${source}` }, source)], '']))));
@@ -1,15 +1,17 @@
1
1
  import { AutolinkParser } from '../../inline';
2
- import { union, constraint, rewrite, open, convert, fmap, lazy } from '../../../combinator';
2
+ import { State } from '../../context';
3
+ import { union, syntax, constraint, rewrite, open, convert, fmap, lazy } from '../../../combinator';
3
4
  import { unsafelink } from '../link';
4
5
  import { emoji } from './hashtag';
5
6
  import { str } from '../../source';
6
- import { State } from '../../context';
7
7
  import { define } from 'typed-dom/dom';
8
8
 
9
- export const hashnum: AutolinkParser.HashnumParser = lazy(() => fmap(rewrite(
10
- constraint(State.shortcut, false,
11
- open('#', str(new RegExp(/^[0-9]{1,9}(?![^\p{C}\p{S}\p{P}\s]|emoji|['_])/u.source.replace(/emoji/, emoji), 'u')))),
12
- convert(
13
- source => `[${source}]{ ${source.slice(1)} }`,
14
- union([unsafelink]))),
15
- ([el]) => [define(el, { class: 'hashnum', href: null })]));
9
+ export const hashnum: AutolinkParser.HashnumParser = lazy(() => rewrite(
10
+ open('#', str(new RegExp(/^[0-9]{1,9}(?![^\p{C}\p{S}\p{P}\s]|emoji|['_])/u.source.replace(/emoji/, emoji), 'u'))),
11
+ union([
12
+ constraint(State.autolink, false, syntax(0, State.autolink, fmap(convert(
13
+ source => `[${source}]{ ${source.slice(1)} }`,
14
+ unsafelink),
15
+ ([el]) => [define(el, { class: 'hashnum', href: null })]))),
16
+ ({ source }) => [[source], ''],
17
+ ])));
@@ -1,8 +1,8 @@
1
1
  import { AutolinkParser } from '../../inline';
2
- import { union, constraint, rewrite, open, convert, fmap, lazy } from '../../../combinator';
2
+ import { State } from '../../context';
3
+ import { union, syntax, constraint, rewrite, open, convert, fmap, lazy } from '../../../combinator';
3
4
  import { unsafelink } from '../link';
4
5
  import { str } from '../../source';
5
- import { State } from '../../context';
6
6
  import { define } from 'typed-dom/dom';
7
7
 
8
8
  // https://example/hashtags/a must be a hashtag page or a redirect page going there.
@@ -10,15 +10,17 @@ import { define } from 'typed-dom/dom';
10
10
  // https://github.com/tc39/proposal-regexp-unicode-property-escapes#matching-emoji
11
11
  export const emoji = String.raw`\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Emoji_Presentation}|\p{Emoji}\uFE0F`;
12
12
 
13
- export const hashtag: AutolinkParser.HashtagParser = lazy(() => fmap(rewrite(
14
- constraint(State.shortcut, false,
13
+ export const hashtag: AutolinkParser.HashtagParser = lazy(() => rewrite(
15
14
  open(
16
15
  '#',
17
16
  str(new RegExp([
18
17
  /^(?!['_])(?=(?:[0-9]{1,9})?(?:[^\d\p{C}\p{S}\p{P}\s]|emoji|'|_(?=[^\p{C}\p{S}\p{P}\s]|emoji|')))/u.source,
19
18
  /(?:[^\p{C}\p{S}\p{P}\s]|emoji|'|_(?=[^\p{C}\p{S}\p{P}\s]|emoji|'))+/u.source,
20
- ].join('').replace(/emoji/g, emoji), 'u')))),
21
- convert(
22
- source => `[${source}]{ ${`/hashtags/${source.slice(1)}`} }`,
23
- union([unsafelink]))),
24
- ([el]) => [define(el, { class: 'hashtag' })]));
19
+ ].join('').replace(/emoji/g, emoji), 'u'))),
20
+ union([
21
+ constraint(State.autolink, false, syntax(0, State.autolink, fmap(convert(
22
+ source => `[${source}]{ ${`/hashtags/${source.slice(1)}`} }`,
23
+ unsafelink),
24
+ ([el]) => [define(el, { class: 'hashtag' })]))),
25
+ ({ source }) => [[source], ''],
26
+ ])));
@@ -1,8 +1,8 @@
1
1
  import { AutolinkParser } from '../../inline';
2
- import { union, tails, some, creation, precedence, validate, focus, rewrite, convert, surround, open, lazy } from '../../../combinator';
2
+ import { State, Recursion, Backtrack } from '../../context';
3
+ import { union, tails, some, syntax, creation, precedence, constraint, validate, focus, rewrite, convert, surround, open, lazy } from '../../../combinator';
3
4
  import { unsafelink } from '../link';
4
5
  import { linebreak, unescsource, str } from '../../source';
5
- import { Backtrack, Recursion } from '../../context';
6
6
 
7
7
  const closer = /^[-+*=~^_,.;:!?]*(?=[\\"`|\[\](){}<>]|$)/;
8
8
 
@@ -10,24 +10,30 @@ export const url: AutolinkParser.UrlParser = lazy(() => validate(['http://', 'ht
10
10
  open(
11
11
  /^https?:\/\/(?=[\x21-\x7E])/,
12
12
  focus(/^[\x21-\x7E]+/, some(union([bracket, some(unescsource, closer)])))),
13
- convert(
14
- url => `{ ${url} }`,
15
- union([unsafelink])))));
13
+ union([
14
+ constraint(State.autolink, false, syntax(0, State.autolink, convert(
15
+ url => `{ ${url} }`,
16
+ unsafelink))),
17
+ ({ source }) => [[source], ''],
18
+ ]))));
16
19
 
17
20
  export const lineurl: AutolinkParser.UrlParser.LineUrlParser = lazy(() => open(
18
21
  linebreak,
19
- tails([
20
- str('!'),
21
- focus(
22
- /^https?:\/\/\S+(?=[^\S\n]*(?:$|\n))/,
23
- convert(
24
- url => `{ ${url} }`,
25
- unsafelink)),
26
- ])));
22
+ focus(
23
+ /^!?https?:\/\/\S+(?=[^\S\n]*(?:$|\n))/,
24
+ tails([
25
+ str('!'),
26
+ union([
27
+ constraint(State.autolink, false, syntax(0, State.autolink, convert(
28
+ url => `{ ${url} }`,
29
+ unsafelink))),
30
+ ({ source }) => [[source], ''],
31
+ ]),
32
+ ]))));
27
33
 
28
- const bracket: AutolinkParser.UrlParser.BracketParser = lazy(() => creation(0, Recursion.terminal, precedence(1, union([
29
- surround(str('('), some(union([bracket, unescsource]), ')'), str(')'), true, undefined, undefined, 3 | Backtrack.url),
30
- surround(str('['), some(union([bracket, unescsource]), ']'), str(']'), true, undefined, undefined, 3 | Backtrack.url),
31
- surround(str('{'), some(union([bracket, unescsource]), '}'), str('}'), true, undefined, undefined, 3 | Backtrack.url),
34
+ const bracket: AutolinkParser.UrlParser.BracketParser = lazy(() => creation(0, Recursion.terminal, union([
35
+ surround(str('('), precedence(1, some(union([bracket, unescsource]), ')')), str(')'), true, undefined, undefined, 3 | Backtrack.url),
36
+ surround(str('['), precedence(1, some(union([bracket, unescsource]), ']')), str(']'), true, undefined, undefined, 3 | Backtrack.url),
37
+ surround(str('{'), precedence(1, some(union([bracket, unescsource]), '}')), str('}'), true, undefined, undefined, 3 | Backtrack.url),
32
38
  surround(str('"'), precedence(2, some(unescsource, '"')), str('"'), true, undefined, undefined, 3 | Backtrack.url),
33
- ]))));
39
+ ])));
@@ -1,5 +1,6 @@
1
1
  import { AutolinkParser } from '../inline';
2
- import { union, some, syntax, constraint, validate, lazy, fmap } from '../../combinator';
2
+ import { State } from '../context';
3
+ import { union, some, syntax, validate, lazy, fmap } from '../../combinator';
3
4
  import { url, lineurl } from './autolink/url';
4
5
  import { email } from './autolink/email';
5
6
  import { channel } from './autolink/channel';
@@ -8,13 +9,11 @@ import { hashtag, emoji } from './autolink/hashtag';
8
9
  import { hashnum } from './autolink/hashnum';
9
10
  import { anchor } from './autolink/anchor';
10
11
  import { str } from '../source';
11
- import { State } from '../context';
12
12
  import { stringify } from '../util';
13
13
 
14
14
  export const autolink: AutolinkParser = lazy(() =>
15
15
  validate(/^(?:[@#>0-9a-z]|\S[#>]|[\r\n]!?https?:\/\/)/iu,
16
- constraint(State.autolink, false,
17
- syntax(0, ~State.shortcut,
16
+ syntax(0, ~State.autolink,
18
17
  union([
19
18
  some(union([lineurl])),
20
19
  fmap(some(union([
@@ -37,4 +36,4 @@ export const autolink: AutolinkParser = lazy(() =>
37
36
  anchor,
38
37
  ])),
39
38
  ns => ns.length === 1 ? ns : [stringify(ns)]),
40
- ])))));
39
+ ]))));
@@ -1,8 +1,8 @@
1
1
  import { BracketParser } from '../inline';
2
+ import { State, Recursion, Backtrack } from '../context';
2
3
  import { union, some, syntax, creation, surround, lazy } from '../../combinator';
3
4
  import { inline } from '../inline';
4
5
  import { str } from '../source';
5
- import { State, Recursion, Backtrack } from '../context';
6
6
  import { unshift, push } from 'spica/array';
7
7
  import { html, defrag } from 'typed-dom/dom';
8
8
 
@@ -32,19 +32,19 @@ export const bracket: BracketParser = lazy(() => union([
32
32
  undefined,
33
33
  ([as, bs = []], rest) => [unshift(as, bs), rest]),
34
34
  // 改行禁止はバックトラックなしでは内側の構文を破壊するため安易に行えない。
35
- surround(str('"'), creation(0, Recursion.bracket, syntax(2, State.none, some(inline, '"', [[/^\\?\n/, 9], ['"', 2]]))), str('"'), true,
35
+ surround(str('"'), creation(0, Recursion.bracket, syntax(2, State.none, some(inline, '"', [['\n', 9], ['"', 2]]))), str('"'), true,
36
36
  undefined,
37
37
  ([as, bs = []], rest) => [unshift(as, bs), rest]),
38
- surround(str('“'), creation(0, Recursion.bracket, syntax(2, State.none, some(inline, '”', [[/^\\?\n/, 9], ['”', 2]]))), str('”'), true,
38
+ surround(str('“'), creation(0, Recursion.bracket, syntax(2, State.none, some(inline, '”', [['\n', 9], ['”', 2]]))), str('”'), true,
39
39
  undefined,
40
40
  ([as, bs = []], rest) => [unshift(as, bs), rest]),
41
- surround(str('‘'), creation(0, Recursion.bracket, syntax(2, State.none, some(inline, '’', [[/^\\?\n/, 9], ['’', 2]]))), str('’'), true,
41
+ surround(str('‘'), creation(0, Recursion.bracket, syntax(2, State.none, some(inline, '’', [['\n', 9], ['’', 2]]))), str('’'), true,
42
42
  undefined,
43
43
  ([as, bs = []], rest) => [unshift(as, bs), rest]),
44
- surround(str('「'), creation(0, Recursion.bracket, syntax(2, State.none, some(inline, '」', [[/^\\?\n/, 9], ['」', 2]]))), str('」'), true,
44
+ surround(str('「'), creation(0, Recursion.bracket, syntax(2, State.none, some(inline, '」', [['\n', 9], ['」', 2]]))), str('」'), true,
45
45
  undefined,
46
46
  ([as, bs = []], rest) => [unshift(as, bs), rest]),
47
- surround(str('『'), creation(0, Recursion.bracket, syntax(2, State.none, some(inline, '』', [[/^\\?\n/, 9], ['』', 2]]))), str('』'), true,
47
+ surround(str('『'), creation(0, Recursion.bracket, syntax(2, State.none, some(inline, '』', [['\n', 9], ['』', 2]]))), str('』'), true,
48
48
  undefined,
49
49
  ([as, bs = []], rest) => [unshift(as, bs), rest]),
50
50
  ]));
@@ -1,6 +1,6 @@
1
1
  import { CodeParser } from '../inline';
2
- import { creation, validate, match } from '../../combinator';
3
2
  import { Recursion } from '../context';
3
+ import { creation, validate, match } from '../../combinator';
4
4
  import { html } from 'typed-dom/dom';
5
5
 
6
6
  export const code: CodeParser = creation(1, Recursion.ignore, validate('`', match(
@@ -1,8 +1,8 @@
1
1
  import { DeletionParser } from '../inline';
2
+ import { State, Recursion } from '../context';
2
3
  import { union, some, syntax, creation, surround, open, lazy } from '../../combinator';
3
4
  import { inline } from '../inline';
4
5
  import { str } from '../source';
5
- import { State, Recursion } from '../context';
6
6
  import { blankWith } from '../visibility';
7
7
  import { unshift } from 'spica/array';
8
8
  import { html, defrag } from 'typed-dom/dom';
@@ -1,10 +1,10 @@
1
1
  import { EmphasisParser } from '../inline';
2
+ import { State, Recursion } from '../context';
2
3
  import { union, some, syntax, creation, surround, open, lazy } from '../../combinator';
3
4
  import { inline } from '../inline';
4
5
  import { emstrong } from './emstrong';
5
6
  import { strong } from './strong';
6
7
  import { str } from '../source';
7
- import { State, Recursion } from '../context';
8
8
  import { startTight, blankWith } from '../visibility';
9
9
  import { unshift } from 'spica/array';
10
10
  import { html, defrag } from 'typed-dom/dom';
@@ -1,11 +1,11 @@
1
1
  import { EmStrongParser, EmphasisParser, StrongParser } from '../inline';
2
+ import { State, Recursion } from '../context';
2
3
  import { Result, IntermediateParser } from '../../combinator/data/parser';
3
4
  import { union, syntax, creation, some, surround, open, lazy, bind } from '../../combinator';
4
5
  import { inline } from '../inline';
5
6
  import { strong } from './strong';
6
7
  import { emphasis } from './emphasis';
7
8
  import { str } from '../source';
8
- import { State, Recursion } from '../context';
9
9
  import { startTight, blankWith } from '../visibility';
10
10
  import { html, defrag } from 'typed-dom/dom';
11
11
  import { unshift } from 'spica/array';
@@ -1,10 +1,10 @@
1
1
  import { ExtensionParser } from '../../inline';
2
+ import { State, Recursion, Backtrack } from '../../context';
2
3
  import { union, inits, some, syntax, creation, precedence, constraint, validate, surround, open, lazy, fmap } from '../../../combinator';
3
4
  import { inline } from '../../inline';
4
5
  import { indexee, identity } from './indexee';
5
6
  import { txt, str } from '../../source';
6
- import { State, Recursion, Backtrack } from '../../context';
7
- import { startTight, trimNodeEnd } from '../../visibility';
7
+ import { startTight, trimBlankNodeEnd } from '../../visibility';
8
8
  import { html, define, defrag } from 'typed-dom/dom';
9
9
 
10
10
  import IndexParser = ExtensionParser.IndexParser;
@@ -17,10 +17,13 @@ export const index: IndexParser = lazy(() => validate('[#', creation(1, Recursio
17
17
  some(inits([
18
18
  inline,
19
19
  signature,
20
- ]), ']', [[/^\\?\n/, 9], [']', 1]])))),
20
+ ]), ']', [['\n', 9], [']', 1]])))),
21
21
  ']',
22
22
  false,
23
- ([, ns], rest) => [[html('a', { 'data-index': dataindex(ns) }, trimNodeEnd(defrag(ns)))], rest],
23
+ ([, ns], rest) =>
24
+ trimBlankNodeEnd(ns).length > 0
25
+ ? [[html('a', { 'data-index': dataindex(ns) }, defrag(ns))], rest]
26
+ : undefined,
24
27
  undefined, 1 | Backtrack.bracket)),
25
28
  ([el]: [HTMLAnchorElement]) => [
26
29
  define(el,
@@ -32,8 +35,8 @@ export const index: IndexParser = lazy(() => validate('[#', creation(1, Recursio
32
35
  ]))));
33
36
 
34
37
  export const signature: IndexParser.SignatureParser = lazy(() => validate('|', creation(1, Recursion.ignore, fmap(open(
35
- '|',
36
- startTight(some(union([bracket, txt]), ']'))),
38
+ /^\|(?!\\?\s)/,
39
+ some(union([bracket, txt]), ']')),
37
40
  ns => [
38
41
  html('span', { class: 'indexer', 'data-index': identity('index', undefined, ns.join(''))!.slice(7) }),
39
42
  ]))));
@@ -1,7 +1,7 @@
1
1
  import { ExtensionParser } from '../../inline';
2
+ import { Recursion } from '../../context';
2
3
  import { union, creation, focus, surround } from '../../../combinator';
3
4
  import { signature } from './index';
4
- import { Recursion } from '../../context';
5
5
  import { html } from 'typed-dom/dom';
6
6
 
7
7
  export const indexer: ExtensionParser.IndexerParser = surround(
@@ -1,7 +1,7 @@
1
1
  import { ExtensionParser } from '../../inline';
2
+ import { State, Recursion } from '../../context';
2
3
  import { union, constraint, creation, validate, surround, clear, fmap } from '../../../combinator';
3
4
  import { str } from '../../source';
4
- import { State, Recursion } from '../../context';
5
5
  import { html } from 'typed-dom/dom';
6
6
 
7
7
  const body = str(/^\$[A-Za-z]*(?:(?:-[A-Za-z][0-9A-Za-z]*)+|-(?:(?:0|[1-9][0-9]*)\.)*(?:0|[1-9][0-9]*)(?![0-9A-Za-z]))/);
@@ -1,8 +1,8 @@
1
1
  import { ExtensionParser } from '../../inline';
2
+ import { State, Recursion, Backtrack } from '../../context';
2
3
  import { union, some, syntax, creation, validate, surround, lazy } from '../../../combinator';
3
4
  import { inline } from '../../inline';
4
5
  import { str } from '../../source';
5
- import { State, Recursion, Backtrack } from '../../context';
6
6
  import { startTight } from '../../visibility';
7
7
  import { unshift } from 'spica/array';
8
8
  import { html, defrag } from 'typed-dom/dom';
@@ -1,8 +1,8 @@
1
1
  import { HTMLParser } from '../inline';
2
+ import { State, Recursion } from '../context';
2
3
  import { union, subsequence, some, syntax, creation, validate, focus, surround, open, match, lazy } from '../../combinator';
3
4
  import { inline } from '../inline';
4
5
  import { str } from '../source';
5
- import { State, Recursion } from '../context';
6
6
  import { isStartLooseNodes, blankWith } from '../visibility';
7
7
  import { memoize } from 'spica/memoize';
8
8
  import { Clock } from 'spica/clock';
@@ -18,48 +18,50 @@ const attrspecs = {
18
18
  Object.setPrototypeOf(attrspecs, null);
19
19
  Object.values(attrspecs).forEach(o => Object.setPrototypeOf(o, null));
20
20
 
21
- export const html: HTMLParser = lazy(() => validate('<', validate(/^<[a-z]+(?=[^\S\n]|>)/i, creation(1, Recursion.inline, syntax(3, State.none, union([
22
- focus(
23
- /^<wbr[^\S\n]*>/i,
24
- () => [[h('wbr')], '']),
25
- surround(
26
- // https://html.spec.whatwg.org/multipage/syntax.html#void-elements
27
- str(/^<(?:area|base|br|col|embed|hr|img|input|link|meta|source|track|wbr)(?=[^\S\n]|>)/i), some(union([attribute])), str(/^[^\S\n]*>/), true,
28
- ([as, bs = [], cs], rest) =>
29
- [[elem(as[0].slice(1), push(unshift(as, bs), cs), [], [])], rest]),
30
- match(
31
- new RegExp(String.raw`^<(${TAGS.join('|')})(?=[^\S\n]|>)`),
32
- memoize(
33
- ([, tag]) =>
34
- surround<HTMLParser.TagParser, string>(surround(
35
- str(`<${tag}`), some(attribute), str(/^[^\S\n]*>/), true),
36
- subsequence([
37
- focus(/^[^\S\n]*\n/, some(inline)),
38
- some(open(/^\n?/, some(inline, blankWith('\n', `</${tag}>`), [[blankWith('\n', `</${tag}>`), 3]]), true)),
39
- ]),
40
- str(`</${tag}>`), true,
41
- ([as, bs = [], cs], rest) =>
42
- [[elem(tag, as, bs, cs)], rest],
43
- ([as, bs = []], rest) =>
44
- [[elem(tag, as, bs, [])], rest]))),
45
- match(
46
- /^<([a-z]+)(?=[^\S\n]|>)/i,
47
- memoize(
48
- ([, tag]) =>
49
- surround<HTMLParser.TagParser, string>(surround(
50
- str(`<${tag}`), some(attribute), str(/^[^\S\n]*>/), true),
51
- subsequence([
52
- focus(/^[^\S\n]*\n/, some(inline)),
53
- some(inline, `</${tag}>`, [[`</${tag}>`, 3]]),
54
- ]),
55
- str(`</${tag}>`), true,
56
- ([as, bs = [], cs], rest) =>
57
- [[elem(tag, as, bs, cs)], rest],
58
- ([as, bs = []], rest) =>
59
- [[elem(tag, as, bs, [])], rest]),
60
- ([, tag]) => tag,
61
- new Clock(10000))),
62
- ]))))));
21
+ export const html: HTMLParser = lazy(() => validate('<', validate(/^<[a-z]+(?=[^\S\n]|>)/i, creation(1, Recursion.inline,
22
+ syntax(3, State.none,
23
+ union([
24
+ focus(
25
+ /^<wbr[^\S\n]*>/i,
26
+ () => [[h('wbr')], '']),
27
+ surround(
28
+ // https://html.spec.whatwg.org/multipage/syntax.html#void-elements
29
+ str(/^<(?:area|base|br|col|embed|hr|img|input|link|meta|source|track|wbr)(?=[^\S\n]|>)/i), some(union([attribute])), str(/^[^\S\n]*>/), true,
30
+ ([as, bs = [], cs], rest) =>
31
+ [[elem(as[0].slice(1), push(unshift(as, bs), cs), [], [])], rest]),
32
+ match(
33
+ new RegExp(String.raw`^<(${TAGS.join('|')})(?=[^\S\n]|>)`),
34
+ memoize(
35
+ ([, tag]) =>
36
+ surround<HTMLParser.TagParser, string>(surround(
37
+ str(`<${tag}`), some(attribute), str(/^[^\S\n]*>/), true),
38
+ subsequence([
39
+ focus(/^[^\S\n]*\n/, some(inline)),
40
+ some(open(/^\n?/, some(inline, blankWith('\n', `</${tag}>`), [[blankWith('\n', `</${tag}>`), 3]]), true)),
41
+ ]),
42
+ str(`</${tag}>`), true,
43
+ ([as, bs = [], cs], rest) =>
44
+ [[elem(tag, as, bs, cs)], rest],
45
+ ([as, bs = []], rest) =>
46
+ [[elem(tag, as, bs, [])], rest]))),
47
+ match(
48
+ /^<([a-z]+)(?=[^\S\n]|>)/i,
49
+ memoize(
50
+ ([, tag]) =>
51
+ surround<HTMLParser.TagParser, string>(surround(
52
+ str(`<${tag}`), some(attribute), str(/^[^\S\n]*>/), true),
53
+ subsequence([
54
+ focus(/^[^\S\n]*\n/, some(inline)),
55
+ some(inline, `</${tag}>`, [[`</${tag}>`, 3]]),
56
+ ]),
57
+ str(`</${tag}>`), true,
58
+ ([as, bs = [], cs], rest) =>
59
+ [[elem(tag, as, bs, cs)], rest],
60
+ ([as, bs = []], rest) =>
61
+ [[elem(tag, as, bs, [])], rest]),
62
+ ([, tag]) => tag,
63
+ new Clock(10000))),
64
+ ]))))));
63
65
 
64
66
  export const attribute: HTMLParser.AttributeParser = union([
65
67
  str(/^[^\S\n]+[a-z]+(?:-[a-z]+)*(?:="[^"\n]*")?(?=[^\S\n]|>)/i),
@@ -1,17 +1,17 @@
1
1
  import { HTMLEntityParser, UnsafeHTMLEntityParser } from '../inline';
2
+ import { Recursion, Command } from '../context';
2
3
  import { union, creation, validate, focus, fmap } from '../../combinator';
3
- import { Recursion } from '../context';
4
4
  import { html } from 'typed-dom/dom';
5
5
  import { reduce } from 'spica/memoize';
6
6
 
7
7
  export const unsafehtmlentity: UnsafeHTMLEntityParser = creation(1, Recursion.ignore, validate('&', focus(
8
8
  /^&[0-9A-Za-z]{1,99};/,
9
- ({ source }) => [[parse(source) ?? `\x1B${source}`], ''])));
9
+ ({ source }) => [[parse(source) ?? `${Command.Escape}${source}`], ''])));
10
10
 
11
11
  export const htmlentity: HTMLEntityParser = fmap(
12
12
  union([unsafehtmlentity]),
13
13
  ([text]) => [
14
- text[0] === '\x1B'
14
+ text[0] === Command.Escape
15
15
  ? html('span', {
16
16
  class: 'invalid',
17
17
  'data-invalid-syntax': 'htmlentity',
@@ -1,8 +1,8 @@
1
1
  import { InsertionParser } from '../inline';
2
+ import { State, Recursion } from '../context';
2
3
  import { union, some, syntax, creation, surround, open, lazy } from '../../combinator';
3
4
  import { inline } from '../inline';
4
5
  import { str } from '../source';
5
- import { State, Recursion } from '../context';
6
6
  import { blankWith } from '../visibility';
7
7
  import { unshift } from 'spica/array';
8
8
  import { html, defrag } from 'typed-dom/dom';
@@ -1,10 +1,15 @@
1
- import { link } from './link';
2
- import { some } from '../../combinator';
1
+ import { LinkParser } from '../inline';
2
+ import { textlink, medialink } from './link';
3
+ import { some, union } from '../../combinator';
3
4
  import { inspect } from '../../debug.test';
4
5
  import { MarkdownParser } from '../../../markdown';
5
6
 
6
7
  describe('Unit: parser/inline/link', () => {
7
8
  describe('link', () => {
9
+ const link: LinkParser = union([
10
+ medialink,
11
+ textlink,
12
+ ]);
8
13
  const parser = (source: string, context: MarkdownParser.Context = {}) => some(link)({ source, context });
9
14
 
10
15
  it('xss', () => {
@@ -1,11 +1,11 @@
1
1
  import { MarkdownParser } from '../../../markdown';
2
2
  import { LinkParser } from '../inline';
3
+ import { State, Recursion, Backtrack } from '../context';
3
4
  import { union, inits, tails, sequence, some, constraint, syntax, creation, precedence, validate, surround, open, dup, reverse, lazy, fmap, bind } from '../../combinator';
4
5
  import { inline, media, shortmedia } from '../inline';
5
6
  import { attributes } from './html';
6
7
  import { linebreak, unescsource, str } from '../source';
7
- import { State, Recursion, Backtrack } from '../context';
8
- import { trimBlankStart, trimNodeEnd } from '../visibility';
8
+ import { trimBlankStart, trimBlankNodeEnd } from '../visibility';
9
9
  import { stringify } from '../util';
10
10
  import { ReadonlyURL } from 'spica/url';
11
11
  import { html, define, defrag } from 'typed-dom/dom';
@@ -15,18 +15,13 @@ const optspec = {
15
15
  } as const;
16
16
  Object.setPrototypeOf(optspec, null);
17
17
 
18
- export const link: LinkParser = lazy(() => validate(['[', '{'], union([
19
- medialink,
20
- textlink,
21
- ])));
22
-
23
- export const textlink: LinkParser.TextLinkParser = lazy(() => creation(1, Recursion.ignore,
18
+ export const textlink: LinkParser.TextLinkParser = lazy(() => validate(['[', '{'], creation(1, Recursion.ignore,
24
19
  constraint(State.link, false,
25
20
  syntax(1, State.linkers | State.media,
26
21
  bind(reverse(tails([
27
22
  dup(surround(
28
23
  '[',
29
- trimBlankStart(some(union([inline]), ']', [[/^\\?\n/, 9], [']', 1]])),
24
+ trimBlankStart(some(union([inline]), ']', [['\n', 9], [']', 1]])),
30
25
  ']',
31
26
  true, undefined, undefined, 1 | Backtrack.bracket)),
32
27
  dup(surround(
@@ -38,11 +33,11 @@ export const textlink: LinkParser.TextLinkParser = lazy(() => creation(1, Recurs
38
33
  ([params, content = []]: [string[], (HTMLElement | string)[]], rest, context) => {
39
34
  assert(!html('div', content).querySelector('a, .media, .annotation, .reference'));
40
35
  assert(content[0] !== '');
41
- if (content.length !== 0 && trimNodeEnd(content = defrag(content)).length === 0) return;
42
- return [[parse(content, params, context)], rest];
43
- })))));
36
+ if (content.length !== 0 && trimBlankNodeEnd(content).length === 0) return;
37
+ return [[parse(defrag(content), params, context)], rest];
38
+ }))))));
44
39
 
45
- export const medialink: LinkParser.MediaLinkParser = lazy(() => creation(1, Recursion.ignore,
40
+ export const medialink: LinkParser.MediaLinkParser = lazy(() => validate(['[', '{'], creation(1, Recursion.ignore,
46
41
  constraint(State.link | State.media, false,
47
42
  syntax(1, State.linkers,
48
43
  bind(reverse(sequence([
@@ -53,7 +48,7 @@ export const medialink: LinkParser.MediaLinkParser = lazy(() => creation(1, Recu
53
48
  dup(surround(/^{(?![{}])/, inits([uri, some(option)]), /^[^\S\n]*}/)),
54
49
  ])),
55
50
  ([params, content = []]: [string[], (HTMLElement | string)[]], rest, context) =>
56
- [[parse(defrag(content), params, context)], rest])))));
51
+ [[parse(defrag(content), params, context)], rest]))))));
57
52
 
58
53
  export const linemedialink: LinkParser.LineMediaLinkParser = surround(
59
54
  linebreak,
@@ -61,7 +56,8 @@ export const linemedialink: LinkParser.LineMediaLinkParser = surround(
61
56
  /^(?=[^\S\n]*(?:$|\n))/);
62
57
 
63
58
  export const unsafelink: LinkParser.UnsafeLinkParser = lazy(() =>
64
- creation(1, Recursion.ignore, precedence(1,
59
+ creation(1, Recursion.ignore,
60
+ precedence(1,
65
61
  bind(reverse(tails([
66
62
  dup(surround(
67
63
  '[',