securemark 0.237.1 → 0.239.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/CHANGELOG.md +12 -0
- package/dist/securemark.js +151 -106
- package/markdown.d.ts +3 -1
- package/package-lock.json +50 -50
- package/package.json +1 -1
- package/src/combinator/data/parser/subsequence.ts +5 -9
- package/src/combinator/data/parser/tails.ts +1 -1
- package/src/combinator/data/parser/union.ts +1 -1
- package/src/debug.test.ts +1 -1
- package/src/parser/block/codeblock.test.ts +5 -2
- package/src/parser/block/codeblock.ts +42 -24
- package/src/parser/block/extension/fig.test.ts +1 -1
- package/src/parser/block/extension/fig.ts +1 -1
- package/src/parser/block/extension/figure.test.ts +2 -0
- package/src/parser/block/extension/figure.ts +21 -24
- package/src/parser/block/olist.test.ts +2 -0
- package/src/parser/block/ulist.test.ts +2 -0
- package/src/parser/block.ts +2 -2
- package/src/parser/inline/comment.ts +1 -1
- package/src/parser/inline/extension/index.test.ts +19 -13
- package/src/parser/inline/extension/index.ts +4 -3
- package/src/parser/inline/extension/indexee.ts +7 -3
- package/src/parser/inline/extension/indexer.test.ts +2 -1
- package/src/parser/inline/extension/indexer.ts +6 -3
- package/src/parser/inline/htmlentity.ts +2 -2
- package/src/parser/inline/media.ts +2 -2
- package/src/parser/inline/ruby.ts +2 -2
- package/src/parser/processor/figure.test.ts +8 -6
- package/src/parser/processor/figure.ts +32 -19
- package/src/parser/segment.test.ts +2 -2
- package/src/parser/segment.ts +2 -2
- package/src/parser/source/str.ts +23 -4
- package/src/parser/source/text.ts +3 -0
- package/src/parser/source.ts +1 -1
- package/src/parser/util.ts +2 -2
|
@@ -3,7 +3,7 @@ import { ExtensionParser } from '../../inline';
|
|
|
3
3
|
import { union, some, validate, guard, context, creator, surround, open, lazy, fmap } from '../../../combinator';
|
|
4
4
|
import { inline } from '../../inline';
|
|
5
5
|
import { indexee, identity } from './indexee';
|
|
6
|
-
import { txt, str } from '../../source';
|
|
6
|
+
import { txt, str, stropt } from '../../source';
|
|
7
7
|
import { startTight, trimNodeEnd } from '../../util';
|
|
8
8
|
import { html, define, defrag } from 'typed-dom';
|
|
9
9
|
import { join } from 'spica/array';
|
|
@@ -23,10 +23,11 @@ export const index: IndexParser = lazy(() => creator(validate('[#', ']', '\n', f
|
|
|
23
23
|
media: false,
|
|
24
24
|
autolink: false,
|
|
25
25
|
}}},
|
|
26
|
+
open(stropt('|'),
|
|
26
27
|
some(union([
|
|
27
28
|
signature,
|
|
28
29
|
inline,
|
|
29
|
-
]), ']', /^\\?\n/)))),
|
|
30
|
+
]), ']', /^\\?\n/), true)))),
|
|
30
31
|
']'),
|
|
31
32
|
ns => [html('a', trimNodeEnd(defrag(ns)))])),
|
|
32
33
|
([el]: [HTMLAnchorElement]) => [
|
|
@@ -40,7 +41,7 @@ export const index: IndexParser = lazy(() => creator(validate('[#', ']', '\n', f
|
|
|
40
41
|
]))));
|
|
41
42
|
|
|
42
43
|
const signature: IndexParser.SignatureParser = lazy(() => creator(fmap(open(
|
|
43
|
-
|
|
44
|
+
'|#',
|
|
44
45
|
startTight(some(union([bracket, txt]), ']'))),
|
|
45
46
|
ns => [
|
|
46
47
|
html('span', { class: 'indexer', 'data-index': identity(join(ns)).slice(6) }),
|
|
@@ -22,12 +22,13 @@ export function text(source: HTMLElement | DocumentFragment, optional = false):
|
|
|
22
22
|
assert(source instanceof DocumentFragment || !source.matches('.indexer'));
|
|
23
23
|
assert(source.querySelectorAll(':scope > .indexer').length <= 1);
|
|
24
24
|
const indexer = source.querySelector(':scope > .indexer');
|
|
25
|
-
if (indexer) return
|
|
26
|
-
|
|
25
|
+
if (!indexer && optional) return '';
|
|
26
|
+
const index = indexer?.getAttribute('data-index');
|
|
27
|
+
if (index) return index;
|
|
27
28
|
assert(!source.querySelector('.annotation, br'));
|
|
28
29
|
const target = source.cloneNode(true) as typeof source;
|
|
29
30
|
for (
|
|
30
|
-
let es = target.querySelectorAll('code[data-src], .math[data-src], .comment, rt, rp, .reference'),
|
|
31
|
+
let es = target.querySelectorAll('code[data-src], .math[data-src], .comment, rt, rp, .reference, .checkbox, ul, ol'),
|
|
31
32
|
i = 0, len = es.length; i < len; ++i) {
|
|
32
33
|
const el = es[i];
|
|
33
34
|
switch (el.tagName) {
|
|
@@ -36,6 +37,8 @@ export function text(source: HTMLElement | DocumentFragment, optional = false):
|
|
|
36
37
|
continue;
|
|
37
38
|
case 'RT':
|
|
38
39
|
case 'RP':
|
|
40
|
+
case 'UL':
|
|
41
|
+
case 'OL':
|
|
39
42
|
el.remove();
|
|
40
43
|
continue;
|
|
41
44
|
}
|
|
@@ -44,6 +47,7 @@ export function text(source: HTMLElement | DocumentFragment, optional = false):
|
|
|
44
47
|
define(el, el.getAttribute('data-src')!);
|
|
45
48
|
continue;
|
|
46
49
|
case 'comment':
|
|
50
|
+
case 'checkbox':
|
|
47
51
|
el.remove();
|
|
48
52
|
continue;
|
|
49
53
|
case 'reference':
|
|
@@ -12,13 +12,14 @@ describe('Unit: parser/inline/extension/indexer', () => {
|
|
|
12
12
|
assert.deepStrictEqual(inspect(parser(' ')), undefined);
|
|
13
13
|
assert.deepStrictEqual(inspect(parser(' #')), undefined);
|
|
14
14
|
assert.deepStrictEqual(inspect(parser(' #a')), undefined);
|
|
15
|
-
assert.deepStrictEqual(inspect(parser(' [#]')), undefined);
|
|
15
|
+
assert.deepStrictEqual(inspect(parser(' [# ]')), undefined);
|
|
16
16
|
assert.deepStrictEqual(inspect(parser(' [#]]')), undefined);
|
|
17
17
|
assert.deepStrictEqual(inspect(parser(' [#a]]')), undefined);
|
|
18
18
|
assert.deepStrictEqual(inspect(parser(' [#&a;]')), undefined);
|
|
19
19
|
});
|
|
20
20
|
|
|
21
21
|
it('valid', () => {
|
|
22
|
+
assert.deepStrictEqual(inspect(parser(' [#]')), [['<span class="indexer" data-index=""></span>'], '']);
|
|
22
23
|
assert.deepStrictEqual(inspect(parser(' [#a]')), [['<span class="indexer" data-index="a"></span>'], '']);
|
|
23
24
|
assert.deepStrictEqual(inspect(parser(' [#a] ')), [['<span class="indexer" data-index="a"></span>'], '']);
|
|
24
25
|
assert.deepStrictEqual(inspect(parser(' [#a ]')), [['<span class="indexer" data-index="a"></span>'], '']);
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import { ExtensionParser } from '../../inline';
|
|
2
|
-
import { union, verify, creator, context, surround, fmap } from '../../../combinator';
|
|
2
|
+
import { union, verify, focus, creator, context, surround, fmap } from '../../../combinator';
|
|
3
3
|
import { index } from './index';
|
|
4
4
|
import { html } from 'typed-dom';
|
|
5
5
|
|
|
6
6
|
export const indexer: ExtensionParser.IndexerParser = creator(fmap(verify(surround(
|
|
7
|
-
/^\s+(?=\[
|
|
7
|
+
/^\s+(?=\[#\S)/,
|
|
8
8
|
context({ syntax: { inline: {
|
|
9
9
|
index: true,
|
|
10
10
|
}}},
|
|
11
|
-
union([
|
|
11
|
+
union([
|
|
12
|
+
focus('[#]', () => [[html('a', { href: '#' })], '']),
|
|
13
|
+
index,
|
|
14
|
+
])),
|
|
12
15
|
/^\s*$/),
|
|
13
16
|
// Indexer is invisible but invalids must be visible.
|
|
14
17
|
([el]) => el.getElementsByClassName('invalid').length === 0),
|
|
@@ -6,12 +6,12 @@ import { reduce } from 'spica/memoize';
|
|
|
6
6
|
|
|
7
7
|
export const unsafehtmlentity: UnsafeHTMLEntityParser = creator(validate('&', focus(
|
|
8
8
|
/^&[0-9A-Za-z]+;/,
|
|
9
|
-
entity => [[parse(entity) ?? `\
|
|
9
|
+
entity => [[parse(entity) ?? `\x1B${entity}`], ''])));
|
|
10
10
|
|
|
11
11
|
export const htmlentity: HTMLEntityParser = fmap(
|
|
12
12
|
union([unsafehtmlentity]),
|
|
13
13
|
([test]) => [
|
|
14
|
-
test[0] === '\
|
|
14
|
+
test[0] === '\x1B'
|
|
15
15
|
? html('span', {
|
|
16
16
|
class: 'invalid',
|
|
17
17
|
'data-invalid-syntax': 'htmlentity',
|
|
@@ -95,13 +95,13 @@ function sanitize(target: HTMLElement, uri: ReadonlyURL, alt: string): boolean {
|
|
|
95
95
|
});
|
|
96
96
|
return false;
|
|
97
97
|
}
|
|
98
|
-
if (alt.includes('\
|
|
98
|
+
if (alt.includes('\x1B')) {
|
|
99
99
|
define(target, {
|
|
100
100
|
class: void target.classList.add('invalid'),
|
|
101
101
|
'data-invalid-syntax': 'media',
|
|
102
102
|
'data-invalid-type': 'content',
|
|
103
103
|
'data-invalid-message': `Cannot use invalid HTML entitiy "${alt.match(/&[0-9A-Za-z]+;/)![0]}"`,
|
|
104
|
-
alt: target.getAttribute('alt')?.replace(/\
|
|
104
|
+
alt: target.getAttribute('alt')?.replace(/\x1B/g, ''),
|
|
105
105
|
});
|
|
106
106
|
return false;
|
|
107
107
|
}
|
|
@@ -83,8 +83,8 @@ function attributes(texts: string[], rubies: string[]): Record<string, string> {
|
|
|
83
83
|
let attrs: Record<string, string> | undefined;
|
|
84
84
|
for (const ss of [texts, rubies]) {
|
|
85
85
|
for (let i = 0; i < ss.length; ++i) {
|
|
86
|
-
if (ss[i].indexOf('\
|
|
87
|
-
ss[i] = ss[i].replace(/\
|
|
86
|
+
if (ss[i].indexOf('\x1B') === -1) continue;
|
|
87
|
+
ss[i] = ss[i].replace(/\x1B/g, '');
|
|
88
88
|
attrs ??= {
|
|
89
89
|
class: 'invalid',
|
|
90
90
|
'data-invalid-syntax': 'ruby',
|
|
@@ -21,6 +21,7 @@ describe('Unit: parser/processor/figure', () => {
|
|
|
21
21
|
const target = parse([
|
|
22
22
|
'$fig-a\n> ',
|
|
23
23
|
'$fig-a',
|
|
24
|
+
'$fig-b',
|
|
24
25
|
'$fig-a',
|
|
25
26
|
].join('\n\n'));
|
|
26
27
|
for (let i = 0; i < 3; ++i) {
|
|
@@ -30,6 +31,7 @@ describe('Unit: parser/processor/figure', () => {
|
|
|
30
31
|
[
|
|
31
32
|
'<figure data-type="quote" data-label="fig-a" data-group="fig" data-number="1" id="label:fig-a"><div><blockquote></blockquote></div><figcaption><span class="figindex">Fig. 1. </span></figcaption></figure>',
|
|
32
33
|
'<p><a class="label" data-label="fig-a" href="#label:fig-a">Fig. 1</a></p>',
|
|
34
|
+
'<p><a class="label invalid" data-label="fig-b" data-invalid-syntax="label" data-invalid-type="reference" data-invalid-message="Missing the target figure">$fig-b</a></p>',
|
|
33
35
|
'<p><a class="label" data-label="fig-a" href="#label:fig-a">Fig. 1</a></p>',
|
|
34
36
|
]);
|
|
35
37
|
}
|
|
@@ -98,7 +100,7 @@ describe('Unit: parser/processor/figure', () => {
|
|
|
98
100
|
'<figure data-type="quote" data-label="fig-a" data-group="fig" data-number="1" id="label:fig-a"><div><blockquote></blockquote></div><figcaption><span class="figindex">Fig. 1. </span></figcaption></figure>',
|
|
99
101
|
'<p><a class="label" data-label="fig-2" href="#label:fig-2">Fig. 2</a></p>',
|
|
100
102
|
'<p><a class="label" data-label="$-4.1.1" href="#label:$-4.1.1">(4.1.1)</a></p>',
|
|
101
|
-
'<p><a class="label
|
|
103
|
+
'<p><a class="label invalid" data-label="fig-1" data-invalid-syntax="label" data-invalid-type="reference" data-invalid-message="Missing the target figure">$fig-1</a></p>',
|
|
102
104
|
]);
|
|
103
105
|
}
|
|
104
106
|
});
|
|
@@ -181,7 +183,7 @@ describe('Unit: parser/processor/figure', () => {
|
|
|
181
183
|
'<h2 id="index:0">0</h2>',
|
|
182
184
|
'<figure data-label="$-0.1.0" data-group="$" class="invalid" data-invalid-syntax="figure" data-invalid-type="argument" data-invalid-message="Base index must be $-x.0 format"></figure>',
|
|
183
185
|
'<figure data-type="quote" data-label="fig-d" data-group="fig" data-number="4.1" id="label:fig-d"><div><blockquote></blockquote></div><figcaption><span class="figindex">Fig. 4.1. </span></figcaption></figure>',
|
|
184
|
-
'<figure data-label="$-0.0" data-group="$" class="invalid" data-invalid-syntax="figure" data-invalid-type="position" data-invalid-message="Base index declarations must be after level 1
|
|
186
|
+
'<figure data-label="$-0.0" data-group="$" class="invalid" data-invalid-syntax="figure" data-invalid-type="position" data-invalid-message="Base index declarations must be after level 1 to 6 headings"></figure>',
|
|
185
187
|
'<figure data-label="$-0.1.0" data-group="$" class="invalid" data-invalid-syntax="figure" data-invalid-type="argument" data-invalid-message="Base index must be $-x.0 format"></figure>',
|
|
186
188
|
'<figure data-label="$-0.4.0" data-group="$" class="invalid" data-invalid-syntax="figure" data-invalid-type="argument" data-invalid-message="Base index must be $-x.0 format"></figure>',
|
|
187
189
|
'<figure data-label="$-0.1.0" data-group="$" class="invalid" data-invalid-syntax="figure" data-invalid-type="argument" data-invalid-message="Base index must be $-x.0 format"></figure>',
|
|
@@ -232,17 +234,17 @@ describe('Unit: parser/processor/figure', () => {
|
|
|
232
234
|
assert.deepStrictEqual(
|
|
233
235
|
[...target.children].map(el => normalize(el.outerHTML)),
|
|
234
236
|
[
|
|
235
|
-
'<figure data-label="$-0.0" data-group="$" class="invalid" data-invalid-syntax="figure" data-invalid-type="position" data-invalid-message="Base index declarations must be after level 1
|
|
237
|
+
'<figure data-label="$-0.0" data-group="$" class="invalid" data-invalid-syntax="figure" data-invalid-type="position" data-invalid-message="Base index declarations must be after level 1 to 6 headings"></figure>',
|
|
236
238
|
'<h2 id="index:0">0</h2>',
|
|
237
239
|
'<figure data-label="$-0.0" data-group="$" hidden="" data-number="0.0"></figure>',
|
|
238
240
|
'<figure data-type="quote" data-label="fig-a" data-group="fig" data-number="0.1" id="label:fig-a"><div><blockquote></blockquote></div><figcaption><span class="figindex">Fig. 0.1. </span></figcaption></figure>',
|
|
239
|
-
'<figure data-label="$-0.0" data-group="$" class="invalid" data-invalid-syntax="figure" data-invalid-type="position" data-invalid-message="Base index declarations must be after level 1
|
|
241
|
+
'<figure data-label="$-0.0" data-group="$" class="invalid" data-invalid-syntax="figure" data-invalid-type="position" data-invalid-message="Base index declarations must be after level 1 to 6 headings"></figure>',
|
|
240
242
|
'<h2 id="index:0">0</h2>',
|
|
241
243
|
'<figure data-type="quote" data-label="fig-b" data-group="fig" data-number="1.1" id="label:fig-b"><div><blockquote></blockquote></div><figcaption><span class="figindex">Fig. 1.1. </span></figcaption></figure>',
|
|
242
|
-
'<figure data-label="$-0.0" data-group="$" class="invalid" data-invalid-syntax="figure" data-invalid-type="position" data-invalid-message="Base index declarations must be after level 1
|
|
244
|
+
'<figure data-label="$-0.0" data-group="$" class="invalid" data-invalid-syntax="figure" data-invalid-type="position" data-invalid-message="Base index declarations must be after level 1 to 6 headings"></figure>',
|
|
243
245
|
'<h3 id="index:0">0</h3>',
|
|
244
246
|
'<figure data-type="quote" data-label="fig-c" data-group="fig" data-number="1.2" id="label:fig-c"><div><blockquote></blockquote></div><figcaption><span class="figindex">Fig. 1.2. </span></figcaption></figure>',
|
|
245
|
-
'<figure data-label="$-1.0" data-group="$" class="invalid" data-invalid-syntax="figure" data-invalid-type="position" data-invalid-message="Base index declarations must be after level 1
|
|
247
|
+
'<figure data-label="$-1.0" data-group="$" class="invalid" data-invalid-syntax="figure" data-invalid-type="position" data-invalid-message="Base index declarations must be after level 1 to 6 headings"></figure>',
|
|
246
248
|
'<h2 id="index:0">0</h2>',
|
|
247
249
|
'<figure data-label="$-0.0" data-group="$" hidden="" data-number="2.0"></figure>',
|
|
248
250
|
'<figure data-type="quote" data-label="fig-d" data-group="fig" data-number="2.1" id="label:fig-d"><div><blockquote></blockquote></div><figcaption><span class="figindex">Fig. 2.1. </span></figcaption></figure>',
|
|
@@ -29,7 +29,7 @@ export function* figure(
|
|
|
29
29
|
yield;
|
|
30
30
|
const def = defs[i];
|
|
31
31
|
if (def.parentNode !== target) continue;
|
|
32
|
-
const { tagName
|
|
32
|
+
const { tagName } = def;
|
|
33
33
|
if (bases.length === 1 && tagName[0] === 'H') continue;
|
|
34
34
|
assert(base === '0' || bases.length > 1);
|
|
35
35
|
const label = tagName === 'FIGURE'
|
|
@@ -38,7 +38,7 @@ export function* figure(
|
|
|
38
38
|
if (label.endsWith('-')) continue;
|
|
39
39
|
if (label.endsWith('-0')) {
|
|
40
40
|
define(def, {
|
|
41
|
-
class: void classList.add('invalid'),
|
|
41
|
+
class: void def.classList.add('invalid'),
|
|
42
42
|
'data-invalid-syntax': 'figure',
|
|
43
43
|
'data-invalid-type': 'argument',
|
|
44
44
|
'data-invalid-message': 'Invalid base index',
|
|
@@ -50,7 +50,7 @@ export function* figure(
|
|
|
50
50
|
// $-x.x.0 is disabled.
|
|
51
51
|
if (label.lastIndexOf('.', label.length - 3) !== -1) {
|
|
52
52
|
define(def, {
|
|
53
|
-
class: void classList.add('invalid'),
|
|
53
|
+
class: void def.classList.add('invalid'),
|
|
54
54
|
'data-invalid-syntax': 'figure',
|
|
55
55
|
'data-invalid-type': 'argument',
|
|
56
56
|
'data-invalid-message': 'Base index must be $-x.0 format',
|
|
@@ -61,17 +61,17 @@ export function* figure(
|
|
|
61
61
|
// $-x.0 after h1-h6.
|
|
62
62
|
if (!/^H[1-6]$/.test(def.previousElementSibling?.tagName ?? '')) {
|
|
63
63
|
define(def, {
|
|
64
|
-
class: void classList.add('invalid'),
|
|
64
|
+
class: void def.classList.add('invalid'),
|
|
65
65
|
'data-invalid-syntax': 'figure',
|
|
66
66
|
'data-invalid-type': 'position',
|
|
67
|
-
'data-invalid-message':
|
|
67
|
+
'data-invalid-message': messages.declaration,
|
|
68
68
|
hidden: null,
|
|
69
69
|
});
|
|
70
70
|
continue;
|
|
71
71
|
}
|
|
72
|
-
else {
|
|
73
|
-
|
|
74
|
-
class: void classList.remove('invalid'),
|
|
72
|
+
else if (def.getAttribute('data-invalid-message') === messages.declaration) {
|
|
73
|
+
define(def, {
|
|
74
|
+
class: void def.classList.remove('invalid'),
|
|
75
75
|
'data-invalid-syntax': null,
|
|
76
76
|
'data-invalid-type': null,
|
|
77
77
|
'data-invalid-message': null,
|
|
@@ -118,7 +118,6 @@ export function* figure(
|
|
|
118
118
|
assert(number.split('.').pop() !== '0');
|
|
119
119
|
!isFixed(label) && numbers.set(group, number);
|
|
120
120
|
assert(!+def.setAttribute('data-number', number));
|
|
121
|
-
opts.id !== '' && def.setAttribute('id', `label:${opts.id ? `${opts.id}:` : ''}${label}`);
|
|
122
121
|
const figindex = group === '$'
|
|
123
122
|
? `(${number})`
|
|
124
123
|
: `${capitalize(group)}${group === 'fig' ? '.' : ''} ${number}`;
|
|
@@ -126,27 +125,35 @@ export function* figure(
|
|
|
126
125
|
def.querySelector(':scope > figcaption > .figindex')!,
|
|
127
126
|
group === '$' ? figindex : `${figindex}. `);
|
|
128
127
|
if (labels.has(label)) {
|
|
129
|
-
if (classList.contains('invalid')
|
|
130
|
-
def.getAttribute('data-invalid-message') !== 'Duplicate label') continue;
|
|
128
|
+
if (def.classList.contains('invalid')) continue;
|
|
131
129
|
define(def, {
|
|
132
130
|
id: null,
|
|
133
|
-
class: void classList.add('invalid'),
|
|
131
|
+
class: void def.classList.add('invalid'),
|
|
134
132
|
'data-invalid-syntax': 'figure',
|
|
135
133
|
'data-invalid-type': 'argument',
|
|
136
|
-
'data-invalid-message':
|
|
134
|
+
'data-invalid-message': messages.duplicate,
|
|
137
135
|
});
|
|
138
136
|
continue;
|
|
139
137
|
}
|
|
140
|
-
else {
|
|
141
|
-
labels.add(label);
|
|
138
|
+
else if (def.getAttribute('data-invalid-message') === messages.duplicate) {
|
|
142
139
|
define(def, {
|
|
143
|
-
class: void classList.remove('invalid'),
|
|
140
|
+
class: void def.classList.remove('invalid'),
|
|
144
141
|
'data-invalid-syntax': null,
|
|
145
142
|
'data-invalid-type': null,
|
|
146
143
|
'data-invalid-message': null,
|
|
147
144
|
});
|
|
148
145
|
}
|
|
146
|
+
labels.add(label);
|
|
147
|
+
opts.id !== '' && def.setAttribute('id', `label:${opts.id ? `${opts.id}:` : ''}${label}`);
|
|
149
148
|
for (const ref of refs.take(label, Infinity)) {
|
|
149
|
+
if (ref.getAttribute('data-invalid-message') === messages.reference) {
|
|
150
|
+
define(ref, {
|
|
151
|
+
class: void ref.classList.remove('invalid'),
|
|
152
|
+
'data-invalid-syntax': null,
|
|
153
|
+
'data-invalid-type': null,
|
|
154
|
+
'data-invalid-message': null,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
150
157
|
if (ref.hash.slice(1) === def.id && ref.innerText === figindex) continue;
|
|
151
158
|
yield define(ref,
|
|
152
159
|
opts.id !== '' ? { href: `#${def.id}` } : { class: `${ref.className} disabled` },
|
|
@@ -154,12 +161,12 @@ export function* figure(
|
|
|
154
161
|
}
|
|
155
162
|
}
|
|
156
163
|
for (const [, ref] of refs) {
|
|
157
|
-
if (opts.id !== '') {
|
|
164
|
+
if (opts.id !== '' && !ref.classList.contains('invalid')) {
|
|
158
165
|
define(ref, {
|
|
159
|
-
class:
|
|
166
|
+
class: void ref.classList.add('invalid'),
|
|
160
167
|
'data-invalid-syntax': 'label',
|
|
161
168
|
'data-invalid-type': 'reference',
|
|
162
|
-
'data-invalid-message':
|
|
169
|
+
'data-invalid-message': messages.reference,
|
|
163
170
|
});
|
|
164
171
|
}
|
|
165
172
|
yield ref;
|
|
@@ -167,6 +174,12 @@ export function* figure(
|
|
|
167
174
|
return;
|
|
168
175
|
}
|
|
169
176
|
|
|
177
|
+
const messages = {
|
|
178
|
+
declaration: 'Base index declarations must be after level 1 to 6 headings',
|
|
179
|
+
duplicate: 'Duplicate label',
|
|
180
|
+
reference: 'Missing the target figure',
|
|
181
|
+
} as const;
|
|
182
|
+
|
|
170
183
|
function increment(bases: readonly string[], el: HTMLHeadingElement): string {
|
|
171
184
|
const index = (+el.tagName[1] - 1 || 1) - 1;
|
|
172
185
|
assert(index >= 0);
|
|
@@ -4,12 +4,12 @@ describe('Unit: parser/segment', () => {
|
|
|
4
4
|
describe('segment', () => {
|
|
5
5
|
it('huge input', () => {
|
|
6
6
|
const result = segment(`${'\n'.repeat(10 * 1000 ** 2)}`).next().value?.split('\n', 1)[0];
|
|
7
|
-
assert(result?.startsWith('\
|
|
7
|
+
assert(result?.startsWith('\x07Too large input'));
|
|
8
8
|
});
|
|
9
9
|
|
|
10
10
|
it('huge segment', () => {
|
|
11
11
|
const result = segment(`${'\n'.repeat(1000 ** 2 - 1)}`).next().value?.split('\n', 1)[0];
|
|
12
|
-
assert(result?.startsWith('\
|
|
12
|
+
assert(result?.startsWith('\x07Too large segment'));
|
|
13
13
|
});
|
|
14
14
|
|
|
15
15
|
it('basic', () => {
|
package/src/parser/segment.ts
CHANGED
|
@@ -23,7 +23,7 @@ const parser: SegmentParser = union([
|
|
|
23
23
|
]);
|
|
24
24
|
|
|
25
25
|
export function* segment(source: string): Generator<string, undefined, undefined> {
|
|
26
|
-
if (!validate(source, MAX_INPUT_SIZE)) return yield `\
|
|
26
|
+
if (!validate(source, MAX_INPUT_SIZE)) return yield `\x07Too large input over ${MAX_INPUT_SIZE.toLocaleString('en')} bytes.\n${source.slice(0, 1001)}`;
|
|
27
27
|
assert(source.length < Number.MAX_SAFE_INTEGER);
|
|
28
28
|
while (source !== '') {
|
|
29
29
|
const result = parser(source, {})!;
|
|
@@ -36,7 +36,7 @@ export function* segment(source: string): Generator<string, undefined, undefined
|
|
|
36
36
|
const seg = segs[i];
|
|
37
37
|
validate(seg, MAX_SEGMENT_SIZE)
|
|
38
38
|
? yield seg
|
|
39
|
-
: yield `\
|
|
39
|
+
: yield `\x07Too large segment over ${MAX_SEGMENT_SIZE.toLocaleString('en')} bytes.\n${seg}`
|
|
40
40
|
}
|
|
41
41
|
source = rest;
|
|
42
42
|
}
|
package/src/parser/source/str.ts
CHANGED
|
@@ -3,8 +3,8 @@ import { StrParser } from '../source';
|
|
|
3
3
|
import { Parser, Context } from '../../combinator/data/parser';
|
|
4
4
|
import { creator } from '../../combinator';
|
|
5
5
|
|
|
6
|
-
export function str(pattern: string | RegExp
|
|
7
|
-
export function str(pattern: string | RegExp
|
|
6
|
+
export function str(pattern: string | RegExp): StrParser;
|
|
7
|
+
export function str(pattern: string | RegExp): Parser<string, Context<StrParser>, []> {
|
|
8
8
|
assert(pattern);
|
|
9
9
|
return typeof pattern === 'string'
|
|
10
10
|
? creator(source => {
|
|
@@ -16,8 +16,27 @@ export function str(pattern: string | RegExp, mustConsume = true): Parser<string
|
|
|
16
16
|
: creator(source => {
|
|
17
17
|
if (source === '') return;
|
|
18
18
|
const m = source.match(pattern);
|
|
19
|
-
return m &&
|
|
19
|
+
return m && m[0].length > 0
|
|
20
20
|
? [[m[0]], source.slice(m[0].length)]
|
|
21
21
|
: undefined;
|
|
22
22
|
});
|
|
23
|
-
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function stropt(pattern: string | RegExp): StrParser;
|
|
26
|
+
export function stropt(pattern: string | RegExp): Parser<string, Context<StrParser>, []> {
|
|
27
|
+
assert(pattern);
|
|
28
|
+
return typeof pattern === 'string'
|
|
29
|
+
? creator(source => {
|
|
30
|
+
if (source === '') return;
|
|
31
|
+
return source.slice(0, pattern.length) === pattern
|
|
32
|
+
? [[pattern], source.slice(pattern.length)]
|
|
33
|
+
: [[], source];
|
|
34
|
+
})
|
|
35
|
+
: creator(source => {
|
|
36
|
+
if (source === '') return;
|
|
37
|
+
const m = source.match(pattern);
|
|
38
|
+
return m
|
|
39
|
+
? [[m[0]], source.slice(m[0].length)]
|
|
40
|
+
: [[], source];
|
|
41
|
+
});
|
|
42
|
+
}
|
|
@@ -24,6 +24,7 @@ export const text: TextParser = creator((source, context) => {
|
|
|
24
24
|
case '。':
|
|
25
25
|
case '!':
|
|
26
26
|
case '?':
|
|
27
|
+
assert(source[0] !== '\x1B');
|
|
27
28
|
return text(source.slice(1), context);
|
|
28
29
|
}
|
|
29
30
|
break;
|
|
@@ -39,8 +40,10 @@ export const text: TextParser = creator((source, context) => {
|
|
|
39
40
|
case '\\':
|
|
40
41
|
switch (source[1]) {
|
|
41
42
|
case undefined:
|
|
43
|
+
assert(source[0] !== '\x1B');
|
|
42
44
|
return [[], ''];
|
|
43
45
|
case '\n':
|
|
46
|
+
assert(source[0] !== '\x1B');
|
|
44
47
|
return [[html('span', { class: 'linebreak' }, ' ')], source.slice(2)];
|
|
45
48
|
default:
|
|
46
49
|
return [[source.slice(1, 2)], source.slice(2)];
|
package/src/parser/source.ts
CHANGED
|
@@ -14,5 +14,5 @@ export import AnyLineParser = SourceParser.AnyLineParser;
|
|
|
14
14
|
export { text, txt, linebreak } from './source/text';
|
|
15
15
|
export { escsource } from './source/escapable';
|
|
16
16
|
export { unescsource } from './source/unescapable';
|
|
17
|
-
export { str } from './source/str';
|
|
17
|
+
export { str, stropt } from './source/str';
|
|
18
18
|
export { contentline, emptyline, anyline } from './source/line';
|
package/src/parser/util.ts
CHANGED
|
@@ -55,7 +55,7 @@ export function startLoose<T extends HTMLElement | string>(parser: Parser<T>, ex
|
|
|
55
55
|
}
|
|
56
56
|
export const isStartLoose = reduce((source: string, context: MarkdownParser.Context, except?: string): boolean => {
|
|
57
57
|
return isStartTight(source.replace(/^[^\S\n]+/, ''), context, except);
|
|
58
|
-
}, (source, _, except = '') => `${source}\
|
|
58
|
+
}, (source, _, except = '') => `${source}\x1E${except}`);
|
|
59
59
|
export function startTight<P extends Parser<unknown>>(parser: P, except?: string): P;
|
|
60
60
|
export function startTight<T>(parser: Parser<T>, except?: string): Parser<T> {
|
|
61
61
|
return (source, context) =>
|
|
@@ -93,7 +93,7 @@ const isStartTight = reduce((source: string, context: MarkdownParser.Context, ex
|
|
|
93
93
|
default:
|
|
94
94
|
return source[0].trimStart() !== '';
|
|
95
95
|
}
|
|
96
|
-
}, (source, _, except = '') => `${source}\
|
|
96
|
+
}, (source, _, except = '') => `${source}\x1E${except}`);
|
|
97
97
|
export function isStartTightNodes(nodes: readonly (HTMLElement | string)[]): boolean {
|
|
98
98
|
if (nodes.length === 0) return true;
|
|
99
99
|
return isVisible(nodes[0], 0);
|