securemark 0.226.1 → 0.226.2

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 CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.226.2
4
+
5
+ - Refactoring.
6
+
3
7
  ## 0.226.1
4
8
 
5
9
  - Refactoring.
package/design.md CHANGED
@@ -148,6 +148,14 @@
148
148
  ゆえに参照箇所に実体を記述する注釈構文および識別に文字列を使用する図表構文を採用し、その表示方法は任意とする。
149
149
  脚注構文は附番が手作業となり参照と実体の対応の管理が困難であるため不採用とし注釈構文により生成可能とする。
150
150
 
151
+ ### 羅列的知識への非依存
152
+
153
+ 構文はその使用のために羅列的知識を求めてはならない。
154
+
155
+ ゆえにバックスラッシュエスケープの特殊処理の対象は改行文字のみに限られる。
156
+ またURL構文はHTMLエンティティの参照の有効性を検査しない。
157
+ HTMLEntity構文はエンティティ追加時の互換性確保のため不正なエンティティを検出する。
158
+
151
159
  ### テンプレート対応
152
160
 
153
161
  ソーステキストは自身をテンプレートとして別のソーステキストを生成するための構文を使用できなければならない。
@@ -1,4 +1,4 @@
1
- /*! securemark v0.226.1 https://github.com/falsandtru/securemark | (c) 2017, falsandtru | UNLICENSED */
1
+ /*! securemark v0.226.2 https://github.com/falsandtru/securemark | (c) 2017, falsandtru | UNLICENSED */
2
2
  require = function () {
3
3
  function r(e, n, t) {
4
4
  function o(i, f) {
@@ -4760,7 +4760,7 @@ require = function () {
4760
4760
  'InvisibleComma',
4761
4761
  'ic'
4762
4762
  ];
4763
- const unreadableEscapableCharacters = unreadableHTMLEntityNames.flatMap(name => (0, parser_1.eval)((0, htmlentity_1.htmlentity)(`&${ name };`, {}), []));
4763
+ const unreadableEscapableCharacters = unreadableHTMLEntityNames.flatMap(name => (0, parser_1.eval)((0, htmlentity_1.unsafehtmlentity)(`&${ name };`, {}), []));
4764
4764
  const unreadableEscapableCharacter = new RegExp(`[${ [...new Set(unreadableEscapableCharacters)].join('') }]`, 'g');
4765
4765
  const unreadableSpecialCharacters = [
4766
4766
  '\u2006',
@@ -5038,8 +5038,8 @@ require = function () {
5038
5038
  class: 'invalid',
5039
5039
  translate: 'no',
5040
5040
  'data-invalid-syntax': 'codeblock',
5041
- 'data-invalid-type': closer ? 'argument' : 'closer',
5042
- 'data-invalid-description': closer ? 'Invalid argument.' : `Missing the closing delimiter ${ delim }.`
5041
+ 'data-invalid-type': !closer ? 'closer' : 'argument',
5042
+ 'data-invalid-description': !closer ? `Missing the closing delimiter ${ delim }.` : 'Invalid argument.'
5043
5043
  }, `${ opener }${ body }${ closer }`)];
5044
5044
  const file = (_a = path.split('/').pop()) !== null && _a !== void 0 ? _a : '';
5045
5045
  const ext = file && file.includes('.', 1) ? file.split('.').pop() : '';
@@ -5176,8 +5176,8 @@ require = function () {
5176
5176
  class: 'invalid',
5177
5177
  translate: 'no',
5178
5178
  'data-invalid-syntax': 'aside',
5179
- 'data-invalid-type': closer ? 'argument' : 'closer',
5180
- 'data-invalid-description': closer ? 'Invalid argument.' : `Missing the closing delimiter ${ delim }.`
5179
+ 'data-invalid-type': !closer ? 'closer' : 'argument',
5180
+ 'data-invalid-description': !closer ? `Missing the closing delimiter ${ delim }.` : 'Invalid argument.'
5181
5181
  }, `${ opener }${ body }${ closer }`)];
5182
5182
  const annotations = (0, typed_dom_1.html)('ol', { class: 'annotations' });
5183
5183
  const references = (0, typed_dom_1.html)('ol', { class: 'references' });
@@ -5231,8 +5231,8 @@ require = function () {
5231
5231
  class: 'invalid',
5232
5232
  translate: 'no',
5233
5233
  'data-invalid-syntax': 'example',
5234
- 'data-invalid-type': closer ? 'argument' : 'closer',
5235
- 'data-invalid-description': closer ? 'Invalid argument.' : `Missing the closing delimiter ${ delim }.`
5234
+ 'data-invalid-type': !closer ? 'closer' : 'argument',
5235
+ 'data-invalid-description': !closer ? `Missing the closing delimiter ${ delim }.` : 'Invalid argument.'
5236
5236
  }, `${ opener }${ body }${ closer }`)];
5237
5237
  switch (type) {
5238
5238
  case 'markdown': {
@@ -5488,8 +5488,8 @@ require = function () {
5488
5488
  class: 'invalid',
5489
5489
  translate: 'no',
5490
5490
  'data-invalid-syntax': 'message',
5491
- 'data-invalid-type': closer ? 'argument' : 'closer',
5492
- 'data-invalid-description': closer ? 'Invalid argument.' : `Missing the closing delimiter ${ delim }.`
5491
+ 'data-invalid-type': !closer ? 'closer' : 'argument',
5492
+ 'data-invalid-description': !closer ? `Missing the closing delimiter ${ delim }.` : 'Invalid argument.'
5493
5493
  }, `${ opener }${ body }${ closer }`)];
5494
5494
  switch (type) {
5495
5495
  case 'note':
@@ -5559,8 +5559,8 @@ require = function () {
5559
5559
  class: 'invalid',
5560
5560
  translate: 'no',
5561
5561
  'data-invalid-syntax': 'extension',
5562
- 'data-invalid-type': closer ? 'syntax' : 'closer',
5563
- 'data-invalid-description': closer ? 'Invalid syntax.' : `Missing the closing delimiter ${ delim }.`
5562
+ 'data-invalid-type': !closer ? 'closer' : 'syntax',
5563
+ 'data-invalid-description': !closer ? `Missing the closing delimiter ${ delim }.` : 'Invalid syntax.'
5564
5564
  }, `${ opener }${ body }${ closer }`)])));
5565
5565
  },
5566
5566
  {
@@ -5593,8 +5593,8 @@ require = function () {
5593
5593
  class: 'invalid',
5594
5594
  translate: 'no',
5595
5595
  'data-invalid-syntax': 'table',
5596
- 'data-invalid-type': closer ? 'argument' : 'closer',
5597
- 'data-invalid-description': closer ? 'Invalid argument.' : `Missing the closing delimiter ${ delim }.`
5596
+ 'data-invalid-type': !closer ? 'closer' : 'argument',
5597
+ 'data-invalid-description': !closer ? `Missing the closing delimiter ${ delim }.` : 'Invalid argument.'
5598
5598
  }, `${ opener }${ body }${ closer }`)],
5599
5599
  ''
5600
5600
  ];
