securemark 0.294.6 → 0.294.7

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.294.7
4
+
5
+ - Refactoring.
6
+
3
7
  ## 0.294.6
4
8
 
5
9
  - Refactoring.
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- /*! securemark v0.294.6 https://github.com/falsandtru/securemark | (c) 2017, falsandtru | UNLICENSED License */
1
+ /*! securemark v0.294.7 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"));
@@ -2894,9 +2894,7 @@ function indent(opener, parser = false, separation = false) {
2894
2894
  } = context;
2895
2895
  context.position = source.length;
2896
2896
  return new parser_1.List([new parser_1.Data(source.slice(position))]);
2897
- }))), ([indent]) => indent.length * 2 + -(indent[0] === ' '), [], 2 ** 4 - 1)), separation), (lines, context) => {
2898
- return parser((0, parser_1.subinput)(trimBlockEnd(lines.foldl((acc, node) => acc + node.value, '')), context));
2899
- }));
2897
+ }))), ([indent]) => indent.length * 2 + -(indent[0] === ' '), [], 2 ** 4 - 1)), separation), (lines, context) => parser((0, parser_1.subinput)(trimBlockEnd(lines.foldl((acc, node) => acc + node.value, '')), context))));
2900
2898
  }
2901
2899
  exports.indent = indent;
2902
2900
  function trimBlockEnd(block) {
@@ -3094,6 +3092,11 @@ function surround(opener, parser, closer, optional = false, f, g, backtracks = [
3094
3092
  case 'object':
3095
3093
  opener = (0, combinator_1.clear)((0, combinator_1.matcher)(opener, true));
3096
3094
  }
3095
+ switch (typeof parser) {
3096
+ case 'string':
3097
+ case 'object':
3098
+ parser = (0, combinator_1.clear)((0, combinator_1.matcher)(parser, true));
3099
+ }
3097
3100
  switch (typeof closer) {
3098
3101
  case 'string':
3099
3102
  case 'object':
@@ -3124,16 +3127,14 @@ function surround(opener, parser, closer, optional = false, f, g, backtracks = [
3124
3127
  if (!nodesM && !optional) {
3125
3128
  setBacktrack(context, backtracks, position);
3126
3129
  const result = g?.([nodesO, nodesM], context);
3127
- revert(context, linebreak);
3128
- return result;
3130
+ return result || void revert(context, linebreak);
3129
3131
  }
3130
3132
  const nodesC = nodesM || optional ? closer(input) : undefined;
3131
3133
  context.range = context.position - position;
3132
3134
  if (!nodesC) {
3133
3135
  setBacktrack(context, backtracks, position);
3134
3136
  const result = g?.([nodesO, nodesM], context);
3135
- revert(context, linebreak);
3136
- return result;
3137
+ return result || void revert(context, linebreak);
3137
3138
  }
3138
3139
  if (context.position === position) {
3139
3140
  return void revert(context, linebreak);
@@ -3142,10 +3143,8 @@ function surround(opener, parser, closer, optional = false, f, g, backtracks = [
3142
3143
  const result = f ? f([nodesO, nodesM, nodesC], context) : nodesO.import(nodesM ?? new parser_1.List()).import(nodesC);
3143
3144
  if (result) {
3144
3145
  context.linebreak ||= linebreak;
3145
- } else {
3146
- revert(context, linebreak);
3147
3146
  }
3148
- return result;
3147
+ return result || void revert(context, linebreak);
3149
3148
  });
3150
3149
  }
3151
3150
  exports.surround = surround;
@@ -3183,7 +3182,7 @@ function isBacktrack(context, backtracks, position = context.position, length =
3183
3182
  }
3184
3183
  exports.isBacktrack = isBacktrack;
3185
3184
  function setBacktrack(context, backtracks, position, length = 1) {
3186
- // 以降バックトラックの可能性がなく記録不要の場合もあるが判別が面倒なので省略
3185
+ // バックトラックの可能性がなく記録不要の場合もあるが判別が面倒なので省略
3187
3186
  const {
3188
3187
  source
3189
3188
  } = context;
@@ -4396,8 +4395,9 @@ exports.normalize = normalize;
4396
4395
  function format(source) {
4397
4396
  return source.replace(/\r\n?/g, '\n');
4398
4397
  }
4398
+ const invalid = new RegExp([/(?![\t\r\n])[\x00-\x1F\x7F]/g.source, /(?!\u200D)[\u2006\u200B-\u200F\u202A-\u202F\u2060\uFEFF]|(?<![\u1820\u1821])\u180E/g.source, /[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g.source].join('|'), 'g');
4399
4399
  function sanitize(source) {
4400
- return source.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]|(?!\u200D)[\u2006\u200B-\u200F\u202A-\u202F\u2060\uFEFF]|(?<![\u1820\u1821])\u180E/g, UNICODE_REPLACEMENT_CHARACTER).replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]?|[\uDC00-\uDFFF]/g, char => char.length === 1 ? UNICODE_REPLACEMENT_CHARACTER : char);
4400
+ return source.replace(invalid, UNICODE_REPLACEMENT_CHARACTER);
4401
4401
  }
4402
4402
  // https://dev.w3.org/html5/html-author/charref
4403
4403
  // https://en.wikipedia.org/wiki/Whitespace_character
@@ -6041,6 +6041,7 @@ const reference_1 = __webpack_require__(9047);
6041
6041
  const template_1 = __webpack_require__(4510);
6042
6042
  const remark_1 = __webpack_require__(8948);
6043
6043
  const extension_1 = __webpack_require__(2743);
6044
+ const label_1 = __webpack_require__(2178);
6044
6045
  const link_1 = __webpack_require__(3628);
6045
6046
  const ruby_1 = __webpack_require__(7304);
6046
6047
  const html_1 = __webpack_require__(5013);
@@ -6096,7 +6097,7 @@ exports.inline = (0, combinator_1.lazy)(() => (0, combinator_1.union)([input =>
6096
6097
  return (0, html_1.html)(input);
6097
6098
  case '$':
6098
6099
  if (source[position + 1] === '{') return (0, math_1.math)(input);
6099
- return (0, extension_1.extension)(input) || (0, math_1.math)(input);
6100
+ return (0, label_1.label)(input) || (0, math_1.math)(input);
6100
6101
  case '+':
6101
6102
  if (source[position + 1] === '+') return (0, insertion_1.insertion)(input);
6102
6103
  break;
@@ -6208,16 +6209,45 @@ const account_1 = __webpack_require__(4107);
6208
6209
  const hashtag_1 = __webpack_require__(5764);
6209
6210
  const hashnum_1 = __webpack_require__(8684);
6210
6211
  const anchor_1 = __webpack_require__(8535);
6211
- exports.autolink = (0, combinator_1.lazy)(() => (0, combinator_1.validate)(new RegExp([/(?<![0-9a-z])@/yi.source, /(?<![^\p{C}\p{S}\p{P}\s]|emoji)#/yiu.source, /(?<![0-9a-z])>>/yi.source, /(?<![0-9a-z][.+-]?|[@#])!?[0-9a-z]/yi.source].join('|').replace(/emoji/g, hashtag_1.emoji.source), 'yiu'), (0, combinator_1.state)(~1 /* State.autolink */, (0, combinator_1.union)([url_1.lineurl, url_1.url, email_1.email,
6212
- // Escape unmatched email-like strings.
6213
- //str(/[0-9a-z]+(?:[_.+-][0-9a-z]+[:@]?|:|@(?=@))*/yi),
6214
- channel_1.channel, account_1.account,
6215
- // Escape unmatched account-like strings.
6216
- //str(/@+(?:[0-9a-z]+(?:[_.+-][0-9a-z]+)*)?/yi),
6217
- hashtag_1.hashtag, hashnum_1.hashnum,
6218
- // Escape unmatched hashtag-like strings.
6219
- //str(new RegExp(/#+(?:(?:[^\p{C}\p{S}\p{P}\s]|emoji)+(?:['_.+-](?:[^\p{C}\p{S}\p{P}\s]|emoji)+)*)?/yu.source.replace(/emoji/g, emoji), 'yu')),
6220
- anchor_1.anchor]))));
6212
+ const text_1 = __webpack_require__(5655);
6213
+ exports.autolink = (0, combinator_1.lazy)(() => (0, combinator_1.state)(~1 /* State.autolink */, input => {
6214
+ const {
6215
+ context: {
6216
+ source,
6217
+ position
6218
+ }
6219
+ } = input;
6220
+ if (position === source.length) return;
6221
+ const fst = source[position];
6222
+ switch (fst) {
6223
+ case '@':
6224
+ return (0, channel_1.channel)(input) || (0, account_1.account)(input);
6225
+ case '#':
6226
+ return (0, hashtag_1.hashtag)(input) || (0, hashnum_1.hashnum)(input);
6227
+ case '>':
6228
+ return (0, anchor_1.anchor)(input);
6229
+ case '!':
6230
+ if (!source.startsWith('http', position + 1)) break;
6231
+ if (position === 0) return (0, url_1.lineurl)(input);
6232
+ switch (source[position - 1]) {
6233
+ case '\r':
6234
+ case '\n':
6235
+ return (0, url_1.lineurl)(input);
6236
+ }
6237
+ break;
6238
+ case 'h':
6239
+ if (!source.startsWith('http', position)) return;
6240
+ if (position === 0) return (0, url_1.lineurl)(input) || (0, url_1.url)(input) || (0, email_1.email)(input);
6241
+ switch (source[position - 1]) {
6242
+ case '\r':
6243
+ case '\n':
6244
+ return (0, url_1.lineurl)(input) || (0, url_1.url)(input) || (0, email_1.email)(input);
6245
+ }
6246
+ return (0, url_1.url)(input) || (0, email_1.email)(input);
6247
+ default:
6248
+ if ((0, text_1.isAlphanumeric)(fst)) return (0, email_1.email)(input);
6249
+ }
6250
+ }));
6221
6251
 
