securemark 0.249.0 → 0.251.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
@@ -736,7 +736,7 @@ export namespace MarkdownParser {
736
736
  }
737
737
  }
738
738
  export interface CommentParser extends
739
- // [# comment #]
739
+ // [% comment %]
740
740
  Inline<'comment'>,
741
741
  Parser<HTMLElement | string, Context, [
742
742
  InlineParser,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securemark",
3
- "version": "0.249.0",
3
+ "version": "0.251.0",
4
4
  "description": "Secure markdown renderer working on browsers for user input data.",
5
5
  "private": false,
6
6
  "homepage": "https://github.com/falsandtru/securemark",
@@ -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.26.0",
37
+ "@typescript-eslint/parser": "^5.27.1",
38
38
  "babel-loader": "^8.2.5",
39
39
  "babel-plugin-unassert": "^3.2.0",
40
40
  "concurrently": "^7.2.1",
41
- "eslint": "^8.16.0",
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.2",
53
53
  "semver": "^7.3.7",
54
- "spica": "0.0.554",
54
+ "spica": "0.0.567",
55
55
  "ts-loader": "^9.3.0",
56
- "typed-dom": "^0.0.297",
57
- "typescript": "4.7.2",
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",
@@ -9,15 +9,18 @@ import { open } from './surround';
9
9
  import { memoize } from 'spica/memoize';
10
10
 
11
11
  export function indent<P extends Parser<unknown>>(parser: P, separation?: boolean): P;
12
- export function indent<T>(parser: Parser<T>, separation = false): Parser<T> {
12
+ export function indent<P extends Parser<unknown>>(opener: RegExp, parser: P, separation?: boolean): P;
13
+ export function indent<T>(opener: RegExp | Parser<T>, parser?: Parser<T> | boolean, separation = false): Parser<T> {
14
+ if (typeof opener === 'function') return indent(/^([ \t])\1*/, opener, parser as boolean);
13
15
  assert(parser);
14
16
  return bind(block(match(
15
- /^(?=(([ \t])\2*))/,
17
+ opener,
16
18
  memoize(
17
- ([, indent]) =>
19
+ ([indent]) =>
18
20
  some(line(open(indent, source => [[unline(source)], '']))),
19
- ([, indent]) => indent.length * 2 + +(indent[0] === ' '), [])), separation),
21
+ ([indent]) => indent.length * 2 + +(indent[0] === ' '), [])), separation),
20
22
  (nodes, rest, context) => {
23
+ assert(parser = parser as Parser<T>);
21
24
  const result = parser(nodes.join('\n'), context);
22
25
  return result && exec(result) === ''
23
26
  ? [eval(result), rest]
@@ -9,11 +9,10 @@ export function match<T>(pattern: RegExp, f: (matched: RegExpMatchArray) => Pars
9
9
  const param = source.match(pattern);
10
10
  if (!param) return;
11
11
  assert(source.startsWith(param[0]));
12
- const rest = source.slice(param[0].length);
13
- const result = f(param)(rest, context);
12
+ const result = f(param)(source, context);
14
13
  assert(check(source, result, false));
15
14
  if (!result) return;
16
- return exec(result).length < source.length && exec(result).length <= rest.length
15
+ return exec(result).length < source.length && exec(result).length <= source.length
17
16
  ? result
18
17
  : undefined;
19
18
  };
@@ -52,11 +52,11 @@ describe('Unit: parser/api/parse', () => {
52
52
  [...parse('<wbr>\na').children].map(el => el.outerHTML),
53
53
  ['<p>&lt;wbr&gt;<br>a</p>']);
54
54
  assert.deepStrictEqual(
55
- [...parse('[#\n<wbr>\n#]').children].map(el => el.outerHTML),
56
- ['<p><span class="comment"><input type="checkbox"><span>[#<br>&lt;wbr&gt;<br>#]</span></span></p>']);
55
+ [...parse('[%\n<wbr>\n%]').children].map(el => el.outerHTML),
56
+ ['<p><span class="comment"><input type="checkbox"><span>[%<br>&lt;wbr&gt;<br>%]</span></span></p>']);
57
57
  assert.deepStrictEqual(
58
- [...parse('[#\n<wbr>\n#]\na').children].map(el => el.outerHTML),
59
- ['<p><span class="comment"><input type="checkbox"><span>[#<br>&lt;wbr&gt;<br>#]</span></span><br>a</p>']);
58
+ [...parse('[%\n<wbr>\n%]\na').children].map(el => el.outerHTML),
59
+ ['<p><span class="comment"><input type="checkbox"><span>[%<br>&lt;wbr&gt;<br>%]</span></span><br>a</p>']);
60
60
  });
