securemark 0.271.0 → 0.273.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securemark",
3
- "version": "0.271.0",
3
+ "version": "0.273.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",
@@ -28,35 +28,35 @@
28
28
  "LICENSE"
29
29
  ],
30
30
  "devDependencies": {
31
- "@types/dompurify": "3.0.0",
31
+ "@types/dompurify": "3.0.2",
32
32
  "@types/jquery": "3.5.16",
33
33
  "@types/mathjax": "0.0.37",
34
34
  "@types/mocha": "10.0.1",
35
35
  "@types/power-assert": "1.5.8",
36
36
  "@types/prismjs": "1.26.0",
37
- "@typescript-eslint/parser": "^5.55.0",
37
+ "@typescript-eslint/parser": "^5.59.0",
38
38
  "babel-loader": "^9.1.2",
39
39
  "babel-plugin-unassert": "^3.2.0",
40
- "concurrently": "^7.6.0",
41
- "eslint": "^8.36.0",
40
+ "concurrently": "^8.0.1",
41
+ "eslint": "^8.39.0",
42
42
  "eslint-plugin-redos": "^4.4.5",
43
- "eslint-webpack-plugin": "^4.0.0",
44
- "glob": "^8.1.0",
45
- "karma": "^6.4.1",
46
- "karma-chrome-launcher": "^3.1.1",
43
+ "eslint-webpack-plugin": "^4.0.1",
44
+ "glob": "^10.2.1",
45
+ "karma": "^6.4.2",
46
+ "karma-chrome-launcher": "^3.2.0",
47
47
  "karma-coverage": "^2.2.0",
48
48
  "karma-firefox-launcher": "^2.1.2",
49
49
  "karma-mocha": "^2.0.1",
50
50
  "karma-power-assert": "^1.0.0",
51
51
  "mocha": "^10.2.0",
52
- "npm-check-updates": "^16.7.12",
53
- "semver": "^7.3.8",
54
- "spica": "0.0.719",
52
+ "npm-check-updates": "^16.10.9",
53
+ "semver": "^7.5.0",
54
+ "spica": "0.0.721",
55
55
  "ts-loader": "^9.4.2",
56
- "typed-dom": "^0.0.317",
57
- "typescript": "4.9.5",
58
- "webpack": "^5.76.1",
59
- "webpack-cli": "^5.0.1",
56
+ "typed-dom": "^0.0.330",
57
+ "typescript": "5.0.4",
58
+ "webpack": "^5.80.0",
59
+ "webpack-cli": "^5.0.2",
60
60
  "webpack-merge": "^5.8.0"
61
61
  },
