securemark 0.280.1 → 0.280.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.280.2
4
+
5
+ - Refactoring.
6
+
3
7
  ## 0.280.1
4
8
 
5
9
  - Refactoring.
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- /*! securemark v0.280.1 https://github.com/falsandtru/securemark | (c) 2017, falsandtru | UNLICENSED License */
1
+ /*! securemark v0.280.2 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"));
@@ -3325,9 +3325,6 @@ const alias_1 = __webpack_require__(5406);
3325
3325
  const parser_1 = __webpack_require__(6728);
3326
3326
  const memo_1 = __webpack_require__(1090);
3327
3327
  function reset(base, parser) {
3328
- if (!('memo' in base)) {
3329
- base.memo = undefined;
3330
- }
3331
3328
  const changes = Object.entries(base);
3332
3329
  const values = Array(changes.length);
3333
3330
  return ({
@@ -3346,44 +3343,33 @@ function context(base, parser) {
3346
3343
  }
3347
3344
  exports.context = context;
3348
3345
  function apply(parser, source, context, changes, values, reset = false) {
3349
- if (context) for (let i = 0; i < changes.length; ++i) {
3346
+ for (let i = 0; i < changes.length; ++i) {
3350
3347
  const change = changes[i];
3351
3348
  const prop = change[0];
3352
3349
  switch (prop) {
3353
3350
  case 'resources':
3354
- if (!reset) break;
3355
- if (prop in context && !(0, alias_1.hasOwnProperty)(context, prop)) break;
3356
- context[prop] = (0, alias_1.ObjectCreate)(change[1]);
3357
- break;
3358
- // @ts-expect-error
3359
- case 'memo':
3360
- if (!reset) break;
3361
- context.memo = new memo_1.Memo({
3362
- targets: context.memo?.targets
3363
- });
3364
- // fallthrough
3365
- default:
3366
- values[i] = context[prop];
3367
- context[prop] = change[1];
3351
+ context[prop] ??= (0, alias_1.ObjectCreate)(change[1]);
3352
+ continue;
3368
3353
  }
3354
+ values[i] = context[prop];
3355
+ context[prop] = change[1];
3369
3356
  }
3370
3357
  const result = parser({
3371
3358
  source,
3372
3359
  context
3373
3360
  });
3374
- if (context) for (let i = 0; i < changes.length; ++i) {
3361
+ for (let i = 0; i < changes.length; ++i) {
3375
3362
  const change = changes[i];
3376
3363
  const prop = change[0];
3377
3364
  switch (prop) {
3378
3365
  case 'resources':
3379
- // @ts-expect-error
3366
+ break;
3380
3367
  case 'memo':
3381
- if (!reset) break;
3382
- // fallthrough
3383
- default:
3384
- context[prop] = values[i];
3385
- values[i] = undefined;
3368
+ context.memo.clear();
3369
+ break;
3386
3370
  }
3371
+ context[prop] = values[i];
3372
+ values[i] = undefined;
3387
3373
  }
3388
3374
  return result;
3389
3375
  }
@@ -3407,7 +3393,7 @@ function syntax(syntax, prec, state, parser) {
3407
3393
  cache ?? memo.set(position, syntax, stateInner, (0, parser_1.eval)(result), source.length - (0, parser_1.exec)(result, '').length);
3408
3394
  }
3409
3395
  if (result && !stateOuter && memo.length >= position + 2) {
3410
- memo.clear(position + 2);
3396
+ memo.resize(position + 2);
3411
3397
  }
3412
3398
  context.state = stateOuter;
3413
3399
  return result;
@@ -3557,7 +3543,7 @@ class Delimiters {
3557
3543
  const {
3558
3544
  signature,
3559
3545
  matcher,
3560
- precedence = 1
3546
+ precedence
3561
3547
  } = delims[i];
3562
3548
  const stack = registry(signature);
3563
3549
  const index = stack[0]?.index ?? delimiters.length;
@@ -3576,7 +3562,7 @@ class Delimiters {
3576
3562
  }
3577
3563
  }
3578
3564
  }
3579
- pop(count = 1) {
3565
+ pop(count) {
3580
3566
  const {
3581
3567
  registry,
3582
3568
  delimiters,
@@ -3640,9 +3626,8 @@ Object.defineProperty(exports, "__esModule", ({
3640
3626
  }));
3641
3627
  exports.Memo = void 0;
3642
3628
  class Memo {
3643
- constructor({
3644
- targets = ~0
3645
- } = {}) {
3629
+ constructor(targets = ~0) {
3630
+ this.targets = targets;
3646
3631
  this.memory = [];
3647
3632
  this.targets = targets;
3648
3633
  }
@@ -3660,15 +3645,18 @@ class Memo {
3660
3645
  //console.log('set', position, syntax, state, record[syntax]?.[state]);
3661
3646
  }
3662
3647
 
3663
- clear(position) {
3648
+ resize(position) {
3664
3649
  const memory = this.memory;
3665
3650
  for (let len = memory.length, i = position; i < len; ++i) {
3666
3651
  memory.pop();
3667
3652
  }
3668
- //console.log('clear', position + 1);
3653
+ //console.log('resize', position + 1);
3669
3654
  }
3670
- }
3671
3655
 
3656
+ clear() {
3657
+ this.memory = [];
3658
+ }
3659
+ }
3672
3660
  exports.Memo = Memo;
3673
3661
 
3674
3662
  /***/ }),
