securemark 0.293.3 → 0.293.4

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.293.4
4
+
5
+ - Refactoring.
6
+
3
7
  ## 0.293.3
4
8
 
5
9
  - Refactoring.
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- /*! securemark v0.293.3 https://github.com/falsandtru/securemark | (c) 2017, falsandtru | UNLICENSED License */
1
+ /*! securemark v0.293.4 https://github.com/falsandtru/securemark | (c) 2017, falsandtru | UNLICENSED License */
2
2
  (function webpackUniversalModuleDefinition(root, factory) {
3
3
  if(typeof exports === 'object' && typeof module === 'object')
4
4
  module.exports = factory(require("Prism"), require("DOMPurify"));
@@ -6737,17 +6737,17 @@ const attrspecs = {
6737
6737
  };
6738
6738
  Object.setPrototypeOf(attrspecs, null);
6739
6739
  Object.values(attrspecs).forEach(o => Object.setPrototypeOf(o, null));
6740
- exports.html = (0, combinator_1.lazy)(() => (0, combinator_1.validate)(/<[a-z]+(?=[^\S\n]|>)/yi, (0, combinator_1.union)([(0, combinator_1.surround)(
6740
+ exports.html = (0, combinator_1.lazy)(() => (0, combinator_1.validate)(/<[a-z]+(?=[ >])/yi, (0, combinator_1.union)([(0, combinator_1.surround)(
6741
6741
  // https://html.spec.whatwg.org/multipage/syntax.html#void-elements
6742
- (0, source_1.str)(/<(?:area|base|br|col|embed|hr|img|input|link|meta|source|track|wbr)(?=[^\S\n]|>)/yi), (0, combinator_1.some)((0, combinator_1.union)([exports.attribute])), (0, combinator_1.open)((0, source_1.str)(/[^\S\n]*/y), (0, source_1.str)('>'), true), true, ([as, bs = [], cs], context) => [[elem(as[0].slice(1), false, (0, array_1.push)((0, array_1.unshift)(as, bs), cs), [], [], context)]], ([as, bs = []], context) => [[elem(as[0].slice(1), false, (0, array_1.unshift)(as, bs), [], [], context)]]), (0, combinator_1.match)(new RegExp(String.raw`<(${TAGS.join('|')})(?=[^\S\n]|>)`, 'y'), (0, memoize_1.memoize)(([, tag]) => (0, combinator_1.surround)((0, combinator_1.surround)((0, source_1.str)(`<${tag}`), (0, combinator_1.some)(exports.attribute), (0, combinator_1.open)((0, source_1.str)(/[^\S\n]*/y), (0, source_1.str)('>'), true), true, ([as, bs = [], cs]) => [(0, array_1.push)((0, array_1.unshift)(as, bs), cs)], ([as, bs = []]) => [(0, array_1.unshift)(as, bs)]),
6742
+ (0, source_1.str)(/<(?:area|base|br|col|embed|hr|img|input|link|meta|source|track|wbr)(?=[ >])/yi), (0, combinator_1.some)((0, combinator_1.union)([exports.attribute])), (0, combinator_1.open)((0, source_1.str)(/ ?/y), (0, source_1.str)('>'), true), true, ([as, bs = [], cs], context) => [[elem(as[0].slice(1), false, (0, array_1.push)((0, array_1.unshift)(as, bs), cs), [], [], context)]], ([as, bs = []], context) => [[elem(as[0].slice(1), false, (0, array_1.unshift)(as, bs), [], [], context)]]), (0, combinator_1.match)(new RegExp(String.raw`<(${TAGS.join('|')})(?=[^\S\n]|>)`, 'y'), (0, memoize_1.memoize)(([, tag]) => (0, combinator_1.surround)((0, combinator_1.surround)((0, source_1.str)(`<${tag}`), (0, combinator_1.some)(exports.attribute), (0, combinator_1.open)((0, source_1.str)(/ ?/y), (0, source_1.str)('>'), true), true, ([as, bs = [], cs]) => [(0, array_1.push)((0, array_1.unshift)(as, bs), cs)], ([as, bs = []]) => [(0, array_1.unshift)(as, bs)]),
6743
6743
  // 不可視のHTML構造が可視構造を変化させるべきでない。
6744
6744
  // 可視のHTMLは優先度変更を検討する。
6745
6745
  // このため<>は将来的に共通構造を変化させる可能性があり
6746
- // 共通構造を変更させない非構造文字列としては依然としてエスケープを要する。
6746
+ // 共通構造を変化させない非構造文字列としては依然としてエスケープを要する。
6747
6747
  (0, combinator_1.precedence)(0, (0, combinator_1.recursion)(4 /* Recursion.inline */, (0, combinator_1.some)((0, combinator_1.union)([(0, combinator_1.some)(inline_1.inline, (0, visibility_1.blankWith)('\n', `</${tag}>`)), (0, combinator_1.open)('\n', (0, combinator_1.some)(inline_1.inline, `</${tag}>`), true)])))), (0, source_1.str)(`</${tag}>`), true, ([as, bs = [], cs], context) => [[elem(tag, true, as, bs, cs, context)]], ([as, bs = []], context) => [[elem(tag, true, as, bs, [], context)]]), ([, tag]) => tag, new Map())), (0, combinator_1.surround)(
6748
6748
  // https://html.spec.whatwg.org/multipage/syntax.html#void-elements
6749
- (0, source_1.str)(/<[a-z]+(?=[^\S\n]|>)/yi), (0, combinator_1.some)((0, combinator_1.union)([exports.attribute])), (0, combinator_1.open)((0, source_1.str)(/[^\S\n]*/y), (0, source_1.str)('>'), true), true, ([as, bs = [], cs], context) => [[elem(as[0].slice(1), false, (0, array_1.push)((0, array_1.unshift)(as, bs), cs), [], [], context)]], ([as, bs = []], context) => [[elem(as[0].slice(1), false, (0, array_1.unshift)(as, bs), [], [], context)]])])));
6750
- exports.attribute = (0, combinator_1.union)([(0, source_1.str)(/[^\S\n]+[a-z]+(?:-[a-z]+)*(?:="(?:\\[^\n]|[^\\\n"])*")?(?=[^\S\n]|>)/yi), (0, source_1.str)(/[^\S\n]+[^\s<>]+/y)]);
6749
+ (0, source_1.str)(/<[a-z]+(?=[ >])/yi), (0, combinator_1.some)((0, combinator_1.union)([exports.attribute])), (0, combinator_1.open)((0, source_1.str)(/ ?/y), (0, source_1.str)('>'), true), true, ([as, bs = [], cs], context) => [[elem(as[0].slice(1), false, (0, array_1.push)((0, array_1.unshift)(as, bs), cs), [], [], context)]], ([as, bs = []], context) => [[elem(as[0].slice(1), false, (0, array_1.unshift)(as, bs), [], [], context)]])])));
6750
+ exports.attribute = (0, combinator_1.union)([(0, source_1.str)(/ [a-z]+(?:-[a-z]+)*(?:="(?:\\[^\n]|[^\\\n"])*")?(?=[ >])/yi), (0, source_1.str)(/ [^\s<>]+/y)]);
6751
6751
  function elem(tag, content, as, bs, cs, context) {
6752
6752
  if (!tags.includes(tag)) return ielem('tag', `Invalid HTML tag name "${tag}"`, context);
6753
6753
  if (content) {
@@ -6916,7 +6916,7 @@ Object.setPrototypeOf(optspec, null);
6916
6916
  exports.textlink = (0, combinator_1.lazy)(() => (0, combinator_1.constraint)(8 /* State.link */, (0, combinator_1.creation)(10, (0, combinator_1.precedence)(1, (0, combinator_1.state)(251 /* State.linkers */, (0, combinator_1.bind)((0, combinator_1.subsequence)([(0, combinator_1.dup)((0, combinator_1.surround)('[', (0, visibility_1.trimBlankStart)((0, combinator_1.some)((0, combinator_1.union)([inline_1.inline]), ']', [[']', 1]])), ']', true, ([, ns = []], context) => context.linebreak === 0 ? [(0, array_1.push)(ns, ["\u001F" /* Command.Separator */])] : undefined, undefined, [3 | 64 /* Backtrack.bracket */, 3 | 16 /* Backtrack.link */, 2 | 8 /* Backtrack.ruby */])),
6917
6917
  // `{ `と`{`で個別にバックトラックが発生し+1nされる。
6918
6918
  // 自己再帰的にパースしてもオプションの不要なパースによる計算量の増加により相殺される。
6919
- (0, combinator_1.dup)((0, combinator_1.surround)(/{(?![{}])/y, (0, combinator_1.inits)([exports.uri, (0, combinator_1.some)(exports.option)]), /[^\S\n]*}/y, false, undefined, ([as, bs], context) => {
6919
+ (0, combinator_1.dup)((0, combinator_1.surround)(/{(?![{}])/y, (0, combinator_1.inits)([exports.uri, (0, combinator_1.some)(exports.option)]), / ?}/y, false, undefined, ([as, bs], context) => {
6920
6920
  if (!bs) return;
6921
6921
  const head = context.position - context.range;
6922
6922
  (0, combinator_1.setBacktrack)(context, [2 | 16 /* Backtrack.link */], head);
@@ -6942,10 +6942,10 @@ exports.textlink = (0, combinator_1.lazy)(() => (0, combinator_1.constraint)(8 /
6942
6942
  if (content.length !== 0 && (0, visibility_1.trimBlankNodeEnd)(content).length === 0) return;
6943
6943
  return [[parse((0, dom_1.defrag)(content), params, context)]];
6944
6944
  }))))));
6945
- exports.medialink = (0, combinator_1.lazy)(() => (0, combinator_1.constraint)(8 /* State.link */ | 4 /* State.media */, (0, combinator_1.validate)(/[[{]/y, (0, combinator_1.creation)(10, (0, combinator_1.state)(251 /* State.linkers */, (0, combinator_1.bind)((0, combinator_1.reverse)((0, combinator_1.sequence)([(0, combinator_1.dup)((0, combinator_1.surround)('[', (0, combinator_1.union)([inline_1.media, inline_1.shortmedia]), ']')), (0, combinator_1.dup)((0, combinator_1.surround)(/{(?![{}])/y, (0, combinator_1.inits)([exports.uri, (0, combinator_1.some)(exports.option)]), /[^\S\n]*}/y))])), ([params, content = []], context) => [[parse((0, dom_1.defrag)(content), params, context)]]))))));
6946
- exports.unsafelink = (0, combinator_1.lazy)(() => (0, combinator_1.creation)(10, (0, combinator_1.bind)((0, combinator_1.reverse)((0, combinator_1.tails)([(0, combinator_1.dup)((0, combinator_1.surround)('[', (0, combinator_1.some)((0, combinator_1.union)([source_1.unescsource]), ']'), ']')), (0, combinator_1.dup)((0, combinator_1.surround)(/{(?![{}])/y, (0, combinator_1.inits)([exports.uri, (0, combinator_1.some)(exports.option)]), /[^\S\n]*}/y))])), ([params, content = []], context) => [[parse((0, dom_1.defrag)(content), params, context)]])));
6947
- exports.uri = (0, combinator_1.union)([(0, combinator_1.open)(/[^\S\n]+/y, (0, source_1.str)(/\S+/y)), (0, source_1.str)(/[^\s{}]+/y)]);
6948
- exports.option = (0, combinator_1.union)([(0, combinator_1.fmap)((0, source_1.str)(/[^\S\n]+nofollow(?=[^\S\n]|})/y), () => [` rel="nofollow"`]), (0, source_1.str)(/[^\S\n]+[a-z]+(?:-[a-z]+)*(?:="(?:\\[^\n]|[^\\\n"])*")?(?=[^\S\n]|})/yi), (0, source_1.str)(/[^\S\n]+[^\s{}]+/y)]);
6945
+ exports.medialink = (0, combinator_1.lazy)(() => (0, combinator_1.constraint)(8 /* State.link */ | 4 /* State.media */, (0, combinator_1.validate)(/[[{]/y, (0, combinator_1.creation)(10, (0, combinator_1.state)(251 /* State.linkers */, (0, combinator_1.bind)((0, combinator_1.reverse)((0, combinator_1.sequence)([(0, combinator_1.dup)((0, combinator_1.surround)('[', (0, combinator_1.union)([inline_1.media, inline_1.shortmedia]), ']')), (0, combinator_1.dup)((0, combinator_1.surround)(/{(?![{}])/y, (0, combinator_1.inits)([exports.uri, (0, combinator_1.some)(exports.option)]), / ?}/y))])), ([params, content = []], context) => [[parse((0, dom_1.defrag)(content), params, context)]]))))));
6946
+ exports.unsafelink = (0, combinator_1.lazy)(() => (0, combinator_1.creation)(10, (0, combinator_1.bind)((0, combinator_1.reverse)((0, combinator_1.tails)([(0, combinator_1.dup)((0, combinator_1.surround)('[', (0, combinator_1.some)((0, combinator_1.union)([source_1.unescsource]), ']'), ']')), (0, combinator_1.dup)((0, combinator_1.surround)(/{(?![{}])/y, (0, combinator_1.inits)([exports.uri, (0, combinator_1.some)(exports.option)]), / ?}/y))])), ([params, content = []], context) => [[parse((0, dom_1.defrag)(content), params, context)]])));
6947
+ exports.uri = (0, combinator_1.union)([(0, combinator_1.open)(/ /y, (0, source_1.str)(/\S+/y)), (0, source_1.str)(/[^\s{}]+/y)]);
6948
+ exports.option = (0, combinator_1.union)([(0, combinator_1.fmap)((0, source_1.str)(/ nofollow(?=[ }])/y), () => [` rel="nofollow"`]), (0, source_1.str)(/ [a-z]+(?:-[a-z]+)*(?:="(?:\\[^\n]|[^\\\n"])*")?(?=[ }])/yi), (0, source_1.str)(/ [^\s{}]+/y)]);
6949
6949
  function parse(content, params, context) {
6950
6950
  const INSECURE_URI = params.shift();
6951
6951
  let uri;
@@ -6966,11 +6966,11 @@ function elem(INSECURE_URI, content, uri, origin) {
6966
6966
  case 'http:':
6967
6967
  case 'https:':
6968
6968
  switch (true) {
6969
- case /[a-z][0-9]*:\/{0,2}\S/i.test((0, util_1.stringify)(content)):
6969
+ case /[0-9a-z]:\S/i.test((0, util_1.stringify)(content)):
6970
6970
  type = 'content';
6971
6971
  message = 'URI must not be contained';
6972
6972
  break;
6973
- case INSECURE_URI.slice(0, 2) === '^/' && /\/\.\.?(?:\/|$)/.test(INSECURE_URI.slice(0, INSECURE_URI.search(/[?#]|$/))):
6973
+ case INSECURE_URI.startsWith('^/') && /\/\.\.?(?:\/|$)/.test(INSECURE_URI.slice(0, INSECURE_URI.search(/[?#]|$/))):
6974
6974
  type = 'argument';
6975
6975
  message = 'Dot-segments cannot be used in subresource paths';
6976
6976
  break;
@@ -7007,13 +7007,13 @@ function elem(INSECURE_URI, content, uri, origin) {
7007
7007
  }
7008
7008
  function resolve(uri, host, source) {
7009
7009
  switch (true) {
7010
- case uri.slice(0, 2) === '^/':
7010
+ case uri.startsWith('^/'):
7011
7011
  const last = host.pathname.slice(host.pathname.lastIndexOf('/') + 1);
7012
7012
  return last.includes('.') // isFile
7013
7013
  // Exclude ISO 6709.
7014
7014
  && /^[0-9]*[a-z][0-9a-z]*$/i.test(last.slice(last.lastIndexOf('.') + 1)) ? `${host.pathname.slice(0, -last.length)}${uri.slice(2)}` : `${host.pathname.replace(/\/?$/, '/')}${uri.slice(2)}`;
7015
7015
  case host.origin === source.origin && host.pathname === source.pathname:
7016
- case uri.slice(0, 2) === '//':
7016
+ case uri.startsWith('//'):
7017
7017
  return uri;
7018
7018
  default:
7019
7019
  const target = new url_1.ReadonlyURL(uri, source.href);
@@ -7022,13 +7022,11 @@ function resolve(uri, host, source) {
7022
7022
  }
7023
7023
  exports.resolve = resolve;
7024
7024
  function decode(uri) {
7025
- const origin = uri.match(/[a-z](?:[-.](?=[0-9a-z])|[0-9a-z])*:(?:\/{0,2}[^/?#\s]+|\/\/(?=[/]))/yi)?.[0] ?? '';
7025
+ const head = /^[a-z]+(?:[.+-][0-9a-z]+)*:\/*[^/?#\s]+/i;
7026
+ const origin = uri.match(head)?.[0] ?? '';
7026
7027
  try {
7027
- let path = decodeURI(uri.slice(origin.length));
7028
- if (!origin && /[a-z](?:[-.](?=[0-9a-z])|[0-9a-z])*:\/{0,2}\S/yi.test(path)) {
7029
- path = uri.slice(origin.length);
7030
- }
7031
- uri = origin + path;
7028
+ const path = decodeURI(uri.slice(origin.length));
7029
+ uri = !origin && head.test(path) ? uri.slice(origin.length) : origin + path;
7032
7030
  } finally {
7033
7031
  return uri.replace(/\s+/g, encodeURI);
7034
7032
  }
@@ -7134,7 +7132,7 @@ const optspec = {
7134
7132
  rel: undefined
7135
7133
  };
7136
7134
  Object.setPrototypeOf(optspec, null);
7137
- exports.media = (0, combinator_1.lazy)(() => (0, combinator_1.constraint)(4 /* State.media */, (0, combinator_1.creation)(10, (0, combinator_1.open)('!', (0, combinator_1.bind)((0, combinator_1.verify)((0, combinator_1.fmap)((0, combinator_1.tails)([(0, combinator_1.dup)((0, combinator_1.surround)('[', (0, combinator_1.precedence)(1, (0, combinator_1.some)((0, combinator_1.union)([htmlentity_1.unsafehtmlentity, bracket, source_1.txt]), ']')), ']', true, ([, ns = []], context) => context.linebreak === 0 ? [ns] : undefined, undefined, [3 | 4 /* Backtrack.escbracket */])), (0, combinator_1.dup)((0, combinator_1.surround)(/{(?![{}])/y, (0, combinator_1.inits)([link_1.uri, (0, combinator_1.some)(option)]), /[^\S\n]*}/y, false, undefined, ([as, bs], context) => {
7135
+ exports.media = (0, combinator_1.lazy)(() => (0, combinator_1.constraint)(4 /* State.media */, (0, combinator_1.creation)(10, (0, combinator_1.open)('!', (0, combinator_1.bind)((0, combinator_1.verify)((0, combinator_1.fmap)((0, combinator_1.tails)([(0, combinator_1.dup)((0, combinator_1.surround)('[', (0, combinator_1.precedence)(1, (0, combinator_1.some)((0, combinator_1.union)([htmlentity_1.unsafehtmlentity, bracket, source_1.txt]), ']')), ']', true, ([, ns = []], context) => context.linebreak === 0 ? [ns] : undefined, undefined, [3 | 4 /* Backtrack.escbracket */])), (0, combinator_1.dup)((0, combinator_1.surround)(/{(?![{}])/y, (0, combinator_1.inits)([link_1.uri, (0, combinator_1.some)(option)]), / ?}/y, false, undefined, ([as, bs], context) => {
7138
7136
  if (!bs) return;
7139
7137
  const head = context.position - context.range;
7140
7138
  (0, combinator_1.setBacktrack)(context, [2 | 16 /* Backtrack.link */], head);
@@ -7183,7 +7181,7 @@ exports.media = (0, combinator_1.lazy)(() => (0, combinator_1.constraint)(4 /* S
7183
7181
  })((0, parser_1.subinput)(`{ ${INSECURE_URI}${linkparams.join('')} }`, context));
7184
7182
  })))));
7185
7183
  const bracket = (0, combinator_1.lazy)(() => (0, combinator_1.recursion)(6 /* Recursion.terminal */, (0, combinator_1.union)([(0, combinator_1.surround)((0, source_1.str)('('), (0, combinator_1.some)((0, combinator_1.union)([htmlentity_1.unsafehtmlentity, bracket, source_1.txt]), ')'), (0, source_1.str)(')'), true, undefined, () => [[]], [3 | 4 /* Backtrack.escbracket */]), (0, combinator_1.surround)((0, source_1.str)('['), (0, combinator_1.some)((0, combinator_1.union)([htmlentity_1.unsafehtmlentity, bracket, source_1.txt]), ']'), (0, source_1.str)(']'), true, undefined, () => [[]], [3 | 4 /* Backtrack.escbracket */]), (0, combinator_1.surround)((0, source_1.str)('{'), (0, combinator_1.some)((0, combinator_1.union)([htmlentity_1.unsafehtmlentity, bracket, source_1.txt]), '}'), (0, source_1.str)('}'), true, undefined, () => [[]], [3 | 4 /* Backtrack.escbracket */]), (0, combinator_1.surround)((0, source_1.str)('"'), (0, combinator_1.precedence)(2, (0, combinator_1.some)((0, combinator_1.union)([htmlentity_1.unsafehtmlentity, source_1.txt]), '"')), (0, source_1.str)('"'), true, undefined, () => [[]], [3 | 4 /* Backtrack.escbracket */])])));
7186
- const option = (0, combinator_1.lazy)(() => (0, combinator_1.union)([(0, combinator_1.surround)((0, combinator_1.open)(/[^\S\n]+/y, (0, source_1.str)(/[1-9][0-9]*/y)), (0, source_1.str)(/[x:]/y), (0, source_1.str)(/[1-9][0-9]*(?=[^\S\n]|})/y), false, ([[a], [b], [c]]) => [b === 'x' ? [`width="${a}"`, `height="${c}"`] : [`aspect-ratio="${a}/${c}"`]]), link_1.option]));
7184
+ const option = (0, combinator_1.lazy)(() => (0, combinator_1.union)([(0, combinator_1.surround)((0, combinator_1.open)(/ /y, (0, source_1.str)(/[1-9][0-9]*/y)), (0, source_1.str)(/[x:]/y), (0, source_1.str)(/[1-9][0-9]*(?=[ }])/y), false, ([[a], [b], [c]]) => [b === 'x' ? [`width="${a}"`, `height="${c}"`] : [`aspect-ratio="${a}/${c}"`]]), link_1.option]));
7187
7185
  function sanitize(target, uri) {
7188
7186
  let type;
7189
7187
  let message;
@@ -7994,7 +7992,7 @@ exports.escsource = void 0;
7994
7992
  const combinator_1 = __webpack_require__(3484);
7995
7993
  const text_1 = __webpack_require__(5655);
7996
7994
  const dom_1 = __webpack_require__(394);
7997
- const delimiter = /(?=[\\$"`\[\](){}\r\n]|\s(?:\$)|:\/\/)/g;
7995
+ const delimiter = /(?=[\\$"`\[\](){}\r\n]|\s\$|:\/\/)/g;
7998
7996
  const escsource = ({
7999
7997
  context
8000
7998
  }) => {
@@ -8030,9 +8028,7 @@ const escsource = ({
8030
8028
  return [[(0, dom_1.html)('br')]];
8031
8029
  default:
8032
8030
  if (context.sequential) return [[char]];
8033
- text_1.nonWhitespace.lastIndex = position + 1;
8034
- const b = (0, text_1.isBlank)(source, position);
8035
- let i = b ? source[position + 1] === '\n' ? position + 1 : text_1.nonWhitespace.test(source) ? text_1.nonWhitespace.lastIndex - 1 : source.length : (0, text_1.next)(source, position, delimiter);
8031
+ let i = (0, text_1.next)(source, position, delimiter);
8036
8032
  i -= position;
8037
8033
  (0, combinator_1.consume)(i - 1, context);
8038
8034
  context.position += i - 1;
@@ -8145,11 +8141,11 @@ exports.strs = strs;
8145
8141
  Object.defineProperty(exports, "__esModule", ({
8146
8142
  value: true
8147
8143
  }));
8148
- exports.isWhitespace = exports.isBlank = exports.backToEmailHead = exports.backToUrlHead = exports.next = exports.linebreak = exports.txt = exports.text = exports.nonWhitespace = void 0;
8144
+ exports.backToEmailHead = exports.backToUrlHead = exports.backToWhitespace = exports.next = exports.canSkip = exports.linebreak = exports.txt = exports.text = exports.nonWhitespace = void 0;
8149
8145
  const combinator_1 = __webpack_require__(3484);
8150
8146
  const dom_1 = __webpack_require__(394);
8151
8147
  //const delimiter = /(?=[\\!@#$&"`\[\](){}<>()[]{}*%|\r\n]|([+~=])\1|\/{3}|\s(?:\\?(?:$|\s)|[$%])|:\/\/)/g;
8152
- exports.nonWhitespace = /[\S\r\n]/g;
8148
+ exports.nonWhitespace = /[^ \t ]/g;
8153
8149
  const text = input => {
8154
8150
  const {
8155
8151
  context
@@ -8184,22 +8180,39 @@ const text = input => {
8184
8180
  default:
8185
8181
  if (context.sequential) return [[char]];
8186
8182
  exports.nonWhitespace.lastIndex = position + 1;
8187
- const b = isBlank(source, position);
8188
- let i = b ? source[position + 1] === '\n' ? position + 1 : exports.nonWhitespace.test(source) ? exports.nonWhitespace.lastIndex - 1 : source.length : next(source, position);
8189
- const lineend = false || b && i === source.length || b && source[i] === '\n' || b && source[i] === '\\' && source[i + 1] === '\n';
8183
+ const s = canSkip(source, position);
8184
+ let i = s ? exports.nonWhitespace.test(source) ? exports.nonWhitespace.lastIndex - 1 : source.length : next(source, position);
8185
+ const lineend = false || s && i === source.length || s && source[i] === '\n';
8190
8186
  i -= position;
8191
- i = lineend ? i : i - +b || 1;
8187
+ i = lineend ? i : i - +s || 1;
8192
8188
  (0, combinator_1.consume)(i - 1, context);
8193
8189
  context.position += i - 1;
8194
8190
  const linestart = position === 0 || source[position - 1] === '\n';
8195
- i = linestart && b && i >= 3 ? i - 3 : 0;
8196
- i += position;
8197
- return i === context.position || b && !linestart || lineend ? [[]] : [[source.slice(i, context.position)]];
8191
+ return position === context.position || s && !linestart || lineend ? [[]] : [[source.slice(position, context.position)]];
8198
8192
  }
8199
8193
  };
8200
8194
  exports.text = text;
8201
8195
  exports.txt = (0, combinator_1.union)([exports.text]);
8202
8196
  exports.linebreak = (0, combinator_1.focus)(/[\r\n]/y, (0, combinator_1.union)([exports.text]));
8197
+ function canSkip(source, position) {
8198
+ if (!isWhitespace(source[position], false)) return false;
8199
+ if (position + 1 === source.length) return true;
8200
+ return isWhitespace(source[position + 1], true);
8201
+ }
8202
+ exports.canSkip = canSkip;
8203
+ function isWhitespace(char, linebreak) {
8204
+ switch (char) {
8205
+ case ' ':
8206
+ case '\t':
8207
+ case ' ':
8208
+ return true;
8209
+ case '\r':
8210
+ case '\n':
8211
+ return linebreak;
8212
+ default:
8213
+ return false;
8214
+ }
8215
+ }
8203
8216
  function next(source, position, delimiter) {
8204
8217
  let index;
8205
8218
  if (delimiter) {
@@ -8212,8 +8225,20 @@ function next(source, position, delimiter) {
8212
8225
  if (index === 0) return source.length;
8213
8226
  const char = source[index];
8214
8227
  switch (char) {
8228
+ case '$':
8229
+ case '%':
8230
+ case '*':
8231
+ case '+':
8232
+ case '~':
8233
+ case '=':
8234
+ case '/':
8235
+ index = backToWhitespace(source, position, index);
8236
+ break;
8237
+ case '[':
8238
+ index = source[index + 1] === '|' ? backToWhitespace(source, position, index) : index;
8239
+ break;
8215
8240
  case ':':
8216
- index = backToUrlHead(source, position, index);
8241
+ index = source.startsWith('//', index + 1) ? backToUrlHead(source, position, index) : index;
8217
8242
  break;
8218
8243
  case '@':
8219
8244
  index = backToEmailHead(source, position, index);
@@ -8222,6 +8247,11 @@ function next(source, position, delimiter) {
8222
8247
  return index;
8223
8248
  }
8224
8249
  exports.next = next;
8250
+ function backToWhitespace(source, position, index) {
8251
+ const prev = index - 1;
8252
+ return prev > position && /\s/.test(source[prev]) ? prev : index;
8253
+ }
8254
+ exports.backToWhitespace = backToWhitespace;
8225
8255
  function backToUrlHead(source, position, index) {
8226
8256
  const delim = index;
8227
8257
  let state = false;
@@ -8317,7 +8347,6 @@ function isAlphanumeric(char) {
8317
8347
  // this[c.charCodeAt(0)] = undefined);
8318
8348
  // }
8319
8349
  //};
8320
- const delimiter = /\s(?:\\?(?:$|\s)|[$%])/y;
8321
8350
  function seek(source, position) {
8322
8351
  for (let i = position + 1; i < source.length; ++i) {
8323
8352
  const fst = source[i];
@@ -8346,7 +8375,6 @@ function seek(source, position) {
8346
8375
  case '{':
8347
8376
  case '}':
8348
8377
  case '*':
8349
- case '%':
8350
8378
  case '|':
8351
8379
  case '\r':
8352
8380
  case '\n':
@@ -8359,77 +8387,41 @@ function seek(source, position) {
8359
8387
  case '/':
8360
8388
  if (source[i + 1] === fst && source[i + 2] === fst) return i;
8361
8389
  continue;
8390
+ case '%':
8391
+ if (source[i + 1] === ']') return i;
8392
+ continue;
8362
8393
  case ':':
8363
8394
  if (source[i + 1] === '/' && source[i + 2] === '/') return i;
8364
8395
  continue;
8365
- //case ' ':
8366
- //case '\t':
8367
- //case ' ':
8368
- // if (i + 1 === source.length) return i;
8369
- // switch (source[i + 1]) {
8370
- // case ' ':
8371
- // case '\t':
8372
- // case '\r':
8373
- // case '\n':
8374
- // case ' ':
8375
- // case '$':
8376
- // case '%':
8377
- // return i;
8378
- // case '\\':
8379
- // if (i + 2 === source.length) return i;
8380
- // switch (source[i + 2]) {
8381
- // case ' ':
8382
- // case '\t':
8383
- // case '\r':
8384
- // case '\n':
8385
- // case ' ':
8386
- // return i;
8387
- // }
8388
- // }
8389
- // continue;
8396
+ case ' ':
8397
+ case '\t':
8398
+ case ' ':
8399
+ if (i + 1 === source.length) return i;
8400
+ switch (source[i + 1]) {
8401
+ case ' ':
8402
+ case '\t':
8403
+ case '\r':
8404
+ case '\n':
8405
+ case ' ':
8406
+ return i;
8407
+ case '\\':
8408
+ if (i + 2 === source.length) return i;
8409
+ switch (source[i + 2]) {
8410
+ case ' ':
8411
+ case '\t':
8412
+ case '\r':
8413
+ case '\n':
8414
+ case ' ':
8415
+ return i;
8416
+ }
8417
+ }
8418
+ continue;
8390
8419
  default:
8391
- delimiter.lastIndex = i;
8392
- if (delimiter.test(source)) return i;
8393
8420
  continue;
8394
8421
  }
8395
8422
  }
8396
8423
  return source.length;
8397
8424
  }
8398
- const blank = /\s(?:$|\s|\\\n)/y;
8399
- function isBlank(source, position) {
8400
- blank.lastIndex = position;
8401
- return blank.test(source);
8402
- // removed by dead control flow
8403
-
8404
- // removed by dead control flow
8405
-
8406
- // removed by dead control flow
8407
-
8408
- // removed by dead control flow
8409
-
8410
- // removed by dead control flow
8411
-
8412
- // removed by dead control flow
8413
-
8414
- // removed by dead control flow
8415
-
8416
- }
8417
- exports.isBlank = isBlank;
8418
- const whitespace = /\s/;
8419
- function isWhitespace(char) {
8420
- whitespace;
8421
- switch (char) {
8422
- case ' ':
8423
- case '\t':
8424
- case '\r':
8425
- case '\n':
8426
- case ' ':
8427
- return true;
8428
- default:
8429
- return false;
8430
- }
8431
- }
8432
- exports.isWhitespace = isWhitespace;
8433
8425
 
8434
8426
  /***/ },
8435
8427
 
@@ -8472,8 +8464,7 @@ const unescsource = ({
8472
8464
  default:
8473
8465
  if (context.sequential) return [[char]];
8474
8466
  text_1.nonWhitespace.lastIndex = position + 1;
8475
- const b = (0, text_1.isBlank)(source, position);
8476
- let i = b ? source[position + 1] === '\n' ? position + 1 : text_1.nonWhitespace.test(source) ? text_1.nonWhitespace.lastIndex - 1 : source.length : (0, text_1.next)(source, position, exports.delimiter);
8467
+ let i = (0, text_1.canSkip)(source, position) ? text_1.nonWhitespace.test(source) ? text_1.nonWhitespace.lastIndex - 1 : source.length : (0, text_1.next)(source, position, exports.delimiter);
8477
8468
  i -= position;
8478
8469
  (0, combinator_1.consume)(i - 1, context);
8479
8470
  context.position += i - 1;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securemark",
3
- "version": "0.293.3",
3
+ "version": "0.293.4",
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,5 +1,4 @@
1
1
  import { Delimiters } from './parser/context/delimiter';
2
- import { MarkdownParser } from '../../../markdown';
3
2
 
4
3
  export type Parser<N, C extends CtxOptions = CtxOptions, D extends Parser<unknown, C>[] = any>
5
4
  = (input: Input<C & Ctx>) => Result<N, C, D>;
@@ -38,8 +37,6 @@ export type IntermediateParser<P extends Parser<unknown>> = Parser<SubNode<P>, C
38
37
  type ExtractSubNode<D extends Parser<unknown>[]> = ExtractSubParser<D> extends infer N ? N extends Parser<infer U> ? U : never : never;
39
38
  type ExtractSubParser<D extends Parser<unknown>[]> = D extends (infer P)[] ? P extends Parser<unknown> ? P : never : never;
40
39
 
41
- export function input(source: string, context: CtxOptions): Input<Ctx>;
42
- export function input(source: string, context: MarkdownParser.Options): Input<MarkdownParser.Context>;
43
40
  export function input(source: string, context: CtxOptions): Input<Ctx> {
44
41
  // @ts-expect-error
45
42
  context.source = source;
@@ -78,7 +78,7 @@ export function bind(target: DocumentFragment | HTMLElement | ShadowRoot, settin
78
78
  for (; index < sourceSegments.length - last; ++index) {
79
79
  assert(rev === revision);
80
80
  const seg = sourceSegments[index];
81
- const es = eval(header(input(seg, { header: index === 0 })) || block(input(seg, context)), []);
81
+ const es = eval(header(input(seg, { header: index === 0 } as MarkdownParser.Context)) || block(input(seg, context)), []);
82
82
  blocks.splice(index, 0, [seg, es, url]);
83
83
  if (es.length === 0) continue;
84
84
  // All deletion processes always run after all addition processes have done.
@@ -34,7 +34,7 @@ export function parse(source: string, opts: Options = {}, context?: MarkdownPars
34
34
  const node = frag();
35
35
  let index = 0;
36
36
  for (const seg of segment(source)) {
37
- node.append(...eval(header(input(seg, { header: index++ === 0 })) || block(input(seg, context)), []));
37
+ node.append(...eval(header(input(seg, { header: index++ === 0 } as MarkdownParser.Context)) || block(input(seg, context)), []));
38
38
  }
39
39
  assert(opts.id !== '' || !node.querySelector('[id], .index[href], .label[href], .annotation > a[href], .reference > a[href]'));
40
40
  if (opts.test) return node;
@@ -69,7 +69,7 @@ describe('Unit: parser/block/dlist', () => {
69
69
  it('indexer', () => {
70
70
  assert.deepStrictEqual(inspect(parser('~ a [|b]'), ctx), [['<dl><dt id="index::b">a<span class="indexer" data-index="b"></span></dt><dd></dd></dl>'], '']);
71
71
  assert.deepStrictEqual(inspect(parser('~ a [|b]\\'), ctx), [['<dl><dt id="index::a_[|b]">a <span class="invalid">[|b]</span></dt><dd></dd></dl>'], '']);
72
- assert.deepStrictEqual(inspect(parser('~ A'), ctx), [['<dl><dt id="index::A">A</dt><dd></dd></dl>'], '']);
72
+ assert.deepStrictEqual(inspect(parser('~ - [|b]'), ctx), [['<dl><dt id="index::b">-<span class="indexer" data-index="b"></span></dt><dd></dd></dl>'], '']);
73
73
  assert.deepStrictEqual(inspect(parser('~ *A*'), ctx), [['<dl><dt id="index::A"><em>A</em></dt><dd></dd></dl>'], '']);
74
74
  assert.deepStrictEqual(inspect(parser('~ `A`'), ctx), [['<dl><dt id="index::`A`"><code data-src="`A`">A</code></dt><dd></dd></dl>'], '']);
75
75
  assert.deepStrictEqual(inspect(parser('~ ${A}$'), ctx), [['<dl><dt id="index::${A}$"><span class="math" translate="no" data-src="${A}$">${A}$</span></dt><dd></dd></dl>'], '']);
@@ -79,6 +79,7 @@ describe('Unit: parser/block/heading', () => {
79
79
  assert.deepStrictEqual(inspect(parser('# a [|b] [|c]'), ctx), [['<h1 id="index::c">a <span class="invalid">[|b]</span><span class="indexer" data-index="c"></span></h1>'], '']);
80
80
  assert.deepStrictEqual(inspect(parser('# a [|b] \n'), ctx), [['<h1 id="index::b">a<span class="indexer" data-index="b"></span></h1>'], '']);
81
81
  assert.deepStrictEqual(inspect(parser('# a \\[|b]'), ctx), [['<h1 id="index::a_[|b]">a [|b]</h1>'], '']);
82
+ assert.deepStrictEqual(inspect(parser('# - [|b]'), ctx), [['<h1 id="index::b">-<span class="indexer" data-index="b"></span></h1>'], '']);
82
83
  assert.deepStrictEqual(inspect(parser('## a [|b] [|c]'), ctx), [['<h2 id="index::c">a <span class="invalid">[|b]</span><span class="indexer" data-index="c"></span></h2>'], '']);
83
84
  });
84
85
 
@@ -138,6 +138,7 @@ describe('Unit: parser/block/olist', () => {
138
138
  assert.deepStrictEqual(inspect(parser('1. [|a]'), ctx), [['<ol><li id="index::[|a]"><span class="invalid">[|a]</span></li></ol>'], '']);
139
139
  assert.deepStrictEqual(inspect(parser('1. a [|]'), ctx), [['<ol><li>a<span class="indexer" data-index=""></span></li></ol>'], '']);
140
140
  assert.deepStrictEqual(inspect(parser('1. a [|b]'), ctx), [['<ol><li id="index::b">a<span class="indexer" data-index="b"></span></li></ol>'], '']);
141
+ assert.deepStrictEqual(inspect(parser('1. - [|]'), ctx), [['<ol><li>-<span class="indexer" data-index=""></span></li></ol>'], '']);
141
142
  assert.deepStrictEqual(inspect(parser('1. [ ] [|a]'), ctx), [['<ol class="checklist"><li id="index::[|a]"><span class="checkbox">☐</span><span class="invalid">[|a]</span></li></ol>'], '']);
142
143
  assert.deepStrictEqual(inspect(parser('1. [ ] a [|b]'), ctx), [['<ol class="checklist"><li id="index::b"><span class="checkbox">☐</span>a<span class="indexer" data-index="b"></span></li></ol>'], '']);
143
144
  assert.deepStrictEqual(inspect(parser('1. a [|]\n 1. c [|d]'), ctx), [['<ol><li>a<span class="indexer" data-index=""></span><ol><li id="index::d">c<span class="indexer" data-index="d"></span></li></ol></li></ol>'], '']);
@@ -75,6 +75,7 @@ describe('Unit: parser/block/ulist', () => {
75
75
  assert.deepStrictEqual(inspect(parser('- [|a]'), ctx), [['<ul><li id="index::[|a]"><span class="invalid">[|a]</span></li></ul>'], '']);
76
76
  assert.deepStrictEqual(inspect(parser('- a [|]'), ctx), [['<ul><li>a<span class="indexer" data-index=""></span></li></ul>'], '']);
77
77
  assert.deepStrictEqual(inspect(parser('- a [|b]'), ctx), [['<ul><li id="index::b">a<span class="indexer" data-index="b"></span></li></ul>'], '']);
78
+ assert.deepStrictEqual(inspect(parser('- - [|b]'), ctx), [['<ul><li id="index::b">-<span class="indexer" data-index="b"></span></li></ul>'], '']);
78
79
  assert.deepStrictEqual(inspect(parser('- [ ] [|a]'), ctx), [['<ul class="checklist"><li id="index::[|a]"><span class="checkbox">☐</span><span class="invalid">[|a]</span></li></ul>'], '']);
79
80
  assert.deepStrictEqual(inspect(parser('- [ ] a [|b]'), ctx), [['<ul class="checklist"><li id="index::b"><span class="checkbox">☐</span>a<span class="indexer" data-index="b"></span></li></ul>'], '']);
80
81
  assert.deepStrictEqual(inspect(parser('- a [|]\n - c [|d]'), ctx), [['<ul><li>a<span class="indexer" data-index=""></span><ul><li id="index::d">c<span class="indexer" data-index="d"></span></li></ul></li></ul>'], '']);
@@ -40,6 +40,7 @@ describe('Unit: parser/inline/emphasis', () => {
40
40
 
41
41
  it('nest', () => {
42
42
  assert.deepStrictEqual(inspect(parser('*a *b**'), ctx), [['<em>a <em>b</em></em>'], '']);
43
+ assert.deepStrictEqual(inspect(parser('*- *b**'), ctx), [['<em>- <em>b</em></em>'], '']);
43
44
  assert.deepStrictEqual(inspect(parser('*a **b***'), ctx), [['<em>a <strong>b</strong></em>'], '']);
44
45
  assert.deepStrictEqual(inspect(parser('*a\\ *b**'), ctx), [['<em>a <em>b</em></em>'], '']);
45
46
  assert.deepStrictEqual(inspect(parser('*a&Tab;*b**'), ctx), [['<em>a\t<em>b</em></em>'], '']);
@@ -67,7 +67,7 @@ describe('Unit: parser/inline/html', () => {
67
67
  assert.deepStrictEqual(inspect(parser('<wbr >'), ctx), [['<wbr>'], '']);
68
68
  assert.deepStrictEqual(inspect(parser('<wbr>a'), ctx), [['<wbr>'], 'a']);
69
69
  assert.deepStrictEqual(inspect(parser('<bdi >a</bdi>'), ctx), [['<bdi>a</bdi>'], '']);
70
- assert.deepStrictEqual(inspect(parser('<bdi >a</bdi>'), ctx), [['<bdi>a</bdi>'], '']);
70
+ assert.deepStrictEqual(inspect(parser('<bdi >a</bdi>'), ctx), [['<span class="invalid">&lt;bdi &gt;a&lt;/bdi&gt;</span>'], '']);
71
71
  assert.deepStrictEqual(inspect(parser('<bdi> a</bdi>'), ctx), [['<bdi> a</bdi>'], '']);
72
72
  assert.deepStrictEqual(inspect(parser('<bdi> a </bdi>'), ctx), [['<bdi> a </bdi>'], '']);
73
73
  assert.deepStrictEqual(inspect(parser('<bdi> a </bdi>'), ctx), [['<bdi> a </bdi>'], '']);
@@ -122,8 +122,8 @@ describe('Unit: parser/inline/html', () => {
122
122
  assert.deepStrictEqual(inspect(parser('<bdo diR="rtl">a</bdo>'), ctx), [['<span class="invalid">&lt;bdo diR="rtl"&gt;a&lt;/bdo&gt;</span>'], '']);
123
123
  assert.deepStrictEqual(inspect(parser('<bdo dir="rtl">a</bdo>'), ctx), [['<bdo dir="rtl">a</bdo>'], '']);
124
124
  assert.deepStrictEqual(inspect(parser('<bdo dir="rtl" >a</bdo>'), ctx), [['<bdo dir="rtl">a</bdo>'], '']);
125
- assert.deepStrictEqual(inspect(parser('<bdo dir="rtl" >a</bdo>'), ctx), [['<bdo dir="rtl">a</bdo>'], '']);
126
- assert.deepStrictEqual(inspect(parser('<bdo dir="rtl">a</bdo>'), ctx), [['<bdo dir="rtl">a</bdo>'], '']);
125
+ assert.deepStrictEqual(inspect(parser('<bdo dir="rtl" >a</bdo>'), ctx), [['<span class="invalid">&lt;bdo dir="rtl" &gt;a&lt;/bdo&gt;</span>'], '']);
126
+ assert.deepStrictEqual(inspect(parser('<bdo dir="rtl">a</bdo>'), ctx), [['<span class="invalid">&lt;bdo dir="rtl"&gt;a&lt;/bdo&gt;</span>'], '']);
127
127
  assert.deepStrictEqual(inspect(parser('<wbr\n>'), ctx), undefined);
128
128
  assert.deepStrictEqual(inspect(parser('<wbr \n>'), ctx), [['<span class="invalid">&lt;wbr </span>'], '\n>']);
129
129
  assert.deepStrictEqual(inspect(parser('<wbr constructor>'), ctx), [['<span class="invalid">&lt;wbr constructor&gt;</span>'], '']);
@@ -20,13 +20,13 @@ const attrspecs = {
20
20
  Object.setPrototypeOf(attrspecs, null);
21
21
  Object.values(attrspecs).forEach(o => Object.setPrototypeOf(o, null));
22
22
 
23
- export const html: HTMLParser = lazy(() => validate(/<[a-z]+(?=[^\S\n]|>)/yi,
23
+ export const html: HTMLParser = lazy(() => validate(/<[a-z]+(?=[ >])/yi,
24
24
  union([
25
25
  surround(
26
26
  // https://html.spec.whatwg.org/multipage/syntax.html#void-elements
27
- str(/<(?:area|base|br|col|embed|hr|img|input|link|meta|source|track|wbr)(?=[^\S\n]|>)/yi),
27
+ str(/<(?:area|base|br|col|embed|hr|img|input|link|meta|source|track|wbr)(?=[ >])/yi),
28
28
  some(union([attribute])),
29
- open(str(/[^\S\n]*/y), str('>'), true),
29
+ open(str(/ ?/y), str('>'), true),
30
30
  true,
31
31
  ([as, bs = [], cs], context) =>
32
32
  [[elem(as[0].slice(1), false, push(unshift(as, bs), cs), [], [], context)]],
@@ -38,14 +38,14 @@ export const html: HTMLParser = lazy(() => validate(/<[a-z]+(?=[^\S\n]|>)/yi,
38
38
  ([, tag]) =>
39
39
  surround<HTMLParser.TagParser, string>(
40
40
  surround(
41
- str(`<${tag}`), some(attribute), open(str(/[^\S\n]*/y), str('>'), true),
41
+ str(`<${tag}`), some(attribute), open(str(/ ?/y), str('>'), true),
42
42
  true,
43
43
  ([as, bs = [], cs]) => [push(unshift(as, bs), cs)],
44
44
  ([as, bs = []]) => [unshift(as, bs)]),
45
45
  // 不可視のHTML構造が可視構造を変化させるべきでない。
46
46
  // 可視のHTMLは優先度変更を検討する。
47
47
  // このため<>は将来的に共通構造を変化させる可能性があり
48
- // 共通構造を変更させない非構造文字列としては依然としてエスケープを要する。
48
+ // 共通構造を変化させない非構造文字列としては依然としてエスケープを要する。
49
49
  precedence(0, recursion(Recursion.inline,
50
50
  some(union([
51
51
  some(inline, blankWith('\n', `</${tag}>`)),
@@ -61,9 +61,9 @@ export const html: HTMLParser = lazy(() => validate(/<[a-z]+(?=[^\S\n]|>)/yi,
61
61
  new Map())),
62
62
  surround(
63
63
  // https://html.spec.whatwg.org/multipage/syntax.html#void-elements
64
- str(/<[a-z]+(?=[^\S\n]|>)/yi),
64
+ str(/<[a-z]+(?=[ >])/yi),
65
65
  some(union([attribute])),
66
- open(str(/[^\S\n]*/y), str('>'), true),
66
+ open(str(/ ?/y), str('>'), true),
67
67
  true,
68
68
  ([as, bs = [], cs], context) =>
69
69
  [[elem(as[0].slice(1), false, push(unshift(as, bs), cs), [], [], context)]],
@@ -72,8 +72,8 @@ export const html: HTMLParser = lazy(() => validate(/<[a-z]+(?=[^\S\n]|>)/yi,
72
72
  ])));
73
73
 
74
74
  export const attribute: HTMLParser.AttributeParser = union([
75
- str(/[^\S\n]+[a-z]+(?:-[a-z]+)*(?:="(?:\\[^\n]|[^\\\n"])*")?(?=[^\S\n]|>)/yi),
76
- str(/[^\S\n]+[^\s<>]+/y),
75
+ str(/ [a-z]+(?:-[a-z]+)*(?:="(?:\\[^\n]|[^\\\n"])*")?(?=[ >])/yi),
76
+ str(/ [^\s<>]+/y),
77
77
  ]);
78
78
 
79
79
  function elem(tag: string, content: boolean, as: string[], bs: (HTMLElement | string)[], cs: string[], context: Ctx): HTMLElement {
@@ -58,6 +58,7 @@ describe('Unit: parser/inline/italic', () => {
58
58
  assert.deepStrictEqual(inspect(parser('//////a//////'), ctx), [['<i><i>a</i></i>'], '']);
59
59
  assert.deepStrictEqual(inspect(parser('//////a///b///'), ctx), [['<i><i>a</i>b</i>'], '']);
60
60
  assert.deepStrictEqual(inspect(parser('///a ///b//////'), ctx), [['<i>a <i>b</i></i>'], '']);
61
+ assert.deepStrictEqual(inspect(parser('///- ///b//////'), ctx), [['<i>- <i>b</i></i>'], '']);
61
62
  assert.deepStrictEqual(inspect(parser('///a\\ ///b//////'), ctx), [['<i>a <i>b</i></i>'], '']);
62
63
  assert.deepStrictEqual(inspect(parser('///a //////b/////////'), ctx), [['<i>a <i><i>b</i></i></i>'], '']);
63
64
  assert.deepStrictEqual(inspect(parser('///a ///b///c///'), ctx), [['<i>a <i>b</i>c</i>'], '']);
@@ -63,8 +63,8 @@ describe('Unit: parser/inline/link', () => {
63
63
  assert.deepStrictEqual(inspect(parser('[]'), ctx), undefined);
64
64
  assert.deepStrictEqual(inspect(parser('[]{}'), ctx), undefined);
65
65
  assert.deepStrictEqual(inspect(parser('[]{ }'), ctx), [['<span class="invalid">[]{ }</span>'], '']);
66
- assert.deepStrictEqual(inspect(parser('[]{ }'), ctx), [['<span class="invalid">[]{ }</span>'], '']);
67
- assert.deepStrictEqual(inspect(parser('[]{ }'), ctx), [['<span class="invalid">[]{ }</span>'], '']);
66
+ assert.deepStrictEqual(inspect(parser('[]{ }'), ctx), undefined);
67
+ assert.deepStrictEqual(inspect(parser('[]{ }'), ctx), undefined);
68
68
  assert.deepStrictEqual(inspect(parser('[]{{}'), ctx), undefined);
69
69
  assert.deepStrictEqual(inspect(parser('[]{}}'), ctx), undefined);
70
70
  assert.deepStrictEqual(inspect(parser('[]{{}}'), ctx), undefined);
@@ -105,12 +105,11 @@ describe('Unit: parser/inline/link', () => {
105
105
  it('basic', () => {
106
106
  assert.deepStrictEqual(inspect(parser('[]{b}'), ctx), [['<a class="url" href="b">b</a>'], '']);
107
107
  assert.deepStrictEqual(inspect(parser('[]{b }'), ctx), [['<a class="url" href="b">b</a>'], '']);
108
- assert.deepStrictEqual(inspect(parser('[]{b }'), ctx), [['<a class="url" href="b">b</a>'], '']);
108
+ assert.deepStrictEqual(inspect(parser('[]{b }'), ctx), [['<span class="invalid">[]{b</span>'], ' }']);
109
109
  assert.deepStrictEqual(inspect(parser('[]{ b }'), ctx), [['<a class="url" href="b">b</a>'], '']);
110
- assert.deepStrictEqual(inspect(parser('[]{ b }'), ctx), [['<a class="url" href="b">b</a>'], '']);
111
- assert.deepStrictEqual(inspect(parser('[]{ b }'), ctx), [['<a class="url" href="b">b</a>'], '']);
112
- assert.deepStrictEqual(inspect(parser('[]{ b }'), ctx), [['<a class="url" href="b">b</a>'], '']);
113
- assert.deepStrictEqual(inspect(parser('[]{ b }'), ctx), [['<a class="url" href="b">b</a>'], '']);
110
+ assert.deepStrictEqual(inspect(parser('[]{ b }'), ctx), [['<span class="invalid">[]{ b</span>'], ' }']);
111
+ assert.deepStrictEqual(inspect(parser('[]{ b }'), ctx), undefined);
112
+ assert.deepStrictEqual(inspect(parser('[]{ b }'), ctx), undefined);
114
113
  assert.deepStrictEqual(inspect(parser('[]{"}'), ctx), [[`<a class="url" href="&quot;">"</a>`], '']);
115
114
  assert.deepStrictEqual(inspect(parser('[]{"}"}'), ctx), [[`<a class="url" href="&quot;">"</a>`], '"}']);
116
115
  assert.deepStrictEqual(inspect(parser('[]{\\}'), ctx), [[`<a class="url" href="\\">\\</a>`], '']);
@@ -209,7 +208,6 @@ describe('Unit: parser/inline/link', () => {
209
208
  assert.deepStrictEqual(inspect(parser('[]{/ aspect-ratio="4/3"}'), ctx), [['<a class="invalid" href="/">/</a>'], '']);
210
209
  assert.deepStrictEqual(inspect(parser('[]{/ 4:3}'), ctx), [['<a class="invalid" href="/">/</a>'], '']);
211
210
  assert.deepStrictEqual(inspect(parser('[]{/ =}'), ctx), [['<a class="invalid" href="/">/</a>'], '']);
212
- assert.deepStrictEqual(inspect(parser('[]{/ name}'), ctx), [['<a class="invalid" href="/">/</a>'], '']);
213
211
  assert.deepStrictEqual(inspect(parser('[]{/\nname}'), ctx), [['<span class="invalid">[]{/</span>'], '\nname}']);
214
212
  });
215
213
 
@@ -221,8 +219,12 @@ describe('Unit: parser/inline/link', () => {
221
219
  assert.deepStrictEqual(inspect(parser('[]{/ nofollow nofollow}'), ctx), [['<a class="invalid" href="/" rel="nofollow">/</a>'], '']);
222
220
  assert.deepStrictEqual(inspect(parser('[]{/ nofollow}'), ctx), [['<a class="url" href="/" rel="nofollow">/</a>'], '']);
223
221
  assert.deepStrictEqual(inspect(parser('[]{/ nofollow }'), ctx), [['<a class="url" href="/" rel="nofollow">/</a>'], '']);
222
+ assert.deepStrictEqual(inspect(parser('[]{/ nofollow }'), ctx), [['<span class="invalid">[]{/ nofollow</span>'], ' }']);
223
+ assert.deepStrictEqual(inspect(parser('[]{/ nofollow}'), ctx), [['<span class="invalid">[]{/</span>'], ' nofollow}']);
224
224
  assert.deepStrictEqual(inspect(parser('[]{ / nofollow}'), ctx), [['<a class="url" href="/" rel="nofollow">/</a>'], '']);
225
225
  assert.deepStrictEqual(inspect(parser('[]{ / nofollow }'), ctx), [['<a class="url" href="/" rel="nofollow">/</a>'], '']);
226
+ assert.deepStrictEqual(inspect(parser('[]{ / nofollow }'), ctx), [['<span class="invalid">[]{ / nofollow</span>'], ' }']);
227
+ assert.deepStrictEqual(inspect(parser('[]{ / nofollow}'), ctx), [['<span class="invalid">[]{ /</span>'], ' nofollow}']);
226
228
  assert.deepStrictEqual(inspect(parser('[]{http://host nofollow}'), ctx), [['<a class="url" href="http://host" target="_blank" rel="nofollow">http://host</a>'], '']);
227
229
  assert.deepStrictEqual(inspect(parser('[!http://host]{http://host nofollow}'), ctx), [['<a class="link" href="http://host" target="_blank" rel="nofollow"><img class="media" data-src="http://host" alt="http://host"></a>'], '']);
228
230
  });
@@ -35,7 +35,7 @@ export const textlink: LinkParser.TextLinkParser = lazy(() => constraint(State.l
35
35
  dup(surround(
36
36
  /{(?![{}])/y,
37
37
  inits([uri, some(option)]),
38
- /[^\S\n]*}/y,
38
+ / ?}/y,
39
39
  false,
40
40
  undefined,
41
41
  ([as, bs], context) => {
@@ -82,7 +82,7 @@ export const medialink: LinkParser.MediaLinkParser = lazy(() => constraint(State
82
82
  '[',
83
83
  union([media, shortmedia]),
84
84
  ']')),
85
- dup(surround(/{(?![{}])/y, inits([uri, some(option)]), /[^\S\n]*}/y)),
85
+ dup(surround(/{(?![{}])/y, inits([uri, some(option)]), / ?}/y)),
86
86
  ])),
87
87
  ([params, content = []]: [string[], (HTMLElement | string)[]], context) =>
88
88
  [[parse(defrag(content), params, context)]]))))));
@@ -94,20 +94,20 @@ export const unsafelink: LinkParser.UnsafeLinkParser = lazy(() =>
94
94
  '[',
95
95
  some(union([unescsource]), ']'),
96
96
  ']')),
97
- dup(surround(/{(?![{}])/y, inits([uri, some(option)]), /[^\S\n]*}/y)),
97
+ dup(surround(/{(?![{}])/y, inits([uri, some(option)]), / ?}/y)),
98
98
  ])),
99
99
  ([params, content = []], context) =>
100
100
  [[parse(defrag(content), params, context)]])));
101
101
 
102
102
  export const uri: LinkParser.ParameterParser.UriParser = union([
103
- open(/[^\S\n]+/y, str(/\S+/y)),
103
+ open(/ /y, str(/\S+/y)),
104
104
  str(/[^\s{}]+/y),
105
105
  ]);
106
106
 
107
107
  export const option: LinkParser.ParameterParser.OptionParser = union([
108
- fmap(str(/[^\S\n]+nofollow(?=[^\S\n]|})/y), () => [` rel="nofollow"`]),
109
- str(/[^\S\n]+[a-z]+(?:-[a-z]+)*(?:="(?:\\[^\n]|[^\\\n"])*")?(?=[^\S\n]|})/yi),
110
- str(/[^\S\n]+[^\s{}]+/y),
108
+ fmap(str(/ nofollow(?=[ }])/y), () => [` rel="nofollow"`]),
109
+ str(/ [a-z]+(?:-[a-z]+)*(?:="(?:\\[^\n]|[^\\\n"])*")?(?=[ }])/yi),
110
+ str(/ [^\s{}]+/y),
111
111
  ]);
112
112
 
113
113
  function parse(
@@ -155,11 +155,11 @@ function elem(
155
155
  case 'https:':
156
156
  assert(uri.host);
157
157
  switch (true) {
158
- case /[a-z][0-9]*:\/{0,2}\S/i.test(stringify(content)):
158
+ case /[0-9a-z]:\S/i.test(stringify(content)):
159
159
  type = 'content';
160
160
  message = 'URI must not be contained';
161
161
  break;
162
- case INSECURE_URI.slice(0, 2) === '^/'
162
+ case INSECURE_URI.startsWith('^/')
163
163
  && /\/\.\.?(?:\/|$)/.test(INSECURE_URI.slice(0, INSECURE_URI.search(/[?#]|$/))):
164
164
  type = 'argument';
165
165
  message = 'Dot-segments cannot be used in subresource paths';
@@ -223,7 +223,7 @@ export function resolve(uri: string, host: URL | Location, source: URL | Locatio
223
223
  assert(uri);
224
224
  assert(uri === uri.trim());
225
225
  switch (true) {
226
- case uri.slice(0, 2) === '^/':
226
+ case uri.startsWith('^/'):
227
227
  const last = host.pathname.slice(host.pathname.lastIndexOf('/') + 1);
228
228
  return last.includes('.') // isFile
229
229
  // Exclude ISO 6709.
@@ -232,7 +232,7 @@ export function resolve(uri: string, host: URL | Location, source: URL | Locatio
232
232
  : `${host.pathname.replace(/\/?$/, '/')}${uri.slice(2)}`;
233
233
  case host.origin === source.origin
234
234
  && host.pathname === source.pathname:
235
- case uri.slice(0, 2) === '//':
235
+ case uri.startsWith('//'):
236
236
  return uri;
237
237
  default:
238
238
  const target = new ReadonlyURL(uri, source.href);
@@ -243,13 +243,13 @@ export function resolve(uri: string, host: URL | Location, source: URL | Locatio
243
243
  }
244
244
 
245
245
  export function decode(uri: string): string {
246
- const origin = uri.match(/[a-z](?:[-.](?=[0-9a-z])|[0-9a-z])*:(?:\/{0,2}[^/?#\s]+|\/\/(?=[/]))/yi)?.[0] ?? '';
246
+ const head = /^[a-z]+(?:[.+-][0-9a-z]+)*:\/*[^/?#\s]+/i;
247
+ const origin = uri.match(head)?.[0] ?? '';
247
248
  try {
248
- let path = decodeURI(uri.slice(origin.length));
249
- if (!origin && /[a-z](?:[-.](?=[0-9a-z])|[0-9a-z])*:\/{0,2}\S/yi.test(path)) {
250
- path = uri.slice(origin.length);
251
- }
252
- uri = origin + path;
249
+ const path = decodeURI(uri.slice(origin.length));
250
+ uri = !origin && head.test(path)
251
+ ? uri.slice(origin.length)
252
+ : origin + path;
253
253
  }
254
254
  finally {
255
255
  return uri.replace(/\s+/g, encodeURI);
@@ -39,6 +39,7 @@ describe('Unit: parser/inline/mark', () => {
39
39
 
40
40
  it('nest', () => {
41
41
  assert.deepStrictEqual(inspect(parser('==a ==b===='), ctx), [['<mark id="mark::a_b">a <mark id="mark::b">b</mark><a href="#mark::b"></a></mark>', '<a href="#mark::a_b"></a>'], '']);
42
+ assert.deepStrictEqual(inspect(parser('==- ==b===='), ctx), [['<mark id="mark::-_b">- <mark id="mark::b">b</mark><a href="#mark::b"></a></mark>', '<a href="#mark::-_b"></a>'], '']);
42
43
  assert.deepStrictEqual(inspect(parser('==a\\ ==b===='), ctx), [['<mark id="mark::a_b">a <mark id="mark::b">b</mark><a href="#mark::b"></a></mark>', '<a href="#mark::a_b"></a>'], '']);
43
44
  assert.deepStrictEqual(inspect(parser('==a&Tab;==b===='), ctx), [['<mark id="mark::a_b=33Mw2l">a\t<mark id="mark::b">b</mark><a href="#mark::b"></a></mark>', '<a href="#mark::a_b=33Mw2l"></a>'], '']);
44
45
  assert.deepStrictEqual(inspect(parser('==a<wbr>==b===='), ctx), [['<mark id="mark::ab">a<wbr><mark id="mark::b">b</mark><a href="#mark::b"></a></mark>', '<a href="#mark::ab"></a>'], '']);
@@ -27,8 +27,8 @@ describe('Unit: parser/inline/media', () => {
27
27
  assert.deepStrictEqual(inspect(parser('![]'), ctx), undefined);
28
28
  assert.deepStrictEqual(inspect(parser('![]{}'), ctx), undefined);
29
29
  assert.deepStrictEqual(inspect(parser('![]{ }'), ctx), [['<span class="invalid">![]{ }</span>'], '']);
30
- assert.deepStrictEqual(inspect(parser('![]{ }'), ctx), [['<span class="invalid">![]{ }</span>'], '']);
31
- assert.deepStrictEqual(inspect(parser('![]{ }'), ctx), [['<span class="invalid">![]{ }</span>'], '']);
30
+ assert.deepStrictEqual(inspect(parser('![]{ }'), ctx), undefined);
31
+ assert.deepStrictEqual(inspect(parser('![]{ }'), ctx), undefined);
32
32
  assert.deepStrictEqual(inspect(parser('![]]{/}'), ctx), undefined);
33
33
  assert.deepStrictEqual(inspect(parser('![]{{}'), ctx), undefined);
34
34
  assert.deepStrictEqual(inspect(parser('![]{}}'), ctx), undefined);
@@ -66,12 +66,11 @@ describe('Unit: parser/inline/media', () => {
66
66
  it('basic', () => {
67
67
  assert.deepStrictEqual(inspect(parser('![]{b}'), ctx), [['<a href="b" target="_blank"><img class="media" data-src="b" alt="b"></a>'], '']);
68
68
  assert.deepStrictEqual(inspect(parser('![]{b }'), ctx), [['<a href="b" target="_blank"><img class="media" data-src="b" alt="b"></a>'], '']);
69
- assert.deepStrictEqual(inspect(parser('![]{b }'), ctx), [['<a href="b" target="_blank"><img class="media" data-src="b" alt="b"></a>'], '']);
69
+ assert.deepStrictEqual(inspect(parser('![]{b }'), ctx), [['<span class="invalid">![]{b</span>'], ' }']);
70
70
  assert.deepStrictEqual(inspect(parser('![]{ b }'), ctx), [['<a href="b" target="_blank"><img class="media" data-src="b" alt="b"></a>'], '']);
71
- assert.deepStrictEqual(inspect(parser('![]{ b }'), ctx), [['<a href="b" target="_blank"><img class="media" data-src="b" alt="b"></a>'], '']);
72
- assert.deepStrictEqual(inspect(parser('![]{ b }'), ctx), [['<a href="b" target="_blank"><img class="media" data-src="b" alt="b"></a>'], '']);
73
- assert.deepStrictEqual(inspect(parser('![]{ b }'), ctx), [['<a href="b" target="_blank"><img class="media" data-src="b" alt="b"></a>'], '']);
74
- assert.deepStrictEqual(inspect(parser('![]{ b }'), ctx), [['<a href="b" target="_blank"><img class="media" data-src="b" alt="b"></a>'], '']);
71
+ assert.deepStrictEqual(inspect(parser('![]{ b }'), ctx), [['<span class="invalid">![]{ b</span>'], ' }']);
72
+ assert.deepStrictEqual(inspect(parser('![]{ b }'), ctx), undefined);
73
+ assert.deepStrictEqual(inspect(parser('![]{ b }'), ctx), undefined);
75
74
  assert.deepStrictEqual(inspect(parser('![]{"}'), ctx), [['<a href="&quot;" target="_blank"><img class="media" data-src="&quot;" alt="&quot;"></a>'], '']);
76
75
  assert.deepStrictEqual(inspect(parser('![]{"}"}'), ctx), [['<a href="&quot;" target="_blank"><img class="media" data-src="&quot;" alt="&quot;"></a>'], '"}']);
77
76
  assert.deepStrictEqual(inspect(parser('![]{\\}'), ctx), [['<a href="\\" target="_blank"><img class="media" data-src="\\" alt="\\"></a>'], '']);
@@ -40,7 +40,7 @@ export const media: MediaParser = lazy(() => constraint(State.media, creation(10
40
40
  dup(surround(
41
41
  /{(?![{}])/y,
42
42
  inits([uri, some(option)]),
43
- /[^\S\n]*}/y,
43
+ / ?}/y,
44
44
  false,
45
45
  undefined,
46
46
  ([as, bs], context) => {
@@ -120,9 +120,9 @@ const bracket: MediaParser.TextParser.BracketParser = lazy(() => recursion(Recur
120
120
 
121
121
  const option: MediaParser.ParameterParser.OptionParser = lazy(() => union([
122
122
  surround(
123
- open(/[^\S\n]+/y, str(/[1-9][0-9]*/y)),
123
+ open(/ /y, str(/[1-9][0-9]*/y)),
124
124
  str(/[x:]/y),
125
- str(/[1-9][0-9]*(?=[^\S\n]|})/y),
125
+ str(/[1-9][0-9]*(?=[ }])/y),
126
126
  false,
127
127
  ([[a], [b], [c]]) => [
128
128
  b === 'x'
@@ -31,7 +31,7 @@ describe('Unit: parser/inline/remark', () => {
31
31
  assert.deepStrictEqual(inspect(parser('[%\\ a %]'), ctx), undefined);
32
32
  assert.deepStrictEqual(inspect(parser('[% a\\ %]'), ctx), [['[%', ' a', ' ', '%', ']'], '']);
33
33
  assert.deepStrictEqual(inspect(parser('[% a%]'), ctx), [['[%', ' a', '%', ']'], '']);
34
- assert.deepStrictEqual(inspect(parser('[% a %%]'), ctx), [['[%', ' a', ' ', '%', '%', ']'], '']);
34
+ assert.deepStrictEqual(inspect(parser('[% a %%]'), ctx), [['[%', ' a %', '%', ']'], '']);
35
35
  assert.deepStrictEqual(inspect(parser('[% [%% %]'), ctx), [['<span class="remark"><input type="checkbox"><span>[% <span class="invalid">[%%</span> %]</span></span>'], '']);
36
36
  assert.deepStrictEqual(inspect(parser('[%% [% %%]'), ctx), [['<span class="invalid">[%%</span>'], ' [% %%]']);
37
37
  assert.deepStrictEqual(inspect(parser('[%% a %]'), ctx), [['<span class="invalid">[%%</span>'], ' a %]']);
@@ -43,7 +43,9 @@ describe('Unit: parser/inline/remark', () => {
43
43
  assert.deepStrictEqual(inspect(parser('[% %]'), ctx), [['<span class="remark"><input type="checkbox"><span>[% %]</span></span>'], '']);
44
44
  assert.deepStrictEqual(inspect(parser('[% %]'), ctx), [['<span class="remark"><input type="checkbox"><span>[% %]</span></span>'], '']);
45
45
  assert.deepStrictEqual(inspect(parser('[% a %]'), ctx), [['<span class="remark"><input type="checkbox"><span>[% a %]</span></span>'], '']);
46
+ assert.deepStrictEqual(inspect(parser('[% - %]'), ctx), [['<span class="remark"><input type="checkbox"><span>[% - %]</span></span>'], '']);
46
47
  assert.deepStrictEqual(inspect(parser('[% a %]'), ctx), [['<span class="remark"><input type="checkbox"><span>[% a %]</span></span>'], '']);
48
+ assert.deepStrictEqual(inspect(parser('[% - %]'), ctx), [['<span class="remark"><input type="checkbox"><span>[% - %]</span></span>'], '']);
47
49
  assert.deepStrictEqual(inspect(parser('[% a b %]'), ctx), [['<span class="remark"><input type="checkbox"><span>[% a b %]</span></span>'], '']);
48
50
  assert.deepStrictEqual(inspect(parser('[% a\nb %]'), ctx), [['<span class="remark"><input type="checkbox"><span>[% a<br>b %]</span></span>'], '']);
49
51
  assert.deepStrictEqual(inspect(parser('[% a %] %]'), ctx), [['<span class="remark"><input type="checkbox"><span>[% a %]</span></span>'], ' %]']);
@@ -39,6 +39,7 @@ describe('Unit: parser/inline/strong', () => {
39
39
 
40
40
  it('nest', () => {
41
41
  assert.deepStrictEqual(inspect(parser('**a *b***'), ctx), [['<strong>a <em>b</em></strong>'], '']);
42
+ assert.deepStrictEqual(inspect(parser('**- *b***'), ctx), [['<strong>- <em>b</em></strong>'], '']);
42
43
  assert.deepStrictEqual(inspect(parser('**a **b****'), ctx), [['<strong>a <strong>b</strong></strong>'], '']);
43
44
  assert.deepStrictEqual(inspect(parser('**a&Tab;**b****'), ctx), [['<strong>a\t<strong>b</strong></strong>'], '']);
44
45
  assert.deepStrictEqual(inspect(parser('**a<wbr>**b****'), ctx), [['<strong>a<wbr><strong>b</strong></strong>'], '']);
@@ -15,6 +15,7 @@ describe('Unit: parser/source/escsource', () => {
15
15
  it('basic', () => {
16
16
  assert.deepStrictEqual(inspect(parser('a'), ctx), [['a'], '']);
17
17
  assert.deepStrictEqual(inspect(parser('ab'), ctx), [['ab'], '']);
18
+ assert.deepStrictEqual(inspect(parser('a b c'), ctx), [['a b c'], '']);
18
19
  assert.deepStrictEqual(inspect(parser('09あいAZaz'), ctx), [['09あいAZaz'], '']);
19
20
  });
20
21
 
@@ -1,10 +1,10 @@
1
1
  import { EscapableSourceParser } from '../source';
2
2
  import { Command } from '../context';
3
3
  import { consume } from '../../combinator';
4
- import { nonWhitespace, isBlank, next } from './text';
4
+ import { next } from './text';
5
5
  import { html } from 'typed-dom/dom';
6
6
 
7
- const delimiter = /(?=[\\$"`\[\](){}\r\n]|\s(?:\$)|:\/\/)/g;
7
+ const delimiter = /(?=[\\$"`\[\](){}\r\n]|\s\$|:\/\/)/g;
8
8
 
9
9
  export const escsource: EscapableSourceParser = ({ context }) => {
10
10
  const { source, position } = context;
@@ -38,15 +38,7 @@ export const escsource: EscapableSourceParser = ({ context }) => {
38
38
  default:
39
39
  assert(char !== '\n');
40
40
  if (context.sequential) return [[char]];
41
- nonWhitespace.lastIndex = position + 1;
42
- const b = isBlank(source, position);
43
- let i = b
44
- ? source[position + 1] === '\n'
45
- ? position + 1
46
- : nonWhitespace.test(source)
47
- ? nonWhitespace.lastIndex - 1
48
- : source.length
49
- : next(source, position, delimiter);
41
+ let i = next(source, position, delimiter);
50
42
  assert(i > position);
51
43
  i -= position;
52
44
  consume(i - 1, context);
@@ -15,6 +15,7 @@ describe('Unit: parser/source/text', () => {
15
15
  it('basic', () => {
16
16
  assert.deepStrictEqual(inspect(parser('a'), ctx), [['a'], '']);
17
17
  assert.deepStrictEqual(inspect(parser('ab'), ctx), [['ab'], '']);
18
+ assert.deepStrictEqual(inspect(parser('a b c'), ctx), [['a b c'], '']);
18
19
  assert.deepStrictEqual(inspect(parser('09あいAZaz'), ctx), [['09あいAZaz'], '']);
19
20
  assert.deepStrictEqual(inspect(parser('a\nb'), ctx), [['a', '<br>', 'b'], '']);
20
21
  });
@@ -39,8 +40,8 @@ describe('Unit: parser/source/text', () => {
39
40
  assert.deepStrictEqual(inspect(parser(' '), ctx), [[], '']);
40
41
  assert.deepStrictEqual(inspect(parser(' \n'), ctx), [['<br>'], '']);
41
42
  assert.deepStrictEqual(inspect(parser(' \n'), ctx), [['<br>'], '']);
42
- assert.deepStrictEqual(inspect(parser(' \\\n'), ctx), [['<br>'], '']);
43
- assert.deepStrictEqual(inspect(parser(' \\\n'), ctx), [['<br>'], '']);
43
+ assert.deepStrictEqual(inspect(parser(' \\\n'), ctx), [[' ', '<br>'], '']);
44
+ assert.deepStrictEqual(inspect(parser(' \\\n'), ctx), [[' ', ' ', '<br>'], '']);
44
45
  assert.deepStrictEqual(inspect(parser(' a'), ctx), [[' a'], '']);
45
46
  assert.deepStrictEqual(inspect(parser(' a'), ctx), [[' ', ' a'], '']);
46
47
  assert.deepStrictEqual(inspect(parser(' a'), ctx), [[' ', ' a'], '']);
@@ -48,8 +49,8 @@ describe('Unit: parser/source/text', () => {
48
49
  assert.deepStrictEqual(inspect(parser('a '), ctx), [['a'], '']);
49
50
  assert.deepStrictEqual(inspect(parser('a \n'), ctx), [['a', '<br>'], '']);
50
51
  assert.deepStrictEqual(inspect(parser('a \n'), ctx), [['a', '<br>'], '']);
51
- assert.deepStrictEqual(inspect(parser('a \\\n'), ctx), [['a', '<br>'], '']);
52
- assert.deepStrictEqual(inspect(parser('a \\\n'), ctx), [['a', '<br>'], '']);
52
+ assert.deepStrictEqual(inspect(parser('a \\\n'), ctx), [['a', ' ', '<br>'], '']);
53
+ assert.deepStrictEqual(inspect(parser('a \\\n'), ctx), [['a', ' ', '<br>'], '']);
53
54
  assert.deepStrictEqual(inspect(parser('a b'), ctx), [['a b'], '']);
54
55
  assert.deepStrictEqual(inspect(parser('a b'), ctx), [['a', ' b'], '']);
55
56
  assert.deepStrictEqual(inspect(parser('a b'), ctx), [['a', ' b'], '']);
@@ -4,7 +4,7 @@ import { union, consume, focus } from '../../combinator';
4
4
  import { html } from 'typed-dom/dom';
5
5
 
6
6
  //const delimiter = /(?=[\\!@#$&"`\[\](){}<>()[]{}*%|\r\n]|([+~=])\1|\/{3}|\s(?:\\?(?:$|\s)|[$%])|:\/\/)/g;
7
- export const nonWhitespace = /[\S\r\n]/g;
7
+ export const nonWhitespace = /[^ \t ]/g;
8
8
 
9
9
  export const text: TextParser = input => {
10
10
  const { context } = input;
@@ -38,29 +38,24 @@ export const text: TextParser = input => {
38
38
  assert(char !== '\n');
39
39
  if (context.sequential) return [[char]];
40
40
  nonWhitespace.lastIndex = position + 1;
41
- const b = isBlank(source, position);
42
- let i = b
43
- ? source[position + 1] === '\n'
44
- ? position + 1
45
- : nonWhitespace.test(source)
46
- ? nonWhitespace.lastIndex - 1
47
- : source.length
41
+ const s = canSkip(source, position);
42
+ let i = s
43
+ ? nonWhitespace.test(source)
44
+ ? nonWhitespace.lastIndex - 1
45
+ : source.length
48
46
  : next(source, position);
49
47
  assert(i > position);
50
48
  const lineend = 0
51
- || b && i === source.length
52
- || b && source[i] === '\n'
53
- || b && source[i] === '\\' && source[i + 1] === '\n';
49
+ || s && i === source.length
50
+ || s && source[i] === '\n';
54
51
  i -= position;
55
- i = lineend ? i : i - +b || 1;
52
+ i = lineend ? i : i - +s || 1;
56
53
  consume(i - 1, context);
57
54
  context.position += i - 1;
58
55
  const linestart = position === 0 || source[position - 1] === '\n';
59
- i = linestart && b && i >= 3 ? i - 3 : 0;
60
- i += position;
61
- return i === context.position || b && !linestart || lineend
56
+ return position === context.position || s && !linestart || lineend
62
57
  ? [[]]
63
- : [[source.slice(i, context.position)]];
58
+ : [[source.slice(position, context.position)]];
64
59
  }
65
60
  };
66
61
 
@@ -72,6 +67,26 @@ export const linebreak: LinebreakParser = focus(/[\r\n]/y, union([
72
67
  text,
73
68
  ])) as LinebreakParser;
74
69
 
70
+ export function canSkip(source: string, position: number): boolean {
71
+ assert(position < source.length);
72
+ if (!isWhitespace(source[position], false)) return false;
73
+ if (position + 1 === source.length) return true;
74
+ return isWhitespace(source[position + 1], true);
75
+ }
76
+ function isWhitespace(char: string, linebreak: boolean): boolean {
77
+ switch (char) {
78
+ case ' ':
79
+ case '\t':
80
+ case ' ':
81
+ return true;
82
+ case '\r':
83
+ case '\n':
84
+ return linebreak;
85
+ default:
86
+ return false;
87
+ }
88
+ }
89
+
75
90
  export function next(source: string, position: number, delimiter?: RegExp): number {
76
91
  let index: number;
77
92
  if (delimiter) {
@@ -86,8 +101,24 @@ export function next(source: string, position: number, delimiter?: RegExp): numb
86
101
  assert(index > position);
87
102
  const char = source[index];
88
103
  switch (char) {
104
+ case '$':
105
+ case '%':
106
+ case '*':
107
+ case '+':
108
+ case '~':
109
+ case '=':
110
+ case '/':
111
+ index = backToWhitespace(source, position, index);
112
+ break;
113
+ case '[':
114
+ index = source[index + 1] === '|'
115
+ ? backToWhitespace(source, position, index)
116
+ : index;
117
+ break;
89
118
  case ':':
90
- index = backToUrlHead(source, position, index);
119
+ index = source.startsWith('//', index + 1)
120
+ ? backToUrlHead(source, position, index)
121
+ : index;
91
122
  break;
92
123
  case '@':
93
124
  index = backToEmailHead(source, position, index);
@@ -96,6 +127,12 @@ export function next(source: string, position: number, delimiter?: RegExp): numb
96
127
  assert(index > position);
97
128
  return index;
98
129
  }
130
+ export function backToWhitespace(source: string, position: number, index: number): number {
131
+ const prev = index - 1;
132
+ return prev > position && /\s/.test(source[prev])
133
+ ? prev
134
+ : index;
135
+ }
99
136
  export function backToUrlHead(source: string, position: number, index: number): number {
100
137
  const delim = index;
101
138
  let state = false;
@@ -151,7 +188,6 @@ export function backToEmailHead(source: string, position: number, index: number)
151
188
  }
152
189
  return index + offset;
153
190
  }
154
-
155
191
  function isAlphanumeric(char: string): boolean {
156
192
  assert(char.length === 1);
157
193
  if (char < '0' || '\x7F' < char) return false;
@@ -195,8 +231,6 @@ function isAlphanumeric(char: string): boolean {
195
231
  // }
196
232
  //};
197
233
 
198
- const delimiter = /\s(?:\\?(?:$|\s)|[$%])/y;
199
-
200
234
  function seek(source: string, position: number): number {
201
235
  for (let i = position + 1; i < source.length; ++i) {
202
236
  const fst = source[i];
@@ -225,7 +259,6 @@ function seek(source: string, position: number): number {
225
259
  case '{':
226
260
  case '}':
227
261
  case '*':
228
- case '%':
229
262
  case '|':
230
263
  case '\r':
231
264
  case '\n':
@@ -238,68 +271,39 @@ function seek(source: string, position: number): number {
238
271
  case '/':
239
272
  if (source[i + 1] === fst && source[i + 2] === fst) return i;
240
273
  continue;
274
+ case '%':
275
+ if (source[i + 1] === ']') return i;
276
+ continue;
241
277
  case ':':
242
278
  if (source[i + 1] === '/' && source[i + 2] === '/') return i;
243
279
  continue;
244
- //case ' ':
245
- //case '\t':
246
- //case ' ':
247
- // if (i + 1 === source.length) return i;
248
- // switch (source[i + 1]) {
249
- // case ' ':
250
- // case '\t':
251
- // case '\r':
252
- // case '\n':
253
- // case ' ':
254
- // case '$':
255
- // case '%':
256
- // return i;
257
- // case '\\':
258
- // if (i + 2 === source.length) return i;
259
- // switch (source[i + 2]) {
260
- // case ' ':
261
- // case '\t':
262
- // case '\r':
263
- // case '\n':
264
- // case ' ':
265
- // return i;
266
- // }
267
- // }
268
- // continue;
280
+ case ' ':
281
+ case '\t':
282
+ case ' ':
283
+ if (i + 1 === source.length) return i;
284
+ switch (source[i + 1]) {
285
+ case ' ':
286
+ case '\t':
287
+ case '\r':
288
+ case '\n':
289
+ case ' ':
290
+ return i;
291
+ case '\\':
292
+ if (i + 2 === source.length) return i;
293
+ switch (source[i + 2]) {
294
+ case ' ':
295
+ case '\t':
296
+ case '\r':
297
+ case '\n':
298
+ case ' ':
299
+ return i;
300
+ }
301
+ }
302
+ continue;
269
303
  default:
270
- delimiter.lastIndex = i;
271
- if (delimiter.test(source)) return i;
272
304
  continue;
273
305
  }
274
306
  assert(false);
275
307
  }
276
308
  return source.length;
277
309
  }
278
-
279
- const blank = /\s(?:$|\s|\\\n)/y;
280
- export function isBlank(source: string, position: number): boolean {
281
- blank.lastIndex = position;
282
- return blank.test(source);
283
- assert(position < source.length);
284
- if (!isWhitespace(source[position])) return false;
285
- if (position + 1 === source.length) return true;
286
- const snd = source[position + 1];
287
- if (isWhitespace(snd)) return true;
288
- if (position + 2 === source.length) return false;
289
- if (snd === '\\' && source[position + 2] === '\n') return true;
290
- return false;
291
- }
292
- const whitespace = /\s/;
293
- export function isWhitespace(char: string): boolean {
294
- whitespace;
295
- switch (char) {
296
- case ' ':
297
- case '\t':
298
- case '\r':
299
- case '\n':
300
- case ' ':
301
- return true
302
- default:
303
- return false;
304
- }
305
- }
@@ -15,6 +15,7 @@ describe('Unit: parser/source/unescapable', () => {
15
15
  it('basic', () => {
16
16
  assert.deepStrictEqual(inspect(parser('a'), ctx), [['a'], '']);
17
17
  assert.deepStrictEqual(inspect(parser('ab'), ctx), [['ab'], '']);
18
+ assert.deepStrictEqual(inspect(parser('a b c'), ctx), [['a', ' b', ' c'], '']);
18
19
  assert.deepStrictEqual(inspect(parser('09あいAZaz'), ctx), [['09', 'あいAZaz'], '']);
19
20
  });
20
21
 
@@ -1,7 +1,7 @@
1
1
  import { UnescapableSourceParser } from '../source';
2
2
  import { Command } from '../context';
3
3
  import { consume } from '../../combinator';
4
- import { nonWhitespace, isBlank, next } from './text';
4
+ import { nonWhitespace, canSkip, next } from './text';
5
5
  import { html } from 'typed-dom/dom';
6
6
 
7
7
  export const delimiter = /(?=(?=[\x00-\x7F])[^0-9A-Za-z]|(?<=[\x00-\x7F])[^\x00-\x7F])/g;
@@ -28,13 +28,10 @@ export const unescsource: UnescapableSourceParser = ({ context }) => {
28
28
  assert(char !== '\n');
29
29
  if (context.sequential) return [[char]];
30
30
  nonWhitespace.lastIndex = position + 1;
31
- const b = isBlank(source, position);
32
- let i = b
33
- ? source[position + 1] === '\n'
34
- ? position + 1
35
- : nonWhitespace.test(source)
36
- ? nonWhitespace.lastIndex - 1
37
- : source.length
31
+ let i = canSkip(source, position)
32
+ ? nonWhitespace.test(source)
33
+ ? nonWhitespace.lastIndex - 1
34
+ : source.length
38
35
  : next(source, position, delimiter);
39
36
  assert(i > position);
40
37
  i -= position;