From 13cb5efecf684873116faa2956843664c4b0d4eb Mon Sep 17 00:00:00 2001 From: Ian Sanders Date: Wed, 27 Aug 2025 21:46:42 +0000 Subject: [PATCH 1/2] Allow extension handlers to fall through to default logic --- dev/lib/index.js | 26 ++++++++++++++++++++++++-- dev/lib/types.d.ts | 5 +++-- readme.md | 4 +++- test/index.js | 45 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 75 insertions(+), 5 deletions(-) diff --git a/dev/lib/index.js b/dev/lib/index.js index 517f2ea..b997998 100644 --- a/dev/lib/index.js +++ b/dev/lib/index.js @@ -1309,9 +1309,15 @@ function extension(combined, extension) { case 'enter': case 'exit': { + const left = combined[key] const right = extension[key] - if (right) { - Object.assign(combined[key], right) + for (const tokenType in right) { + if (right[tokenType]) { + left[tokenType] = combineHandles( + left[tokenType], + right[tokenType] + ) + } } break @@ -1322,6 +1328,22 @@ function extension(combined, extension) { } } +/** + * Creates a new handle that calls `right` first, then falls through to `left` + * only if `right` explicitly returns `false`. + * @param {Handle?} left + * @param {Handle} right + * @returns {Handle} + */ +function combineHandles(left, right) { + if (!left) return right + + return function (...params) { + const rightResult = right.apply(this, params) + return rightResult === false ? left.apply(this, params) : rightResult + } +} + /** @type {OnEnterError} */ function defaultOnError(left, right) { if (left) { diff --git a/dev/lib/types.d.ts b/dev/lib/types.d.ts index 710de76..ef835c3 100644 --- a/dev/lib/types.d.ts +++ b/dev/lib/types.d.ts @@ -226,9 +226,10 @@ export type Handles = Record * @param token * Current token. * @returns - * Nothing. + * Nothing, if the token was fully handled by this extension and should be ignored by previous extensions / default logic. + * Or `false`, if the token has not been handled by this extension and handling should fall back to preceding logic. */ -export type Handle = (this: CompileContext, token: Token) => undefined | void +export type Handle = (this: CompileContext, token: Token) => undefined | void | false /** * Handle the case where the `right` token is open, but it is closed (by the diff --git a/readme.md b/readme.md index 48ad08d..212fd1b 100644 --- a/readme.md +++ b/readme.md @@ -248,7 +248,9 @@ Handle a token (TypeScript type). ###### Returns -Nothing (`undefined`). +Nothing, if the token was fully handled and should be ignored by previous +extensions / default logic. Or `false`, if the token has not been handled here +and the parser should fall back to preceding logic. ### `OnEnterError` diff --git a/test/index.js b/test/index.js index dd5399b..3ead60f 100644 --- a/test/index.js +++ b/test/index.js @@ -146,6 +146,51 @@ test('fromMarkdown', async function (t) { ) }) + await t.test( + 'should allow extension handlers to fall through', + async function () { + assert.deepEqual( + fromMarkdown('a\nb', { + mdastExtensions: [ + { + enter: { + paragraph() { + return false + } + } + } + ] + }), + { + type: 'root', + children: [ + { + type: 'paragraph', + children: [ + { + type: 'text', + value: 'a\nb', + position: { + start: {line: 1, column: 1, offset: 0}, + end: {line: 2, column: 2, offset: 3} + } + } + ], + position: { + start: {line: 1, column: 1, offset: 0}, + end: {line: 2, column: 2, offset: 3} + } + } + ], + position: { + start: {line: 1, column: 1, offset: 0}, + end: {line: 2, column: 2, offset: 3} + } + } + ) + } + ) + await t.test('should support multiple extensions', async function () { assert.deepEqual( fromMarkdown('a\nb', { From 5de21af2394ae8ac4fd90a4053cfdccd5eb4acff Mon Sep 17 00:00:00 2001 From: Ian Sanders Date: Wed, 27 Aug 2025 22:05:15 +0000 Subject: [PATCH 2/2] Fix formatting --- dev/lib/index.js | 6 +++--- dev/lib/types.d.ts | 5 ++++- readme.md | 5 +++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/dev/lib/index.js b/dev/lib/index.js index b997998..c772960 100644 --- a/dev/lib/index.js +++ b/dev/lib/index.js @@ -1338,9 +1338,9 @@ function extension(combined, extension) { function combineHandles(left, right) { if (!left) return right - return function (...params) { - const rightResult = right.apply(this, params) - return rightResult === false ? left.apply(this, params) : rightResult + return function (...parameters) { + const rightResult = right.apply(this, parameters) + return rightResult === false ? left.apply(this, parameters) : rightResult } } diff --git a/dev/lib/types.d.ts b/dev/lib/types.d.ts index ef835c3..082dae2 100644 --- a/dev/lib/types.d.ts +++ b/dev/lib/types.d.ts @@ -229,7 +229,10 @@ export type Handles = Record * Nothing, if the token was fully handled by this extension and should be ignored by previous extensions / default logic. * Or `false`, if the token has not been handled by this extension and handling should fall back to preceding logic. */ -export type Handle = (this: CompileContext, token: Token) => undefined | void | false +export type Handle = ( + this: CompileContext, + token: Token +) => undefined | void | false /** * Handle the case where the `right` token is open, but it is closed (by the diff --git a/readme.md b/readme.md index 212fd1b..438a370 100644 --- a/readme.md +++ b/readme.md @@ -249,8 +249,9 @@ Handle a token (TypeScript type). ###### Returns Nothing, if the token was fully handled and should be ignored by previous -extensions / default logic. Or `false`, if the token has not been handled here -and the parser should fall back to preceding logic. +extensions / default logic. +Or `false`, if the token has not been handled here and the parser should fall +back to preceding logic. ### `OnEnterError`