securemark 0.248.0 → 0.249.1

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 (39) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/index.js +329 -271
  3. package/karma.conf.js +1 -1
  4. package/markdown.d.ts +10 -4
  5. package/package.json +12 -12
  6. package/src/combinator/control/manipulation/indent.ts +1 -2
  7. package/src/parser/inline/annotation.test.ts +2 -2
  8. package/src/parser/inline/annotation.ts +3 -4
  9. package/src/parser/inline/comment.ts +1 -1
  10. package/src/parser/inline/deletion.ts +3 -3
  11. package/src/parser/inline/emphasis.test.ts +5 -4
  12. package/src/parser/inline/emphasis.ts +8 -3
  13. package/src/parser/inline/emstrong.ts +14 -6
  14. package/src/parser/inline/extension/index.ts +1 -2
  15. package/src/parser/inline/extension/label.ts +1 -2
  16. package/src/parser/inline/html.test.ts +5 -2
  17. package/src/parser/inline/html.ts +12 -22
  18. package/src/parser/inline/insertion.ts +3 -3
  19. package/src/parser/inline/link.test.ts +2 -2
  20. package/src/parser/inline/link.ts +3 -4
  21. package/src/parser/inline/mark.test.ts +5 -4
  22. package/src/parser/inline/mark.ts +3 -3
  23. package/src/parser/inline/media.ts +3 -3
  24. package/src/parser/inline/reference.test.ts +5 -5
  25. package/src/parser/inline/reference.ts +7 -9
  26. package/src/parser/inline/ruby.ts +4 -4
  27. package/src/parser/inline/strong.test.ts +5 -4
  28. package/src/parser/inline/strong.ts +7 -3
  29. package/src/parser/inline.test.ts +1 -0
  30. package/src/parser/processor/figure.ts +18 -19
  31. package/src/parser/source/escapable.test.ts +4 -3
  32. package/src/parser/source/escapable.ts +3 -3
  33. package/src/parser/source/text.test.ts +4 -4
  34. package/src/parser/source/text.ts +3 -3
  35. package/src/parser/source/unescapable.test.ts +4 -3
  36. package/src/parser/source/unescapable.ts +3 -3
  37. package/src/parser/util.ts +26 -13
  38. package/src/renderer/render/media/image.ts +3 -3
  39. package/src/renderer/render/media/video.ts +2 -2
package/karma.conf.js CHANGED
@@ -12,7 +12,7 @@ module.exports = function (config) {
12
12
  { pattern: 'https://cdnjs.cloudflare.com/ajax/libs/dompurify/2.3.8/purify.js', watched: false, served: false, included: true, integrity: 'sha512-QaF+0tDlqVmwZaQSc0kImgYmw+Cd66TxA5D9X70I5V9BNSqk6yBTbyqw2VEUsVYV5OTbxw8HD9d45on1wvYv7g==' },
13
13
  { pattern: 'dist/**/*.{js,map}', watched: true, served: true, included: true },
14
14
  ],
15
- reporters: ['dots', 'coverage'],
15
+ reporters: ['dots'],
16
16
  preprocessors: {
17
17
  'dist/**/*.js': ['coverage'],
18
18
  },