@@ -3972,7 +3960,6 @@ Object.defineProperty(exports, "__esModule", ({
3972
3960
  }));
3973
3961
  exports.bind = void 0;
3974
3962
  const parser_1 = __webpack_require__(6728);
3975
- const memo_1 = __webpack_require__(1090);
3976
3963
  const segment_1 = __webpack_require__(9002);
3977
3964
  const header_1 = __webpack_require__(5702);
3978
3965
  const block_1 = __webpack_require__(4032);
@@ -3985,12 +3972,8 @@ const array_1 = __webpack_require__(8112);
3985
3972
  function bind(target, settings) {
3986
3973
  let context = {
3987
3974
  ...settings,
3988
- host: settings.host ?? new url_1.ReadonlyURL(location.pathname, location.origin),
3989
- memo: new memo_1.Memo({
3990
- targets: 472 /* State.backtrackers */
3991
- })
3975
+ host: settings.host ?? new url_1.ReadonlyURL(location.pathname, location.origin)
3992
3976
  };
3993
-
3994
3977
  if (context.id?.match(/[^0-9a-z/-]/i)) throw new Error('Invalid ID: ID must be alphanumeric');
3995
3978
  if (context.host?.origin === 'null') throw new Error(`Invalid host: ${context.host.href}`);
3996
3979
  const blocks = [];
@@ -4297,7 +4280,6 @@ Object.defineProperty(exports, "__esModule", ({
4297
4280
  }));
4298
4281
  exports.parse = void 0;
4299
4282
  const parser_1 = __webpack_require__(6728);
4300
- const memo_1 = __webpack_require__(1090);
4301
4283
  const segment_1 = __webpack_require__(9002);
4302
4284
  const header_1 = __webpack_require__(5702);
4303
4285
  const block_1 = __webpack_require__(4032);
@@ -4316,14 +4298,8 @@ function parse(source, opts = {}, context) {
4316
4298
  url: url ? new url_1.ReadonlyURL(url) : context?.url,
4317
4299
  id: opts.id ?? context?.id,
4318
4300
  caches: context?.caches,
4319
- ...(context?.resources && {
4320
- resources: context.resources
4321
- }),
4322
- memo: new memo_1.Memo({
4323
- targets: 472 /* State.backtrackers */
4324
- })
4301
+ resources: context?.resources
4325
4302
  };
4326
-
4327
4303
  if (context.id?.match(/[^0-9a-z/-]/i)) throw new Error('Invalid ID: ID must be alphanumeric');
4328
4304
  if (context.host?.origin === 'null') throw new Error(`Invalid host: ${context.host.href}`);
4329
4305
  const node = (0, dom_1.frag)();
@@ -4376,6 +4352,7 @@ Object.defineProperty(exports, "__esModule", ({
4376
4352
  }));
4377
4353
  exports.block = void 0;
4378
4354
  const combinator_1 = __webpack_require__(2087);
4355
+ const memo_1 = __webpack_require__(1090);
4379
4356
  const source_1 = __webpack_require__(6743);
4380
4357
  const pagebreak_1 = __webpack_require__(4107);
4381
4358
  const heading_1 = __webpack_require__(4623);
@@ -4394,12 +4371,13 @@ const reply_1 = __webpack_require__(9978);
4394
4371
  const paragraph_1 = __webpack_require__(6457);
4395
4372
  const random_1 = __webpack_require__(7325);
4396
4373
  const dom_1 = __webpack_require__(3252);
4397
- exports.block = (0, combinator_1.creation)(0, false, error((0, combinator_1.reset)({
4374
+ exports.block = (0, combinator_1.creation)(0, false, (0, combinator_1.reset)({
4398
4375
  resources: {
4399
4376
  clock: 20000,
4400
4377
  recursion: 20 + 1
4401
- }
4402
- }, (0, combinator_1.union)([source_1.emptyline, pagebreak_1.pagebreak, heading_1.heading, ulist_1.ulist, olist_1.olist, ilist_1.ilist, dlist_1.dlist, table_1.table, codeblock_1.codeblock, mathblock_1.mathblock, extension_1.extension, sidefence_1.sidefence, blockquote_1.blockquote, mediablock_1.mediablock, reply_1.reply, paragraph_1.paragraph]))));
4378
+ },
4379
+ memo: new memo_1.Memo(472 /* State.backtrackers */)
4380
+ }, error((0, combinator_1.union)([source_1.emptyline, pagebreak_1.pagebreak, heading_1.heading, ulist_1.ulist, olist_1.olist, ilist_1.ilist, dlist_1.dlist, table_1.table, codeblock_1.codeblock, mathblock_1.mathblock, extension_1.extension, sidefence_1.sidefence, blockquote_1.blockquote, mediablock_1.mediablock, reply_1.reply, paragraph_1.paragraph]))));
4403
4381
  function error(parser) {
4404
4382
  return (0, combinator_1.recover)((0, combinator_1.fallback)((0, combinator_1.open)('\x07', ({
4405
4383
  source
@@ -6084,7 +6062,7 @@ exports.bracket = (0, combinator_1.lazy)(() => (0, combinator_1.union)([(0, comb
6084
6062
  class: 'paren'
6085
6063
  }, (0, dom_1.defrag)((0, array_1.push)((0, array_1.unshift)(as, bs), cs)))], rest], ([as, bs = []], rest) => [(0, array_1.unshift)(as, bs), rest]), (0, combinator_1.surround)((0, source_1.str)('('), (0, combinator_1.creation)((0, combinator_1.syntax)(0 /* Syntax.none */, 2, 0 /* State.none */, (0, source_1.str)(new RegExp(index.source.replace(', ', '[,、]').replace(/[09AZaz.]|\-(?!\w)/g, c => c.trimStart() && String.fromCharCode(c.charCodeAt(0) + 0xFEE0)))))), (0, source_1.str)(')')), (0, combinator_1.surround)((0, source_1.str)('('), (0, combinator_1.creation)((0, combinator_1.syntax)(8 /* Syntax.bracket */, 2, 0 /* State.none */, (0, combinator_1.some)(inline_1.inline, ')', [[/^\\?\n/, 3], [')', 2]]))), (0, source_1.str)(')'), true, ([as, bs = [], cs], rest) => [[(0, dom_1.html)('span', {
6086
6064
  class: 'paren'
6087
- }, (0, dom_1.defrag)((0, array_1.push)((0, array_1.unshift)(as, bs), cs)))], rest], ([as, bs = []], rest) => [(0, array_1.unshift)(as, bs), rest]), (0, combinator_1.surround)((0, source_1.str)('['), (0, combinator_1.creation)((0, combinator_1.syntax)(8 /* Syntax.bracket */, 2, 0 /* State.none */, (0, combinator_1.some)(inline_1.inline, ']', [[/^\\?\n/, 3], [']', 2]]))), (0, source_1.str)(']'), true, undefined, ([as, bs = []], rest) => [(0, array_1.unshift)(as, bs), rest]), (0, combinator_1.surround)((0, source_1.str)('{'), (0, combinator_1.creation)((0, combinator_1.syntax)(8 /* Syntax.bracket */, 2, 0 /* State.none */, (0, combinator_1.some)(inline_1.inline, '}', [[/^\\?\n/, 3], ['}', 2]]))), (0, source_1.str)('}'), true, undefined, ([as, bs = []], rest) => [(0, array_1.unshift)(as, bs), rest]), (0, combinator_1.surround)((0, source_1.str)('"'), (0, combinator_1.creation)((0, combinator_1.syntax)(0 /* Syntax.none */, 3, 0 /* State.none */, (0, combinator_1.some)(inline_1.inline, '"', [[/^\\?\n/, 4], ['"', 3]]))), (0, source_1.str)('"'), true, undefined, ([as, bs = []], rest) => [(0, array_1.unshift)(as, bs), rest])]));
6065
+ }, (0, dom_1.defrag)((0, array_1.push)((0, array_1.unshift)(as, bs), cs)))], rest], ([as, bs = []], rest) => [(0, array_1.unshift)(as, bs), rest]), (0, combinator_1.surround)((0, source_1.str)('['), (0, combinator_1.creation)((0, combinator_1.syntax)(8 /* Syntax.bracket */, 2, 0 /* State.none */, (0, combinator_1.some)(inline_1.inline, ']', [[/^\\?\n/, 3], [']', 2]]))), (0, source_1.str)(']'), true, undefined, ([as, bs = []], rest) => [(0, array_1.unshift)(as, bs), rest]), (0, combinator_1.surround)((0, source_1.str)('['), (0, combinator_1.creation)((0, combinator_1.syntax)(8 /* Syntax.bracket */, 2, 0 /* State.none */, (0, combinator_1.some)(inline_1.inline, ']', [[/^\\?\n/, 3], [']', 2]]))), (0, source_1.str)(']'), true, undefined, ([as, bs = []], rest) => [(0, array_1.unshift)(as, bs), rest]), (0, combinator_1.surround)((0, source_1.str)('{'), (0, combinator_1.creation)((0, combinator_1.syntax)(8 /* Syntax.bracket */, 2, 0 /* State.none */, (0, combinator_1.some)(inline_1.inline, '}', [[/^\\?\n/, 3], ['}', 2]]))), (0, source_1.str)('}'), true, undefined, ([as, bs = []], rest) => [(0, array_1.unshift)(as, bs), rest]), (0, combinator_1.surround)((0, source_1.str)('{'), (0, combinator_1.creation)((0, combinator_1.syntax)(8 /* Syntax.bracket */, 2, 0 /* State.none */, (0, combinator_1.some)(inline_1.inline, '}', [[/^\\?\n/, 3], ['}', 2]]))), (0, source_1.str)('}'), true, undefined, ([as, bs = []], rest) => [(0, array_1.unshift)(as, bs), rest]), (0, combinator_1.surround)((0, source_1.str)('"'), (0, combinator_1.creation)((0, combinator_1.syntax)(0 /* Syntax.none */, 3, 0 /* State.none */, (0, combinator_1.some)(inline_1.inline, '"', [[/^\\?\n/, 4], ['"', 3]]))), (0, source_1.str)('"'), true, undefined, ([as, bs = []], rest) => [(0, array_1.unshift)(as, bs), rest]), (0, combinator_1.surround)((0, source_1.str)('“'), (0, combinator_1.creation)((0, combinator_1.syntax)(0 /* Syntax.none */, 3, 0 /* State.none */, (0, combinator_1.some)(inline_1.inline, '”', [[/^\\?\n/, 4], ['”', 3]]))), (0, source_1.str)('”'), true, undefined, ([as, bs = []], rest) => [(0, array_1.unshift)(as, bs), rest]), (0, combinator_1.surround)((0, source_1.str)('‘'), (0, combinator_1.creation)((0, combinator_1.syntax)(0 /* Syntax.none */, 3, 0 /* State.none */, (0, combinator_1.some)(inline_1.inline, '’', [[/^\\?\n/, 4], ['’', 3]]))), (0, source_1.str)('’'), true, undefined, ([as, bs = []], rest) => [(0, array_1.unshift)(as, bs), rest]), (0, combinator_1.surround)((0, source_1.str)('「'), (0, combinator_1.creation)((0, combinator_1.syntax)(0 /* Syntax.none */, 3, 0 /* State.none */, (0, combinator_1.some)(inline_1.inline, '」', [[/^\\?\n/, 4], ['」', 3]]))), (0, source_1.str)('」'), true, undefined, ([as, bs = []], rest) => [(0, array_1.unshift)(as, bs), rest]), (0, combinator_1.surround)((0, source_1.str)('『'), (0, combinator_1.creation)((0, combinator_1.syntax)(0 /* Syntax.none */, 3, 0 /* State.none */, (0, combinator_1.some)(inline_1.inline, '』', [[/^\\?\n/, 4], ['』', 3]]))), (0, source_1.str)('』'), true, undefined, ([as, bs = []], rest) => [(0, array_1.unshift)(as, bs), rest])]));
6088
6066
 