@@ -5957,7 +5957,7 @@ require = function () {
5957
5957
  const global_1 = _dereq_('spica/global');
5958
5958
  const combinator_1 = _dereq_('../../combinator');
5959
5959
  const typed_dom_1 = _dereq_('typed-dom');
5960
- const opener = /^(\$\$)(?!\$)([^\n]*)(?:$|\n)/;
5960
+ const opener = /^(\${2,})(?!\$)([^\n]*)(?:$|\n)/;
5961
5961
  exports.segment = (0, combinator_1.block)((0, combinator_1.validate)('$$', (0, combinator_1.clear)((0, combinator_1.fence)(opener, 100))));
5962
5962
  exports.segment_ = (0, combinator_1.block)((0, combinator_1.validate)('$$', (0, combinator_1.clear)((0, combinator_1.fence)(opener, 100, false))), false);
5963
5963
  exports.mathblock = (0, combinator_1.block)((0, combinator_1.validate)('$$', (0, combinator_1.fmap)((0, combinator_1.fence)(opener, 100), ([body, closer, opener, delim, param], _, {
@@ -5966,15 +5966,15 @@ require = function () {
5966
5966
  } = {}
5967
5967
  }) => {
5968
5968
  var _a;
5969
- return [closer && param.trimStart() === '' ? ((_a = cache === null || cache === void 0 ? void 0 : cache.get(`$$\n${ body }$$`)) === null || _a === void 0 ? void 0 : _a.cloneNode(true)) || (0, typed_dom_1.html)('div', {
5969
+ return [delim.length === 2 && closer && param.trimStart() === '' ? ((_a = cache === null || cache === void 0 ? void 0 : cache.get(`\n${ body }`)) === null || _a === void 0 ? void 0 : _a.cloneNode(true)) || (0, typed_dom_1.html)('div', {
5970
5970
  class: 'math',
5971
5971
  translate: 'no'
5972
- }, `$$\n${ body }$$`) : (0, typed_dom_1.html)('pre', {
5972
+ }, `${ delim }\n${ body }${ delim }`) : (0, typed_dom_1.html)('pre', {
5973
5973
  class: 'invalid',
5974
5974
  translate: 'no',
5975
5975
  'data-invalid-syntax': 'mathblock',
5976
- 'data-invalid-type': closer ? 'argument' : 'closer',
5977
- 'data-invalid-description': closer ? 'Invalid argument.' : `Missing the closing delimiter ${ delim }.`
5976
+ 'data-invalid-type': delim.length > 2 ? 'syntax' : !closer ? 'closer' : 'argument',
5977
+ 'data-invalid-description': delim.length > 2 ? 'Invalid syntax' : !closer ? `Missing the closing delimiter ${ delim }.` : 'Invalid argument.'
5978
5978
  }, `${ opener }${ body }${ closer }`)];
5979
5979
  })));
5980
5980
  },
@@ -6844,13 +6844,7 @@ require = function () {
6844
6844
  /[0-9]{1,4}|[A-Za-z]/
6845
6845
  ].map(r => r.source).join('|') })`);
6846
6846
  const indexFW = new RegExp(index.source.replace(/[019AZaz](?!,)/g, c => String.fromCharCode(c.charCodeAt(0) + 65248)));
6847
- exports.bracket = (0, combinator_1.lazy)(() => (0, combinator_1.validate)([
6848
- '(',
6849
- '\uFF08',
6850
- '[',
6851
- '{',
6852
- '"'
6853
- ], (0, combinator_1.union)([
6847
+ exports.bracket = (0, combinator_1.lazy)(() => (0, combinator_1.union)([
6854
6848
  (0, combinator_1.surround)((0, source_1.str)('('), (0, source_1.str)(index), (0, source_1.str)(')'), false, ([as, bs = [], cs], rest) => [
6855
6849
  (0, typed_dom_1.defrag)((0, array_1.push)((0, array_1.unshift)(as, bs), cs)),
6856
6850
  rest
@@ -6885,7 +6879,7 @@ require = function () {
6885
6879
  (0, array_1.unshift)(as, bs),
6886
6880
  rest
6887
6881
  ])
6888
- ])));
6882
+ ]));
6889
6883
  },
6890
6884
  {
6891
6885
  '../../combinator': 30,
@@ -6903,7 +6897,7 @@ require = function () {
6903
6897
  exports.code = void 0;
6904
6898
  const combinator_1 = _dereq_('../../combinator');
6905
6899
  const typed_dom_1 = _dereq_('typed-dom');
6906
- exports.code = (0, combinator_1.creator)((0, combinator_1.validate)('`', '`', '\n', (0, combinator_1.match)(/^(`+)(?!`)([^\n]*?[^`\n])\1(?!`)/, ([whole, , body]) => rest => [
6900
+ exports.code = (0, combinator_1.creator)((0, combinator_1.validate)('`', (0, combinator_1.match)(/^(`+)(?!`)([^\n]*?[^`\n])\1(?!`)/, ([whole, , body]) => rest => [
6907
6901
  [(0, typed_dom_1.html)('code', { 'data-src': whole }, format(body))],
6908
6902
  rest
6909
6903
  ])));