package/markdown.d.ts CHANGED
@@ -994,7 +994,6 @@ export namespace MarkdownParser {
994
994
  Inline<'html/tag'>,
995
995
  Parser<HTMLElement | string, Context, [
996
996
  InlineParser,
997
- InlineParser,
998
997
  ]> {
999
998
  }
1000
999
  export namespace TagParser {
@@ -1027,7 +1026,7 @@ export namespace MarkdownParser {
1027
1026
  Inline<'mark'>,
1028
1027
  Parser<HTMLElement | string, Context, [
1029
1028
  InlineParser,
1030
- InlineParser,
1029
+ MarkParser,
1031
1030
  ]> {
1032
1031
  }
1033
1032
  export interface EmStrongParser extends
@@ -1043,7 +1042,10 @@ export namespace MarkdownParser {
1043
1042
  Inline<'strong'>,
1044
1043
  Parser<HTMLElement | string, Context, [
1045
1044
  InlineParser,
1046
- InlineParser,
1045
+ Parser<HTMLElement | string, Context, [
1046
+ EmStrongParser,
1047
+ StrongParser,
1048
+ ]>,
1047
1049
  ]> {
1048
1050
  }
1049
1051
  export interface EmphasisParser extends
@@ -1052,7 +1054,11 @@ export namespace MarkdownParser {
1052
1054
  Parser<HTMLElement | string, Context, [
1053
1055
  StrongParser,
1054
1056
  InlineParser,
1055
- InlineParser,
1057
+ Parser<HTMLElement | string, Context, [
1058
+ EmStrongParser,
1059
+ StrongParser,
1060
+ EmphasisParser,
1061
+ ]>,
1056
1062
  ]> {
1057
1063
  }
1058
1064
  export interface CodeParser extends
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securemark",
3
- "version": "0.248.0",
3
+ "version": "0.249.1",
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",
@@ -34,11 +34,11 @@
34
34
  "@types/mocha": "9.1.1",
35
35
  "@types/power-assert": "1.5.8",
36
36
  "@types/prismjs": "1.26.0",
37
- "@typescript-eslint/parser": "^5.25.0",
37
+ "@typescript-eslint/parser": "^5.27.1",
38
38
  "babel-loader": "^8.2.5",
39
39
  "babel-plugin-unassert": "^3.2.0",
40
- "concurrently": "^7.2.0",
41
- "eslint": "^8.16.0",
40
+ "concurrently": "^7.2.1",
41
+ "eslint": "^8.17.0",
42
42
  "eslint-plugin-redos": "^4.4.0",
43
43
  "eslint-webpack-plugin": "^3.1.1",
44
44
  "glob": "^8.0.3",
@@ -49,22 +49,22 @@
49
49
  "karma-mocha": "^2.0.1",
50
50
  "karma-power-assert": "^1.0.0",
51
51
  "mocha": "^10.0.0",
52
- "npm-check-updates": "^13.0.3",
52
+ "npm-check-updates": "^13.1.1",
53
53
  "semver": "^7.3.7",
54
- "spica": "0.0.551",
54
+ "spica": "0.0.564",
55
55
  "ts-loader": "^9.3.0",
56
- "typed-dom": "^0.0.295",
57
- "typescript": "4.6.4",
58
- "webpack": "^5.72.1",
56
+ "typed-dom": "^0.0.298",
57
+ "typescript": "4.7.3",
58
+ "webpack": "^5.73.0",
59
59
  "webpack-cli": "^4.9.2",
60
60
  "webpack-merge": "^5.8.0"
61
61
  },
62
62
  "scripts": {
63
63
  "update": "ncu -u && npm i --no-shrinkwrap && bundle update",
64
- "dev": "concurrently \"webpack --env mode=test -w\" \"karma start --auto-watch --reporters dots\" \"webpack --env mode=dist -w --output-path ./gh-pages/assets/dist\" \"bundle exec jekyll serve -s ./gh-pages -d ./gh-pages/_site --incremental\"",
65
- "test": "webpack --env mode=test && karma start --single-run --concurrency 1",
64
+ "dev": "concurrently \"webpack --env mode=test -w\" \"karma start --auto-watch\" \"webpack --env mode=dist -w --output-path ./gh-pages/assets/dist\" \"bundle exec jekyll serve -s ./gh-pages -d ./gh-pages/_site --incremental\"",
65
+ "test": "webpack --env mode=test && karma start --single-run --concurrency 1 --reporters dots,coverage",
66
66
  "lint": "webpack --env mode=lint",
67
- "bench": "webpack --env mode=bench && karma start --single-run --concurrency 1 --reporters dots",
67
+ "bench": "webpack --env mode=bench && karma start --single-run --concurrency 1",
68
68
  "dist": "webpack --env mode=dist"
69
69
  },
70
70
  "author": "falsandtru",
@@ -7,7 +7,6 @@ import { bind } from '../monad/bind';
7
7
  import { match } from './match';
8
8
  import { open } from './surround';
9
9
  import { memoize } from 'spica/memoize';
10
- import { join } from 'spica/array';
11
10
 
12
11
  export function indent<P extends Parser<unknown>>(parser: P, separation?: boolean): P;
13
12
  export function indent<T>(parser: Parser<T>, separation = false): Parser<T> {
@@ -19,7 +18,7 @@ export function indent<T>(parser: Parser<T>, separation = false): Parser<T> {
19
18
  some(line(open(indent, source => [[unline(source)], '']))),
20
19
  ([, indent]) => indent.length * 2 + +(indent[0] === ' '), [])), separation),
21
20
  (nodes, rest, context) => {
22
- const result = parser(join(nodes, '\n'), context);
21
+ const result = parser(nodes.join('\n'), context);
23
22
  return result && exec(result) === ''
24
23
  ? [eval(result), rest]
25
24
  : undefined;
@@ -16,9 +16,7 @@ describe('Unit: parser/inline/annotation', () => {
16
16
  assert.deepStrictEqual(inspect(parser('(( ))')), undefined);
17
17
  assert.deepStrictEqual(inspect(parser('((\n))')), undefined);
18
18
  assert.deepStrictEqual(inspect(parser('((\na))')), undefined);
19
- assert.deepStrictEqual(inspect(parser('((\\ a))')), undefined);
20
19
  assert.deepStrictEqual(inspect(parser('((\\\na))')), undefined);
21
- assert.deepStrictEqual(inspect(parser('((<wbr>a))')), undefined);
22
20
  assert.deepStrictEqual(inspect(parser('((a\n))')), undefined);
23
21
  assert.deepStrictEqual(inspect(parser('((a\\\n))')), undefined);
24
22
  assert.deepStrictEqual(inspect(parser('((a\nb))')), undefined);
@@ -33,6 +31,8 @@ describe('Unit: parser/inline/annotation', () => {
33
31
  it('basic', () => {
34
32
  assert.deepStrictEqual(inspect(parser('(( a))')), [['<sup class="annotation">a</sup>'], '']);
35
33
  assert.deepStrictEqual(inspect(parser('(( a ))')), [['<sup class="annotation">a</sup>'], '']);
34
+ assert.deepStrictEqual(inspect(parser('((\\ a))')), [['<sup class="annotation">a</sup>'], '']);
35
+ assert.deepStrictEqual(inspect(parser('((<wbr>a))')), [['<sup class="annotation">a</sup>'], '']);
36
36
  assert.deepStrictEqual(inspect(parser('((a))')), [['<sup class="annotation">a</sup>'], '']);
37
37
  assert.deepStrictEqual(inspect(parser('((a ))')), [['<sup class="annotation">a</sup>'], '']);
38
38
  assert.deepStrictEqual(inspect(parser('((a ))')), [['<sup class="annotation">a</sup>'], '']);
@@ -2,13 +2,12 @@ import { undefined } from 'spica/global';
2
2
  import { AnnotationParser } from '../inline';
3
3
  import { union, some, validate, guard, context, creator, surround, lazy, fmap } from '../../combinator';
4
4
  import { inline } from '../inline';
5
- import { startLoose, trimSpaceStart, trimNodeEnd } from '../util';
5
+ import { trimBlankInline } from '../util';
6
6
  import { html, defrag } from 'typed-dom/dom';
7
7
 
8
8
  export const annotation: AnnotationParser = lazy(() => creator(validate('((', '))', '\n', fmap(surround(
9
9
  '((',
10
10
  guard(context => context.syntax?.inline?.annotation ?? true,
11
- startLoose(
12
11
  context({ syntax: { inline: {
13
12
  annotation: false,
14
13
  // Redundant
@@ -20,6 +19,6 @@ export const annotation: AnnotationParser = lazy(() => creator(validate('((', ')
20
19
  //link: true,
21
20
  //autolink: true,
22
21
  }}, delimiters: undefined },
23
- trimSpaceStart(union([some(inline, ')', /^\\?\n/)]))))),
22
+ trimBlankInline(some(union([inline]), ')', /^\\?\n/)))),
24
23
  '))'),
25
- ns => [html('sup', { class: 'annotation' }, trimNodeEnd(defrag(ns)))]))));
24
+ ns => [html('sup', { class: 'annotation' }, defrag(ns))]))));
@@ -12,7 +12,7 @@ export const comment: CommentParser = lazy(() => creator(validate('[#', match(
12
12
  ([, fence]) =>
13
13
  surround(
14
14
  open(str(`[${fence}`), some(text, new RegExp(String.raw`^\s+${fence}\]|^\S`)), true),
15
- union([some(inline, new RegExp(String.raw`^\s+${fence}\]`))]),
15
+ some(union([inline]), new RegExp(String.raw`^\s+${fence}\]`)),
16
16
  close(some(text, /^\S/), str(`${fence}]`)), true,
17
17
  ([as, bs = [], cs], rest) => [[
18
18
  html('span', { class: 'comment' }, [
@@ -2,15 +2,15 @@ import { DeletionParser } from '../inline';
2
2
  import { union, some, creator, surround, open, lazy } from '../../combinator';
3
3
  import { inline } from '../inline';
4
4
  import { str } from '../source';
5
- import { blank } from '../util';
5
+ import { blankWith } from '../util';
6
6
  import { html, defrag } from 'typed-dom/dom';
7
7
  import { unshift } from 'spica/array';
8
8
 
9
9
  export const deletion: DeletionParser = lazy(() => creator(surround(
10
10
  str('~~'),
11
11
  some(union([
12
- some(inline, blank(/\n/, '~~')),
13
- open(/^\n/, some(inline, '~'), true),
12
+ some(inline, blankWith('\n', '~~')),
13
+ open('\n', some(inline, '~'), true),
14
14
  ])),
15
15
  str('~~'), false,
16
16
  ([, bs], rest) => [[html('del', defrag(bs))], rest],
@@ -9,10 +9,11 @@ describe('Unit: parser/inline/emphasis', () => {
9
9
  it('invalid', () => {
10
10
  assert.deepStrictEqual(inspect(parser('*')), undefined);
11
11
  assert.deepStrictEqual(inspect(parser('*a')), [['*', 'a'], '']);
12
- assert.deepStrictEqual(inspect(parser('*a *')), [['*', 'a', ' ', '*'], '']);
13
- assert.deepStrictEqual(inspect(parser('*a\n*')), [['*', 'a', '<br>', '*'], '']);
14
- assert.deepStrictEqual(inspect(parser('*a\\ *')), [['*', 'a', ' ', '*'], '']);
15
- assert.deepStrictEqual(inspect(parser('*a\\\n*')), [['*', 'a', '<span class="linebreak"> </span>', '*'], '']);
12
+ assert.deepStrictEqual(inspect(parser('*a *')), [['*', 'a'], ' *']);
13
+ assert.deepStrictEqual(inspect(parser('*a *')), [['*', 'a', ' '], ' *']);
14
+ assert.deepStrictEqual(inspect(parser('*a\n*')), [['*', 'a'], '\n*']);
15
+ assert.deepStrictEqual(inspect(parser('*a\\ *')), [['*', 'a'], '\\ *']);
16
+ assert.deepStrictEqual(inspect(parser('*a\\\n*')), [['*', 'a'], '\\\n*']);
16
17
  assert.deepStrictEqual(inspect(parser('*a**b')), [['*', 'a', '**', 'b'], '']);
17
18
  assert.deepStrictEqual(inspect(parser('*a**b*')), [['*', 'a', '**', 'b', '*'], '']);
18
19
  assert.deepStrictEqual(inspect(parser('* *')), undefined);
@@ -1,9 +1,10 @@
1
1
  import { EmphasisParser } from '../inline';
2
2
  import { union, some, creator, surround, open, lazy } from '../../combinator';
3
3
  import { inline } from '../inline';
4
+ import { emstrong } from './emstrong';
4
5
  import { strong } from './strong';
5
6
  import { str } from '../source';
6
- import { startTight, blank } from '../util';
7
+ import { startTight, blankWith } from '../util';
7
8
  import { html, defrag } from 'typed-dom/dom';
8
9
  import { unshift } from 'spica/array';
9
10
 
@@ -11,8 +12,12 @@ export const emphasis: EmphasisParser = lazy(() => creator(surround(
11
12
  str('*'),
12
13
  startTight(some(union([
13
14
  strong,
14
- some(inline, blank('', '*')),
15
- open(some(inline, '*'), inline),
15
+ some(inline, blankWith('*')),
16
+ open(some(inline, '*'), union([
17
+ emstrong,
18
+ strong,
19
+ emphasis,
20
+ ])),
16
21
  ])), '*'),
17
22
  str('*'), false,
18
23
  ([, bs], rest) => [[html('em', defrag(bs))], rest],
@@ -4,25 +4,33 @@ import { Result, IntermediateParser } from '../../combinator/data/parser';
4
4
  import { union, some, creator, surround, open, lazy, bind } from '../../combinator';
5
5
  import { inline } from '../inline';
6
6
  import { strong } from './strong';
7
+ import { emphasis } from './emphasis';
7
8
  import { str } from '../source';
8
- import { startTight, blank } from '../util';
9
+ import { startTight, blankWith } from '../util';
9
10
  import { html, defrag } from 'typed-dom/dom';
10
11
  import { unshift } from 'spica/array';
11
12
 
12
13
  const substrong: IntermediateParser<StrongParser> = lazy(() => some(union([
13
- some(inline, blank('', '**')),
14
- open(some(inline, '*'), inline),
14
+ some(inline, blankWith('**')),
15
+ open(some(inline, '*'), union([
16
+ emstrong,
17
+ strong,
18
+ ])),
15
19
  ])));
16
20
  const subemphasis: IntermediateParser<EmphasisParser> = lazy(() => some(union([
17
21
  strong,
18
- some(inline, blank('', '*')),
19
- open(some(inline, '*'), inline),
22
+ some(inline, blankWith('*')),
23
+ open(some(inline, '*'), union([
24
+ emstrong,
25
+ strong,
26
+ emphasis,
27
+ ])),
20
28
  ])));
21
29
 
22
30
  export const emstrong: EmStrongParser = lazy(() => creator(surround(
23
31
  str('***'),
24
32
  startTight(some(union([
25
- some(inline, blank('', '*')),
33
+ some(inline, blankWith('*')),
26
34
  open(some(inline, '*'), inline),
27
35
  ]))),
28
36
  str(/^\*{1,3}/), false,
@@ -6,7 +6,6 @@ import { indexee, identity } from './indexee';
6
6
  import { txt, str, stropt } from '../../source';
7
7
  import { startTight, trimNodeEnd } from '../../util';
8
8
  import { html, define, defrag } from 'typed-dom/dom';
9
- import { join } from 'spica/array';
10
9
 
11
10
  import IndexParser = ExtensionParser.IndexParser;
12
11
 
@@ -44,7 +43,7 @@ const signature: IndexParser.SignatureParser = lazy(() => creator(fmap(open(
44
43
  '|#',
45
44
  startTight(some(union([bracket, txt]), ']'))),
46
45
  ns => [
47
- html('span', { class: 'indexer', 'data-index': identity(join(ns)).slice(6) }),
46
+ html('span', { class: 'indexer', 'data-index': identity(ns.join('')).slice(6) }),
48
47
  ])));
49
48
 
50
49
  const bracket: IndexParser.SignatureParser.BracketParser = lazy(() => creator(union([
@@ -3,7 +3,6 @@ import { ExtensionParser } from '../../inline';
3
3
  import { union, validate, guard, creator, surround, clear, fmap } from '../../../combinator';
4
4
  import { str } from '../../source';
5
5
  import { html } from 'typed-dom/dom';
6
- import { join } from 'spica/array';
7
6
 
8
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]))/);
9
8
 
@@ -48,5 +47,5 @@ function increment(number: string, position: number): string {
48
47
  ? 0
49
48
  : 1;
50
49
  }
51
- return join(ms, '.');
50
+ return ms.join('.');
52
51
  }
@@ -29,11 +29,12 @@ describe('Unit: parser/inline/html', () => {
29
29
  assert.deepStrictEqual(inspect(parser('<small>z')), undefined);
30
30
  assert.deepStrictEqual(inspect(parser('<small></small>')), undefined);
31
31
  assert.deepStrictEqual(inspect(parser('<small> </small>')), undefined);
32
+ assert.deepStrictEqual(inspect(parser('<small>\\ </small>')), undefined);
33
+ assert.deepStrictEqual(inspect(parser('<small>&Tab;</small>')), undefined);
34
+ assert.deepStrictEqual(inspect(parser('<small><wbr></small>')), undefined);
32
35
  assert.deepStrictEqual(inspect(parser('<small>\n</small>')), undefined);
33
36
  assert.deepStrictEqual(inspect(parser('<small>\na</small>')), undefined);
34
- assert.deepStrictEqual(inspect(parser('<small>\\ a</small>')), undefined);
35
37
  assert.deepStrictEqual(inspect(parser('<small>\\\na</small>')), undefined);
36
- assert.deepStrictEqual(inspect(parser('<small><wbr></small>')), undefined);
37
38
  assert.deepStrictEqual(inspect(parser('<small>a')), undefined);
38
39
  assert.deepStrictEqual(inspect(parser('<small>a</BDO>')), undefined);
39
40
  assert.deepStrictEqual(inspect(parser('<SMALL>a</SMALL>')), undefined);
@@ -60,6 +61,8 @@ describe('Unit: parser/inline/html', () => {
60
61
  assert.deepStrictEqual(inspect(parser('<small> a</small>')), [['<small> a</small>'], '']);
61
62
  assert.deepStrictEqual(inspect(parser('<small> a </small>')), [['<small> a </small>'], '']);
62
63
  assert.deepStrictEqual(inspect(parser('<small> a </small>')), [['<small> a </small>'], '']);
64
+ assert.deepStrictEqual(inspect(parser('<small>\\ a</small>')), [['<small> a</small>'], '']);
65
+ assert.deepStrictEqual(inspect(parser('<small><wbr>a</small>')), [['<small><wbr>a</small>'], '']);
63
66
  assert.deepStrictEqual(inspect(parser('<small>a</small>')), [['<small>a</small>'], '']);
64
67
  assert.deepStrictEqual(inspect(parser('<small>a</small>a')), [['<small>a</small>'], 'a']);
65
68
  assert.deepStrictEqual(inspect(parser('<small>a </small>')), [['<small>a </small>'], '']);
@@ -1,14 +1,13 @@
1
- import { undefined } from 'spica/global';
2
- import { isFrozen, ObjectEntries } from 'spica/alias';
1
+ import { undefined, Object } from 'spica/global';
3
2
  import { HTMLParser } from '../inline';
4
3
  import { union, some, validate, creator, surround, open, match, lazy } from '../../combinator';
5
4
  import { inline } from '../inline';
6
5
  import { str } from '../source';
7
- import { startLoose, blank } from '../util';
6
+ import { startLoose, blankWith } from '../util';
8
7
  import { html as h, defrag } from 'typed-dom/dom';
9
8
  import { memoize } from 'spica/memoize';
10
9
  import { Cache } from 'spica/cache';
11
- import { unshift, push, splice, join } from 'spica/array';
10
+ import { unshift, push, splice } from 'spica/array';
12
11
 
13
12
  const tags = Object.freeze(['wbr', 'sup', 'sub', 'small', 'bdo', 'bdi']);
14
13
  const attrspec = {
@@ -33,35 +32,27 @@ export const html: HTMLParser = lazy(() => creator(validate('<', validate(/^<[a-
33
32
  /^(?=<(sup|sub|small|bdo|bdi)(?=[^\S\n]|>))/,
34
33
  memoize(
35
34
  ([, tag]) =>
36
- validate(`<${tag}`, `</${tag}>`,
37
35
  surround<HTMLParser.TagParser, string>(surround(
38
36
  str(`<${tag}`), some(attribute), str(/^\s*>/), true),
39
37
  startLoose(some(union([
40
- some(inline, blank(/\n/, `</${tag}>`)),
41
- open(/^\n/, some(inline, `</${tag}>`), true),
42
- ]), `</${tag}>`), `</${tag}>`),
38
+ open(/^\n?/, some(inline, blankWith('\n', `</${tag}>`)), true),
39
+ ])), `</${tag}>`),
43
40
  str(`</${tag}>`), false,
44
41
  ([as, bs, cs], rest) =>
45
- [[elem(tag, as, defrag(bs), cs)], rest],
46
- ([as, bs], rest) =>
47
- as.length === 1 ? [unshift(as, bs), rest] : undefined)),
42
+ [[elem(tag, as, bs, cs)], rest]),
48
43
  ([, tag]) => tags.indexOf(tag), [])),
49
44
  match(
50
45
  /^(?=<([a-z]+)(?=[^\S\n]|>))/,
51
46
  memoize(
52
47
  ([, tag]) =>
53
- validate(`<${tag}`, `</${tag}>`,
54
48
  surround<HTMLParser.TagParser, string>(surround(
55
49
  str(`<${tag}`), some(attribute), str(/^\s*>/), true),
56
50
  startLoose(some(union([
57
- some(inline, blank(/\n/, `</${tag}>`)),
58
- open(/^\n/, some(inline, `</${tag}>`), true),
59
- ]), `</${tag}>`), `</${tag}>`),
51
+ open(/^\n?/, some(inline, blankWith('\n', `</${tag}>`)), true),
52
+ ])), `</${tag}>`),
60
53
  str(`</${tag}>`), false,
61
54
  ([as, bs, cs], rest) =>
62
- [[elem(tag, as, defrag(bs), cs)], rest],
63
- ([as, bs], rest) =>
64
- as.length === 1 ? [unshift(as, bs), rest] : undefined)),
55
+ [[elem(tag, as, bs, cs)], rest]),
65
56
  ([, tag]) => tag,
66
57
  new Cache(10000))),
67
58
  ])))));
@@ -73,13 +64,12 @@ export const attribute: HTMLParser.TagParser.AttributeParser = union([
73
64
  function elem(tag: string, as: string[], bs: (HTMLElement | string)[], cs: string[]): HTMLElement {
74
65
  assert(as.length > 0);
75
66
  assert(as[0][0] === '<' && as[as.length - 1].slice(-1) === '>');
76
- assert(bs.length === defrag(bs).length);
77
67
  assert(cs.length === 1);
78
68
  if (!tags.includes(tag)) return invalid('tag', `Invalid HTML tag <${tag}>`, as, bs, cs);
79
69
  const attrs = attributes('html', [], attrspec[tag], as.slice(1, -1));
80
70
  return 'data-invalid-syntax' in attrs
81
71
  ? invalid('attribute', 'Invalid HTML attribute', as, bs, cs)
82
- : h(tag as 'span', attrs, bs);
72
+ : h(tag as 'span', attrs, defrag(bs));
83
73
  }
84
74
  function invalid(type: string, message: string, as: (HTMLElement | string)[], bs: (HTMLElement | string)[], cs: (HTMLElement | string)[]): HTMLElement {
85
75
  return h('span', {
@@ -92,7 +82,7 @@ function invalid(type: string, message: string, as: (HTMLElement | string)[], bs
92
82
 
93
83
  const requiredAttributes = memoize(
94
84
  (spec: Readonly<Record<string, readonly (string | undefined)[] | undefined>>) =>
95
- ObjectEntries(spec).flatMap(([k, v]) => v && isFrozen(v) ? [k] : []),
85
+ Object.entries(spec).flatMap(([k, v]) => v && Object.isFrozen(v) ? [k] : []),
96
86
  new WeakMap());
97
87
 
98
88
  export function attributes(
@@ -122,7 +112,7 @@ export function attributes(
122
112
  }
123
113
  invalid ||= !!spec && !requiredAttributes(spec).every(name => name in attrs);
124
114
  if (invalid) {
125
- attrs['class'] = join(classes.includes('invalid') ? classes : unshift(classes, ['invalid']), ' ');
115
+ attrs['class'] = (classes.includes('invalid') ? classes : unshift(classes, ['invalid'])).join(' ');
126
116
  attrs['data-invalid-syntax'] = syntax;
127
117
  attrs['data-invalid-type'] = 'argument';
128
118
  attrs['data-invalid-message'] = 'Invalid argument';
@@ -2,15 +2,15 @@ import { InsertionParser } from '../inline';
2
2
  import { union, some, creator, surround, open, lazy } from '../../combinator';
3
3
  import { inline } from '../inline';
4
4
  import { str } from '../source';
5
- import { blank } from '../util';
5
+ import { blankWith } from '../util';
6
6
  import { html, defrag } from 'typed-dom/dom';
7
7
  import { unshift } from 'spica/array';
8
8
 
9
9
  export const insertion: InsertionParser = lazy(() => creator(surround(
10
10
  str('++'),
11
11
  some(union([
12
- some(inline, blank(/\n/, '++')),
13
- open(/^\n/, some(inline, '+'), true),
12
+ some(inline, blankWith('\n', '++')),
13
+ open('\n', some(inline, '+'), true),
14
14
  ])),
15
15
  str('++'), false,
16
16
  ([, bs], rest) => [[html('ins', defrag(bs))], rest],
@@ -67,8 +67,6 @@ describe('Unit: parser/inline/link', () => {
67
67
  assert.deepStrictEqual(inspect(parser('[[]{b}')), undefined);
68
68
  assert.deepStrictEqual(inspect(parser('[]]{b}')), undefined);
69
69
  assert.deepStrictEqual(inspect(parser('[a]{}')), undefined);
70
- assert.deepStrictEqual(inspect(parser('[\\ a]{b}')), undefined);
71
- assert.deepStrictEqual(inspect(parser('[ \\ a]{b}')), undefined);
72
70
  assert.deepStrictEqual(inspect(parser('[a\nb]{b}')), undefined);
73
71
  assert.deepStrictEqual(inspect(parser('[a\\\nb]{b}')), undefined);
74
72
  assert.deepStrictEqual(inspect(parser('[<wbr>]{b}')), undefined);
@@ -118,6 +116,8 @@ describe('Unit: parser/inline/link', () => {
118
116
  assert.deepStrictEqual(inspect(parser('[]{^/b}', { host: new URL('/0.0,0.0,0z', location.origin) })), [[`<a href="/0.0,0.0,0z/b">^/b</a>`], '']);
119
117
  assert.deepStrictEqual(inspect(parser('[ a]{b}')), [['<a href="b">a</a>'], '']);
120
118
  assert.deepStrictEqual(inspect(parser('[ a ]{b}')), [['<a href="b">a</a>'], '']);
119
+ assert.deepStrictEqual(inspect(parser('[\\ a]{b}')), [['<a href="b">a</a>'], '']);
120
+ assert.deepStrictEqual(inspect(parser('[ \\ a]{b}')), [['<a href="b">a</a>'], '']);
121
121
  assert.deepStrictEqual(inspect(parser('[a ]{b}')), [['<a href="b">a</a>'], '']);
122
122
  assert.deepStrictEqual(inspect(parser('[a ]{b}')), [['<a href="b">a</a>'], '']);
123
123
  assert.deepStrictEqual(inspect(parser('[a]{b}')), [['<a href="b">a</a>'], '']);
@@ -6,7 +6,7 @@ import { inline, media, shortmedia } from '../inline';
6
6
  import { attributes } from './html';
7
7
  import { autolink } from '../autolink';
8
8
  import { str } from '../source';
9
- import { startLoose, trimSpaceStart, trimNodeEnd, stringify } from '../util';
9
+ import { trimBlankInline, stringify } from '../util';
10
10
  import { html, define, defrag } from 'typed-dom/dom';
11
11
  import { ReadonlyURL } from 'spica/url';
12
12
 
@@ -26,7 +26,6 @@ export const link: LinkParser = lazy(() => creator(10, validate(['[', '{'], '}',
26
26
  surround('[', shortmedia, ']'),
27
27
  surround(
28
28
  '[',
29
- startLoose(
30
29
  context({ syntax: { inline: {
31
30
  annotation: false,
32
31
  reference: false,
@@ -37,7 +36,7 @@ export const link: LinkParser = lazy(() => creator(10, validate(['[', '{'], '}',
37
36
  media: false,
38
37
  autolink: false,
39
38
  }}},
40
- trimSpaceStart(some(inline, ']', /^\\?\n/)))),
39
+ trimBlankInline(some(inline, ']', /^\\?\n/))),
41
40
  ']',
42
41
  true),
43
42
  ]))),
@@ -52,7 +51,7 @@ export const link: LinkParser = lazy(() => creator(10, validate(['[', '{'], '}',
52
51
  assert(!INSECURE_URI.match(/\s/));
53
52
  const el = elem(
54
53
  INSECURE_URI,
55
- trimNodeEnd(defrag(content)),
54
+ defrag(content),
56
55
  new ReadonlyURL(
57
56
  resolve(INSECURE_URI, context.host ?? location, context.url ?? context.host ?? location),
58
57
  context.host?.href || location.href),
@@ -12,10 +12,11 @@ describe('Unit: parser/inline/mark', () => {
12
12
  assert.deepStrictEqual(inspect(parser('==')), undefined);
13
13
  assert.deepStrictEqual(inspect(parser('==a')), [['==', 'a'], '']);
14
14
  assert.deepStrictEqual(inspect(parser('==a=')), [['==', 'a', '='], '']);
15
- assert.deepStrictEqual(inspect(parser('==a ==')), [['==', 'a', ' ', '=='], '']);
16
- assert.deepStrictEqual(inspect(parser('==a\n==')), [['==', 'a', '<br>', '=='], '']);
17
- assert.deepStrictEqual(inspect(parser('==a\\ ==')), [['==', 'a', ' ', '=='], '']);
18
- assert.deepStrictEqual(inspect(parser('==a\\\n==')), [['==', 'a', '<span class="linebreak"> </span>', '=='], '']);
15
+ assert.deepStrictEqual(inspect(parser('==a ==')), [['==', 'a'], ' ==']);
16
+ assert.deepStrictEqual(inspect(parser('==a ==')), [['==', 'a', ' '], ' ==']);
17
+ assert.deepStrictEqual(inspect(parser('==a\n==')), [['==', 'a'], '\n==']);
18
+ assert.deepStrictEqual(inspect(parser('==a\\ ==')), [['==', 'a'], '\\ ==']);
19
+ assert.deepStrictEqual(inspect(parser('==a\\\n==')), [['==', 'a'], '\\\n==']);
19
20
  assert.deepStrictEqual(inspect(parser('== ==')), undefined);
20
21
  assert.deepStrictEqual(inspect(parser('== a==')), undefined);
21
22
  assert.deepStrictEqual(inspect(parser('== a ==')), undefined);
@@ -2,15 +2,15 @@ import { MarkParser } from '../inline';
2
2
  import { union, some, creator, surround, open, lazy } from '../../combinator';
3
3
  import { inline } from '../inline';
4
4
  import { str } from '../source';
5
- import { startTight, blank } from '../util';
5
+ import { startTight, blankWith } from '../util';
6
6
  import { html, defrag } from 'typed-dom/dom';
7
7
  import { unshift } from 'spica/array';
8
8
 
9
9
  export const mark: MarkParser = lazy(() => creator(surround(
10
10
  str('=='),
11
11
  startTight(some(union([
12
- some(inline, blank('', '==')),
13
- open(some(inline, '='), inline),
12
+ some(inline, blankWith('==')),
13
+ open(some(inline, '='), mark),
14
14
  ]))),
15
15
  str('=='), false,
16
16
  ([, bs], rest) => [[html('mark', defrag(bs))], rest],
@@ -7,7 +7,7 @@ import { unsafehtmlentity } from './htmlentity';
7
7
  import { txt, str } from '../source';
8
8
  import { html, define } from 'typed-dom/dom';
9
9
  import { ReadonlyURL } from 'spica/url';
10
- import { unshift, push, join } from 'spica/array';
10
+ import { unshift, push } from 'spica/array';
11
11
 
12
12
  const optspec = {
13
13
  'width': [],
@@ -28,7 +28,7 @@ export const media: MediaParser = lazy(() => creator(10, validate(['![', '!{'],
28
28
  true)),
29
29
  dup(surround(/^{(?![{}])/, inits([uri, some(option)]), /^[^\S\n]*}/)),
30
30
  ]))),
31
- ([as, bs]) => bs ? [[join(as).trim() || join(as)], bs] : [[''], as]),
31
+ ([as, bs]) => bs ? [[as.join('').trim() || as.join('')], bs] : [[''], as]),
32
32
  ([[text]]) => text === '' || text.trim() !== ''),
33
33
  ([[text], params], rest, context) => {
34
34
  assert(text === text.trim());
@@ -52,7 +52,7 @@ export const media: MediaParser = lazy(() => creator(10, validate(['![', '!{'],
52
52
  return fmap(
53
53
  link as MediaParser,
54
54
  ([link]) => [define(link, { target: '_blank' }, [el])])
55
- (`{ ${INSECURE_URI}${join(params)} }${rest}`, context);
55
+ (`{ ${INSECURE_URI}${params.join('')} }${rest}`, context);
56
56
  }))));
57
57
 
58
58
  const bracket: MediaParser.TextParser.BracketParser = lazy(() => union([
@@ -16,9 +16,7 @@ describe('Unit: parser/inline/reference', () => {
16
16
  assert.deepStrictEqual(inspect(parser('[[ ]]')), undefined);
17
17
  assert.deepStrictEqual(inspect(parser('[[\n]]')), undefined);
18
18
  assert.deepStrictEqual(inspect(parser('[[\na]]')), undefined);
19
- assert.deepStrictEqual(inspect(parser('[[\\ a]]')), undefined);
20
19
  assert.deepStrictEqual(inspect(parser('[[\\\na]]')), undefined);
21
- assert.deepStrictEqual(inspect(parser('[[<wbr>a]]')), undefined);
22
20
  assert.deepStrictEqual(inspect(parser('[[a\n]]')), undefined);
23
21
  assert.deepStrictEqual(inspect(parser('[[a\\\n]]')), undefined);
24
22
  assert.deepStrictEqual(inspect(parser('[[a\nb]]')), undefined);
@@ -33,6 +31,8 @@ describe('Unit: parser/inline/reference', () => {
33
31
  it('basic', () => {
34
32
  assert.deepStrictEqual(inspect(parser('[[ a]]')), [['<sup class="reference">a</sup>'], '']);
35
33
  assert.deepStrictEqual(inspect(parser('[[ a ]]')), [['<sup class="reference">a</sup>'], '']);
34
+ assert.deepStrictEqual(inspect(parser('[[\\ a]]')), [['<sup class="reference">a</sup>'], '']);
35
+ assert.deepStrictEqual(inspect(parser('[[<wbr>a]]')), [['<sup class="reference">a</sup>'], '']);
36
36
  assert.deepStrictEqual(inspect(parser('[[a]]')), [['<sup class="reference">a</sup>'], '']);
37
37
  assert.deepStrictEqual(inspect(parser('[[a ]]')), [['<sup class="reference">a</sup>'], '']);
38
38
  assert.deepStrictEqual(inspect(parser('[[a ]]')), [['<sup class="reference">a</sup>'], '']);
@@ -68,8 +68,8 @@ describe('Unit: parser/inline/reference', () => {
68
68
  assert.deepStrictEqual(inspect(parser('[[^a|b]]')), [['<sup class="reference" data-abbr="a">b</sup>'], '']);
69
69
  assert.deepStrictEqual(inspect(parser('[[^a|b ]]')), [['<sup class="reference" data-abbr="a">b</sup>'], '']);
70
70
  assert.deepStrictEqual(inspect(parser('[[^a|b ]]')), [['<sup class="reference" data-abbr="a">b</sup>'], '']);
71
- assert.deepStrictEqual(inspect(parser('[[^a|<wbr>]]')), [['<sup class="invalid">^a|</sup>'], '']);
72
- assert.deepStrictEqual(inspect(parser('[[^a|<wbr>b]]')), [['<sup class="invalid">^a|<wbr>b</sup>'], '']);
71
+ assert.deepStrictEqual(inspect(parser('[[^a|<wbr>]]')), [['<sup class="reference" data-abbr="a"></sup>'], '']);
72
+ assert.deepStrictEqual(inspect(parser('[[^a|<wbr>b]]')), [['<sup class="reference" data-abbr="a">b</sup>'], '']);
73
73
  assert.deepStrictEqual(inspect(parser('[[^a|^b]]')), [['<sup class="reference" data-abbr="a">^b</sup>'], '']);
74
74
  assert.deepStrictEqual(inspect(parser('[[^a| ]]')), [['<sup class="reference" data-abbr="a"></sup>'], '']);
75
75
  assert.deepStrictEqual(inspect(parser('[[^a| b]]')), [['<sup class="reference" data-abbr="a">b</sup>'], '']);
@@ -87,7 +87,7 @@ describe('Unit: parser/inline/reference', () => {
87
87
  assert.deepStrictEqual(inspect(parser(`[[^A's, Aces']]`)), [[`<sup class="reference" data-abbr="A's, Aces'"></sup>`], '']);
88
88
  assert.deepStrictEqual(inspect(parser('[[^^]]')), [['<sup class="invalid">^^</sup>'], '']);
89
89
  assert.deepStrictEqual(inspect(parser('[[\\^]]')), [['<sup class="reference">^</sup>'], '']);
90
- assert.deepStrictEqual(inspect(parser('[[^ ]]')), [['<sup class="invalid">^</sup>'], '']);
90
+ assert.deepStrictEqual(inspect(parser('[[^ ]]')), [['<sup class="invalid">^ </sup>'], '']);
91
91
  assert.deepStrictEqual(inspect(parser('[[^ a]]')), [['<sup class="invalid">^ a</sup>'], '']);
92
92
  assert.deepStrictEqual(inspect(parser('[[^ |]]')), [['<sup class="invalid">^ |</sup>'], '']);
93
93
  assert.deepStrictEqual(inspect(parser('[[^ |b]]')), [['<sup class="invalid">^ |b</sup>'], '']);