6089
6067
  /***/ }),
6090
6068
 
@@ -7582,7 +7560,7 @@ exports.isAlphanumeric = exports.linebreak = exports.txt = exports.text = export
7582
7560
  const combinator_1 = __webpack_require__(2087);
7583
7561
  const str_1 = __webpack_require__(2790);
7584
7562
  const dom_1 = __webpack_require__(3252);
7585
- exports.delimiter = /[\s\x00-\x7F()]|\S[#>]/u;
7563
+ exports.delimiter = /[\s\x00-\x7F()[]{}“”‘’「」『』]|\S[#>]/u;
7586
7564
  exports.nonWhitespace = /[\S\n]|$/u;
7587
7565
  exports.nonAlphanumeric = /[^0-9A-Za-z]|\S[#>]|$/u;
7588
7566
  const repeat = (0, str_1.str)(/^(.)\1*/);
package/markdown.d.ts CHANGED
@@ -1246,6 +1246,12 @@ export namespace MarkdownParser {
1246
1246
  InlineParser,
1247
1247
  InlineParser,
1248
1248
  InlineParser,
1249
+ InlineParser,
1250
+ InlineParser,
1251
+ InlineParser,
1252
+ InlineParser,
1253
+ InlineParser,
1254
+ InlineParser,
1249
1255
  ]> {
1250
1256
  }
1251
1257
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securemark",
3
- "version": "0.280.1",
3
+ "version": "0.280.2",
4
4
  "description": "Secure markdown renderer working on browsers for user input data.",
5
5
  "private": false,
6
6
  "homepage": "https://github.com/falsandtru/securemark",
@@ -37,12 +37,12 @@ export class Delimiters {
37
37
  delims: readonly {
38
38
  readonly signature: string;
39
39
  readonly matcher: (source: string) => boolean | undefined;
40
- readonly precedence?: number;
40
+ readonly precedence: number;
41
41
  }[]
42
42
  ): void {
43
43
  const { registry, delimiters, order } = this;
44
44
  for (let i = 0; i < delims.length; ++i) {
45
- const { signature, matcher, precedence = 1 } = delims[i];
45
+ const { signature, matcher, precedence } = delims[i];
46
46
  const stack = registry(signature);
47
47
  const index = stack[0]?.index ?? delimiters.length;
48
48
  if (stack.length === 0 || precedence > delimiters[index].precedence) {
@@ -61,7 +61,7 @@ export class Delimiters {
61
61
  }
62
62
  }
63
63
  }
64
- public pop(count = 1): void {
64
+ public pop(count: number): void {
65
65
  assert(count > 0);
66
66
  const { registry, delimiters, order } = this;
67
67
  for (let i = 0; i < count; ++i) {
@@ -1,9 +1,10 @@
1
1
  export class Memo {
2
- constructor({ targets = ~0 } = {}) {
2
+ constructor(
3
+ public readonly targets = ~0,
4
+ ) {
3
5
  this.targets = targets;
4
6
  }
5
- public readonly targets: number;
6
- private readonly memory: Record<number, Record<number, readonly [any[], number] | readonly []>>[/* pos */] = [];
7
+ private memory: Record<number, Record<number, readonly [any[], number] | readonly []>>[/* pos */] = [];
7
8
  public get length(): number {
8
9
  return this.memory.length;
9
10
  }
@@ -32,11 +33,14 @@ export class Memo {
32
33
  : [];
33
34
  //console.log('set', position, syntax, state, record[syntax]?.[state]);
34
35
  }
35
- public clear(position: number): void {
36
+ public resize(position: number): void {
36
37
  const memory = this.memory;
37
38
  for (let len = memory.length, i = position; i < len; ++i) {
38
39
  memory.pop();
39
40
  }
40
- //console.log('clear', position + 1);
41
+ //console.log('resize', position + 1);
42
+ }
43
+ public clear(): void {
44
+ this.memory = [];
41
45
  }
42
46
  }
@@ -1,12 +1,9 @@
1
- import { hasOwnProperty, ObjectCreate } from 'spica/alias';
1
+ import { ObjectCreate } from 'spica/alias';
2
2
  import { Parser, Result, Ctx, Tree, Context, eval, exec } from '../../data/parser';
3
3
  import { Memo } from './context/memo';
4
4
 
5
5
  export function reset<P extends Parser<unknown>>(base: Context<P>, parser: P): P;
6
6
  export function reset<T>(base: Ctx, parser: Parser<T>): Parser<T> {
7
- if (!('memo' in base)) {
8
- base.memo = undefined;
9
- }
10
7
  assert(Object.getPrototypeOf(base) === Object.prototype);
11
8
  assert(Object.freeze(base));
12
9
  const changes = Object.entries(base);
@@ -27,41 +24,38 @@ export function context<T>(base: Ctx, parser: Parser<T>): Parser<T> {
27
24
 
28
25
  function apply<P extends Parser<unknown>>(parser: P, source: string, context: Context<P>, changes: readonly [string, unknown][], values: unknown[], reset?: boolean): Result<Tree<P>>;
29
26
  function apply<T>(parser: Parser<T>, source: string, context: Ctx, changes: readonly [string, unknown][], values: unknown[], reset = false): Result<T> {
30
- if (context) for (let i = 0; i < changes.length; ++i) {
27
+ for (let i = 0; i < changes.length; ++i) {
31
28
  const change = changes[i];
32
29
  const prop = change[0];
33
30
  switch (prop) {
34
31
  case 'resources':
35
- if (!reset) break;
36
- assert(typeof change[1] === 'object');
37
- assert(context[prop] || !(prop in context));
38
- if (prop in context && !hasOwnProperty(context, prop)) break;
39
- context[prop as string] = ObjectCreate(change[1] as object);
40
- break;
41
- // @ts-expect-error
42
- case 'memo':
43
- if (!reset) break;
44
- context.memo = new Memo({ targets: context.memo?.targets });
45
- // fallthrough
46
- default:
47
- values[i] = context[prop];
48
- context[prop] = change[1];
32
+ assert(reset);
33
+ assert(!context.offset);
34
+ assert(!context.precedence);
35
+ assert(!context.delimiters);
36
+ assert(!context.state);
37
+ assert(!context.memo);
38
+ context[prop as string] ??= ObjectCreate(change[1] as object);
39
+ continue;
49
40
  }
41
+ values[i] = context[prop];
42
+ context[prop] = change[1];
50
43
  }
51
44
  const result = parser({ source, context });
52
- if (context) for (let i = 0; i < changes.length; ++i) {
45
+ for (let i = 0; i < changes.length; ++i) {
53
46
  const change = changes[i];
54
47
  const prop = change[0];
55
48
  switch (prop) {
56
49
  case 'resources':
57
- // @ts-expect-error
50
+ assert(reset);
51
+ break;
58
52
  case 'memo':
59
- if (!reset) break;
60
- // fallthrough
61
- default:
62
- context[prop] = values[i];
63
- values[i] = undefined;
53
+ assert(reset);
54
+ context.memo!.clear();
55
+ break;
64
56
  }
57
+ context[prop] = values[i];
58
+ values[i] = undefined;
65
59
  }
66
60
  return result;
67
61
  }
@@ -87,7 +81,7 @@ export function syntax<T>(syntax: number, prec: number, state: number, parser?:
87
81
  }
88
82
  if (result && !stateOuter && memo.length! >= position + 2) {
89
83
  assert(!(stateOuter & memo.targets));
90
- memo.clear(position + 2);
84
+ memo.resize(position + 2);
91
85
  }
92
86
  context.state = stateOuter;
93
87
  return result;
@@ -1,11 +1,9 @@
1
1
  import { ParserSettings, Progress } from '../../..';
2
2
  import { MarkdownParser } from '../../../markdown';
3
3
  import { eval } from '../../combinator/data/parser';
4
- import { Memo } from '../../combinator/data/parser/context/memo';
5
4
  import { segment, validate, MAX_INPUT_SIZE } from '../segment';
6
5
  import { header } from '../header';
7
6
  import { block } from '../block';
8
- import { State } from '../context';
9
7
  import { normalize } from './normalize';
10
8
  import { headers } from './header';
11
9
  import { figure } from '../processor/figure';
@@ -24,12 +22,12 @@ export function bind(target: DocumentFragment | HTMLElement | ShadowRoot, settin
24
22
  let context: MarkdownParser.Context = {
25
23
  ...settings,
26
24
  host: settings.host ?? new ReadonlyURL(location.pathname, location.origin),
27
- memo: new Memo({ targets: State.backtrackers }),
28
25
  };
29
26
  assert(!context.offset);
30
27
  assert(!context.precedence);
31
28
  assert(!context.delimiters);
32
29
  assert(!context.state);
30
+ assert(!context.memo);
33
31
  if (context.id?.match(/[^0-9a-z/-]/i)) throw new Error('Invalid ID: ID must be alphanumeric');
34
32
  if (context.host?.origin === 'null') throw new Error(`Invalid host: ${context.host.href}`);
35
33
  type Block = readonly [segment: string, blocks: readonly HTMLElement[], url: string];
@@ -333,7 +333,6 @@ describe('Unit: parser/api/parse', () => {
333
333
 
334
334
  it('creation', function () {
335
335
  this.timeout(5000);
336
- // 実測500ms程度
337
336
  assert.deepStrictEqual(
338
337
  [...parse('.'.repeat(20000)).children].map(el => el.outerHTML),
339
338
  [`<p>${'.'.repeat(20000)}</p>`]);
@@ -341,7 +340,6 @@ describe('Unit: parser/api/parse', () => {
341
340
 
342
341
  it('creation error', function () {
343
342
  this.timeout(5000);
344
- // 実測500ms程度
345
343
  assert.deepStrictEqual(
346
344
  [...parse('.'.repeat(20001)).children].map(el => el.outerHTML.replace(/:\w+/, ':rnd')),
347
345
  [
@@ -350,6 +348,16 @@ describe('Unit: parser/api/parse', () => {
350
348
  ]);
351
349
  });
352
350
 
351
+ it('recovery', function () {
352
+ this.timeout(5000);
353
+ assert.deepStrictEqual(
354
+ [...parse(`!>> ${'['.repeat(20)}${'{a}'.repeat(518)}\n> ${'{a}'.repeat(4)}\n\na`).children].map(el => el.outerHTML),
355
+ [
356
+ `<blockquote><blockquote><section><p>${'['.repeat(20)}${'<a class="url" href="a">a</a>'.repeat(518)}</p><h2>References</h2><ol class="references"></ol></section></blockquote><section><h1 class="error">Error: Too many creations</h1><pre class="error" translate="no">{a}{a}{a}{a}</pre><h2>References</h2><ol class="references"></ol></section></blockquote>`,
357
+ '<p>a</p>',
358
+ ]);
359
+ });
360
+
353
361
  });
354
362
 
355
363
  });
@@ -1,11 +1,9 @@
1
1
  import { ParserOptions } from '../../..';
2
2
  import { MarkdownParser } from '../../../markdown';
3
3
  import { eval } from '../../combinator/data/parser';
4
- import { Memo } from '../../combinator/data/parser/context/memo';
5
4
  import { segment, validate, MAX_SEGMENT_SIZE } from '../segment';
6
5
  import { header } from '../header';
7
6
  import { block } from '../block';
8
- import { State } from '../context';
9
7
  import { normalize } from './normalize';
10
8
  import { headers } from './header';
11
9
  import { figure } from '../processor/figure';
@@ -26,15 +24,13 @@ export function parse(source: string, opts: Options = {}, context?: MarkdownPars
26
24
  url: url ? new ReadonlyURL(url as ':') : context?.url,
27
25
  id: opts.id ?? context?.id,
28
26
  caches: context?.caches,
29
- ...context?.resources && {
30
- resources: context.resources,
31
- },
32
- memo: new Memo({ targets: State.backtrackers }),
27
+ resources: context?.resources,
33
28
  };
34
29
  assert(!context.offset);
35
30
  assert(!context.precedence);
36
31
  assert(!context.delimiters);
37
32
  assert(!context.state);
33
+ assert(!context.memo);
38
34
  if (context.id?.match(/[^0-9a-z/-]/i)) throw new Error('Invalid ID: ID must be alphanumeric');
39
35
  if (context.host?.origin === 'null') throw new Error(`Invalid host: ${context.host.href}`);
40
36
  const node = frag();
@@ -1,5 +1,6 @@
1
1
  import { MarkdownParser } from '../../markdown';
2
2
  import { union, reset, creation, open, fallback, recover } from '../combinator';
3
+ import { Memo } from '../combinator/data/parser/context/memo';
3
4
  import { emptyline } from './source';
4
5
  import { pagebreak } from './block/pagebreak';
5
6
  import { heading } from './block/heading';
@@ -16,6 +17,7 @@ import { blockquote } from './block/blockquote';
16
17
  import { mediablock } from './block/mediablock';
17
18
  import { reply } from './block/reply';
18
19
  import { paragraph } from './block/paragraph';
20
+ import { State } from './context';
19
21
  import { rnd0Z } from 'spica/random';
20
22
  import { html } from 'typed-dom/dom';
21
23
 
@@ -36,9 +38,9 @@ export import MediaBlockParser = BlockParser.MediaBlockParser;
36
38
  export import ReplyParser = BlockParser.ReplyParser;
37
39
  export import ParagraphParser = BlockParser.ParagraphParser;
38
40
 
39
- export const block: BlockParser = creation(0, false, error(
40
- reset({ resources: { clock: 20000, recursion: 20 + 1 } },
41
- union([
41
+ export const block: BlockParser = creation(0, false,
42
+ reset({ resources: { clock: 20000, recursion: 20 + 1 }, memo: new Memo(State.backtrackers) },
43
+ error(union([
42
44
  emptyline,
43
45
  pagebreak,
44
46
  heading,
@@ -20,10 +20,28 @@ export const bracket: BracketParser = lazy(() => union([
20
20
  surround(str('['), creation(syntax(Syntax.bracket, 2, State.none, some(inline, ']', [[/^\\?\n/, 3], [']', 2]]))), str(']'), true,
21
21
  undefined,
22
22
  ([as, bs = []], rest) => [unshift(as, bs), rest]),
23
+ surround(str('['), creation(syntax(Syntax.bracket, 2, State.none, some(inline, ']', [[/^\\?\n/, 3], [']', 2]]))), str(']'), true,
24
+ undefined,
25
+ ([as, bs = []], rest) => [unshift(as, bs), rest]),
23
26
  surround(str('{'), creation(syntax(Syntax.bracket, 2, State.none, some(inline, '}', [[/^\\?\n/, 3], ['}', 2]]))), str('}'), true,
24
27
  undefined,
25
28
  ([as, bs = []], rest) => [unshift(as, bs), rest]),
29
+ surround(str('{'), creation(syntax(Syntax.bracket, 2, State.none, some(inline, '}', [[/^\\?\n/, 3], ['}', 2]]))), str('}'), true,
30
+ undefined,
31
+ ([as, bs = []], rest) => [unshift(as, bs), rest]),
26
32
  surround(str('"'), creation(syntax(Syntax.none, 3, State.none, some(inline, '"', [[/^\\?\n/, 4], ['"', 3]]))), str('"'), true,
27
33
  undefined,
28
34
  ([as, bs = []], rest) => [unshift(as, bs), rest]),
35
+ surround(str('“'), creation(syntax(Syntax.none, 3, State.none, some(inline, '”', [[/^\\?\n/, 4], ['”', 3]]))), str('”'), true,
36
+ undefined,
37
+ ([as, bs = []], rest) => [unshift(as, bs), rest]),
38
+ surround(str('‘'), creation(syntax(Syntax.none, 3, State.none, some(inline, '’', [[/^\\?\n/, 4], ['’', 3]]))), str('’'), true,
39
+ undefined,
40
+ ([as, bs = []], rest) => [unshift(as, bs), rest]),
41
+ surround(str('「'), creation(syntax(Syntax.none, 3, State.none, some(inline, '」', [[/^\\?\n/, 4], ['」', 3]]))), str('」'), true,
42
+ undefined,
43
+ ([as, bs = []], rest) => [unshift(as, bs), rest]),
44
+ surround(str('『'), creation(syntax(Syntax.none, 3, State.none, some(inline, '』', [[/^\\?\n/, 4], ['』', 3]]))), str('』'), true,
45
+ undefined,
46
+ ([as, bs = []], rest) => [unshift(as, bs), rest]),
29
47
  ]));
@@ -3,7 +3,7 @@ import { union, creation, focus } from '../../combinator';
3
3
  import { str } from './str';
4
4
  import { html } from 'typed-dom/dom';
5
5
 
6
- export const delimiter = /[\s\x00-\x7F()]|\S[#>]/u;
6
+ export const delimiter = /[\s\x00-\x7F()[]{}“”‘’「」『』]|\S[#>]/u;
7
7
  export const nonWhitespace = /[\S\n]|$/u;
8
8
  export const nonAlphanumeric = /[^0-9A-Za-z]|\S[#>]|$/u;
9
9
  const repeat = str(/^(.)\1*/);