61
61
 
62
62
  it('linebreak', () => {
@@ -80,6 +80,12 @@ describe('Unit: parser/api/parse', () => {
80
80
  ['<p>a<br>\\<br>b</p>']);
81
81
  });
82
82
 
83
+ it('indent', () => {
84
+ assert.deepStrictEqual(
85
+ [...parse(' a').children].map(el => el.outerHTML),
86
+ ['<p> a</p>']);
87
+ });
88
+
83
89
  it('url', () => {
84
90
  assert.deepStrictEqual(
85
91
  [...parse([
@@ -220,8 +226,8 @@ describe('Unit: parser/api/parse', () => {
220
226
 
221
227
  it('creation', () => {
222
228
  assert.deepStrictEqual(
223
- [...parse('"[# '.repeat(100)).children].map(el => el.outerHTML),
224
- [`<p>${'"[# '.repeat(100).trim()}</p>`]);
229
+ [...parse('"[% '.repeat(100)).children].map(el => el.outerHTML),
230
+ [`<p>${'"[% '.repeat(100).trim()}</p>`]);
225
231
  });
226
232
 
227
233
  it('recursion', () => {
@@ -72,7 +72,7 @@ describe('Unit: parser/block/blockquote', () => {
72
72
  assert.deepStrictEqual(inspect(parser('!> a\\\nb')), [['<blockquote><section><p>a<span class="linebreak"> </span>b</p><ol class="annotations"></ol><ol class="references"></ol></section></blockquote>'], '']);
73
73
  assert.deepStrictEqual(inspect(parser('!> *a\nb*')), [['<blockquote><section><p><em>a<br>b</em></p><ol class="annotations"></ol><ol class="references"></ol></section></blockquote>'], '']);
74
74
  assert.deepStrictEqual(inspect(parser('!> *a\n> b*')), [['<blockquote><section><p><em>a<br>b</em></p><ol class="annotations"></ol><ol class="references"></ol></section></blockquote>'], '']);
75
- assert.deepStrictEqual(inspect(parser('!> a \n b c ')), [['<blockquote><section><p>a<br> b c</p><ol class="annotations"></ol><ol class="references"></ol></section></blockquote>'], '']);
75
+ assert.deepStrictEqual(inspect(parser('!> a \n b c ')), [['<blockquote><section><p> a<br> b c</p><ol class="annotations"></ol><ol class="references"></ol></section></blockquote>'], '']);
76
76
  assert.deepStrictEqual(inspect(parser('!>> a')), [['<blockquote><blockquote><section><p>a</p><ol class="annotations"></ol><ol class="references"></ol></section></blockquote></blockquote>'], '']);
77
77
  assert.deepStrictEqual(inspect(parser('!>> a\n> b')), [['<blockquote><blockquote><section><p>a</p><ol class="annotations"></ol><ol class="references"></ol></section></blockquote><section><p>b</p><ol class="annotations"></ol><ol class="references"></ol></section></blockquote>'], '']);
78
78
  assert.deepStrictEqual(inspect(parser('!> - a')), [['<blockquote><section><ul><li>a</li></ul><ol class="annotations"></ol><ol class="references"></ol></section></blockquote>'], '']);
@@ -90,7 +90,7 @@ describe('Unit: parser/block/blockquote', () => {
90
90
  assert.deepStrictEqual(inspect(parser('!> \na')), [['<blockquote><section><p>a</p><ol class="annotations"></ol><ol class="references"></ol></section></blockquote>'], '']);
91
91
  assert.deepStrictEqual(inspect(parser('!>\n')), undefined);
92
92
  assert.deepStrictEqual(inspect(parser('!>\na')), [['<blockquote><section><p>a</p><ol class="annotations"></ol><ol class="references"></ol></section></blockquote>'], '']);
93
- assert.deepStrictEqual(inspect(parser('!>\n a')), [['<blockquote><section><p>a</p><ol class="annotations"></ol><ol class="references"></ol></section></blockquote>'], '']);
93
+ assert.deepStrictEqual(inspect(parser('!>\n a')), [['<blockquote><section><p> a</p><ol class="annotations"></ol><ol class="references"></ol></section></blockquote>'], '']);
94
94
  assert.deepStrictEqual(inspect(parser('!>\n>')), [['<blockquote><section><ol class="annotations"></ol><ol class="references"></ol></section></blockquote>'], '']);
95
95
  assert.deepStrictEqual(inspect(parser('!>\n> a')), [['<blockquote><section><p>a</p><ol class="annotations"></ol><ol class="references"></ol></section></blockquote>'], '']);
96
96
  assert.deepStrictEqual(inspect(parser('!>> ## a\n> ## a')), [['<blockquote><blockquote><section><h2>a</h2><ol class="annotations"></ol><ol class="references"></ol></section></blockquote><section><h2>a</h2><ol class="annotations"></ol><ol class="references"></ol></section></blockquote>'], '']);
@@ -33,6 +33,7 @@ describe('Unit: parser/block/dlist', () => {
33
33
  it('single', () => {
34
34
  assert.deepStrictEqual(inspect(parser('~ a')), [['<dl><dt id="index:a">a</dt><dd></dd></dl>'], '']);
35
35
  assert.deepStrictEqual(inspect(parser('~ \\')), [['<dl><dt id="index:\\">\\</dt><dd></dd></dl>'], '']);
36
+ assert.deepStrictEqual(inspect(parser('~ \\ a')), [['<dl><dt id="index:a">a</dt><dd></dd></dl>'], '']);
36
37
  assert.deepStrictEqual(inspect(parser('~ a\nb')), [['<dl><dt id="index:a">a</dt><dd>b</dd></dl>'], '']);
37
38
  assert.deepStrictEqual(inspect(parser('~ a\nb\n')), [['<dl><dt id="index:a">a</dt><dd>b</dd></dl>'], '']);
38
39
  assert.deepStrictEqual(inspect(parser('~ a\nb\nc')), [['<dl><dt id="index:a">a</dt><dd>b<br>c</dd></dl>'], '']);
@@ -57,7 +58,7 @@ describe('Unit: parser/block/dlist', () => {
57
58
 
58
59
  it('multiple', () => {
59
60
  assert.deepStrictEqual(inspect(parser('~ a\n: b\n~ c\n: d')), [['<dl><dt id="index:a">a</dt><dd>b</dd><dt id="index:c">c</dt><dd>d</dd></dl>'], '']);
60
- assert.deepStrictEqual(inspect(parser('~ a\n b\nc\n d')), [['<dl><dt id="index:a">a</dt><dd>b<br>c<br> d</dd></dl>'], '']);
61
+ assert.deepStrictEqual(inspect(parser('~ a\n b\nc\n d')), [['<dl><dt id="index:a">a</dt><dd> b<br>c<br> d</dd></dl>'], '']);
61
62
  assert.deepStrictEqual(inspect(parser('~ a\n: b\nc\n: d')), [['<dl><dt id="index:a">a</dt><dd>b<br>c</dd><dd>d</dd></dl>'], '']);
62
63
  assert.deepStrictEqual(inspect(parser('~ a\n: b\nc\n~ d\n: e')), [['<dl><dt id="index:a">a</dt><dd>b<br>c</dd><dt id="index:d">d</dt><dd>e</dd></dl>'], '']);
63
64
  assert.deepStrictEqual(inspect(parser('~ a\n: b\n~ c\n: d\n~ e\n: f')), [['<dl><dt id="index:a">a</dt><dd>b</dd><dt id="index:c">c</dt><dd>d</dd><dt id="index:e">e</dt><dd>f</dd></dl>'], '']);
@@ -1,9 +1,9 @@
1
1
  import { DListParser } from '../block';
2
- import { union, inits, some, block, line, validate, rewrite, context, creator, open, trim, lazy, fmap } from '../../combinator';
2
+ import { union, inits, some, block, line, validate, rewrite, context, creator, open, trimEnd, lazy, fmap } from '../../combinator';
3
3
  import { inline, indexee, indexer } from '../inline';
4
4
  import { anyline } from '../source';
5
5
  import { localize } from '../locale';
6
- import { visualize } from '../util';
6
+ import { visualize, trimBlank } from '../util';
7
7
  import { html, defrag } from 'typed-dom/dom';
8
8
  import { push } from 'spica/array';
9
9
 
@@ -25,7 +25,7 @@ export const dlist: DListParser = lazy(() => block(localize(fmap(validate(
25
25
 
26
26
  const term: DListParser.TermParser = creator(line(indexee(fmap(open(
27
27
  /^~[^\S\n]+(?=\S)/,
28
- trim(visualize(some(union([indexer, inline])))),
28
+ visualize(trimBlank(some(union([indexer, inline])))),
29
29
  true),
30
30
  ns => [html('dt', defrag(ns))]))));
31
31
 
@@ -33,7 +33,7 @@ const desc: DListParser.DescriptionParser = creator(block(fmap(open(
33
33
  /^:[^\S\n]+(?=\S)|/,
34
34
  rewrite(
35
35
  some(anyline, /^[~:][^\S\n]+\S/),
36
- trim(visualize(some(union([inline]))))),
36
+ visualize(trimEnd(some(union([inline]))))),
37
37
  true),
38
38
  ns => [html('dd', defrag(ns))]),
39
39
  false));
@@ -1,6 +1,6 @@
1
1
  import { undefined } from 'spica/global';
2
2
  import { ExtensionParser } from '../../block';
3
- import { union, inits, sequence, some, block, line, fence, rewrite, context, close, match, convert, trim, fallback, fmap } from '../../../combinator';
3
+ import { union, inits, sequence, some, block, line, fence, rewrite, context, close, match, convert, trimEnd, fallback, fmap } from '../../../combinator';
4
4
  import { str, contentline, emptyline } from '../../source';
5
5
  import { label, segment as seg_label } from '../../inline/extension/label';
6
6
  import { ulist } from '../ulist';
@@ -15,7 +15,7 @@ import { blockquote, segment as seg_blockquote } from '../blockquote';
15
15
  import { placeholder, segment_ as seg_placeholder } from './placeholder';
16
16
  import { inline, media, shortmedia } from '../../inline';
17
17
  import { localize } from '../../locale';
18
- import { visualize } from '../../util';
18
+ import { visualize, trimBlank } from '../../util';
19
19
  import { html, defrag } from 'typed-dom/dom';
20
20
  import { memoize } from 'spica/memoize';
21
21
  import { unshift } from 'spica/array';
@@ -23,7 +23,7 @@ import { unshift } from 'spica/array';
23
23
  import FigureParser = ExtensionParser.FigureParser;
24
24
 
25
25
  export const segment: FigureParser.SegmentParser = block(match(
26
- /^(~{3,})(?:figure[^\S\n]|(?=\[?\$))/,
26
+ /^(~{3,})(?:figure[^\S\n])?(?=\[?\$)/,
27
27
  memoize(
28
28
  ([, fence], closer = new RegExp(String.raw`^${fence}[^\S\n]*(?:$|\n)`)) => close(
29
29
  sequence([
@@ -49,7 +49,7 @@ export const segment: FigureParser.SegmentParser = block(match(
49
49
  ([, fence]) => fence.length, [])));
50
50
 
51
51
  export const figure: FigureParser = block(fallback(rewrite(segment, fmap(
52
- convert(source => source.slice(source.match(/^~+(?:figure[^\S\n]+)?/)![0].length, source.trimEnd().lastIndexOf('\n')),
52
+ convert(source => source.slice(source.match(/^~+(?:\w+\s+)?/)![0].length, source.trimEnd().lastIndexOf('\n')),
53
53
  sequence([
54
54
  line(sequence([label, str(/^(?=\s).*\n/)])),
55
55
  inits([
@@ -70,7 +70,7 @@ export const figure: FigureParser = block(fallback(rewrite(segment, fmap(
70
70
  emptyline,
71
71
  block(localize(
72
72
  context({ syntax: { inline: { media: false } } },
73
- trim(visualize(some(inline)))))),
73
+ visualize(trimBlank(trimEnd(some(inline))))))),
74
74
  ]),
75
75
  ])),
76
76
  ([label, param, content, ...caption]: [HTMLAnchorElement, string, ...HTMLElement[]]) => [
@@ -40,6 +40,7 @@ describe('Unit: parser/block/heading', () => {
40
40
  assert.deepStrictEqual(inspect(parser('# a\\')), [['<h1 id="index:a">a</h1>'], '']);
41
41
  assert.deepStrictEqual(inspect(parser('# a\\\n')), [['<h1 id="index:a">a</h1>'], '']);
42
42
  assert.deepStrictEqual(inspect(parser('# \\')), [['<h1 id="index:\\">\\</h1>'], '']);
43
+ assert.deepStrictEqual(inspect(parser('# \\ a')), [['<h1 id="index:a">a</h1>'], '']);
43
44
  assert.deepStrictEqual(inspect(parser('## \\')), [['<h2 id="index:\\">\\</h2>'], '']);
44
45
  assert.deepStrictEqual(inspect(parser('# @a')), [['<h1 id="index:@a">@a</h1>'], '']);
45
46
  assert.deepStrictEqual(inspect(parser('## @a')), [['<h2 id="index:@a"><a href="/@a" class="account">@a</a></h2>'], '']);
@@ -1,8 +1,8 @@
1
1
  import { HeadingParser } from '../block';
2
- import { union, some, block, line, validate, focus, rewrite, context, open, trim, fmap } from '../../combinator';
2
+ import { union, some, block, line, validate, focus, rewrite, context, open, fmap } from '../../combinator';
3
3
  import { inline, indexee, indexer } from '../inline';
4
4
  import { str } from '../source';
5
- import { visualize } from '../util';
5
+ import { visualize, trimBlank } from '../util';
6
6
  import { html, defrag } from 'typed-dom/dom';
7
7
 
8
8
  export const segment: HeadingParser.SegmentParser = block(validate('#', focus(
@@ -21,13 +21,13 @@ export const heading: HeadingParser = block(rewrite(segment,
21
21
  line(indexee(fmap(union([
22
22
  open(
23
23
  str(/^##+/),
24
- trim(visualize(some(union([indexer, inline])))), true),
24
+ visualize(trimBlank(some(union([indexer, inline])))), true),
25
25
  open(
26
26
  str('#'),
27
27
  context({ syntax: { inline: {
28
28
  autolink: false,
29
29
  }}},
30
- trim(visualize(some(union([indexer, inline]))))), true),
30
+ visualize(trimBlank(some(union([indexer, inline]))))), true),
31
31
  ]),
32
32
  ([h, ...ns]: [string, ...(HTMLElement | string)[]]) => [
33
33
  h.length <= 6
@@ -9,20 +9,27 @@ describe('Unit: parser/block/indentblock', () => {
9
9
  it('invalid', () => {
10
10
  assert.deepStrictEqual(inspect(parser('')), undefined);
11
11
  assert.deepStrictEqual(inspect(parser('\na')), undefined);
12
- assert.deepStrictEqual(inspect(parser(' a')), undefined);
13
- assert.deepStrictEqual(inspect(parser(' \ta')), undefined);
14
- assert.deepStrictEqual(inspect(parser(' a\nb')), undefined);
15
- assert.deepStrictEqual(inspect(parser(' a\n b')), undefined);
12
+ assert.deepStrictEqual(inspect(parser('a')), undefined);
13
+ assert.deepStrictEqual(inspect(parser(' a')), undefined);
14
+ assert.deepStrictEqual(inspect(parser(' a')), undefined);
15
+ assert.deepStrictEqual(inspect(parser(' \ta')), undefined);
16
+ assert.deepStrictEqual(inspect(parser(' a\nb')), undefined);
17
+ assert.deepStrictEqual(inspect(parser(' a\n b')), undefined);
18
+ assert.deepStrictEqual(inspect(parser(' a\n\tb')), undefined);
16
19
  });
17
20
 
18
21
  it('valid', () => {
19
- assert.deepStrictEqual(inspect(parser(' a')), [['<pre class="text">a</pre>'], '']);
20
- assert.deepStrictEqual(inspect(parser(' a ')), [['<pre class="text">a </pre>'], '']);
21
- assert.deepStrictEqual(inspect(parser(' a \n')), [['<pre class="text">a </pre>'], '']);
22
- assert.deepStrictEqual(inspect(parser(' a \n b')), [['<pre class="text">a <br> b</pre>'], '']);
23
- assert.deepStrictEqual(inspect(parser(' a\\\n b')), [['<pre class="text">a\\<br>b</pre>'], '']);
22
+ assert.deepStrictEqual(inspect(parser(' a')), [['<pre class="text">a</pre>'], '']);
23
+ assert.deepStrictEqual(inspect(parser(' a ')), [['<pre class="text">a </pre>'], '']);
24
+ assert.deepStrictEqual(inspect(parser(' a \n')), [['<pre class="text">a </pre>'], '']);
25
+ assert.deepStrictEqual(inspect(parser(' a \n b')), [['<pre class="text">a <br> b</pre>'], '']);
26
+ assert.deepStrictEqual(inspect(parser(' a \\\n b')), [['<pre class="text">a \\<br> b</pre>'], '']);
27
+ assert.deepStrictEqual(inspect(parser(' a')), [['<pre class="text"> a</pre>'], '']);
28
+ assert.deepStrictEqual(inspect(parser(' a')), [['<pre class="text">a</pre>'], '']);
29
+ assert.deepStrictEqual(inspect(parser(' \ta')), [['<pre class="text">\ta</pre>'], '']);
24
30
  assert.deepStrictEqual(inspect(parser('\ta')), [['<pre class="text">a</pre>'], '']);
25
- assert.deepStrictEqual(inspect(parser('\t a')), [['<pre class="text"> a</pre>'], '']);
31
+ assert.deepStrictEqual(inspect(parser('\t\ta')), [['<pre class="text">a</pre>'], '']);
32
+ assert.deepStrictEqual(inspect(parser('\t a')), [['<pre class="text"> a</pre>'], '']);
26
33
  });
27
34
 
28
35
  });
@@ -1,13 +1,13 @@
1
1
  import { IndentBlockParser } from '../block';
2
- import { union, block, validate, indent, convert } from '../../combinator';
2
+ import { union, block, indent, convert } from '../../combinator';
3
3
  import { codeblock } from './codeblock';
4
4
 
5
5
  // 空行を含むインデントブロックはインデントの違いによるセグメント分割の境界が視認不能となるため採用しない
6
6
 
7
- export const indentblock: IndentBlockParser = block(validate(/^(?: |\t)/, indent(convert(
7
+ export const indentblock: IndentBlockParser = block(indent(/^( {4}|\t)\1*/, convert(
8
8
  source => {
9
9
  const fence = (source.match(/^`{3,}(?=[^\S\n]*$)/mg) ?? [])
10
10
  .reduce((max, fence) => fence > max ? fence : max, '``') + '`';
11
11
  return `${fence}\n${source}\n${fence}`;
12
12
  },
13
- union([codeblock])), true)));
13
+ union([codeblock])), true));
@@ -1,17 +1,18 @@
1
1
  import { undefined } from 'spica/global';
2
2
  import { OListParser } from '../block';
3
- import { union, inits, subsequence, some, block, line, validate, indent, focus, rewrite, context, creator, open, match, trim, trimStart, fallback, lazy, fmap } from '../../combinator';
3
+ import { union, inits, subsequence, some, block, line, validate, indent, focus, rewrite, context, creator, open, match, trim, fallback, lazy, fmap } from '../../combinator';
4
4
  import { checkbox, ulist_, fillFirstLine } from './ulist';
5
5
  import { ilist_ } from './ilist';
6
6
  import { inline, indexee, indexer } from '../inline';
7
7
  import { contentline } from '../source';
8
+ import { trimBlank } from '../util';
8
9
  import { html, define, defrag } from 'typed-dom/dom';
9
10
  import { memoize } from 'spica/memoize';
10
11
  import { shift } from 'spica/array';
11
12
 
12
13
  const openers = {
13
- '.': /^(?:[0-9]+|[a-z]+|[A-Z]+)(?:-(?!-)[0-9]*)*(?![^\S\n])\.?(?=$|\s)/,
14
- '(': /^\((?:[0-9]*|[a-z]*)(?![^)\n])\)?(?:-(?!-)[0-9]*)*(?=$|\s)/,
14
+ '.': /^([0-9]+|[a-z]+|[A-Z]+)(?:-(?!-)[0-9]*)*(?![^\S\n])\.?(?=$|\s)/,
15
+ '(': /^\(([0-9]*|[a-z]*)(?![^)\n])\)?(?:-(?!-)[0-9]*)*(?=$|\s)/,
15
16
  } as const;
16
17
 
17
18
  export const olist: OListParser = lazy(() => block(validate(
@@ -24,10 +25,10 @@ export const olist: OListParser = lazy(() => block(validate(
24
25
 
25
26
  export const olist_: OListParser = lazy(() => block(union([
26
27
  match(
27
- new RegExp(`^(?=${openers['.'].source.replace('?:', '')})`),
28
+ openers['.'],
28
29
  memoize(ms => list(type(ms[1]), '.'), ms => type(ms[1]).charCodeAt(0) || 0, [])),
29
30
  match(
30
- new RegExp(`^(?=${openers['('].source.replace('?:', '')})`),
31
+ openers['('],
31
32
  memoize(ms => list(type(ms[1]), '('), ms => type(ms[1]).charCodeAt(0) || 0, [])),
32
33
  ])));
33
34
 
@@ -35,7 +36,7 @@ const list = (type: string, form: string): OListParser.ListParser => fmap(
35
36
  some(creator(union([
36
37
  indexee(fmap(fallback(
37
38
  inits([
38
- line(open(heads[form], trim(subsequence([checkbox, trimStart(some(union([indexer, inline])))])), true)),
39
+ line(open(heads[form], trim(subsequence([checkbox, trimBlank(some(union([indexer, inline])))])), true)),
39
40
  indent(union([ulist_, olist_, ilist_])),
40
41
  ]),
41
42
  invalid),
@@ -33,7 +33,7 @@ describe('Unit: parser/block/paragraph', () => {
33
33
  assert.deepStrictEqual(inspect(parser('***a*b\n<wbr>**\nc')), [['<p>**<em>a</em>b<br><wbr>**<br>c</p>'], '']);
34
34
  assert.deepStrictEqual(inspect(parser('***a**b\n<wbr>*\nc')), [['<p>*<strong>a</strong>b<br><wbr>*<br>c</p>'], '']);
35
35
  assert.deepStrictEqual(inspect(parser('==a\n<wbr>==\nb')), [['<p>==a<br><wbr>==<br>b</p>'], '']);
36
- assert.deepStrictEqual(inspect(parser(' a')), [['<p>a</p>'], '']);
36
+ assert.deepStrictEqual(inspect(parser(' a')), [['<p> a</p>'], '']);
37
37
  });
38
38
 
39
39
  it('anchor', () => {
@@ -50,18 +50,18 @@ describe('Unit: parser/block/paragraph', () => {
50
50
  assert.deepStrictEqual(inspect(parser('a\n>>1')), [['<p>a<br><a href="?at=1" class="anchor">&gt;&gt;1</a></p>'], '']);
51
51
  assert.deepStrictEqual(inspect(parser('a\n>>1\nb')), [['<p>a<br><a href="?at=1" class="anchor">&gt;&gt;1</a><br>b</p>'], '']);
52
52
  assert.deepStrictEqual(inspect(parser('a\n>> b\nc')), [['<p>a<br>&gt;&gt; b<br>c</p>'], '']);
53
- assert.deepStrictEqual(inspect(parser(' >>1')), [['<p><a href="?at=1" class="anchor">&gt;&gt;1</a></p>'], '']);
54
- assert.deepStrictEqual(inspect(parser(' >>>1')), [['<p>&gt;<a href="?at=1" class="anchor">&gt;&gt;1</a></p>'], '']);
53
+ assert.deepStrictEqual(inspect(parser(' >>1')), [['<p> <a href="?at=1" class="anchor">&gt;&gt;1</a></p>'], '']);
54
+ assert.deepStrictEqual(inspect(parser(' >>>1')), [['<p> &gt;<a href="?at=1" class="anchor">&gt;&gt;1</a></p>'], '']);
55
55
  });
56
56
 
57
57
  it('comment', () => {
58
- assert.deepStrictEqual(inspect(parser('[# a #]')), [['<p><span class="comment"><input type="checkbox"><span>[# a #]</span></span></p>'], '']);
59
- assert.deepStrictEqual(inspect(parser('[# a #]b')), [['<p><span class="comment"><input type="checkbox"><span>[# a #]</span></span>b</p>'], '']);
60
- assert.deepStrictEqual(inspect(parser('[# a #]\nb')), [['<p><span class="comment"><input type="checkbox"><span>[# a #]</span></span><br>b</p>'], '']);
61
- assert.deepStrictEqual(inspect(parser('[## a ##]')), [['<p><span class="comment"><input type="checkbox"><span>[## a ##]</span></span></p>'], '']);
62
- assert.deepStrictEqual(inspect(parser('[#\n<wbr>\n#]')), [['<p><span class="comment"><input type="checkbox"><span>[#<br>&lt;wbr&gt;<br>#]</span></span></p>'], '']);
63
- assert.deepStrictEqual(inspect(parser('[#\n<wbr>\n#]a')), [['<p><span class="comment"><input type="checkbox"><span>[#<br>&lt;wbr&gt;<br>#]</span></span>a</p>'], '']);
64
- assert.deepStrictEqual(inspect(parser('[#\n<wbr>\n#]\na')), [['<p><span class="comment"><input type="checkbox"><span>[#<br>&lt;wbr&gt;<br>#]</span></span><br>a</p>'], '']);
58
+ assert.deepStrictEqual(inspect(parser('[% a %]')), [['<p><span class="comment"><input type="checkbox"><span>[% a %]</span></span></p>'], '']);
59
+ assert.deepStrictEqual(inspect(parser('[% a %]b')), [['<p><span class="comment"><input type="checkbox"><span>[% a %]</span></span>b</p>'], '']);
60
+ assert.deepStrictEqual(inspect(parser('[% a %]\nb')), [['<p><span class="comment"><input type="checkbox"><span>[% a %]</span></span><br>b</p>'], '']);
61
+ assert.deepStrictEqual(inspect(parser('[%% a %%]')), [['<p><span class="comment"><input type="checkbox"><span>[%% a %%]</span></span></p>'], '']);
62
+ assert.deepStrictEqual(inspect(parser('[%\n<wbr>\n%]')), [['<p><span class="comment"><input type="checkbox"><span>[%<br>&lt;wbr&gt;<br>%]</span></span></p>'], '']);
63
+ assert.deepStrictEqual(inspect(parser('[%\n<wbr>\n%]a')), [['<p><span class="comment"><input type="checkbox"><span>[%<br>&lt;wbr&gt;<br>%]</span></span>a</p>'], '']);
64
+ assert.deepStrictEqual(inspect(parser('[%\n<wbr>\n%]\na')), [['<p><span class="comment"><input type="checkbox"><span>[%<br>&lt;wbr&gt;<br>%]</span></span><br>a</p>'], '']);
65
65
  });
66
66
 
67
67
  it('template', () => {
@@ -1,10 +1,10 @@
1
1
  import { ParagraphParser } from '../block';
2
- import { union, some, block, trim, fmap } from '../../combinator';
2
+ import { union, some, block, trimEnd, fmap } from '../../combinator';
3
3
  import { inline } from '../inline';
4
4
  import { localize } from '../locale';
5
5
  import { visualize } from '../util';
6
6
  import { html, defrag } from 'typed-dom/dom';
7
7
 
8
8
  export const paragraph: ParagraphParser = block(localize(fmap(
9
- trim(visualize(some(union([inline])))),
9
+ visualize(trimEnd(some(union([inline])))),
10
10
  ns => [html('p', defrag(ns))])));
@@ -1,5 +1,5 @@
1
1
  import { ReplyParser } from '../block';
2
- import { inits, subsequence, some, block, validate, rewrite, trim, fmap } from '../../combinator';
2
+ import { inits, subsequence, some, block, validate, rewrite, trimEnd, fmap } from '../../combinator';
3
3
  import { cite } from './reply/cite';
4
4
  import { quote, syntax as delimiter } from './reply/quote';
5
5
  import { inline } from '../inline';
@@ -26,7 +26,7 @@ export const reply: ReplyParser = block(validate('>', localize(fmap(
26
26
  some(quote),
27
27
  fmap(rewrite(
28
28
  some(anyline, delimiter),
29
- trim(visualize(some(inline)))),
29
+ visualize(trimEnd(some(inline)))),
30
30
  ns => push(ns, [html('br')])),
31
31
  ])),
32
32
  ]),
@@ -1,11 +1,12 @@
1
1
  import { UListParser } from '../block';
2
- import { union, inits, subsequence, some, block, line, validate, indent, focus, rewrite, context, creator, open, trim, trimStart, fallback, lazy, fmap } from '../../combinator';
2
+ import { union, inits, subsequence, some, block, line, validate, indent, focus, rewrite, context, creator, open, trim, fallback, lazy, fmap } from '../../combinator';
3
3
  import { olist_ } from './olist';
4
4
  import { ilist_ } from './ilist';
5
5
  import { inline, indexer, indexee } from '../inline';
6
+ import { contentline } from '../source';
7
+ import { trimBlank } from '../util';
6
8
  import { html, defrag } from 'typed-dom/dom';
7
9
  import { unshift } from 'spica/array';
8
- import { contentline } from '../source';
9
10
 
10
11
  export const ulist: UListParser = lazy(() => block(validate(
11
12
  /^-(?=[^\S\n]|\n[^\S\n]*\S)/,
@@ -17,7 +18,7 @@ export const ulist_: UListParser = lazy(() => block(fmap(validate(
17
18
  some(creator(union([
18
19
  indexee(fmap(fallback(
19
20
  inits([
20
- line(open(/^-(?:$|\s)/, trim(subsequence([checkbox, trimStart(some(union([indexer, inline])))])), true)),
21
+ line(open(/^-(?:$|\s)/, trim(subsequence([checkbox, trimBlank(some(union([indexer, inline])))])), true)),
21
22
  indent(union([ulist_, olist_, ilist_])),
22
23
  ]),
23
24
  invalid),
@@ -2,7 +2,7 @@ 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 { trimBlankInline } from '../util';
5
+ import { trimBlank } 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(
@@ -19,6 +19,6 @@ export const annotation: AnnotationParser = lazy(() => creator(validate('((', ')
19
19
  //link: true,
20
20
  //autolink: true,
21
21
  }}, delimiters: undefined },
22
- trimBlankInline(some(union([inline]), ')', /^\\?\n/)))),
22
+ trimBlank(some(union([inline]), ')', /^\\?\n/)))),
23
23
  '))'),
24
24
  ns => [html('sup', { class: 'annotation' }, defrag(ns))]))));
@@ -5,7 +5,7 @@ import { html } from 'typed-dom/dom';
5
5
  export const code: CodeParser = creator(validate('`', match(
6
6
  /^(`+)(?!`)([^\n]*?[^`\n])\1(?!`)/,
7
7
  ([whole, , body]) => rest =>
8
- [[html('code', { 'data-src': whole }, format(body))], rest])));
8
+ [[html('code', { 'data-src': whole }, format(body))], rest.slice(whole.length)])));
9
9
 
10
10
  function format(text: string): string {
11
11
  assert(text.length > 0);