62
62
  "scripts": {
@@ -27,7 +27,7 @@ describe('Unit: parser/api/bind', () => {
27
27
  return acc;
28
28
  }
29
29
 
30
- const cfgs = { footnotes: { references: html('ol') } };
30
+ const cfgs = { notes: { references: html('ol') } };
31
31
 
32
32
  it('huge input', () => {
33
33
  const iter = bind(html('div'), { ...cfgs, id: '' }).parse(`${'\n'.repeat(10 * 1000 ** 2)}`);
@@ -211,18 +211,18 @@ describe('Unit: parser/api/bind', () => {
211
211
  ]).outerHTML,
212
212
  ]);
213
213
  assert.deepStrictEqual(
214
- cfgs.footnotes.references?.outerHTML,
214
+ cfgs.notes.references?.outerHTML,
215
215
  html('ol', [
216
216
  html('li', { id: 'reference::def:1' }, [
217
- html('span', { id: 'note::1' }, '1'),
217
+ html('span', '1'),
218
218
  html('sup', [html('a', { href: '#reference::ref:1' }, '^1')]),
219
219
  ]),
220
220
  html('li', { id: 'reference::def:2' }, [
221
- html('span', { id: 'note::2' }, '2'),
221
+ html('span', '2'),
222
222
  html('sup', [html('a', { href: '#reference::ref:2' }, '^2')]),
223
223
  ]),
224
224
  html('li', { id: 'reference::def:3' }, [
225
- html('span', { id: 'note::3' }, '3'),
225
+ html('span', '3'),
226
226
  html('sup', [html('a', { href: '#reference::ref:3' }, '^3')]),
227
227
  ]),
228
228
  ]).outerHTML);
@@ -9,7 +9,7 @@ import { State } from '../context';
9
9
  import { normalize } from './normalize';
10
10
  import { headers } from './header';
11
11
  import { figure } from '../processor/figure';
12
- import { footnote } from '../processor/footnote';
12
+ import { note } from '../processor/note';
13
13
  import { ReadonlyURL } from 'spica/url';
14
14
  import { push, splice } from 'spica/array';
15
15
 
@@ -116,17 +116,17 @@ export function bind(target: DocumentFragment | HTMLElement | ShadowRoot, settin
116
116
  yield { type: 'block', value: el };
117
117
  if (rev !== revision) return yield { type: 'cancel' };
118
118
  }
119
- for (const el of figure(next(0)?.parentNode ?? target, settings.footnotes, context)) {
119
+ for (const el of figure(next(0)?.parentNode ?? target, settings.notes, context)) {
120
120
  assert(rev === revision);
121
121
  el
122
122
  ? yield { type: 'figure', value: el }
123
123
  : yield { type: 'break' };
124
124
  if (rev !== revision) return yield { type: 'cancel' };
125
125
  }
126
- for (const el of footnote(next(0)?.parentNode ?? target, settings.footnotes, context, bottom)) {
126
+ for (const el of note(next(0)?.parentNode ?? target, settings.notes, context, bottom)) {
127
127
  assert(rev === revision);
128
128
  el
129
- ? yield { type: 'footnote', value: el }
129
+ ? yield { type: 'note', value: el }
130
130
  : yield { type: 'break' };
131
131
  if (rev !== revision) return yield { type: 'cancel' };
132
132
  }
@@ -124,7 +124,7 @@ describe('Unit: parser/api/parse', () => {
124
124
  '<p><a class="index" href="#index::a">a</a></p>',
125
125
  '<figure data-type="math" data-label="$-a" data-group="$" data-number="1" id="label:$-a"><figcaption><span class="figindex">(1)</span><span class="figtext"></span></figcaption><div><div class="math" translate="no">$$\n$$</div></div></figure>',
126
126
  '<p><a class="label" data-label="$-a" href="#label:$-a">(1)</a></p>',
127
- '<p><sup class="annotation" id="annotation::ref:1" title="a"><span hidden="">a</span><a href="#annotation::def:1">*1</a></sup></p>',
127
+ '<p><sup class="annotation" id="annotation::ref:1" title="a"><span hidden="">a</span><a href="#annotation::def:a">*1</a></sup></p>',
128
128
  '<p><a class="url" href="https://source/x/a" target="_blank">a</a></p>',
129
129
  '<p><a class="url" href="https://source/a" target="_blank">/a</a></p>',
130
130
  '<p><a class="url" href="/z/a">^/a</a></p>',
@@ -135,7 +135,7 @@ describe('Unit: parser/api/parse', () => {
135
135
  '<p><a href="https://source/x/a" target="_blank"><img class="media" data-src="https://source/x/a" alt=""></a></p>',
136
136
  '<p><a href="/z/a" target="_blank"><img class="media" data-src="/z/a" alt=""></a></p>',
137
137
  '<p><a href="https://source/a" target="_blank"><img class="media" data-src="https://source/a" alt=""></a></p>',
138
- '<ol class="annotations"><li id="annotation::def:1" data-marker="*1"><span id="note::a">a</span><sup><a href="#annotation::ref:1">^1</a></sup></li></ol>',
138
+ '<ol class="annotations"><li id="annotation::def:a" data-marker="*1"><span>a</span><sup><a href="#annotation::ref:1">^1</a></sup></li></ol>',
139
139
  ]);
140
140
  assert.deepStrictEqual(
141
141
  [...parse([
@@ -201,18 +201,18 @@ describe('Unit: parser/api/parse', () => {
201
201
  ]);
202
202
  });
203
203
 
204
- it('footnote', () => {
205
- const footnotes = { references: html('ol') };
204
+ it('note', () => {
205
+ const notes = { references: html('ol') };
206
206
  assert.deepStrictEqual(
207
- [...parse('$-a\n$$\n$$\n\n(($-a[[b]][[c_d_]]))', { footnotes }).children].map(el => el.outerHTML),
207
+ [...parse('$-a\n$$\n$$\n\n(($-a[[^b]]))[[^b|$-a]]', { notes }).children].map(el => el.outerHTML),
208
208
  [
209
209
  '<figure data-type="math" data-label="$-a" data-group="$" data-number="1" id="label:$-a"><figcaption><span class="figindex">(1)</span><span class="figtext"></span></figcaption><div><div class="math" translate="no">$$\n$$</div></div></figure>',
210
- '<p><sup class="annotation" id="annotation::ref:1" title="(1)"><span hidden=""><a class="label" data-label="$-a" href="#label:$-a">(1)</a><sup class="reference" id="reference::ref:1" title="b"><span hidden="">b</span><a href="#reference::def:1">[1]</a></sup><sup class="reference" id="reference::ref:2" title="cd"><span hidden="">c<em>d</em></span><a href="#reference::def:2">[2]</a></sup></span><a href="#annotation::def:1">*1</a></sup></p>',
211
- '<ol class="annotations"><li id="annotation::def:1" data-marker="*1"><span id="note::(1)"><a class="label" data-label="$-a" href="#label:$-a">(1)</a><sup class="reference" id="reference::ref:1" title="b"><span hidden="">b</span><a href="#reference::def:1">[1]</a></sup><sup class="reference" id="reference::ref:2" title="cd"><span hidden="">c<em>d</em></span><a href="#reference::def:2">[2]</a></sup></span><sup><a href="#annotation::ref:1">^1</a></sup></li></ol>',
210
+ '<p><sup class="annotation" id="annotation::ref:1" title="(1)"><span hidden=""><a class="label" data-label="$-a" href="#label:$-a">(1)</a><sup class="reference" data-abbr="b"><span></span></sup></span><a href="#annotation::def:(1)">*1</a></sup><sup class="reference" data-abbr="b" id="reference::ref:1" title="(1)"><span hidden=""><a class="label" data-label="$-a" href="#label:$-a">(1)</a></span><a href="#reference::def:b">[b]</a></sup></p>',
211
+ '<ol class="annotations"><li id="annotation::def:(1)" data-marker="*1"><span><a class="label" data-label="$-a" href="#label:$-a">(1)</a><sup class="reference" data-abbr="b" id="reference::ref:2" title="(1)"><span hidden=""></span><a href="#reference::def:b">[b]</a></sup></span><sup><a href="#annotation::ref:1">^1</a></sup></li></ol>',
212
212
  ]);
213
213
  assert.deepStrictEqual(
214
- footnotes.references.outerHTML,
215
- '<ol><li id="reference::def:1"><span id="note::b">b</span><sup><a href="#reference::ref:1">^1</a></sup></li><li id="reference::def:2"><span id="note::cd">c<em>d</em></span><sup><a href="#reference::ref:2">^2</a></sup></li></ol>');
214
+ notes.references.outerHTML,
215
+ '<ol><li id="reference::def:b"><span><a class="label" data-label="$-a" href="#label:$-a">(1)</a></span><sup><a href="#reference::ref:1" title="(1)">^1</a><a href="#reference::ref:2">^2</a></sup></li></ol>');
216
216
  });
217
217
 
218
218
  it('normalize', () => {
@@ -9,7 +9,7 @@ import { State } from '../context';
9
9
  import { normalize } from './normalize';
10
10
  import { headers } from './header';
11
11
  import { figure } from '../processor/figure';
12
- import { footnote } from '../processor/footnote';
12
+ import { note } from '../processor/note';
13
13
  import { ReadonlyURL } from 'spica/url';
14
14
  import { frag } from 'typed-dom/dom';
15
15
 
@@ -41,9 +41,9 @@ export function parse(source: string, opts: Options = {}, context?: MarkdownPars
41
41
  }
42
42
  assert(opts.id !== '' || !node.querySelector('[id], .index[href], .label[href], .annotation > a[href], .reference > a[href]'));
43
43
  if (opts.test) return node;
44
- for (const _ of figure(node, opts.footnotes, context));
45
- for (const _ of footnote(node, opts.footnotes, context));
44
+ for (const _ of figure(node, opts.notes, context));
45
+ for (const _ of note(node, opts.notes, context));
46
46
  assert(opts.id !== '' || !node.querySelector('[id], .index[href], .label[href], .annotation > a[href], .reference > a[href]'));
47
- assert(opts.id !== '' || !opts.footnotes?.references.querySelector('[id], .index[href], .label[href]'));
47
+ assert(opts.id !== '' || !opts.notes?.references.querySelector('[id], .index[href], .label[href]'));
48
48
  return node;
49
49
  }
@@ -41,7 +41,7 @@ const markdown: BlockquoteParser.MarkdownParser = lazy(() => fmap(
41
41
  const references = html('ol', { class: 'references' });
42
42
  const document = parse(source, {
43
43
  id: '',
44
- footnotes: {
44
+ notes: {
45
45
  references,
46
46
  },
47
47
  }, context);
@@ -1,6 +1,6 @@
1
1
  import { ExtensionParser } from '../../block';
2
2
  import { block, validate, fence, fmap } from '../../../combinator';
3
- import { identity, text } from '../../inline/extension/indexee';
3
+ import { identity, index } from '../../inline/extension/indexee';
4
4
  import { parse } from '../../api/parse';
5
5
  import { html } from 'typed-dom/dom';
6
6
 
@@ -21,7 +21,7 @@ export const aside: ExtensionParser.AsideParser = block(validate('~~~', fmap(
21
21
  const references = html('ol', { class: 'references' });
22
22
  const document = parse(body.slice(0, -1), {
23
23
  id: '',
24
- footnotes: {
24
+ notes: {
25
25
  references,
26
26
  },
27
27
  }, context);
@@ -34,9 +34,9 @@ export const aside: ExtensionParser.AsideParser = block(validate('~~~', fmap(
34
34
  'data-invalid-type': 'content',
35
35
  'data-invalid-message': 'Missing the title at the first line',
36
36
  }, `${opener}${body}${closer}`)];
37
- assert(identity(context.id, text(heading)));
37
+ assert(identity(context.id, index(heading)));
38
38
  return [
39
- html('aside', { id: identity(context.id, text(heading)), class: 'aside' }, [
39
+ html('aside', { id: identity(context.id, index(heading)), class: 'aside' }, [
40
40
  document,
41
41
  html('h2', 'References'),
42
42
  references,
@@ -26,7 +26,7 @@ export const example: ExtensionParser.ExampleParser = block(validate('~~~', fmap
26
26
  const references = html('ol', { class: 'references' });
27
27
  const document = parse(body.slice(0, -1), {
28
28
  id: '',
29
- footnotes: {
29
+ notes: {
30
30
  references,
31
31
  },
32
32
  }, context);
@@ -13,7 +13,7 @@ export const url: AutolinkParser.UrlParser = lazy(() => validate(['http://', 'ht
13
13
  url => `{ ${url} }`,
14
14
  union([unsafelink])))));
15
15
 
16
- export const lineurl: AutolinkParser.UrlParser.LineUrlParser = open(
16
+ export const lineurl: AutolinkParser.UrlParser.LineUrlParser = lazy(() => open(
17
17
  linebreak,
18
18
  tails([
19
19
  str('!'),
@@ -22,7 +22,7 @@ export const lineurl: AutolinkParser.UrlParser.LineUrlParser = open(
22
22
  convert(
23
23
  url => `{ ${url} }`,
24
24
  unsafelink)),
25
- ]));
25
+ ])));
26
26
 
27
27
  const bracket: AutolinkParser.UrlParser.BracketParser = lazy(() => creation(precedence(2, union([
28
28
  surround('(', some(union([bracket, unescsource]), ')'), ')', true),
@@ -1,5 +1,5 @@
1
1
  import { AutolinkParser } from '../inline';
2
- import { union, some, syntax, constraint, validate, focus, fmap } from '../../combinator';
2
+ import { union, some, syntax, constraint, validate, focus, lazy, fmap } from '../../combinator';
3
3
  import { url, lineurl } from './autolink/url';
4
4
  import { email } from './autolink/email';
5
5
  import { channel } from './autolink/channel';
@@ -11,7 +11,7 @@ import { str } from '../source';
11
11
  import { Syntax, State } from '../context';
12
12
  import { stringify } from '../util';
13
13
 
14
- export const autolink: AutolinkParser =
14
+ export const autolink: AutolinkParser = lazy(() =>
15
15
  validate(/^(?:[@#>0-9a-z\r\n]|\S[#>])/i,
16
16
  constraint(State.autolink, false,
17
17
  syntax(Syntax.autolink, 1, 1, ~State.shortcut,
@@ -44,4 +44,4 @@ export const autolink: AutolinkParser =
44
44
  anchor,
45
45
  ])),
46
46
  ns => ns.length === 1 ? ns : [stringify(ns)]),
47
- ]))));
47
+ ])))));
@@ -1,14 +1,15 @@
1
1
  import { MarkdownParser } from '../../../../markdown';
2
2
  import { Parser } from '../../../combinator/data/parser';
3
3
  import { fmap } from '../../../combinator';
4
+ import { reduce } from 'spica/memoize';
4
5
  import { define } from 'typed-dom/dom';
5
6
 
6
7
  export function indexee<P extends Parser<unknown, MarkdownParser.Context>>(parser: P, optional?: boolean): P;
7
8
  export function indexee(parser: Parser<HTMLElement, MarkdownParser.Context>, optional?: boolean): Parser<HTMLElement> {
8
- return fmap(parser, ([el], _, { id }) => [define(el, { id: identity(id, text(el, optional)) })]);
9
+ return fmap(parser, ([el], _, { id }) => [define(el, { id: identity(id, index(el, optional)) })]);
9
10
  }
10
11
 
11
- export function identity(id: string | undefined, text: string, name: 'index' | 'mark' | 'note' = 'index'): string | undefined {
12
+ export function identity(id: string | undefined, text: string, name: 'index' | 'mark' = 'index'): string | undefined {
12
13
  assert(!id?.match(/[^0-9a-z/-]/i));
13
14
  assert(!text.includes('\n'));
14
15
  if (id === '') return undefined;
@@ -20,7 +21,6 @@ export function identity(id: string | undefined, text: string, name: 'index' | '
20
21
  case 'index':
21
22
  return `${name}:${id ?? ''}:${cs.slice(0, 97).join('')}...`;
22
23
  case 'mark':
23
- case 'note':
24
24
  return `${name}:${id ?? ''}:${cs.slice(0, 50).join('')}...${cs.slice(-47).join('')}`;
25
25
  }
26
26
  assert(false);
@@ -32,7 +32,7 @@ assert(identity(undefined, '0'.repeat(100 - 1) + 1, 'mark')!.slice(6) === '0'.re
32
32
  assert(identity(undefined, '0'.repeat(100) + 1, 'mark')!.slice(6) === '0'.repeat(50) + '...' + '0'.repeat(47 - 1) + 1);
33
33
  assert(identity(undefined, '0'.repeat(200) + 1, 'mark')!.slice(6) === '0'.repeat(50) + '...' + '0'.repeat(47 - 1) + 1);
34
34
 
35
- export function text(source: Element | DocumentFragment, optional = false): string {
35
+ export function index(source: Element | DocumentFragment, optional = false): string {
36
36
  assert(source instanceof DocumentFragment || !source.matches('.indexer'));
37
37
  assert(source.querySelectorAll(':scope > .indexer').length <= 1);
38
38
  if (!source.firstChild) return '';
@@ -40,6 +40,10 @@ export function text(source: Element | DocumentFragment, optional = false): stri
40
40
  const index = indexer?.getAttribute('data-index');
41
41
  if (index) return index;
42
42
  if (index === '' && optional) return '';
43
+ return text(source);
44
+ }
45
+
46
+ export const text = reduce((source: Element | DocumentFragment): string => {
43
47
  assert(!navigator.userAgent.includes('Chrome') || !source.querySelector('br:not(:has(+ :is(ul, ol)))'));
44
48
  const target = source.cloneNode(true) as typeof source;
45
49
  for (let es = target.querySelectorAll('code[data-src], .math[data-src], .comment, rt, rp, br, .annotation, .reference, .checkbox, ul, ol'),
@@ -72,4 +76,4 @@ export function text(source: Element | DocumentFragment, optional = false): stri
72
76
  // Better:
73
77
  //return target.innerText;
74
78
  return target.textContent!;
75
- }
79
+ });
@@ -73,11 +73,11 @@ const bracket: MediaParser.TextParser.BracketParser = lazy(() => creation(union(
73
73
  surround(str('"'), precedence(8, some(union([unsafehtmlentity, txt]), '"')), str('"'), true),
74
74
  ])));
75
75
 
76
- const option: MediaParser.ParameterParser.OptionParser = union([
76
+ const option: MediaParser.ParameterParser.OptionParser = lazy(() => union([
77
77
  fmap(str(/^[^\S\n]+[1-9][0-9]*x[1-9][0-9]*(?=[^\S\n]|})/), ([opt]) => [` width="${opt.slice(1).split('x')[0]}"`, ` height="${opt.slice(1).split('x')[1]}"`]),
78
78
  fmap(str(/^[^\S\n]+[1-9][0-9]*:[1-9][0-9]*(?=[^\S\n]|})/), ([opt]) => [` aspect-ratio="${opt.slice(1).split(':').join('/')}"`]),
79
79
  linkoption,
80
- ]);
80
+ ]));
81
81
 
82
82
  function sanitize(target: HTMLElement, uri: ReadonlyURL, alt: string): boolean {
83
83
  assert(target.tagName === 'IMG');
@@ -1,5 +1,5 @@
1
1
  import { MarkdownParser } from '../../markdown';
2
- import { union } from '../combinator';
2
+ import { union, lazy } from '../combinator';
3
3
  import { annotation } from './inline/annotation';
4
4
  import { reference } from './inline/reference';
5
5
  import { template } from './inline/template';
@@ -45,7 +45,7 @@ export import ShortMediaParser = InlineParser.ShortMediaParser;
45
45
  export import AutolinkParser = InlineParser.AutolinkParser;
46
46
  export import BracketParser = InlineParser.BracketParser;
47
47
 
48
- export const inline: InlineParser = union([
48
+ export const inline: InlineParser = lazy(() => union([
49
49
  annotation,
50
50
  reference,
51
51
  template,
@@ -68,7 +68,7 @@ export const inline: InlineParser = union([
68
68
  autolink,
69
69
  bracket,
70
70
  text
71
- ]);
71
+ ]));
72
72
 
73
73
  export { indexee } from './inline/extension/indexee';
74
74
  export { indexer } from './inline/extension/indexer';
@@ -6,12 +6,12 @@ import { querySelectorAll } from 'typed-dom/query';
6
6
 
7
7
  export function* figure(
8
8
  target: ParentNode & Node,
9
- footnotes?: { readonly references: HTMLOListElement; },
9
+ notes?: { readonly references: HTMLOListElement; },
10
10
  opts: { readonly id?: string; } = {},
11
11
  ): Generator<HTMLAnchorElement | undefined, undefined, undefined> {
12
12
  const refs = new MultiQueue<string, HTMLAnchorElement>(push(
13
13
  querySelectorAll(target, 'a.label:not(.disabled)[data-label]'),
14
- footnotes && querySelectorAll(footnotes.references, 'a.label:not(.disabled)') || [])
14
+ notes && querySelectorAll(notes.references, 'a.label:not(.disabled)') || [])
15
15
  .map(el => [el.getAttribute('data-label')!, el]));
16
16
  const labels = new Set<string>();
17
17
  const numbers = new Map<string, string>();