securemark 0.260.4 → 0.260.5

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.260.5
4
+
5
+ - Refactoring.
6
+
3
7
  ## 0.260.4
4
8
 
5
9
  - Refactoring.
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- /*! securemark v0.260.4 https://github.com/falsandtru/securemark | (c) 2017, falsandtru | UNLICENSED License */
1
+ /*! securemark v0.260.5 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("DOMPurify"), require("Prism"));
@@ -2888,12 +2888,16 @@ const parser_1 = __webpack_require__(6728);
2888
2888
  const memo_1 = __webpack_require__(1090);
2889
2889
 
2890
2890
  function reset(base, parser) {
2891
+ if (!('memo' in base)) {
2892
+ base.memo = global_1.undefined;
2893
+ }
2894
+
2891
2895
  const changes = global_1.Object.entries(base);
2892
2896
  const values = (0, global_1.Array)(changes.length);
2893
2897
  return ({
2894
2898
  source,
2895
2899
  context
2896
- }) => apply(parser, source, (0, alias_1.ObjectCreate)(context), changes, values);
2900
+ }) => apply(parser, source, (0, alias_1.ObjectCreate)(context), changes, values, true);
2897
2901
  }
2898
2902
 
2899
2903
  exports.reset = reset;
@@ -2909,18 +2913,25 @@ function context(base, parser) {
2909
2913
 
2910
2914
  exports.context = context;
2911
2915
 
2912
- function apply(parser, source, context, changes, values) {
2916
+ function apply(parser, source, context, changes, values, reset = false) {
2913
2917
  if (context) for (let i = 0; i < changes.length; ++i) {
2914
2918
  const change = changes[i];
2915
2919
  const prop = change[0];
2916
2920
 
2917
2921
  switch (prop) {
2918
2922
  case 'resources':
2919
- if (prop in context && !(0, alias_1.hasOwnProperty)(context, prop)) break; // @ts-expect-error
2920
-
2923
+ if (!reset) break;
2924
+ if (prop in context && !(0, alias_1.hasOwnProperty)(context, prop)) break;
2921
2925
  context[prop] = (0, alias_1.ObjectCreate)(change[1]);
2922
2926
  break;
2923
2927
 
2928
+ case 'memo':
2929
+ if (!reset) break;
2930
+ context.memo = new memo_1.Memo({
2931
+ targets: context.memo?.targets
2932
+ });
2933
+ break;
2934
+
2924
2935
  default:
2925
2936
  values[i] = context[prop];
2926
2937
  context[prop] = change[1];
@@ -2935,8 +2946,11 @@ function apply(parser, source, context, changes, values) {
2935
2946
  const prop = change[0];
2936
2947
 
2937
2948
  switch (prop) {
2938
- case 'resources':
2939
- break;
2949
+ case 'resources': // @ts-expect-error
2950
+
2951
+ case 'memo':
2952
+ if (!reset) break;
2953
+ // fallthrough
2940
2954
 
2941
2955
  default:
2942
2956
  context[prop] = values[i];
@@ -2953,26 +2967,25 @@ function syntax(syntax, prec, cost, state, parser) {
2953
2967
  }) => {
2954
2968
  if (source === '') return;
2955
2969
  const memo = context.memo ??= new memo_1.Memo();
2956
- context.memorable ??= ~0;
2957
2970
  context.offset ??= 0;
2958
2971
  const position = source.length + context.offset;
2959
- const st0 = context.state ?? 0;
2960
- const st1 = context.state = st0 | state;
2961
- const cache = syntax && memo.get(position, syntax, st1);
2972
+ const stateOuter = context.state ?? 0;
2973
+ const stateInner = context.state = stateOuter | state;
2974
+ const cache = syntax && stateInner & memo.targets && memo.get(position, syntax, stateInner);
2962
2975
  const result = cache ? cache.length === 0 ? global_1.undefined : [cache[0], source.slice(cache[1])] : parser({
2963
2976
  source,
2964
2977
  context
2965
2978
  });
2966
2979
 
2967
- if (syntax && st0 & context.memorable) {
2968
- cache ?? memo.set(position, syntax, st1, (0, parser_1.eval)(result), source.length - (0, parser_1.exec)(result, '').length);
2980
+ if (syntax && stateOuter & memo.targets) {
2981
+ cache ?? memo.set(position, syntax, stateInner, (0, parser_1.eval)(result), source.length - (0, parser_1.exec)(result, '').length);
2969
2982
  }
2970
2983
 
2971
- if (result && !st0 && memo.length >= position + 2) {
2984
+ if (result && !stateOuter && memo.length >= position + 2) {
2972
2985
  memo.clear(position + 2);
2973
2986
  }
2974
2987
 
2975
- context.state = st0;
2988
+ context.state = stateOuter;
2976
2989
  return result;
2977
2990
  }));
2978
2991
  }
@@ -3200,8 +3213,11 @@ Object.defineProperty(exports, "__esModule", ({
3200
3213
  exports.Memo = void 0;
3201
3214
 
3202
3215
  class Memo {
3203
- constructor() {
3216
+ constructor({
3217
+ targets = ~0
3218
+ } = {}) {
3204
3219
  this.memory = [];
3220
+ this.targets = targets;
3205
3221
  }
3206
3222
 
3207
3223
  get length() {
@@ -3595,6 +3611,8 @@ const global_1 = __webpack_require__(4128);
3595
3611
 
3596
3612
  const parser_1 = __webpack_require__(6728);
3597
3613
 
3614
+ const memo_1 = __webpack_require__(1090);
3615
+
3598
3616
  const segment_1 = __webpack_require__(9002);
3599
3617
 
3600
3618
  const header_1 = __webpack_require__(5702);
@@ -3616,9 +3634,11 @@ const array_1 = __webpack_require__(8112);
3616
3634
  function bind(target, settings) {
3617
3635
  let context = { ...settings,
3618
3636
  host: settings.host ?? new url_1.ReadonlyURL(global_1.location.pathname, global_1.location.origin),
3619
- memorable: 236
3620
- /* State.backtrackable */
3637
+ memo: new memo_1.Memo({
3638
+ targets: 236
3639
+ /* State.backtrackers */
3621
3640
 
3641
+ })
3622
3642
  };