@@ -7301,14 +7295,14 @@ require = function () {
7301
7295
  exports.segment = (0, combinator_1.clear)((0, combinator_1.validate)([
7302
7296
  '[$',
7303
7297
  '$'
7304
- ], '-', '\n', (0, combinator_1.union)([
7298
+ ], (0, combinator_1.union)([
7305
7299
  (0, combinator_1.surround)('[', body, ']'),
7306
7300
  body
7307
7301
  ])));
7308
7302
  exports.label = (0, combinator_1.creator)((0, combinator_1.validate)([
7309
7303
  '[$',
7310
7304
  '$'
7311
- ], '-', '\n', (0, combinator_1.fmap)((0, combinator_1.guard)(context => {
7305
+ ], (0, combinator_1.fmap)((0, combinator_1.guard)(context => {
7312
7306
  var _a, _b, _c;
7313
7307
  return (_c = (_b = (_a = context.syntax) === null || _a === void 0 ? void 0 : _a.inline) === null || _b === void 0 ? void 0 : _b.label) !== null && _c !== void 0 ? _c : true;
7314
7308
  }, (0, combinator_1.union)([
@@ -7413,7 +7407,7 @@ require = function () {
7413
7407
  };
7414
7408
  (0, alias_1.ObjectSetPrototypeOf)(attrspec, null);
7415
7409
  (0, alias_1.ObjectValues)(attrspec).forEach(o => (0, alias_1.ObjectSetPrototypeOf)(o, null));
7416
- exports.html = (0, combinator_1.lazy)(() => (0, combinator_1.creator)((0, combinator_1.validate)('<', '>', '\n', (0, combinator_1.validate)(/^<[a-z]+(?=[^\S\n]|>)/, (0, combinator_1.union)([
7410
+ exports.html = (0, combinator_1.lazy)(() => (0, combinator_1.creator)((0, combinator_1.validate)('<', (0, combinator_1.validate)(/^<[a-z]+(?=[^\S\n]|>)/, (0, combinator_1.union)([
7417
7411
  (0, combinator_1.match)(/^(?=<(wbr)(?=[^\S\n]|>))/, (0, memoize_1.memoize)(([, tag]) => (0, combinator_1.surround)((0, source_1.str)(`<${ tag }`), (0, combinator_1.some)((0, combinator_1.union)([exports.attribute])), (0, source_1.str)('>'), true, ([, as = []], rest) => [
7418
7412
  [(0, typed_dom_1.html)(tag, attributes('html', [], attrspec[tag], as))],
7419
7413
  rest
@@ -7535,14 +7529,26 @@ require = function () {
7535
7529
  function (_dereq_, module, exports) {
7536
7530
  'use strict';
7537
7531
  Object.defineProperty(exports, '__esModule', { value: true });
7538
- exports.htmlentity = void 0;
7532
+ exports.htmlentity = exports.unsafehtmlentity = void 0;
7539
7533
  const combinator_1 = _dereq_('../../combinator');
7540
7534
  const typed_dom_1 = _dereq_('typed-dom');
7541
7535
  const parser = (0, typed_dom_1.html)('textarea');
7542
- exports.htmlentity = (0, combinator_1.creator)((0, combinator_1.validate)('&', ';', '\n', (0, combinator_1.focus)(/^&[0-9A-Za-z]+;/, entity => [
7536
+ exports.unsafehtmlentity = (0, combinator_1.creator)((0, combinator_1.validate)('&', (0, combinator_1.verify)((0, combinator_1.focus)(/^&[0-9A-Za-z]+;/, entity => [
7543
7537
  [(parser.innerHTML = entity, parser.value)],
7544
7538
  ''
7545
- ])));
7539
+ ]), ([str]) => str[0] !== '&' || str.length < 3)));
7540
+ exports.htmlentity = (0, combinator_1.creator)((0, combinator_1.validate)('&', (0, combinator_1.focus)(/^&[0-9A-Za-z]+;/, (0, combinator_1.union)([
7541
+ exports.unsafehtmlentity,
7542
+ str => [
7543
+ [(0, typed_dom_1.html)('span', {
7544
+ class: 'invalid',
7545
+ 'data-invalid-syntax': 'htmlentity',
7546
+ 'data-invalid-type': 'syntax',
7547
+ 'data-invalid-description': 'Invalid HTML entity.'
7548
+ }, str)],
7549
+ ''
7550
+ ]
7551
+ ]))));
7546
7552
  },
7547
7553
  {
7548
7554
  '../../combinator': 30,
@@ -7762,7 +7768,7 @@ require = function () {
7762
7768
  const util_1 = _dereq_('../util');
7763
7769
  const typed_dom_1 = _dereq_('typed-dom');
7764
7770
  const disallowedCommand = /\\(?:begin|tiny|huge|large)(?![0-9a-z])/i;
7765
- exports.math = (0, combinator_1.lazy)(() => (0, combinator_1.creator)((0, combinator_1.validate)('$', '$', '\n', (0, combinator_1.rewrite)((0, combinator_1.union)([
7771
+ exports.math = (0, combinator_1.lazy)(() => (0, combinator_1.creator)((0, combinator_1.validate)('$', (0, combinator_1.rewrite)((0, combinator_1.union)([
7766
7772
  (0, combinator_1.surround)('$', (0, combinator_1.verify)((0, source_1.str)(/^(?![\s{}#$%&]|\d+(?:[,.]\d+)*[^-+*/=<>^_~\\$]|-[\da-z]|[a-z]+-)(?:\\\$|[\x20-\x23\x25-\x7E])+/i), util_1.isEndTightNodes), /^\$(?![0-9a-z])/i),
7767
7773
  (0, combinator_1.surround)('$', bracket, '$')
7768
7774
  ]), (source, {
@@ -7826,7 +7832,7 @@ require = function () {
7826
7832
  return (_c = (_b = (_a = context.syntax) === null || _a === void 0 ? void 0 : _a.inline) === null || _b === void 0 ? void 0 : _b.media) !== null && _c !== void 0 ? _c : true;
7827
7833
  }, (0, combinator_1.tails)([
7828
7834
  (0, combinator_1.dup)((0, combinator_1.surround)(/^\[(?!\s*\\\s)/, (0, combinator_1.some)((0, combinator_1.union)([
7829
- htmlentity_1.htmlentity,
7835
+ htmlentity_1.unsafehtmlentity,
7830
7836
  bracket,
7831
7837
  source_1.txt
7832
7838
  ]), ']', /^\\?\n/), ']', true)),
@@ -7866,7 +7872,7 @@ require = function () {
7866
7872
  })));
7867
7873
  const bracket = (0, combinator_1.lazy)(() => (0, combinator_1.union)([
7868
7874
  (0, combinator_1.surround)((0, source_1.str)('('), (0, combinator_1.some)((0, combinator_1.union)([
7869
- htmlentity_1.htmlentity,
7875
+ htmlentity_1.unsafehtmlentity,
7870
7876
  bracket,
7871
7877
  source_1.txt
7872
7878
  ]), ')'), (0, source_1.str)(')'), true, global_1.undefined, ([as, bs = []], rest) => [
@@ -7874,7 +7880,7 @@ require = function () {
7874
7880
  rest
7875
7881
  ]),
7876
7882
  (0, combinator_1.surround)((0, source_1.str)('['), (0, combinator_1.some)((0, combinator_1.union)([
7877
- htmlentity_1.htmlentity,
7883
+ htmlentity_1.unsafehtmlentity,
7878
7884
  bracket,
7879
7885
  source_1.txt
7880
7886
  ]), ']'), (0, source_1.str)(']'), true, global_1.undefined, ([as, bs = []], rest) => [
@@ -7882,7 +7888,7 @@ require = function () {
7882
7888
  rest
7883
7889
  ]),
7884
7890
  (0, combinator_1.surround)((0, source_1.str)('{'), (0, combinator_1.some)((0, combinator_1.union)([
7885
- htmlentity_1.htmlentity,
7891
+ htmlentity_1.unsafehtmlentity,
7886
7892
  bracket,
7887
7893
  source_1.txt
7888
7894
  ]), '}'), (0, source_1.str)('}'), true, global_1.undefined, ([as, bs = []], rest) => [
@@ -7890,7 +7896,7 @@ require = function () {
7890
7896
  rest
7891
7897
  ]),
7892
7898
  (0, combinator_1.surround)((0, source_1.str)('"'), (0, combinator_1.some)((0, combinator_1.union)([
7893
- htmlentity_1.htmlentity,
7899
+ htmlentity_1.unsafehtmlentity,
7894
7900
  source_1.txt
7895
7901
  ]), '"'), (0, source_1.str)('"'), true)
7896
7902
  ]));
@@ -8052,10 +8058,12 @@ require = function () {
8052
8058
  while (source !== '') {
8053
8059
  switch (source[0]) {
8054
8060
  case '&': {
8055
- const result = (0, htmlentity_1.htmlentity)(source, context);
8056
- acc[acc.length - 1] += (0, parser_1.eval)(result, [source[0]])[0];
8057
- source = (0, parser_1.exec)(result, source.slice(1));
8058
- continue;
8061
+ const result = (0, htmlentity_1.unsafehtmlentity)(source, context);
8062
+ if (result) {
8063
+ acc[acc.length - 1] += (0, parser_1.eval)(result, [source[0]])[0];
8064
+ source = (0, parser_1.exec)(result, source.slice(1));
8065
+ continue;
8066
+ }
8059
8067
  }
8060
8068
  default: {
8061
8069
  if (source[0].trimStart() === '') {
@@ -8417,6 +8425,7 @@ require = function () {
8417
8425
  const buffer = new multimap_1.MultiMap();
8418
8426
  const titles = new global_1.Map();
8419
8427
  const check = footnotes.some(el => target.contains(el));
8428
+ let style;
8420
8429
  for (let refs = target.querySelectorAll(`sup.${ syntax }:not(.disabled)`), i = 0, len = refs.length; i < len; ++i) {
8421
8430
  yield;
8422
8431
  const ref = refs[i];
@@ -8425,6 +8434,15 @@ require = function () {
8425
8434
  const identifier = identify(ref);
8426
8435
  const abbr = ref.getAttribute('data-abbr') || global_1.undefined;
8427
8436
  const content = contentify(ref);
8437
+ style !== null && style !== void 0 ? style : style = abbr ? 'abbr' : 'count';
8438
+ if (style === 'count' ? abbr : !abbr) {
8439
+ (0, typed_dom_1.define)(ref, {
8440
+ class: `${ ref.className } invalid`,
8441
+ 'data-invalid-syntax': syntax,
8442
+ 'data-invalid-type': 'style',
8443
+ 'data-invalid-description': `${ syntax[0].toUpperCase() + syntax.slice(1) } style must be consistent.`
8444
+ });
8445
+ }
8428
8446
  if (((_a = ref.firstElementChild) === null || _a === void 0 ? void 0 : _a.getAttribute('hidden')) !== '') {
8429
8447
  ref.replaceChildren((0, typed_dom_1.html)('span', { hidden: '' }, ref.childNodes));
8430
8448
  } else {
@@ -8442,6 +8460,8 @@ require = function () {
8442
8460
  if (title && !blank && def.childNodes.length === 1) {
8443
8461
  def.insertBefore(content.cloneNode(true), def.lastChild);
8444
8462
  for (const ref of buffer.take(identifier, global_1.Infinity)) {
8463
+ if (ref.getAttribute('data-invalid-type') !== 'content')
8464
+ continue;
8445
8465
  (0, typed_dom_1.define)(ref, {
8446
8466
  title,
8447
8467
  class: void ref.classList.remove('invalid'),
@@ -8997,7 +9017,7 @@ require = function () {
8997
9017
  return ((_a = source[1]) === null || _a === void 0 ? void 0 : _a.trimStart()) !== '';
8998
9018
  case '&':
8999
9019
  switch (true) {
9000
- case source.length > 2 && source[1] !== ' ' && ((_b = (0, parser_1.eval)((0, htmlentity_1.htmlentity)(source, context))) === null || _b === void 0 ? void 0 : _b[0].trimStart()) === '':
9020
+ case source.length > 2 && source[1] !== ' ' && ((_b = (0, parser_1.eval)((0, htmlentity_1.unsafehtmlentity)(source, context))) === null || _b === void 0 ? void 0 : _b[0].trimStart()) === '':
9001
9021
  return false;
9002
9022
  }
9003
9023
  return true;
package/markdown.d.ts CHANGED
@@ -844,7 +844,7 @@ export namespace MarkdownParser {
844
844
  export interface TextParser extends
845
845
  Inline<'media/text'>,
846
846
  Parser<string[], Context, [
847
- HTMLEntityParser,
847
+ UnsafeHTMLEntityParser,
848
848
  TextParser.BracketParser,
849
849
  SourceParser.TxtParser,
850
850
  ]> {
@@ -854,22 +854,22 @@ export namespace MarkdownParser {
854
854
  Inline<'media/text/bracket'>,
855
855
  Parser<string, Context, [
856
856
  Parser<string, Context, [
857
- HTMLEntityParser,
857
+ UnsafeHTMLEntityParser,
858
858
  BracketParser,
859
859
  SourceParser.TxtParser,
860
860
  ]>,
861
861
  Parser<string, Context, [
862
- HTMLEntityParser,
862
+ UnsafeHTMLEntityParser,
863
863
  BracketParser,
864
864
  SourceParser.TxtParser,
865
865
  ]>,
866
866
  Parser<string, Context, [
867
- HTMLEntityParser,
867
+ UnsafeHTMLEntityParser,
868
868
  BracketParser,
869
869
  SourceParser.TxtParser,
870
870
  ]>,
871
871
  Parser<string, Context, [
872
- HTMLEntityParser,
872
+ UnsafeHTMLEntityParser,
873
873
  SourceParser.TxtParser,
874
874
  ]>,
875
875
  ]> {
@@ -1003,6 +1003,14 @@ export namespace MarkdownParser {
1003
1003
  export interface HTMLEntityParser extends
1004
1004
  // &copy;
1005
1005
  Inline<'htmlentity'>,
1006
+ Parser<HTMLSpanElement | string, Context, [
1007
+ UnsafeHTMLEntityParser,
1008
+ Parser<HTMLSpanElement, Context, []>,
1009
+ ]> {
1010
+ }
1011
+ export interface UnsafeHTMLEntityParser extends
1012
+ // &copy;
1013
+ Inline<'unsafehtmlentity'>,
1006
1014
  Parser<string, Context, []> {
1007
1015
  }
1008
1016
  export interface ShortmediaParser extends
package/package-lock.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securemark",
3
- "version": "0.226.1",
3
+ "version": "0.226.2",
4
4
  "lockfileVersion": 1,
5
5
  "requires": true,
6
6
  "dependencies": {
@@ -548,9 +548,9 @@
548
548
  "dev": true
549
549
  },
550
550
  "@types/node": {
551
- "version": "17.0.0",
552
- "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.0.tgz",
553
- "integrity": "sha512-eMhwJXc931Ihh4tkU+Y7GiLzT/y/DBNpNtr4yU9O2w3SYBsr9NaOPhQlLKRmoWtI54uNwuo0IOUFQjVOTZYRvw==",
551
+ "version": "17.0.4",
552
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.4.tgz",
553
+ "integrity": "sha512-6xwbrW4JJiJLgF+zNypN5wr2ykM9/jHcL7rQ8fZe2vuftggjzZeRSM4OwRc6Xk8qWjwJ99qVHo/JgOGmomWRog==",
554
554
  "dev": true
555
555
  },
556
556
  "@types/power-assert": {
@@ -1790,9 +1790,9 @@
1790
1790
  "dev": true
1791
1791
  },
1792
1792
  "caniuse-lite": {
1793
- "version": "1.0.30001291",
1794
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001291.tgz",
1795
- "integrity": "sha512-roMV5V0HNGgJ88s42eE70sstqGW/gwFndosYrikHthw98N5tLnOTxFqMLQjZVRxTWFlJ4rn+MsgXrR7MDPY4jA==",
1793
+ "version": "1.0.30001292",
1794
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001292.tgz",
1795
+ "integrity": "sha512-jnT4Tq0Q4ma+6nncYQVe7d73kmDmE9C3OGTx3MvW7lBM/eY1S1DZTMBON7dqV481RhNiS5OxD7k9JQvmDOTirw==",
1796
1796
  "dev": true
1797
1797
  },
1798
1798
  "chalk": {
@@ -2867,9 +2867,9 @@
2867
2867
  "dev": true
2868
2868
  },
2869
2869
  "electron-to-chromium": {
2870
- "version": "1.4.24",
2871
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.24.tgz",
2872
- "integrity": "sha512-erwx5r69B/WFfFuF2jcNN0817BfDBdC4765kQ6WltOMuwsimlQo3JTEq0Cle+wpHralwdeX3OfAtw/mHxPK0Wg==",
2870
+ "version": "1.4.28",
2871
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.28.tgz",
2872
+ "integrity": "sha512-Gzbf0wUtKfyPaqf0Plz+Ctinf9eQIzxEqBHwSvbGfeOm9GMNdLxyu1dNiCUfM+x6r4BE0xUJNh3Nmg9gfAtTmg==",
2873
2873
  "dev": true
2874
2874
  },
2875
2875
  "elliptic": {
@@ -5929,9 +5929,9 @@
5929
5929
  }
5930
5930
  },
5931
5931
  "istanbul-reports": {
5932
- "version": "3.1.1",
5933
- "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.1.tgz",
5934
- "integrity": "sha512-q1kvhAXWSsXfMjCdNHNPKZZv94OlspKnoGv+R9RGbnqOOQ0VbNfLFgQDVgi7hHenKsndGq3/o0OBdzDXthWcNw==",
5932
+ "version": "3.1.2",
5933
+ "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.2.tgz",
5934
+ "integrity": "sha512-0gHxuT1NNC0aEIL1zbJ+MTgPbbHhU77eJPuU35WKA7TgXiSNlCAx4PENoMrH0Or6M2H80TaZcWKhM0IK6V8gRw==",
5935
5935
  "dev": true,
5936
5936
  "requires": {
5937
5937
  "html-escaper": "^2.0.0",
@@ -6134,9 +6134,9 @@
6134
6134
  }
6135
6135
  },
6136
6136
  "karma-coverage-istanbul-instrumenter": {
6137
- "version": "1.0.3",
6138
- "resolved": "https://registry.npmjs.org/karma-coverage-istanbul-instrumenter/-/karma-coverage-istanbul-instrumenter-1.0.3.tgz",
6139
- "integrity": "sha512-zyaPd28Bz5bRbtIbz3N+t8P/cfohmggrpM0PkkV5dYTSh7tYYZJIYx7WxpywgR9av6CbTeeZw/XS9tr9n+qLuA==",
6137
+ "version": "1.0.4",
6138
+ "resolved": "https://registry.npmjs.org/karma-coverage-istanbul-instrumenter/-/karma-coverage-istanbul-instrumenter-1.0.4.tgz",
6139
+ "integrity": "sha512-lde9WPGld1fuawEqoaibd+lq6wrs5HHwEnfzHUh5LlpU9TJqUCjhtHLFVUxJtGEhi/XUZJmlOKuk59NZJrKamg==",
6140
6140
  "dev": true,
6141
6141
  "requires": {
6142
6142
  "convert-source-map": "^1.7.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securemark",
3
- "version": "0.226.1",
3
+ "version": "0.226.2",
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",
@@ -1,4 +1,4 @@
1
- import { htmlentity } from '../inline/htmlentity';
1
+ import { unsafehtmlentity } from '../inline/htmlentity';
2
2
  import { eval } from '../../combinator/data/parser';
3
3
 
4
4
  const UNICODE_REPLACEMENT_CHARACTER = '\uFFFD';
@@ -59,7 +59,7 @@ const unreadableHTMLEntityNames = [
59
59
  'ic',
60
60
  ];
61
61
  const unreadableEscapableCharacters = unreadableHTMLEntityNames
62
- .flatMap(name => eval(htmlentity(`&${name};`, {}), []));
62
+ .flatMap(name => eval(unsafehtmlentity(`&${name};`, {}), []));
63
63
  assert(unreadableEscapableCharacters.length === unreadableHTMLEntityNames.length);
64
64
  const unreadableEscapableCharacter = new RegExp(`[${
65
65
  [...new Set<string>(unreadableEscapableCharacters)].join('')
@@ -29,8 +29,8 @@ export const codeblock: CodeBlockParser = block(validate('```', fmap(
29
29
  class: 'invalid',
30
30
  translate: 'no',
31
31
  'data-invalid-syntax': 'codeblock',
32
- 'data-invalid-type': closer ? 'argument' : 'closer',
33
- 'data-invalid-description': closer ? 'Invalid argument.' : `Missing the closing delimiter ${delim}.`,
32
+ 'data-invalid-type': !closer ? 'closer' : 'argument',
33
+ 'data-invalid-description': !closer ? `Missing the closing delimiter ${delim}.` : 'Invalid argument.',
34
34
  }, `${opener}${body}${closer}`)];
35
35
  const file = path.split('/').pop() ?? '';
36
36
  const ext = file && file.includes('.', 1)
@@ -12,8 +12,8 @@ export const aside: ExtensionParser.AsideParser = creator(100, block(validate('~
12
12
  class: 'invalid',
13
13
  translate: 'no',
14
14
  'data-invalid-syntax': 'aside',
15
- 'data-invalid-type': closer ? 'argument' : 'closer',
16
- 'data-invalid-description': closer ? 'Invalid argument.' : `Missing the closing delimiter ${delim}.`,
15
+ 'data-invalid-type': !closer ? 'closer' : 'argument',
16
+ 'data-invalid-description': !closer ? `Missing the closing delimiter ${delim}.` : 'Invalid argument.',
17
17
  }, `${opener}${body}${closer}`)];
18
18
  const annotations = html('ol', { class: 'annotations' });
19
19
  const references = html('ol', { class: 'references' });
@@ -15,8 +15,8 @@ export const example: ExtensionParser.ExampleParser = creator(100, block(validat
15
15
  class: 'invalid',
16
16
  translate: 'no',
17
17
  'data-invalid-syntax': 'example',
18
- 'data-invalid-type': closer ? 'argument' : 'closer',
19
- 'data-invalid-description': closer ? 'Invalid argument.' : `Missing the closing delimiter ${delim}.`,
18
+ 'data-invalid-type': !closer ? 'closer' : 'argument',
19
+ 'data-invalid-description': !closer ? `Missing the closing delimiter ${delim}.` : 'Invalid argument.',
20
20
  }, `${opener}${body}${closer}`)];
21
21
  switch (type) {
22
22
  case 'markdown': {
@@ -24,8 +24,8 @@ export const message: MessageParser = block(validate('~~~', fmap(
24
24
  class: 'invalid',
25
25
  translate: 'no',
26
26
  'data-invalid-syntax': 'message',
27
- 'data-invalid-type': closer ? 'argument' : 'closer',
28
- 'data-invalid-description': closer ? 'Invalid argument.' : `Missing the closing delimiter ${delim}.`,
27
+ 'data-invalid-type': !closer ? 'closer' : 'argument',
28
+ 'data-invalid-description': !closer ? `Missing the closing delimiter ${delim}.` : 'Invalid argument.',
29
29
  }, `${opener}${body}${closer}`)];
30
30
  switch (type) {
31
31
  case 'note':
@@ -17,7 +17,7 @@ export const placeholder: ExtensionParser.PlaceholderParser = block(validate('~~
17
17
  class: 'invalid',
18
18
  translate: 'no',
19
19
  'data-invalid-syntax': 'extension',
20
- 'data-invalid-type': closer ? 'syntax' : 'closer',
21
- 'data-invalid-description': closer ? 'Invalid syntax.' : `Missing the closing delimiter ${delim}.`,
20
+ 'data-invalid-type': !closer ? 'closer' : 'syntax',
21
+ 'data-invalid-description': !closer ? `Missing the closing delimiter ${delim}.` : 'Invalid syntax.',
22
22
  }, `${opener}${body}${closer}`)
23
23
  ])));
@@ -31,8 +31,8 @@ export const table: TableParser = block(validate('~~~', recover(bind(
31
31
  class: 'invalid',
32
32
  translate: 'no',
33
33
  'data-invalid-syntax': 'table',
34
- 'data-invalid-type': closer ? 'argument' : 'closer',
35
- 'data-invalid-description': closer ? 'Invalid argument.' : `Missing the closing delimiter ${delim}.`,
34
+ 'data-invalid-type': !closer ? 'closer' : 'argument',
35
+ 'data-invalid-description': !closer ? `Missing the closing delimiter ${delim}.` : 'Invalid argument.',
36
36
  }, `${opener}${body}${closer}`)], ''];
37
37
  return parser(body, context) ?? [[html('table')], ''];
38
38
  }),
@@ -17,9 +17,10 @@ describe('Unit: parser/block/mathblock', () => {
17
17
  assert.deepStrictEqual(inspect(parser('$$\na\n$$\nb')), [['<pre class="invalid" translate="no">$$\na\n$$\nb</pre>'], '']);
18
18
  assert.deepStrictEqual(inspect(parser('$$$\n\n\n$$$')), undefined);
19
19
  assert.deepStrictEqual(inspect(parser('$$ $$\n$$')), undefined);
20
- assert.deepStrictEqual(inspect(parser('$$latex\n$$')), [['<pre class="invalid" translate="no">$$latex\n$$</pre>'], '']);
21
- assert.deepStrictEqual(inspect(parser('$$$\n$$')), undefined);
22
- assert.deepStrictEqual(inspect(parser('$$$\n$$$')), undefined);
20
+ assert.deepStrictEqual(inspect(parser('$$lang\n$$')), [['<pre class="invalid" translate="no">$$lang\n$$</pre>'], '']);
21
+ assert.deepStrictEqual(inspect(parser('$$ param\n$$')), [['<pre class="invalid" translate="no">$$ param\n$$</pre>'], '']);
22
+ assert.deepStrictEqual(inspect(parser('$$$\n$$')), [['<pre class="invalid" translate="no">$$$\n$$</pre>'], '']);
23
+ assert.deepStrictEqual(inspect(parser('$$$\n$$$')), [['<pre class="invalid" translate="no">$$$\n$$$</pre>'], '']);
23
24
  assert.deepStrictEqual(inspect(parser(' $$\n$$')), undefined);
24
25
  assert.deepStrictEqual(inspect(parser(`$$\n0${'\n'.repeat(101)}$$`), '>'), [['<pre class="invalid" translate="no">'], '']);
25
26
  });
@@ -3,7 +3,7 @@ import { MathBlockParser } from '../block';
3
3
  import { block, validate, fence, clear, fmap } from '../../combinator';
4
4
  import { html } from 'typed-dom';
5
5
 
6
- const opener = /^(\$\$)(?!\$)([^\n]*)(?:$|\n)/;
6
+ const opener = /^(\${2,})(?!\$)([^\n]*)(?:$|\n)/;
7
7
 
8
8
  export const segment: MathBlockParser.SegmentParser = block(validate('$$',
9
9
  clear(fence(opener, 100))));
@@ -15,14 +15,14 @@ export const mathblock: MathBlockParser = block(validate('$$', fmap(
15
15
  fence(opener, 100),
16
16
  // Bug: Type mismatch between outer and inner.
17
17
  ([body, closer, opener, delim, param]: string[], _, { caches: { math: cache = undefined } = {} }) => [
18
- closer && param.trimStart() === ''
19
- ? cache?.get(`$$\n${body}$$`)?.cloneNode(true) as HTMLDivElement ||
20
- html('div', { class: 'math', translate: 'no' }, `$$\n${body}$$`)
18
+ delim.length === 2 && closer && param.trimStart() === ''
19
+ ? cache?.get(`\n${body}`)?.cloneNode(true) as HTMLDivElement ||
20
+ html('div', { class: 'math', translate: 'no' }, `${delim}\n${body}${delim}`)
21
21
  : html('pre', {
22
22
  class: 'invalid',
23
23
  translate: 'no',
24
24
  'data-invalid-syntax': 'mathblock',
25
- 'data-invalid-type': closer ? 'argument' : 'closer',
26
- 'data-invalid-description': closer ? 'Invalid argument.' : `Missing the closing delimiter ${delim}.`,
25
+ 'data-invalid-type': delim.length > 2 ? 'syntax' : !closer ? 'closer' : 'argument',
26
+ 'data-invalid-description': delim.length > 2 ? 'Invalid syntax' : !closer ? `Missing the closing delimiter ${delim}.` : 'Invalid argument.',
27
27
  }, `${opener}${body}${closer}`),
28
28
  ])));
@@ -1,6 +1,6 @@
1
1
  import { undefined } from 'spica/global';
2
2
  import { BracketParser } from '../inline';
3
- import { union, some, validate, surround, lazy } from '../../combinator';
3
+ import { union, some, surround, lazy } from '../../combinator';
4
4
  import { inline } from '../inline';
5
5
  import { str } from '../source';
6
6
  import { html, defrag } from 'typed-dom';
@@ -12,7 +12,7 @@ const index = new RegExp(`^(?:${[
12
12
  ].map(r => r.source).join('|')})`);
13
13
  const indexFW = new RegExp(index.source.replace(/[019AZaz](?!,)/g, c => String.fromCharCode(c.charCodeAt(0) + 0xfee0)));
14
14
 
15
- export const bracket: BracketParser = lazy(() => validate(['(', '(', '[', '{', '"'], union([
15
+ export const bracket: BracketParser = lazy(() => union([
16
16
  surround(str('('), str(index), str(')'), false,
17
17
  ([as, bs = [], cs], rest) => [defrag(push(unshift(as, bs), cs)), rest]),
18
18
  surround(str('('), str(indexFW), str(')'), false,
@@ -33,4 +33,4 @@ export const bracket: BracketParser = lazy(() => validate(['(', '(', '[', '{',
33
33
  surround(str('"'), some(inline, '"', '"'), str('"'), true,
34
34
  undefined,
35
35
  ([as, bs = []], rest) => [unshift(as, bs), rest]),
36
- ])));
36
+ ]));
@@ -2,7 +2,7 @@ import { CodeParser } from '../inline';
2
2
  import { validate, creator, match } from '../../combinator';
3
3
  import { html } from 'typed-dom';
4
4
 
5
- export const code: CodeParser = creator(validate('`', '`', '\n', match(
5
+ export const code: CodeParser = creator(validate('`', match(
6
6
  /^(`+)(?!`)([^\n]*?[^`\n])\1(?!`)/,
7
7
  ([whole, , body]) => rest =>
8
8
  [[html('code', { 'data-src': whole }, format(body))], rest])));
@@ -75,7 +75,7 @@ describe('Unit: parser/inline/extension/index', () => {
75
75
  assert.deepStrictEqual(inspect(parser('[#a|#b c]')), [['<a class="index" href="#index:b_c">a<span class="indexer" data-index="b_c"></span></a>'], '']);
76
76
  assert.deepStrictEqual(inspect(parser('[#a|#b c]')), [['<a class="index" href="#index:b_c">a<span class="indexer" data-index="b_c"></span></a>'], '']);
77
77
  assert.deepStrictEqual(inspect(parser('[#a|#[]]')), [['<a class="index" href="#index:[]">a<span class="indexer" data-index="[]"></span></a>'], '']);
78
- assert.deepStrictEqual(inspect(parser('[#a|#&amp;]')), [['<a class="index" href="#index:&amp;amp;">a<span class="indexer" data-index="&amp;amp;"></span></a>'], '']);
78
+ assert.deepStrictEqual(inspect(parser('[#a|#&copy;]')), [['<a class="index" href="#index:&amp;copy;">a<span class="indexer" data-index="&amp;copy;"></span></a>'], '']);
79
79
  assert.deepStrictEqual(inspect(parser('[#a |]')), [['<a class="index" href="#index:a_|">a |</a>'], '']);
80
80
  assert.deepStrictEqual(inspect(parser('[#a |#]')), [['<a class="index" href="#index:a_|#">a |#</a>'], '']);
81
81
  assert.deepStrictEqual(inspect(parser('[#a |#b]')), [['<a class="index" href="#index:b">a<span class="indexer" data-index="b"></span></a>'], '']);
@@ -7,12 +7,12 @@ import { join } from 'spica/array';
7
7
 
8
8
  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
9
 
10
- export const segment: ExtensionParser.LabelParser.SegmentParser = clear(validate(['[$', '$'], '-', '\n', union([
10
+ export const segment: ExtensionParser.LabelParser.SegmentParser = clear(validate(['[$', '$'], union([
11
11
  surround('[', body, ']'),
12
12
  body,
13
13
  ])));
14
14
 
15
- export const label: ExtensionParser.LabelParser = creator(validate(['[$', '$'], '-', '\n', fmap(
15
+ export const label: ExtensionParser.LabelParser = creator(validate(['[$', '$'], fmap(
16
16
  guard(context => context.syntax?.inline?.label ?? true,
17
17
  union([
18
18
  surround('[', body, ']'),
@@ -20,7 +20,7 @@ const attrspec = {
20
20
  ObjectSetPrototypeOf(attrspec, null);
21
21
  ObjectValues(attrspec).forEach(o => ObjectSetPrototypeOf(o, null));
22
22
 
23
- export const html: HTMLParser = lazy(() => creator(validate('<', '>', '\n', validate(/^<[a-z]+(?=[^\S\n]|>)/, union([
23
+ export const html: HTMLParser = lazy(() => creator(validate('<', validate(/^<[a-z]+(?=[^\S\n]|>)/, union([
24
24
  match(
25
25
  /^(?=<(wbr)(?=[^\S\n]|>))/,
26
26
  memoize(
@@ -13,6 +13,7 @@ describe('Unit: parser/inline/htmlentity', () => {
13
13
  assert.deepStrictEqual(inspect(parser('&;')), undefined);
14
14
  assert.deepStrictEqual(inspect(parser('& ;')), undefined);
15
15
  assert.deepStrictEqual(inspect(parser('&\n;')), undefined);
16
+ assert.deepStrictEqual(inspect(parser('&a;')), [['<span class="invalid">&amp;a;</span>'], '']);
16
17
  assert.deepStrictEqual(inspect(parser('&#;')), undefined);
17
18
  assert.deepStrictEqual(inspect(parser('&#g;')), undefined);
18
19
  assert.deepStrictEqual(inspect(parser('&#x;')), undefined);
@@ -1,9 +1,24 @@
1
- import { HTMLEntityParser } from '../inline';
2
- import { validate, focus, creator } from '../../combinator';
1
+ import { HTMLEntityParser, UnsafeHTMLEntityParser } from '../inline';
2
+ import { union, validate, verify, focus, creator } from '../../combinator';
3
3
  import { html } from 'typed-dom';
4
4
 
5
5
  const parser = html('textarea');
6
6
 
7
- export const htmlentity: HTMLEntityParser = creator(validate('&', ';', '\n', focus(
7
+ export const unsafehtmlentity: UnsafeHTMLEntityParser = creator(validate('&', verify(focus(
8
8
  /^&[0-9A-Za-z]+;/,
9
- entity => [[(parser.innerHTML = entity, parser.value)], ''])));
9
+ entity => [[(parser.innerHTML = entity, parser.value)], '']),
10
+ ([str]) => str[0] !== '&' || str.length < 3)));
11
+
12
+ export const htmlentity: HTMLEntityParser = creator(validate('&', focus(
13
+ /^&[0-9A-Za-z]+;/,
14
+ union([
15
+ unsafehtmlentity,
16
+ str => [[
17
+ html('span', {
18
+ class: 'invalid',
19
+ 'data-invalid-syntax': 'htmlentity',
20
+ 'data-invalid-type': 'syntax',
21
+ 'data-invalid-description': 'Invalid HTML entity.',
22
+ }, str),
23
+ ], '']
24
+ ]))));
@@ -98,7 +98,7 @@ describe('Unit: parser/inline/link', () => {
98
98
  assert.deepStrictEqual(inspect(parser('[]{\\ }')), [[`<a href="\\">\\</a>`], '']);
99
99
  assert.deepStrictEqual(inspect(parser('[]{\\b}')), [[`<a href="\\b">\\b</a>`], '']);
100
100
  assert.deepStrictEqual(inspect(parser('[]{?b=c+d&\\#}')), [['<a href="?b=c+d&amp;\\#">?b=c+d&amp;\\#</a>'], '']);
101
- assert.deepStrictEqual(inspect(parser('[]{?&amp;}')), [['<a href="?&amp;amp;">?&amp;amp;</a>'], '']);
101
+ assert.deepStrictEqual(inspect(parser('[]{?&copy;}')), [['<a href="?&amp;copy;">?&amp;copy;</a>'], '']);
102
102
  assert.deepStrictEqual(inspect(parser('[]{#}')), [['<a href="#">#</a>'], '']);
103
103
  assert.deepStrictEqual(inspect(parser('[]{#b}')), [['<a href="#b">#b</a>'], '']);
104
104
  assert.deepStrictEqual(inspect(parser('[]{./b}')), [['<a href="./b">./b</a>'], '']);
@@ -6,7 +6,7 @@ import { html } from 'typed-dom';
6
6
 
7
7
  const disallowedCommand = /\\(?:begin|tiny|huge|large)(?![0-9a-z])/i;
8
8
 
9
- export const math: MathParser = lazy(() => creator(validate('$', '$', '\n', rewrite(
9
+ export const math: MathParser = lazy(() => creator(validate('$', rewrite(
10
10
  union([
11
11
  surround(
12
12
  '$',
@@ -4,7 +4,7 @@ import { MediaParser } from '../inline';
4
4
  import { union, inits, tails, some, validate, verify, guard, creator, surround, open, dup, lazy, fmap, bind } from '../../combinator';
5
5
  import { link, uri, option as linkoption, resolve } from './link';
6
6
  import { attributes } from './html';
7
- import { htmlentity } from './htmlentity';
7
+ import { unsafehtmlentity } from './htmlentity';
8
8
  import { txt, str } from '../source';
9
9
  import { html, define } from 'typed-dom';
10
10
  import { ReadonlyURL } from 'spica/url';
@@ -23,7 +23,7 @@ export const media: MediaParser = lazy(() => creator(10, bind(verify(fmap(open(
23
23
  validate(['[', '{'], '}', '\n',
24
24
  guard(context => context.syntax?.inline?.media ?? true,
25
25
  tails([
26
- dup(surround(/^\[(?!\s*\\\s)/, some(union([htmlentity, bracket, txt]), ']', /^\\?\n/), ']', true)),
26
+ dup(surround(/^\[(?!\s*\\\s)/, some(union([unsafehtmlentity, bracket, txt]), ']', /^\\?\n/), ']', true)),
27
27
  dup(surround(/^{(?![{}])/, inits([uri, some(option)]), /^[^\S\n]?}/)),
28
28
  ])))),
29
29
  ([as, bs]) => bs ? [[join(as).trim() || join(as)], bs] : [[''], as]),
@@ -53,10 +53,10 @@ export const media: MediaParser = lazy(() => creator(10, bind(verify(fmap(open(
53
53
  })));
54
54
 
55
55
  const bracket: MediaParser.TextParser.BracketParser = lazy(() => union([
56
- surround(str('('), some(union([htmlentity, bracket, txt]), ')'), str(')'), true, undefined, ([as, bs = []], rest) => [unshift(as, bs), rest]),
57
- surround(str('['), some(union([htmlentity, bracket, txt]), ']'), str(']'), true, undefined, ([as, bs = []], rest) => [unshift(as, bs), rest]),
58
- surround(str('{'), some(union([htmlentity, bracket, txt]), '}'), str('}'), true, undefined, ([as, bs = []], rest) => [unshift(as, bs), rest]),
59
- surround(str('"'), some(union([htmlentity, txt]), '"'), str('"'), true),
56
+ surround(str('('), some(union([unsafehtmlentity, bracket, txt]), ')'), str(')'), true, undefined, ([as, bs = []], rest) => [unshift(as, bs), rest]),
57
+ surround(str('['), some(union([unsafehtmlentity, bracket, txt]), ']'), str(']'), true, undefined, ([as, bs = []], rest) => [unshift(as, bs), rest]),
58
+ surround(str('{'), some(union([unsafehtmlentity, bracket, txt]), '}'), str('}'), true, undefined, ([as, bs = []], rest) => [unshift(as, bs), rest]),
59
+ surround(str('"'), some(union([unsafehtmlentity, txt]), '"'), str('"'), true),
60
60
  ]));
61
61
 
62
62
  const option: MediaParser.ParameterParser.OptionParser = union([
@@ -2,7 +2,7 @@ import { undefined } from 'spica/global';
2
2
  import { RubyParser } from '../inline';
3
3
  import { eval, exec } from '../../combinator/data/parser';
4
4
  import { sequence, validate, verify, focus, creator, surround, lazy, bind } from '../../combinator';
5
- import { htmlentity } from './htmlentity';
5
+ import { unsafehtmlentity } from './htmlentity';
6
6
  import { text as txt } from '../source';
7
7
  import { isStartTightNodes } from '../util';
8
8
  import { html, defrag } from 'typed-dom';
@@ -51,11 +51,15 @@ const text: RubyParser.TextParser = creator((source, context) => {
51
51
  while (source !== '') {
52
52
  assert(source[0] !== '\n');
53
53
  switch (source[0]) {
54
+ // @ts-expect-error
54
55
  case '&': {
55
- const result = htmlentity(source, context);
56
- acc[acc.length - 1] += eval(result, [source[0]])[0];
57
- source = exec(result, source.slice(1));
58
- continue;
56
+ const result = unsafehtmlentity(source, context);
57
+ if (result) {
58
+ acc[acc.length - 1] += eval(result, [source[0]])[0];
59
+ source = exec(result, source.slice(1));
60
+ continue;
61
+ }
62
+ // fallthrough
59
63
  }
60
64
  default: {
61
65
  if (source[0].trimStart() === '') {
@@ -44,6 +44,7 @@ export import CodeParser = InlineParser.CodeParser;
44
44
  export import MathParser = InlineParser.MathParser;
45
45
  export import MediaParser = InlineParser.MediaParser;
46
46
  export import HTMLEntityParser = InlineParser.HTMLEntityParser;
47
+ export import UnsafeHTMLEntityParser = InlineParser.UnsafeHTMLEntityParser;
47
48
  export import ShortmediaParser = InlineParser.ShortmediaParser;
48
49
  export import AutolinkParser = InlineParser.AutolinkParser;
49
50
  export import BracketParser = InlineParser.BracketParser;
@@ -40,6 +40,7 @@ function build(
40
40
  const buffer = new MultiMap<string, HTMLElement>();
41
41
  const titles = new Map<string, string>();
42
42
  const check = footnotes.some(el => target.contains(el));
43
+ let style: 'count' | 'abbr';
43
44
  for (
44
45
  let refs = target.querySelectorAll<HTMLElement>(`sup.${syntax}:not(.disabled)`),
45
46
  i = 0, len = refs.length; i < len; ++i) {
@@ -49,6 +50,15 @@ function build(
49
50
  const identifier = identify(ref);
50
51
  const abbr = ref.getAttribute('data-abbr') || undefined;
51
52
  const content = contentify(ref);
53
+ style ??= abbr ? 'abbr' : 'count';
54
+ if (style === 'count' ? abbr : !abbr) {
55
+ define(ref, {
56
+ class: `${ref.className} invalid`,
57
+ 'data-invalid-syntax': syntax,
58
+ 'data-invalid-type': 'style',
59
+ 'data-invalid-description': `${syntax[0].toUpperCase() + syntax.slice(1)} style must be consistent.`,
60
+ });
61
+ }
52
62
  if (ref.firstElementChild?.getAttribute('hidden') !== '') {
53
63
  ref.replaceChildren(html('span', { hidden: '' }, ref.childNodes));
54
64
  }
@@ -79,6 +89,7 @@ function build(
79
89
  def.insertBefore(content.cloneNode(true), def.lastChild);
80
90
  assert(def.childNodes.length > 1);
81
91
  for (const ref of buffer.take(identifier, Infinity)) {
92
+ if (ref.getAttribute('data-invalid-type') !== 'content') continue;
82
93
  define(ref, {
83
94
  title,
84
95
  class: void ref.classList.remove('invalid'),
@@ -46,6 +46,7 @@ export const text: TextParser = creator((source, context) => {
46
46
  case '?': {
47
47
  const i = source.slice(1).search(nonWhitespace) + 1;
48
48
  if (i > 0 && source.slice(i, i + 2) === '\\\n') return [[source[0], html('span', { class: 'linebreak' })], source.slice(i + 2)];
49
+ // fallthrough
49
50
  }
50
51
  default:
51
52
  assert(source[0] !== '\n');
@@ -3,7 +3,7 @@ import { MarkdownParser } from '../../markdown';
3
3
  import { Parser, eval } from '../combinator/data/parser';
4
4
  import { union, some, verify, clear, convert, trim } from '../combinator';
5
5
  import { comment } from './inline/comment';
6
- import { htmlentity } from './inline/htmlentity';
6
+ import { unsafehtmlentity } from './inline/htmlentity';
7
7
  import { linebreak, unescsource, str } from './source';
8
8
  import { push, pop } from 'spica/array';
9
9
 
@@ -104,7 +104,7 @@ function isStartTight(source: string, context: MarkdownParser.Context): boolean
104
104
  switch (true) {
105
105
  case source.length > 2
106
106
  && source[1] !== ' '
107
- && eval(htmlentity(source, context))?.[0].trimStart() === '':
107
+ && eval(unsafehtmlentity(source, context))?.[0].trimStart() === '':
108
108
  return false;
109
109
  }
110
110
  return true;