6222
6252
  /***/ },
6223
6253
 
@@ -6234,10 +6264,9 @@ exports.account = void 0;
6234
6264
  const parser_1 = __webpack_require__(605);
6235
6265
  const combinator_1 = __webpack_require__(3484);
6236
6266
  const link_1 = __webpack_require__(3628);
6237
- const source_1 = __webpack_require__(8745);
6238
6267
  const dom_1 = __webpack_require__(394);
6239
6268
  // https://example/@user must be a user page or a redirect page going there.
6240
- exports.account = (0, combinator_1.lazy)(() => (0, combinator_1.rewrite)((0, combinator_1.surround)(/(?<![0-9a-z])@/yi, (0, source_1.str)(/[0-9a-z](?:(?:[0-9a-z]|-(?=[0-9a-z])){0,61}[0-9a-z])?(?:\.[0-9a-z](?:(?:[0-9a-z]|-(?=[0-9a-z])){0,61}[0-9a-z])?)*\//yi), (0, source_1.str)(/[a-z][0-9a-z]*(?:[-.][0-9a-z]+)*(?![-.]?[0-9a-z@#]|>>|:\S)/yi), true, undefined, undefined, [3 | 0 /* Backtrack.autolink */]), (0, combinator_1.constraint)(1 /* State.autolink */, (0, combinator_1.state)(1 /* State.autolink */, (0, combinator_1.fmap)((0, combinator_1.convert)(source => `[${source}]{ ${source.includes('/') ? `https://${source.slice(1).replace('/', '/@')}` : `/${source}`} }`, (0, combinator_1.union)([link_1.unsafelink]), false), ([{
6269
+ exports.account = (0, combinator_1.lazy)(() => (0, combinator_1.rewrite)((0, combinator_1.surround)(/(?<![0-9a-z])@/yi, /[0-9a-z](?:(?:[0-9a-z]|-(?=[0-9a-z])){0,61}[0-9a-z])?(?:\.[0-9a-z](?:(?:[0-9a-z]|-(?=[0-9a-z])){0,61}[0-9a-z])?)*\//yi, /[a-z][0-9a-z]*(?:[-.][0-9a-z]+)*(?![-.]?[0-9a-z@#]|>>|:\S)/yi, true, undefined, undefined, [3 | 0 /* Backtrack.autolink */]), (0, combinator_1.constraint)(1 /* State.autolink */, (0, combinator_1.state)(1 /* State.autolink */, (0, combinator_1.fmap)((0, combinator_1.convert)(source => `[${source}]{ ${source.includes('/') ? `https://${source.slice(1).replace('/', '/@')}` : `/${source}`} }`, (0, combinator_1.union)([link_1.unsafelink]), false), ([{
6241
6270
  value
6242
6271
  }]) => new parser_1.List([new parser_1.Data((0, dom_1.define)(value, {
6243
6272
  class: 'account'
@@ -6258,7 +6287,6 @@ exports.anchor = void 0;
6258
6287
  const parser_1 = __webpack_require__(605);
6259
6288
  const combinator_1 = __webpack_require__(3484);
6260
6289
  const link_1 = __webpack_require__(3628);
6261
- const source_1 = __webpack_require__(8745);
6262
6290
  const dom_1 = __webpack_require__(394);
6263
6291
  // Timeline(pseudonym): user/tid
6264
6292
  // Thread(anonymous): cid
@@ -6267,7 +6295,7 @@ const dom_1 = __webpack_require__(394);
6267
6295
  // cid: YYYY-MMDD-HHMM-SSmmm
6268
6296
  // 内部表現はUnixTimeに統一する(時系列順)
6269
6297
  // 外部表現は投稿ごとに投稿者の投稿時のタイムゾーンに統一する(非時系列順)
6270
- exports.anchor = (0, combinator_1.lazy)(() => (0, combinator_1.rewrite)((0, combinator_1.open)(/(?<![0-9a-z])>>/yi, (0, source_1.str)(/(?:[a-z][0-9a-z]*(?:-[0-9a-z]+)*\/)?[0-9a-z]+(?:-[0-9a-z]+)*(?!-?[0-9a-z@#]|>>|:\S)/yi), false, [3 | 0 /* Backtrack.autolink */]), (0, combinator_1.constraint)(1 /* State.autolink */, (0, combinator_1.state)(1 /* State.autolink */, (0, combinator_1.fmap)((0, combinator_1.convert)(source => `[${source}]{ ${source.includes('/') ? `/@${source.slice(2).replace('/', '/timeline?at=')}` : `?at=${source.slice(2)}`} }`, (0, combinator_1.union)([link_1.unsafelink]), false), ([{
6298
+ exports.anchor = (0, combinator_1.lazy)(() => (0, combinator_1.rewrite)((0, combinator_1.open)(/(?<![0-9a-z])>>/yi, /(?:[a-z][0-9a-z]*(?:-[0-9a-z]+)*\/)?[0-9a-z]+(?:-[0-9a-z]+)*(?!-?[0-9a-z@#]|>>|:\S)/yi, false, [3 | 0 /* Backtrack.autolink */]), (0, combinator_1.constraint)(1 /* State.autolink */, (0, combinator_1.state)(1 /* State.autolink */, (0, combinator_1.fmap)((0, combinator_1.convert)(source => `[${source}]{ ${source.includes('/') ? `/@${source.slice(2).replace('/', '/timeline?at=')}` : `?at=${source.slice(2)}`} }`, (0, combinator_1.union)([link_1.unsafelink]), false), ([{
6271
6299
  value
6272
6300
  }]) => new parser_1.List([new parser_1.Data((0, dom_1.define)(value, {
6273
6301
  class: 'anchor'
@@ -6292,7 +6320,7 @@ const hashtag_1 = __webpack_require__(5764);
6292
6320
  const source_1 = __webpack_require__(8745);
6293
6321
  const dom_1 = __webpack_require__(394);
6294
6322
  // https://example/@user?ch=a+b must be a user channel page or a redirect page going there.
6295
- exports.channel = (0, combinator_1.lazy)(() => (0, combinator_1.rewrite)((0, combinator_1.sequence)([(0, combinator_1.surround)(/(?<![0-9a-z])@/yi, (0, source_1.str)(/[0-9a-z](?:(?:[0-9a-z]|-(?=[0-9a-z])){0,61}[0-9a-z])?(?:\.[0-9a-z](?:(?:[0-9a-z]|-(?=[0-9a-z])){0,61}[0-9a-z])?)*\//yi), (0, source_1.str)(/[a-z][0-9a-z]*(?:[-.][0-9a-z]+)*(?![-.]?[0-9a-z@]|>>|:\S)/yi), true, undefined, undefined, [3 | 0 /* Backtrack.autolink */]), (0, combinator_1.some)((0, combinator_1.verify)((0, combinator_1.surround)('#', (0, source_1.str)(new RegExp([/(?!['_])(?:[^\p{C}\p{S}\p{P}\s]|emoji|'(?=[0-9A-Za-z])|_(?=[^\p{C}\p{S}\p{P}\s]|emoji))+/yu.source].join('').replace(/emoji/g, hashtag_1.emoji.source), 'yu')), (0, source_1.str)(new RegExp([/(?![0-9a-z@]|>>|:\S|[^\p{C}\p{S}\p{P}\s]|emoji)/yu.source].join('').replace(/emoji/g, hashtag_1.emoji.source), 'yu')), false, undefined, undefined, [3 | 0 /* Backtrack.autolink */]), ([{
6323
+ exports.channel = (0, combinator_1.lazy)(() => (0, combinator_1.rewrite)((0, combinator_1.sequence)([(0, combinator_1.surround)(/(?<![0-9a-z])@/yi, /[0-9a-z](?:(?:[0-9a-z]|-(?=[0-9a-z])){0,61}[0-9a-z])?(?:\.[0-9a-z](?:(?:[0-9a-z]|-(?=[0-9a-z])){0,61}[0-9a-z])?)*\//yi, /[a-z][0-9a-z]*(?:[-.][0-9a-z]+)*(?![-.]?[0-9a-z@]|>>|:\S)/yi, true, undefined, undefined, [3 | 0 /* Backtrack.autolink */]), (0, combinator_1.some)((0, combinator_1.verify)((0, combinator_1.surround)('#', (0, source_1.str)(new RegExp([/(?!['_])(?:[^\p{C}\p{S}\p{P}\s]|emoji|'(?=[0-9A-Za-z])|_(?=[^\p{C}\p{S}\p{P}\s]|emoji))+/yu.source].join('|').replace(/emoji/g, hashtag_1.emoji.source), 'yu')), new RegExp([/(?![0-9a-z@]|>>|:\S|[^\p{C}\p{S}\p{P}\s]|emoji)/yu.source].join('|').replace(/emoji/g, hashtag_1.emoji.source), 'yu'), false, undefined, undefined, [3 | 0 /* Backtrack.autolink */]), ([{
6296
6324
  value
6297
6325
  }]) => !/^[0-9]{1,4}$|^[0-9]{5}/.test(value)))]), (0, combinator_1.constraint)(1 /* State.autolink */, (0, combinator_1.state)(1 /* State.autolink */, (0, combinator_1.fmap)((0, combinator_1.convert)(source => `[${source}]{ ${source.includes('/') ? `https://${source.slice(1, source.indexOf('#')).replace('/', '/@')}` : `/${source.slice(0, source.indexOf('#'))}`} }`, (0, combinator_1.union)([link_1.unsafelink]), false), ([{
6298
6326
  value: el
@@ -6353,9 +6381,8 @@ const parser_1 = __webpack_require__(605);
6353
6381
  const combinator_1 = __webpack_require__(3484);
6354
6382
  const link_1 = __webpack_require__(3628);
6355
6383
  const hashtag_1 = __webpack_require__(5764);
6356
- const source_1 = __webpack_require__(8745);
6357
6384
  const dom_1 = __webpack_require__(394);
6358
- exports.hashnum = (0, combinator_1.lazy)(() => (0, combinator_1.rewrite)((0, combinator_1.open)(new RegExp([/(?<![^\p{C}\p{S}\p{P}\s]|emoji)#/yiu.source].join('').replace(/emoji/g, hashtag_1.emoji.source), 'yu'), (0, source_1.str)(new RegExp([/[0-9]{1,9}(?![0-9a-z@#]|>>|:\S|[^\p{C}\p{S}\p{P}\s]|emoji)/yu.source].join('').replace(/emoji/g, hashtag_1.emoji.source), 'yu')), false, [1 | 0 /* Backtrack.autolink */]), (0, combinator_1.constraint)(1 /* State.autolink */, (0, combinator_1.state)(1 /* State.autolink */, (0, combinator_1.fmap)((0, combinator_1.convert)(source => `[${source}]{ ${source.slice(1)} }`, (0, combinator_1.union)([link_1.unsafelink]), false), ([{
6385
+ exports.hashnum = (0, combinator_1.lazy)(() => (0, combinator_1.rewrite)((0, combinator_1.open)(new RegExp([/(?<![^\p{C}\p{S}\p{P}\s]|emoji)#/yu.source].join('|').replace(/emoji/g, hashtag_1.emoji.source), 'yu'), new RegExp([/[0-9]{1,9}(?![0-9a-z@#]|>>|:\S|[^\p{C}\p{S}\p{P}\s]|emoji)/yu.source].join('|').replace(/emoji/g, hashtag_1.emoji.source), 'yu'), false, [1 | 0 /* Backtrack.autolink */]), (0, combinator_1.constraint)(1 /* State.autolink */, (0, combinator_1.state)(1 /* State.autolink */, (0, combinator_1.fmap)((0, combinator_1.convert)(source => `[${source}]{ ${source.slice(1)} }`, (0, combinator_1.union)([link_1.unsafelink]), false), ([{
6359
6386
  value
6360
6387
  }]) => new parser_1.List([new parser_1.Data((0, dom_1.define)(value, {
6361
6388
  class: 'hashnum',
@@ -6382,7 +6409,7 @@ const dom_1 = __webpack_require__(394);
6382
6409
  // https://example/hashtags/a must be a hashtag page or a redirect page going there.
6383
6410
  // https://github.com/tc39/proposal-regexp-unicode-property-escapes#matching-emoji
6384
6411
  exports.emoji = /\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Emoji_Presentation}|\p{Emoji}\uFE0F|\u200D/u;
6385
- exports.hashtag = (0, combinator_1.lazy)(() => (0, combinator_1.rewrite)((0, combinator_1.verify)((0, combinator_1.surround)(new RegExp([/(?<![^\p{C}\p{S}\p{P}\s]|emoji)#/yiu.source].join('').replace(/emoji/g, exports.emoji.source), 'yu'), (0, source_1.str)(new RegExp([/(?!['_])(?:[^\p{C}\p{S}\p{P}\s]|emoji|'(?=[0-9A-Za-z])|_(?=[^\p{C}\p{S}\p{P}\s]|emoji))+/yu.source].join('').replace(/emoji/g, exports.emoji.source), 'yu')), (0, source_1.str)(new RegExp([/(?![0-9a-z@#]|>>|:\S|[^\p{C}\p{S}\p{P}\s]|emoji)/yu.source].join('').replace(/emoji/g, exports.emoji.source), 'yu')), false, undefined, undefined, [3 | 0 /* Backtrack.autolink */]), ([{
6412
+ exports.hashtag = (0, combinator_1.lazy)(() => (0, combinator_1.rewrite)((0, combinator_1.verify)((0, combinator_1.surround)(new RegExp([/(?<![^\p{C}\p{S}\p{P}\s]|emoji)#/yu.source].join('|').replace(/emoji/g, exports.emoji.source), 'yu'), (0, source_1.str)(new RegExp([/(?!['_])(?:[^\p{C}\p{S}\p{P}\s]|emoji|'(?=[0-9A-Za-z])|_(?=[^\p{C}\p{S}\p{P}\s]|emoji))+/yu.source].join('|').replace(/emoji/g, exports.emoji.source), 'yu')), new RegExp([/(?![0-9a-z@#]|>>|:\S|[^\p{C}\p{S}\p{P}\s]|emoji)/yu.source].join('|').replace(/emoji/g, exports.emoji.source), 'yu'), false, undefined, undefined, [3 | 0 /* Backtrack.autolink */]), ([{
6386
6413
  value
6387
6414
  }]) => !/^[0-9]{1,4}$|^[0-9]{5}/.test(value)), (0, combinator_1.constraint)(1 /* State.autolink */, (0, combinator_1.state)(1 /* State.autolink */, (0, combinator_1.fmap)((0, combinator_1.convert)(source => `[${source}]{ ${`/hashtags/${source.slice(1)}`} }`, (0, combinator_1.union)([link_1.unsafelink]), false), ([{
6388
6415
  value
@@ -7065,7 +7092,7 @@ Object.setPrototypeOf(attrspecs, null);
7065
7092
  Object.values(attrspecs).forEach(o => Object.setPrototypeOf(o, null));
7066
7093
  exports.html = (0, combinator_1.lazy)(() => (0, combinator_1.validate)(/<[a-z]+(?=[ >])/yi, (0, combinator_1.union)([(0, combinator_1.surround)(
7067
7094
  // https://html.spec.whatwg.org/multipage/syntax.html#void-elements
7068
- (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 = new parser_1.List(), cs], context) => new parser_1.List([new parser_1.Data(elem(as.head.value.slice(1), false, [...(0, util_1.unwrap)(as.import(bs).import(cs))], new parser_1.List(), new parser_1.List(), context))]), ([as, bs = new parser_1.List()], context) => new parser_1.List([new parser_1.Data(elem(as.head.value.slice(1), false, [...(0, util_1.unwrap)(as.import(bs))], new parser_1.List(), new parser_1.List(), context))])), (0, combinator_1.match)(new RegExp(String.raw`<(${TAGS.join('|')})(?=[ >])`, '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 = new parser_1.List(), cs]) => as.import(bs).import(cs), ([as, bs = new parser_1.List()]) => as.import(bs)),
7095
+ (0, source_1.str)(/<(?:area|base|br|col|embed|hr|img|input|link|meta|source|track|wbr)(?=[ >])/y), (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 = new parser_1.List(), cs], context) => new parser_1.List([new parser_1.Data(elem(as.head.value.slice(1), false, [...(0, util_1.unwrap)(as.import(bs).import(cs))], new parser_1.List(), new parser_1.List(), context))]), ([as, bs = new parser_1.List()], context) => new parser_1.List([new parser_1.Data(elem(as.head.value.slice(1), false, [...(0, util_1.unwrap)(as.import(bs))], new parser_1.List(), new parser_1.List(), context))])), (0, combinator_1.match)(new RegExp(String.raw`<(${TAGS.join('|')})(?=[ >])`, '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 = new parser_1.List(), cs]) => as.import(bs).import(cs), ([as, bs = new parser_1.List()]) => as.import(bs)),
7069
7096
  // 不可視のHTML構造が可視構造を変化させるべきでない。
7070
7097
  // 可視のHTMLは優先度変更を検討する。
7071
7098
  // このため<>は将来的に共通構造を変化させる可能性があり
@@ -8509,7 +8536,7 @@ exports.strs = strs;
8509
8536
  Object.defineProperty(exports, "__esModule", ({
8510
8537
  value: true
8511
8538
  }));
8512
- exports.backToEmailHead = exports.backToUrlHead = exports.backToWhitespace = exports.next = exports.canSkip = exports.linebreak = exports.txt = exports.text = exports.nonWhitespace = void 0;
8539
+ exports.isAlphanumeric = exports.backToEmailHead = exports.backToUrlHead = exports.backToWhitespace = exports.next = exports.canSkip = exports.linebreak = exports.txt = exports.text = exports.nonWhitespace = void 0;
8513
8540
  const parser_1 = __webpack_require__(605);
8514
8541
  const combinator_1 = __webpack_require__(3484);
8515
8542
  const dom_1 = __webpack_require__(394);
@@ -8670,8 +8697,9 @@ function backToEmailHead(source, position, index) {
8670
8697
  exports.backToEmailHead = backToEmailHead;
8671
8698
  function isAlphanumeric(char) {
8672
8699
  if (char < '0' || '\x7F' < char) return false;
8673
- return '0' <= char && char <= '9' || 'a' <= char && char <= 'z' || 'A' <= char && char <= 'Z';
8700
+ return '0' <= char && char <= '9' || 'A' <= char && char <= 'Z' || 'a' <= char && char <= 'z';
8674
8701
  }
8702
+ exports.isAlphanumeric = isAlphanumeric;
8675
8703
  //const dict = new class {
8676
8704
  // constructor() {
8677
8705
  // [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securemark",
3
- "version": "0.294.6",
3
+ "version": "0.294.7",
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",
@@ -16,7 +16,7 @@ export function indent<N>(opener: RegExp | Parser<N>, parser: Parser<N> | boolea
16
16
  opener = / {1,4}|\t{1,2}/y;
17
17
  }
18
18
  assert(!opener.flags.match(/[gm]/) && opener.sticky && !opener.source.startsWith('^'));
19
- assert(parser);
19
+ assert(parser = parser as Parser<N>);
20
20
  return failsafe(bind(block(match(
21
21
  opener,
22
22
  memoize(
@@ -27,10 +27,8 @@ export function indent<N>(opener: RegExp | Parser<N>, parser: Parser<N> | boolea
27
27
  return new List([new Data(source.slice(position))]);
28
28
  }))),
29
29
  ([indent]) => indent.length * 2 + -(indent[0] === ' '), [], 2 ** 4 - 1)), separation),
30
- (lines, context) => {
31
- assert(parser = parser as Parser<N>);
32
- return parser(subinput(trimBlockEnd(lines.foldl((acc, node) => acc + node.value, '')), context));
33
- }));
30
+ (lines, context) =>
31
+ parser(subinput(trimBlockEnd(lines.foldl((acc, node) => acc + node.value, '')), context))));
34
32
  }
35
33
 
36
34
  function trimBlockEnd(block: string): string {
@@ -29,8 +29,22 @@ export function surround<P extends Parser<unknown>, S = string>(
29
29
  g?: (rss: [List<Data<S>>, List<Data<Node<P>>> | undefined], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
30
30
  backtracks?: readonly number[],
31
31
  ): P;
32
+ export function surround<P extends Parser<string>, S = string>(
33
+ opener: string | RegExp | Parser<S, Context<P>>, parser: string | RegExp | P, closer: string | RegExp | Parser<S, Context<P>>,
34
+ optional?: false,
35
+ f?: (rss: [List<Data<S>>, List<Data<Node<P>>>, List<Data<S>>], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
36
+ g?: (rss: [List<Data<S>>, List<Data<Node<P>>> | undefined], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
37
+ backtracks?: readonly number[],
38
+ ): P;
39
+ export function surround<P extends Parser<string>, S = string>(
40
+ opener: string | RegExp | Parser<S, Context<P>>, parser: string | RegExp | P, closer: string | RegExp | Parser<S, Context<P>>,
41
+ optional?: boolean,
42
+ f?: (rss: [List<Data<S>>, List<Data<Node<P>>> | undefined, List<Data<S>>], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
43
+ g?: (rss: [List<Data<S>>, List<Data<Node<P>>> | undefined], context: Context<P>) => Result<Node<P>, Context<P>, SubParsers<P>>,
44
+ backtracks?: readonly number[],
45
+ ): P;
32
46
  export function surround<N>(
33
- opener: string | RegExp | Parser<N>, parser: Parser<N>, closer: string | RegExp | Parser<N>,
47
+ opener: string | RegExp | Parser<N>, parser: string | RegExp | Parser<N>, closer: string | RegExp | Parser<N>,
34
48
  optional: boolean = false,
35
49
  f?: (rss: [List<Data<N>>, List<Data<N>>, List<Data<N>>], context: Ctx) => Result<N>,
36
50
  g?: (rss: [List<Data<N>>, List<Data<N>> | undefined], context: Ctx) => Result<N>,
@@ -41,11 +55,19 @@ export function surround<N>(
41
55
  case 'object':
42
56
  opener = clear(matcher(opener, true));
43
57
  }
58
+ assert(opener);
59
+ switch (typeof parser) {
60
+ case 'string':
61
+ case 'object':
62
+ parser = clear(matcher(parser, true));
63
+ }
64
+ assert(parser);
44
65
  switch (typeof closer) {
45
66
  case 'string':
46
67
  case 'object':
47
68
  closer = clear(matcher(closer, true));
48
69
  }
70
+ assert(closer);
49
71
  return failsafe(input => {
50
72
  const { context } = input;
51
73
  const { source, position } = context;
@@ -66,8 +88,7 @@ export function surround<N>(
66
88
  if (!nodesM && !optional) {
67
89
  setBacktrack(context, backtracks, position);
68
90
  const result = g?.([nodesO, nodesM], context);
69
- revert(context, linebreak);
70
- return result;
91
+ return result || void revert(context, linebreak);
71
92
  }
72
93
  const nodesC = nodesM || optional ? closer(input) : undefined;
73
94
  assert(context.position >= position);
@@ -75,8 +96,7 @@ export function surround<N>(
75
96
  if (!nodesC) {
76
97
  setBacktrack(context, backtracks, position);
77
98
  const result = g?.([nodesO, nodesM], context);
78
- revert(context, linebreak);
79
- return result;
99
+ return result || void revert(context, linebreak);
80
100
  }
81
101
  if (context.position === position) {
82
102
  return void revert(context, linebreak);
@@ -88,10 +108,7 @@ export function surround<N>(
88
108
  if (result) {
89
109
  context.linebreak ||= linebreak;
90
110
  }
91
- else {
92
- revert(context, linebreak);
93
- }
94
- return result;
111
+ return result || void revert(context, linebreak);
95
112
  });
96
113
  }
97
114
  export function open<P extends Parser<unknown>>(
@@ -100,13 +117,19 @@ export function open<P extends Parser<unknown>>(
100
117
  optional?: boolean,
101
118
  backtracks?: readonly number[],
102
119
  ): P;
120
+ export function open<P extends Parser<string>>(
121
+ opener: string | RegExp | Parser<Node<P>, Context<P>>,
122
+ parser: string | RegExp | P,
123
+ optional?: boolean,
124
+ backtracks?: readonly number[],
125
+ ): P;
103
126
  export function open<N>(
104
127
  opener: string | RegExp | Parser<N, Ctx>,
105
- parser: Parser<N>,
128
+ parser: string | RegExp | Parser<N>,
106
129
  optional?: boolean,
107
130
  backtracks?: readonly number[],
108
131
  ): Parser<N> {
109
- return surround(opener, parser, '', optional, undefined, undefined, backtracks);
132
+ return surround(opener, parser as Parser<N>, '', optional, undefined, undefined, backtracks);
110
133
  }
111
134
  export function close<P extends Parser<unknown>>(
112
135
  parser: P,
@@ -114,13 +137,19 @@ export function close<P extends Parser<unknown>>(
114
137
  optional?: boolean,
115
138
  backtracks?: readonly number[],
116
139
  ): P;
140
+ export function close<P extends Parser<string>>(
141
+ parser: string | RegExp | P,
142
+ closer: string | RegExp | Parser<Node<P>, Context<P>>,
143
+ optional?: boolean,
144
+ backtracks?: readonly number[],
145
+ ): P;
117
146
  export function close<N>(
118
- parser: Parser<N>,
147
+ parser: string | RegExp | Parser<N>,
119
148
  closer: string | RegExp | Parser<N, Ctx>,
120
149
  optional?: boolean,
121
150
  backtracks?: readonly number[],
122
151
  ): Parser<N> {
123
- return surround('', parser, closer, optional, undefined, undefined, backtracks);
152
+ return surround('', parser as Parser<N>, closer, optional, undefined, undefined, backtracks);
124
153
  }
125
154
 
126
155
  const statesize = 2;
@@ -153,7 +182,7 @@ export function setBacktrack(
153
182
  position: number,
154
183
  length: number = 1,
155
184
  ): void {
156
- // 以降バックトラックの可能性がなく記録不要の場合もあるが判別が面倒なので省略
185
+ // バックトラックの可能性がなく記録不要の場合もあるが判別が面倒なので省略
157
186
  const { source } = context;
158
187
  if (position === source.length) return;
159
188
  if (length === 0) return;
@@ -9,17 +9,16 @@ export function normalize(source: string): string {
9
9
  }
10
10
 
11
11
  function format(source: string): string {
12
- return source
13
- .replace(/\r\n?/g, '\n');
12
+ return source.replace(/\r\n?/g, '\n');
14
13
  }
15
14
 
15
+ const invalid = new RegExp([
16
+ /(?![\t\r\n])[\x00-\x1F\x7F]/g.source,
17
+ /(?!\u200D)[\u2006\u200B-\u200F\u202A-\u202F\u2060\uFEFF]|(?<![\u1820\u1821])\u180E/g.source,
18
+ /[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g.source,
19
+ ].join('|'), 'g');
16
20
  function sanitize(source: string): string {
17
- return source
18
- .replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]|(?!\u200D)[\u2006\u200B-\u200F\u202A-\u202F\u2060\uFEFF]|(?<![\u1820\u1821])\u180E/g, UNICODE_REPLACEMENT_CHARACTER)
19
- .replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]?|[\uDC00-\uDFFF]/g, char =>
20
- char.length === 1
21
- ? UNICODE_REPLACEMENT_CHARACTER
22
- : char);
21
+ return source.replace(invalid, UNICODE_REPLACEMENT_CHARACTER);
23
22
  }
24
23
 
25
24
  // https://dev.w3.org/html5/html-author/charref
@@ -110,7 +109,6 @@ assert(unreadableSpecialCharacters.every(c => sanitize(c) === UNICODE_REPLACEMEN
110
109
 
111
110
  // 特殊不可視文字はエディタおよびソースビューアでは等幅および強調表示により可視化する
112
111
  export function escape(source: string): string {
113
- return source
114
- .replace(unreadableEscapeCharacter, char =>
115
- `&${unreadableEscapeHTMLEntityNames[unreadableEscapeCharacters.indexOf(char)]};`);
112
+ return source.replace(unreadableEscapeCharacter, char =>
113
+ `&${unreadableEscapeHTMLEntityNames[unreadableEscapeCharacters.indexOf(char)]};`);
116
114
  }
@@ -361,9 +361,9 @@ describe('Unit: parser/api/parse', () => {
361
361
 
362
362
  it('backtrack', function () {
363
363
  this.timeout(5000);
364
- // 最悪計算量での実行速度はCommonMarkの公式JS実装の32nより速い。
364
+ // 最悪計算量での実行速度はCommonMarkの公式JS実装の32nに対して3倍遅い程度。
365
365
  // 5n = annotation/reference + link + url/math + ruby + text
366
- const source = `((([[[[#$[${'.'.repeat(19997)}`;
366
+ const source = `((([[[[#$[${'.'.repeat(19998)}`;
367
367
  assert.deepStrictEqual(
368
368
  [...parse(source, {}, { resources: { clock: 100000, recursions: [100] } }).children]
369
369
  .map(el => el.tagName),
@@ -372,7 +372,7 @@ describe('Unit: parser/api/parse', () => {
372
372
 
373
373
  it('backtrack error', function () {
374
374
  this.timeout(5000);
375
- const source = `((([[[[#$[${'.'.repeat(19997 + 1)}`;
375
+ const source = `((([[[[#$[${'.'.repeat(19998 + 1)}`;
376
376
  assert.deepStrictEqual(
377
377
  [...parse(source, {}, { resources: { clock: 100000, recursions: [100] } }).children]
378
378
  .map(el => el.tagName),
@@ -3,7 +3,6 @@ import { State, Backtrack } from '../../context';
3
3
  import { List, Data } from '../../../combinator/data/parser';
4
4
  import { union, state, constraint, rewrite, surround, convert, fmap, lazy } from '../../../combinator';
5
5
  import { unsafelink } from '../link';
6
- import { str } from '../../source';
7
6
  import { define } from 'typed-dom/dom';
8
7
 
9
8
  // https://example/@user must be a user page or a redirect page going there.
@@ -11,8 +10,8 @@ import { define } from 'typed-dom/dom';
11
10
  export const account: AutolinkParser.AccountParser = lazy(() => rewrite(
12
11
  surround(
13
12
  /(?<![0-9a-z])@/yi,
14
- str(/[0-9a-z](?:(?:[0-9a-z]|-(?=[0-9a-z])){0,61}[0-9a-z])?(?:\.[0-9a-z](?:(?:[0-9a-z]|-(?=[0-9a-z])){0,61}[0-9a-z])?)*\//yi),
15
- str(/[a-z][0-9a-z]*(?:[-.][0-9a-z]+)*(?![-.]?[0-9a-z@#]|>>|:\S)/yi),
13
+ /[0-9a-z](?:(?:[0-9a-z]|-(?=[0-9a-z])){0,61}[0-9a-z])?(?:\.[0-9a-z](?:(?:[0-9a-z]|-(?=[0-9a-z])){0,61}[0-9a-z])?)*\//yi,
14
+ /[a-z][0-9a-z]*(?:[-.][0-9a-z]+)*(?![-.]?[0-9a-z@#]|>>|:\S)/yi,
16
15
  true, undefined, undefined,
17
16
  [3 | Backtrack.autolink]),
18
17
  constraint(State.autolink, state(State.autolink, fmap(convert(
@@ -3,7 +3,6 @@ import { State, Backtrack } from '../../context';
3
3
  import { List, Data } from '../../../combinator/data/parser';
4
4
  import { union, state, constraint, rewrite, open, convert, fmap, lazy } from '../../../combinator';
5
5
  import { unsafelink } from '../link';
6
- import { str } from '../../source';
7
6
  import { define } from 'typed-dom/dom';
8
7
 
9
8
  // Timeline(pseudonym): user/tid
@@ -19,7 +18,7 @@ import { define } from 'typed-dom/dom';
19
18
  export const anchor: AutolinkParser.AnchorParser = lazy(() => rewrite(
20
19
  open(
21
20
  /(?<![0-9a-z])>>/yi,
22
- str(/(?:[a-z][0-9a-z]*(?:-[0-9a-z]+)*\/)?[0-9a-z]+(?:-[0-9a-z]+)*(?!-?[0-9a-z@#]|>>|:\S)/yi),
21
+ /(?:[a-z][0-9a-z]*(?:-[0-9a-z]+)*\/)?[0-9a-z]+(?:-[0-9a-z]+)*(?!-?[0-9a-z@#]|>>|:\S)/yi,
23
22
  false,
24
23
  [3 | Backtrack.autolink]),
25
24
  constraint(State.autolink, state(State.autolink, fmap(convert(
@@ -13,18 +13,18 @@ export const channel: AutolinkParser.ChannelParser = lazy(() => rewrite(
13
13
  sequence([
14
14
  surround(
15
15
  /(?<![0-9a-z])@/yi,
16
- str(/[0-9a-z](?:(?:[0-9a-z]|-(?=[0-9a-z])){0,61}[0-9a-z])?(?:\.[0-9a-z](?:(?:[0-9a-z]|-(?=[0-9a-z])){0,61}[0-9a-z])?)*\//yi),
17
- str(/[a-z][0-9a-z]*(?:[-.][0-9a-z]+)*(?![-.]?[0-9a-z@]|>>|:\S)/yi),
16
+ /[0-9a-z](?:(?:[0-9a-z]|-(?=[0-9a-z])){0,61}[0-9a-z])?(?:\.[0-9a-z](?:(?:[0-9a-z]|-(?=[0-9a-z])){0,61}[0-9a-z])?)*\//yi,
17
+ /[a-z][0-9a-z]*(?:[-.][0-9a-z]+)*(?![-.]?[0-9a-z@]|>>|:\S)/yi,
18
18
  true, undefined, undefined,
19
19
  [3 | Backtrack.autolink]),
20
20
  some(verify(surround(
21
21
  '#',
22
22
  str(new RegExp([
23
23
  /(?!['_])(?:[^\p{C}\p{S}\p{P}\s]|emoji|'(?=[0-9A-Za-z])|_(?=[^\p{C}\p{S}\p{P}\s]|emoji))+/yu.source,
24
- ].join('').replace(/emoji/g, emoji.source), 'yu')),
25
- str(new RegExp([
24
+ ].join('|').replace(/emoji/g, emoji.source), 'yu')),
25
+ new RegExp([
26
26
  /(?![0-9a-z@]|>>|:\S|[^\p{C}\p{S}\p{P}\s]|emoji)/yu.source,
27
- ].join('').replace(/emoji/g, emoji.source), 'yu')),
27
+ ].join('|').replace(/emoji/g, emoji.source), 'yu'),
28
28
  false, undefined, undefined,
29
29
  [3 | Backtrack.autolink]),
30
30
  ([{ value }]) => !/^[0-9]{1,4}$|^[0-9]{5}/.test(value as string))),
@@ -4,17 +4,16 @@ import { List, Data } from '../../../combinator/data/parser';
4
4
  import { union, state, constraint, rewrite, open, convert, fmap, lazy } from '../../../combinator';
5
5
  import { unsafelink } from '../link';
6
6
  import { emoji } from './hashtag';
7
- import { str } from '../../source';
8
7
  import { define } from 'typed-dom/dom';
9
8
 
10
9
  export const hashnum: AutolinkParser.HashnumParser = lazy(() => rewrite(
11
10
  open(
12
11
  new RegExp([
13
- /(?<![^\p{C}\p{S}\p{P}\s]|emoji)#/yiu.source,
14
- ].join('').replace(/emoji/g, emoji.source), 'yu'),
15
- str(new RegExp([
12
+ /(?<![^\p{C}\p{S}\p{P}\s]|emoji)#/yu.source,
13
+ ].join('|').replace(/emoji/g, emoji.source), 'yu'),
14
+ new RegExp([
16
15
  /[0-9]{1,9}(?![0-9a-z@#]|>>|:\S|[^\p{C}\p{S}\p{P}\s]|emoji)/yu.source,
17
- ].join('').replace(/emoji/g, emoji.source), 'yu')),
16
+ ].join('|').replace(/emoji/g, emoji.source), 'yu'),
18
17
  false,
19
18
  [1 | Backtrack.autolink]),
20
19
  constraint(State.autolink, state(State.autolink, fmap(convert(
@@ -14,14 +14,14 @@ export const emoji = /\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Emoji_Presen
14
14
  export const hashtag: AutolinkParser.HashtagParser = lazy(() => rewrite(
15
15
  verify(surround(
16
16
  new RegExp([
17
- /(?<![^\p{C}\p{S}\p{P}\s]|emoji)#/yiu.source,
18
- ].join('').replace(/emoji/g, emoji.source), 'yu'),
17
+ /(?<![^\p{C}\p{S}\p{P}\s]|emoji)#/yu.source,
18
+ ].join('|').replace(/emoji/g, emoji.source), 'yu'),
19
19
  str(new RegExp([
20
20
  /(?!['_])(?:[^\p{C}\p{S}\p{P}\s]|emoji|'(?=[0-9A-Za-z])|_(?=[^\p{C}\p{S}\p{P}\s]|emoji))+/yu.source,
21
- ].join('').replace(/emoji/g, emoji.source), 'yu')),
22
- str(new RegExp([
21
+ ].join('|').replace(/emoji/g, emoji.source), 'yu')),
22
+ new RegExp([
23
23
  /(?![0-9a-z@#]|>>|:\S|[^\p{C}\p{S}\p{P}\s]|emoji)/yu.source,
24
- ].join('').replace(/emoji/g, emoji.source), 'yu')),
24
+ ].join('|').replace(/emoji/g, emoji.source), 'yu'),
25
25
  false, undefined, undefined,
26
26
  [3 | Backtrack.autolink]),
27
27
  ([{ value }]) => !/^[0-9]{1,4}$|^[0-9]{5}/.test(value)),
@@ -1,35 +1,47 @@
1
1
  import { AutolinkParser } from '../inline';
2
2
  import { State } from '../context';
3
- import { union, state, validate, lazy } from '../../combinator';
3
+ import { state, lazy } from '../../combinator';
4
4
  import { url, lineurl } from './autolink/url';
5
5
  import { email } from './autolink/email';
6
6
  import { channel } from './autolink/channel';
7
7
  import { account } from './autolink/account';
8
- import { hashtag, emoji } from './autolink/hashtag';
8
+ import { hashtag } from './autolink/hashtag';
9
9
  import { hashnum } from './autolink/hashnum';
10
10
  import { anchor } from './autolink/anchor';
11
+ import { isAlphanumeric } from '../source/text';
11
12
 
12
13
  export const autolink: AutolinkParser = lazy(() =>
13
- validate(new RegExp([
14
- /(?<![0-9a-z])@/yi.source,
15
- /(?<![^\p{C}\p{S}\p{P}\s]|emoji)#/yiu.source,
16
- /(?<![0-9a-z])>>/yi.source,
17
- /(?<![0-9a-z][.+-]?|[@#])!?[0-9a-z]/yi.source,
18
- ].join('|').replace(/emoji/g, emoji.source), 'yiu'),
19
14
  state(~State.autolink,
20
- union([
21
- lineurl,
22
- url,
23
- email,
24
- // Escape unmatched email-like strings.
25
- //str(/[0-9a-z]+(?:[_.+-][0-9a-z]+[:@]?|:|@(?=@))*/yi),
26
- channel,
27
- account,
28
- // Escape unmatched account-like strings.
29
- //str(/@+(?:[0-9a-z]+(?:[_.+-][0-9a-z]+)*)?/yi),
30
- hashtag,
31
- hashnum,
32
- // Escape unmatched hashtag-like strings.
33
- //str(new RegExp(/#+(?:(?:[^\p{C}\p{S}\p{P}\s]|emoji)+(?:['_.+-](?:[^\p{C}\p{S}\p{P}\s]|emoji)+)*)?/yu.source.replace(/emoji/g, emoji), 'yu')),
34
- anchor,
35
- ]))));
15
+ input => {
16
+ const { context: { source, position } } = input;
17
+ if (position === source.length) return;
18
+ const fst = source[position];
19
+ switch (fst) {
20
+ case '@':
21
+ return channel(input) || account(input);
22
+ case '#':
23
+ return hashtag(input) || hashnum(input);
24
+ case '>':
25
+ return anchor(input);
26
+ case '!':
27
+ if (!source.startsWith('http', position + 1)) break;
28
+ if (position === 0) return lineurl(input);
29
+ switch (source[position - 1]) {
30
+ case '\r':
31
+ case '\n':
32
+ return lineurl(input);
33
+ }
34
+ break;
35
+ case 'h':
36
+ if (!source.startsWith('http', position)) return;
37
+ if (position === 0) return lineurl(input) || url(input) || email(input);
38
+ switch (source[position - 1]) {
39
+ case '\r':
40
+ case '\n':
41
+ return lineurl(input) || url(input) || email(input);
42
+ }
43
+ return url(input) || email(input);
44
+ default:
45
+ if (isAlphanumeric(fst)) return email(input);
46
+ }
47
+ }));
@@ -23,7 +23,7 @@ export const html: HTMLParser = lazy(() => validate(/<[a-z]+(?=[ >])/yi,
23
23
  union([
24
24
  surround(
25
25
  // https://html.spec.whatwg.org/multipage/syntax.html#void-elements
26
- str(/<(?:area|base|br|col|embed|hr|img|input|link|meta|source|track|wbr)(?=[ >])/yi),
26
+ str(/<(?:area|base|br|col|embed|hr|img|input|link|meta|source|track|wbr)(?=[ >])/y),
27
27
  some(union([attribute])),
28
28
  open(str(/ ?/y), str('>'), true),
29
29
  true,
@@ -23,7 +23,7 @@ export const htmlentity: HTMLEntityParser = fmap(
23
23
  : new Data(html('span', {
24
24
  class: 'invalid',
25
25
  ...invalid('htmlentity', 'syntax', 'Invalid HTML entity'),
26
- }, value))
26
+ }, value))
27
27
  ]));
28
28
 
29
29
  const parser = (el => (entity: string): string => {
@@ -5,6 +5,7 @@ import { reference } from './inline/reference';
5
5
  import { template } from './inline/template';
6
6
  import { remark } from './inline/remark';
7
7
  import { extension } from './inline/extension';
8
+ import { label } from './inline/extension/label';
8
9
  import { textlink } from './inline/link';
9
10
  import { ruby } from './inline/ruby';
10
11
  import { html } from './inline/html';
@@ -94,7 +95,7 @@ export const inline: InlineParser = lazy(() => union([
94
95
  return html(input);
95
96
  case '$':
96
97
  if (source[position + 1] === '{') return math(input);
97
- return extension(input)
98
+ return label(input)
98
99
  || math(input);
99
100
  case '+':
100
101
  if (source[position + 1] === '+') return insertion(input);
@@ -185,12 +185,12 @@ export function backToEmailHead(source: string, position: number, index: number)
185
185
  ? delim
186
186
  : index;
187
187
  }
188
- function isAlphanumeric(char: string): boolean {
188
+ export function isAlphanumeric(char: string): boolean {
189
189
  assert(char.length === 1);
190
190
  if (char < '0' || '\x7F' < char) return false;
191
191
  return '0' <= char && char <= '9'
192
- || 'a' <= char && char <= 'z'
193
- || 'A' <= char && char <= 'Z';
192
+ || 'A' <= char && char <= 'Z'
193
+ || 'a' <= char && char <= 'z';
194
194
  }
195
195
 
196
196
  //const dict = new class {