3623
3643
  if (context.host?.origin === 'null') throw new Error(`Invalid host: ${context.host.href}`);
3624
3644
  const blocks = [];
@@ -3958,6 +3978,8 @@ const global_1 = __webpack_require__(4128);
3958
3978
 
3959
3979
  const parser_1 = __webpack_require__(6728);
3960
3980
 
3981
+ const memo_1 = __webpack_require__(1090);
3982
+
3961
3983
  const segment_1 = __webpack_require__(9002);
3962
3984
 
3963
3985
  const header_1 = __webpack_require__(5702);
@@ -3988,9 +4010,11 @@ function parse(source, opts = {}, context) {
3988
4010
  ...(context?.resources && {
3989
4011
  resources: context.resources
3990
4012
  }),
3991
- memorable: 236
3992
- /* State.backtrackable */
4013
+ memo: new memo_1.Memo({
4014
+ targets: 236
4015
+ /* State.backtrackers */
3993
4016
 
4017
+ })
3994
4018
  };
3995
4019
  if (context.host?.origin === 'null') throw new Error(`Invalid host: ${context.host.href}`);
3996
4020
  const node = (0, dom_1.frag)();
@@ -5948,8 +5972,8 @@ exports.autolink = (0, combinator_1.fmap)((0, combinator_1.validate)(/^(?:[@#>0-
5948
5972
  /* State.autolink */
5949
5973
  , false, (0, combinator_1.syntax)(2
5950
5974
  /* Syntax.autolink */
5951
- , 1, 1, 0
5952
- /* State.none */
5975
+ , 1, 1, ~1
5976
+ /* State.shortcut */
5953
5977
  , (0, combinator_1.some)((0, combinator_1.union)([url_1.url, email_1.email, // Escape unmatched email-like strings.
5954
5978
  (0, source_1.str)(/^[0-9a-z]+(?:[.+_-][0-9a-z]+)*(?:@(?:[0-9a-z]+(?:[.-][0-9a-z]+)*)?)*/i), channel_1.channel, account_1.account, // Escape unmatched account-like strings.
5955
5979
  (0, source_1.str)(/^@+[0-9a-z]*(?:-[0-9a-z]+)*/i), // Escape invalid leading characters.
@@ -6502,10 +6526,12 @@ const dom_1 = __webpack_require__(3252);
6502
6526
 
6503
6527
  exports.index = (0, combinator_1.lazy)(() => (0, combinator_1.validate)('[#', (0, combinator_1.fmap)((0, indexee_1.indexee)((0, combinator_1.surround)('[#', (0, combinator_1.constraint)(32
6504
6528
  /* State.index */
6505
- , false, (0, combinator_1.syntax)(1024
6529
+ , false, (0, combinator_1.syntax)(2048
6506
6530
  /* Syntax.index */
6507
- , 2, 1, 254
6508
- /* State.linkable */
6531
+ , 2, 1, 250
6532
+ /* State.linkers */
6533
+ | 4
6534
+ /* State.media */
6509
6535
  , (0, visibility_1.startTight)((0, combinator_1.open)((0, source_1.stropt)(/^\|?/), (0, visibility_1.trimBlankEnd)((0, combinator_1.some)((0, combinator_1.union)([signature, inline_1.inline]), ']', [[/^\\?\n/, 9], [']', 2]])), true)))), ']', false, ([, ns], rest) => [[(0, dom_1.html)('a', (0, dom_1.defrag)(ns))], rest])), ([el]) => [(0, dom_1.define)(el, {
6510
6536
  id: el.id ? null : global_1.undefined,
6511
6537
  class: 'index',
@@ -6707,7 +6733,7 @@ const array_1 = __webpack_require__(8112); // Don't use the symbols already used
6707
6733
  // All syntax surrounded by square brackets shouldn't contain line breaks.
6708
6734
 
6709
6735
 
6710
- exports.placeholder = (0, combinator_1.lazy)(() => (0, combinator_1.validate)(['[:', '[^'], (0, combinator_1.surround)((0, source_1.str)(/^\[[:^]/), (0, combinator_1.syntax)(512
6736
+ exports.placeholder = (0, combinator_1.lazy)(() => (0, combinator_1.validate)(['[:', '[^'], (0, combinator_1.surround)((0, source_1.str)(/^\[[:^]/), (0, combinator_1.syntax)(1024
6711
6737
  /* Syntax.placeholder */
6712
6738
  , 2, 1, 0
6713
6739
  /* State.none */
@@ -6928,8 +6954,10 @@ const textlink = (0, combinator_1.lazy)(() => (0, combinator_1.constraint)(8
6928
6954
  /* State.link */
6929
6955
  , false, (0, combinator_1.syntax)(256
6930
6956
  /* Syntax.link */
6931
- , 2, 10, 254
6932
- /* State.linkable */
6957
+ , 2, 10, 250
6958
+ /* State.linkers */
6959
+ | 4
6960
+ /* State.media */
6933
6961
  , (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)([inline_1.inline]), ']', [[/^\\?\n/, 9], [']', 2]]), ']', true)), (0, combinator_1.dup)((0, combinator_1.surround)(/^{(?![{}])/, (0, combinator_1.inits)([exports.uri, (0, combinator_1.some)(exports.option)]), /^[^\S\n]*}/))])), ([params, content = []], rest, context) => {
6934
6962
  return parse(content, params, rest, context);
6935
6963
  }))));
@@ -6939,10 +6967,8 @@ const medialink = (0, combinator_1.lazy)(() => (0, combinator_1.constraint)(8
6939
6967
  /* State.media */
6940
6968
  , false, (0, combinator_1.syntax)(256
6941
6969
  /* Syntax.link */
6942
- , 2, 10, 254
6943
- /* State.linkable */
6944
- ^ 4
6945
- /* State.media */
6970
+ , 2, 10, 250
6971
+ /* State.linkers */
6946
6972
  , (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)(/^{(?![{}])/, (0, combinator_1.inits)([exports.uri, (0, combinator_1.some)(exports.option)]), /^[^\S\n]*}/))])), ([params, content = []], rest, context) => parse(content, params, rest, context)))));
6947
6973
  exports.unsafelink = (0, combinator_1.lazy)(() => (0, combinator_1.creation)(10, (0, combinator_1.precedence)(2, (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)(/^{(?![{}])/, (0, combinator_1.inits)([exports.uri, (0, combinator_1.some)(exports.option)]), /^[^\S\n]*}/))])), ([params, content = []], rest, context) => parse(content, params, rest, context)))));
6948
6974
  exports.uri = (0, combinator_1.union)([(0, combinator_1.open)(/^[^\S\n]+/, (0, source_1.str)(/^\S+/)), (0, source_1.str)(/^[^\s{}]+/)]);
@@ -7172,8 +7198,8 @@ exports.media = (0, combinator_1.lazy)(() => (0, combinator_1.validate)(['![', '
7172
7198
  /* State.media */
7173
7199
  , false, (0, combinator_1.syntax)(64
7174
7200
  /* Syntax.media */
7175
- , 2, 10, 0
7176
- /* State.none */
7201
+ , 2, 10, ~8
7202
+ /* State.link */
7177
7203
  , (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.some)((0, combinator_1.union)([htmlentity_1.unsafehtmlentity, bracket, source_1.txt]), ']', [[/^\\?\n/, 9]]), ']', true)), (0, combinator_1.dup)((0, combinator_1.surround)(/^{(?![{}])/, (0, combinator_1.inits)([link_1.uri, (0, combinator_1.some)(option)]), /^[^\S\n]*}/))]), ([as, bs]) => bs ? [[as.join('').trim() || as.join('')], bs] : [[''], as]), ([[text]]) => text === '' || text.trim() !== ''), ([[text], params], rest, context) => {
7178
7204
  const INSECURE_URI = params.shift();
7179
7205
  const url = new url_1.ReadonlyURL((0, link_1.resolve)(INSECURE_URI, context.host ?? global_1.location, context.url ?? context.host ?? global_1.location), context.host?.href || global_1.location.href);
@@ -7195,7 +7221,7 @@ exports.media = (0, combinator_1.lazy)(() => (0, combinator_1.validate)(['![', '
7195
7221
  if (context.state & 8
7196
7222
  /* State.link */
7197
7223
  ) return [[el], rest];
7198
- if (cache && cache.tagName !== 'IMG') return (0, combinator_1.creation)(10, (..._) => [[el], rest])({
7224
+ if (cache && cache.tagName !== 'IMG') return (0, combinator_1.creation)(10, _ => [[el], rest])({
7199
7225
  source: '!',
7200
7226
  context
7201
7227
  });
@@ -7279,7 +7305,7 @@ const dom_1 = __webpack_require__(3252);
7279
7305
 
7280
7306
  exports.reference = (0, combinator_1.lazy)(() => (0, combinator_1.surround)('[[', (0, combinator_1.constraint)(64
7281
7307
  /* State.reference */
7282
- , false, (0, combinator_1.syntax)(4096
7308
+ , false, (0, combinator_1.syntax)(8192
7283
7309
  /* Syntax.reference */
7284
7310
  , 6, 1, 128
7285
7311
  /* State.annotation */
@@ -7335,11 +7361,17 @@ const dom_1 = __webpack_require__(3252);
7335
7361
 
7336
7362
  const array_1 = __webpack_require__(8112);
7337
7363
 
7338
- exports.ruby = (0, combinator_1.lazy)(() => (0, combinator_1.validate)('[', (0, combinator_1.syntax)(0
7339
- /* Syntax.none */
7340
- , 2, 1, 0
7341
- /* State.none */
7342
- , (0, combinator_1.fmap)((0, combinator_1.verify)((0, combinator_1.sequence)([(0, combinator_1.surround)('[', (0, combinator_1.focus)(/^(?:\\[^\n]|[^\\[\](){}"\n])+(?=]\()/, text), ']'), (0, combinator_1.surround)('(', (0, combinator_1.focus)(/^(?:\\[^\n]|[^\\[\](){}"\n])+(?=\))/, text), ')')]), ([texts]) => (0, visibility_1.isStartTightNodes)(texts)), ([texts, rubies]) => {
7364
+ exports.ruby = (0, combinator_1.lazy)(() => (0, combinator_1.validate)('[', (0, combinator_1.syntax)(512
7365
+ /* Syntax.ruby */
7366
+ , 2, 1, -1
7367
+ /* State.all */
7368
+ , (0, combinator_1.fmap)((0, combinator_1.verify)((0, combinator_1.fmap)((0, combinator_1.sequence)([(0, combinator_1.surround)('[', (0, source_1.str)(/^(?:\\[^\n]|[^\\[\](){}"\n])+/), ']'), (0, combinator_1.surround)('(', (0, source_1.str)(/^(?:\\[^\n]|[^\\[\](){}"\n])+/), ')')]), ([texts, rubies], _, context) => [(0, parser_1.eval)(text({
7369
+ source: texts,
7370
+ context
7371
+ }), [])[0] ?? '', (0, parser_1.eval)(text({
7372
+ source: rubies,
7373
+ context
7374
+ }), [])[0] ?? '']), ([texts, rubies]) => texts && rubies && (0, visibility_1.isStartTightNodes)(texts)), ([texts, rubies]) => {
7343
7375
  texts[texts.length - 1] === '' && texts.pop();
7344
7376
 
7345
7377
  switch (true) {
@@ -7499,8 +7531,8 @@ const array_1 = __webpack_require__(8112);
7499
7531
 
7500
7532
  exports.template = (0, combinator_1.lazy)(() => (0, combinator_1.surround)('{{', (0, combinator_1.syntax)(0
7501
7533
  /* Syntax.none */
7502
- , 2, 1, 0
7503
- /* State.none */
7534
+ , 2, 1, -1
7535
+ /* State.all */
7504
7536
  , (0, combinator_1.some)((0, combinator_1.union)([bracket, source_1.escsource]), '}')), '}}', true, ([, ns = []], rest) => [[(0, dom_1.html)('span', {
7505
7537
  class: 'template'
7506
7538
  }, `{{${ns.join('').replace(/\x1B/g, '')}}}`)], rest]));
package/markdown.d.ts CHANGED
@@ -829,8 +829,8 @@ export namespace MarkdownParser {
829
829
  // [AB](a b)
830
830
  Inline<'ruby'>,
831
831
  Parser<HTMLElement, Context, [
832
- RubyParser.TextParser,
833
- RubyParser.TextParser,
832
+ SourceParser.StrParser,
833
+ SourceParser.StrParser,
834
834
  ]> {
835
835
  }
836
836
  export namespace RubyParser {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securemark",
3
- "version": "0.260.4",
3
+ "version": "0.260.5",
4
4
  "description": "Secure markdown renderer working on browsers for user input data.",
5
5
  "private": false,
6
6
  "homepage": "https://github.com/falsandtru/securemark",
@@ -1,4 +1,8 @@
1
1
  export class Memo {
2
+ constructor({ targets = ~0 } = {}) {
3
+ this.targets = targets;
4
+ }
5
+ public readonly targets: number;
2
6
  private readonly memory: Record<string, readonly [any[], number] | readonly []>[/* pos */] = [];
3
7
  public get length(): number {
4
8
  return this.memory.length;
@@ -5,12 +5,15 @@ import { Memo } from './context/memo';
5
5
 
6
6
  export function reset<P extends Parser<unknown>>(base: Context<P>, parser: P): P;
7
7
  export function reset<T>(base: Ctx, parser: Parser<T>): Parser<T> {
8
+ if (!('memo' in base)) {
9
+ base.memo = undefined;
10
+ }
8
11
  assert(Object.getPrototypeOf(base) === Object.prototype);
9
12
  assert(Object.freeze(base));
10
13
  const changes = Object.entries(base);
11
14
  const values = Array(changes.length);
12
15
  return ({ source, context }) =>
13
- apply(parser, source, ObjectCreate(context), changes, values);
16
+ apply(parser, source, ObjectCreate(context), changes, values, true);
14
17
  }
15
18
 
16
19
  export function context<P extends Parser<unknown>>(base: Context<P>, parser: P): P;
@@ -23,18 +26,22 @@ export function context<T>(base: Ctx, parser: Parser<T>): Parser<T> {
23
26
  apply(parser, source, context, changes, values);
24
27
  }
25
28
 
26
- function apply<P extends Parser<unknown>>(parser: P, source: string, context: Context<P>, changes: [string, any][], values: any[]): Result<Tree<P>>;
27
- function apply<T>(parser: Parser<T>, source: string, context: Ctx, changes: [string, any][], values: any[]): Result<T> {
29
+ function apply<P extends Parser<unknown>>(parser: P, source: string, context: Context<P>, changes: [string, any][], values: any[], reset?: boolean): Result<Tree<P>>;
30
+ function apply<T>(parser: Parser<T>, source: string, context: Ctx, changes: [string, any][], values: any[], reset = false): Result<T> {
28
31
  if (context) for (let i = 0; i < changes.length; ++i) {
29
32
  const change = changes[i];
30
33
  const prop = change[0];
31
34
  switch (prop) {
32
35
  case 'resources':
36
+ if (!reset) break;
33
37
  assert(typeof change[1] === 'object');
34
38
  assert(context[prop] || !(prop in context));
35
39
  if (prop in context && !hasOwnProperty(context, prop)) break;
36
- // @ts-expect-error
37
- context[prop] = ObjectCreate(change[1]);
40
+ context[prop as string] = ObjectCreate(change[1]);
41
+ break;
42
+ case 'memo':
43
+ if (!reset) break;
44
+ context.memo = new Memo({ targets: context.memo?.targets });
38
45
  break;
39
46
  default:
40
47
  values[i] = context[prop];
@@ -47,7 +54,10 @@ function apply<T>(parser: Parser<T>, source: string, context: Ctx, changes: [str
47
54
  const prop = change[0];
48
55
  switch (prop) {
49
56
  case 'resources':
50
- break;
57
+ // @ts-expect-error
58
+ case 'memo':
59
+ if (!reset) break;
60
+ // fallthrough
51
61
  default:
52
62
  context[prop] = values[i];
53
63
  values[i] = undefined;
@@ -61,26 +71,25 @@ export function syntax<T>(syntax: number, prec: number, cost: number, state: num
61
71
  return creation(cost, precedence(prec, ({ source, context }) => {
62
72
  if (source === '') return;
63
73
  const memo = context.memo ??= new Memo();
64
- context.memorable ??= ~0;
65
74
  context.offset ??= 0;
66
75
  const position = source.length + context.offset!;
67
- const st0 = context.state ?? 0;
68
- const st1 = context.state = st0 | state;
69
- const cache = syntax && memo.get(position, syntax, st1);
76
+ const stateOuter = context.state ?? 0;
77
+ const stateInner = context.state = stateOuter | state;
78
+ const cache = syntax && stateInner & memo.targets && memo.get(position, syntax, stateInner);
70
79
  const result: Result<T> = cache
71
80
  ? cache.length === 0
72
81
  ? undefined
73
82
  : [cache[0], source.slice(cache[1])]
74
83
  : parser!({ source, context });
75
- if (syntax && st0 & context.memorable!) {
76
- cache ?? memo.set(position, syntax, st1, eval(result), source.length - exec(result, '').length);
77
- assert.deepStrictEqual(cache && cache, cache && memo.get(position, syntax, st1));
84
+ if (syntax && stateOuter & memo.targets) {
85
+ cache ?? memo.set(position, syntax, stateInner, eval(result), source.length - exec(result, '').length);
86
+ assert.deepStrictEqual(cache && cache, cache && memo.get(position, syntax, stateInner));
78
87
  }
79
- if (result && !st0 && memo.length! >= position + 2) {
80
- assert(!(st0 & context.memorable!));
88
+ if (result && !stateOuter && memo.length! >= position + 2) {
89
+ assert(!(stateOuter & memo.targets));
81
90
  memo.clear(position + 2);
82
91
  }
83
- context.state = st0;
92
+ context.state = stateOuter;
84
93
  return result;
85
94
  }));
86
95
  }
@@ -20,7 +20,6 @@ export interface Ctx {
20
20
  precedence?: number;
21
21
  delimiters?: Delimiters;
22
22
  state?: number;
23
- memorable?: number;
24
23
  memo?: Memo;
25
24
  }
26
25
  export type Tree<P extends Parser<unknown>> = P extends Parser<infer T> ? T : never;
@@ -2,6 +2,7 @@ import { undefined, location } from 'spica/global';
2
2
  import { ParserSettings, Progress } from '../../..';
3
3
  import { MarkdownParser } from '../../../markdown';
4
4
  import { eval } from '../../combinator/data/parser';
5
+ import { Memo } from '../../combinator/data/parser/context/memo';
5
6
  import { segment, validate, MAX_INPUT_SIZE } from '../segment';
6
7
  import { header } from '../header';
7
8
  import { block } from '../block';
@@ -24,7 +25,7 @@ export function bind(target: DocumentFragment | HTMLElement | ShadowRoot, settin
24
25
  let context: MarkdownParser.Context = {
25
26
  ...settings,
26
27
  host: settings.host ?? new ReadonlyURL(location.pathname, location.origin),
27
- memorable: State.backtrackable,
28
+ memo: new Memo({ targets: State.backtrackers }),
28
29
  };
29
30
  if (context.host?.origin === 'null') throw new Error(`Invalid host: ${context.host.href}`);
30
31
  assert(!settings.id);
@@ -2,6 +2,7 @@ import { location } from 'spica/global';
2
2
  import { ParserOptions } from '../../..';
3
3
  import { MarkdownParser } from '../../../markdown';
4
4
  import { eval } from '../../combinator/data/parser';
5
+ import { Memo } from '../../combinator/data/parser/context/memo';
5
6
  import { segment, validate, MAX_SEGMENT_SIZE } from '../segment';
6
7
  import { header } from '../header';
7
8
  import { block } from '../block';
@@ -30,7 +31,7 @@ export function parse(source: string, opts: Options = {}, context?: MarkdownPars
30
31
  ...context?.resources && {
31
32
  resources: context.resources,
32
33
  },
33
- memorable: State.backtrackable,
34
+ memo: new Memo({ targets: State.backtrackers }),
34
35
  };
35
36
  if (context.host?.origin === 'null') throw new Error(`Invalid host: ${context.host.href}`);
36
37
  const node = frag();
@@ -1,8 +1,9 @@
1
1
  export const enum Syntax {
2
- reference = 1 << 12,
3
- comment = 1 << 11,
4
- index = 1 << 10,
5
- placeholder = 1 << 9,
2
+ reference = 1 << 13,
3
+ comment = 1 << 12,
4
+ index = 1 << 11,
5
+ placeholder = 1 << 10,
6
+ ruby = 1 << 9,
6
7
  link = 1 << 8,
7
8
  bracket = 1 << 7,
8
9
  media = 1 << 6,
@@ -25,15 +26,15 @@ export const enum State {
25
26
  autolink = 1 << 1,
26
27
  shortcut = 1 << 0,
27
28
  none = 0,
28
- linkable = 0
29
+ all = ~0,
30
+ linkers = 0
29
31
  | State.annotation
30
32
  | State.reference
31
33
  | State.index
32
34
  | State.label
33
35
  | State.link
34
- | State.media
35
36
  | State.autolink,
36
- backtrackable = 0
37
+ backtrackers = 0
37
38
  | State.annotation
38
39
  | State.reference
39
40
  | State.index
@@ -14,7 +14,7 @@ import { stringify } from '../util';
14
14
  export const autolink: AutolinkParser = fmap(
15
15
  validate(/^(?:[@#>0-9a-z]|\S[#>])/i,
16
16
  constraint(State.autolink, false,
17
- syntax(Syntax.autolink, 1, 1, State.none,
17
+ syntax(Syntax.autolink, 1, 1, ~State.shortcut,
18
18
  some(union([
19
19
  url,
20
20
  email,
@@ -13,7 +13,7 @@ import IndexParser = ExtensionParser.IndexParser;
13
13
  export const index: IndexParser = lazy(() => validate('[#', fmap(indexee(surround(
14
14
  '[#',
15
15
  constraint(State.index, false,
16
- syntax(Syntax.index, 2, 1, State.linkable,
16
+ syntax(Syntax.index, 2, 1, State.linkers | State.media,
17
17
  startTight(
18
18
  open(stropt(/^\|?/), trimBlankEnd(some(union([
19
19
  signature,
@@ -25,7 +25,7 @@ export const link: LinkParser = lazy(() => validate(['[', '{'], union([
25
25
 
26
26
  const textlink: LinkParser.TextLinkParser = lazy(() =>
27
27
  constraint(State.link, false,
28
- syntax(Syntax.link, 2, 10, State.linkable,
28
+ syntax(Syntax.link, 2, 10, State.linkers | State.media,
29
29
  bind(reverse(tails([
30
30
  dup(surround(
31
31
  '[',
@@ -41,7 +41,7 @@ const textlink: LinkParser.TextLinkParser = lazy(() =>
41
41
 
42
42
  const medialink: LinkParser.MediaLinkParser = lazy(() =>
43
43
  constraint(State.link | State.media, false,
44
- syntax(Syntax.link, 2, 10, State.linkable ^ State.media,
44
+ syntax(Syntax.link, 2, 10, State.linkers,
45
45
  bind(reverse(sequence([
46
46
  dup(surround(
47
47
  '[',
@@ -21,7 +21,7 @@ Object.setPrototypeOf(optspec, null);
21
21
  export const media: MediaParser = lazy(() => validate(['![', '!{'], open(
22
22
  '!',
23
23
  constraint(State.media, false,
24
- syntax(Syntax.media, 2, 10, State.none,
24
+ syntax(Syntax.media, 2, 10, ~State.link,
25
25
  bind(verify(fmap(tails([
26
26
  dup(surround(
27
27
  '[',
@@ -55,7 +55,7 @@ export const media: MediaParser = lazy(() => validate(['![', '!{'], open(
55
55
  el.style.aspectRatio = el.getAttribute('aspect-ratio')!;
56
56
  }
57
57
  if (context.state! & State.link) return [[el], rest];
58
- if (cache && cache.tagName !== 'IMG') return creation(10, (..._) => [[el!], rest])({ source: '!', context });
58
+ if (cache && cache.tagName !== 'IMG') return creation(10, _ => [[el!], rest])({ source: '!', context });
59
59
  return fmap(
60
60
  unsafelink as MediaParser,
61
61
  ([link]) => [define(link, { class: null, target: '_blank' }, [el])])
@@ -1,20 +1,24 @@
1
1
  import { undefined } from 'spica/global';
2
2
  import { RubyParser } from '../inline';
3
3
  import { eval, exec } from '../../combinator/data/parser';
4
- import { sequence, syntax, creation, validate, verify, focus, surround, lazy, fmap } from '../../combinator';
4
+ import { sequence, syntax, creation, validate, verify, surround, lazy, fmap } from '../../combinator';
5
5
  import { unsafehtmlentity } from './htmlentity';
6
- import { text as txt } from '../source';
6
+ import { text as txt, str } from '../source';
7
7
  import { Syntax, State } from '../context';
8
8
  import { isStartTightNodes } from '../visibility';
9
9
  import { html, defrag } from 'typed-dom/dom';
10
10
  import { unshift, push } from 'spica/array';
11
11
 
12
- export const ruby: RubyParser = lazy(() => validate('[', syntax(Syntax.none, 2, 1, State.none, fmap(verify(
12
+ export const ruby: RubyParser = lazy(() => validate('[', syntax(Syntax.ruby, 2, 1, State.all, fmap(verify(fmap(
13
13
  sequence([
14
- surround('[', focus(/^(?:\\[^\n]|[^\\[\](){}"\n])+(?=]\()/, text), ']'),
15
- surround('(', focus(/^(?:\\[^\n]|[^\\[\](){}"\n])+(?=\))/, text), ')'),
14
+ surround('[', str(/^(?:\\[^\n]|[^\\[\](){}"\n])+/), ']'),
15
+ surround('(', str(/^(?:\\[^\n]|[^\\[\](){}"\n])+/), ')'),
16
16
  ]),
17
- ([texts]) => isStartTightNodes(texts)),
17
+ ([texts, rubies], _, context) => [
18
+ eval(text({ source: texts, context }), [])[0] ?? '',
19
+ eval(text({ source: rubies, context }), [])[0] ?? '',
20
+ ]),
21
+ ([texts, rubies]) => texts && rubies && isStartTightNodes(texts)),
18
22
  ([texts, rubies]) => {
19
23
  texts[texts.length - 1] === '' && texts.pop();
20
24
  switch (true) {
@@ -7,7 +7,7 @@ import { html } from 'typed-dom/dom';
7
7
  import { unshift } from 'spica/array';
8
8
 
9
9
  export const template: TemplateParser = lazy(() => surround(
10
- '{{', syntax(Syntax.none, 2, 1, State.none, some(union([bracket, escsource]), '}')), '}}', true,
10
+ '{{', syntax(Syntax.none, 2, 1, State.all, some(union([bracket, escsource]), '}')), '}}', true,
11
11
  ([, ns = []], rest) => [[html('span', { class: 'template' }, `{{${ns.join('').replace(/\x1B/g, '')}}}`)], rest]));
12
12
 
13
13
  const bracket: TemplateParser.BracketParser = lazy(() => creation(union([