From 5c06045eb949ebf5e7c86b9b0cd69f4d8017b35a Mon Sep 17 00:00:00 2001 From: Luffy <52o@qq52o.cn> Date: Thu, 11 Jun 2026 12:23:22 +0800 Subject: [PATCH 1/4] fix: improve cache handling and error recovery in embed processing --- src/core/render/embed.js | 8 ++++---- src/core/util/core.js | 7 +++++-- test/integration/embed.test.js | 22 ++++++++++++++++++++++ test/unit/core-util.test.js | 30 ++++++++++++++++++++++++++++-- 4 files changed, 59 insertions(+), 8 deletions(-) diff --git a/src/core/render/embed.js b/src/core/render/embed.js index 2730bc78c8..4e6e564d86 100644 --- a/src/core/render/embed.js +++ b/src/core/render/embed.js @@ -122,7 +122,7 @@ function walkFetchEmbed({ embedTokens, compile, fetch }, cb) { }; if (currentToken.embed.url) { - get(currentToken.embed.url).then(next); + get(currentToken.embed.url).then(next, () => next()); } else { next(currentToken.embed.html); } @@ -133,7 +133,7 @@ function walkFetchEmbed({ embedTokens, compile, fetch }, cb) { export function prerenderEmbed({ compiler, raw = '', fetch }, done) { const hit = cached[raw]; - if (hit) { + if (hit !== undefined) { const copy = hit.slice(); copy.links = hit.links; return done(copy); @@ -205,7 +205,7 @@ export function prerenderEmbed({ compiler, raw = '', fetch }, done) { walkFetchEmbed( { compile, embedTokens, fetch }, ({ embedToken, token, rowIndex, cellIndex, tokenRef }) => { - if (token) { + if (token && embedToken) { Object.assign(links, embedToken.links); if (typeof rowIndex === 'number' && typeof cellIndex === 'number') { @@ -269,7 +269,7 @@ export function prerenderEmbed({ compiler, raw = '', fetch }, done) { }); } } - } else { + } else if (!token) { cached[raw] = tokens.concat(); tokens.links = cached[raw].links = links; done(tokens); diff --git a/src/core/util/core.js b/src/core/util/core.js index 04f3fbdd12..827b79c605 100644 --- a/src/core/util/core.js +++ b/src/core/util/core.js @@ -10,8 +10,11 @@ export function cached(fn) { const cache = Object.create(null); return function (str) { const key = isPrimitive(str) ? str : JSON.stringify(str); - const hit = cache[key]; - return hit || (cache[key] = fn(str)); + if (key in cache) { + return cache[key]; + } + + return (cache[key] = fn(str)); }; } diff --git a/test/integration/embed.test.js b/test/integration/embed.test.js index 49207148c3..51dd74d3ba 100644 --- a/test/integration/embed.test.js +++ b/test/integration/embed.test.js @@ -262,6 +262,28 @@ Command | Description | Parameters expect(mainText).not.toContain("_media/second.md ':include'"); }); + test('failed embed URL does not block page render', async () => { + await docsifyInit({ + markdown: { + homepage: ` + # Embed Test + + [missing](_media/missing.md ':include') + + Text after missing embed + + [ok](_media/ok.md ':include') + `, + }, + routes: { + '_media/ok.md': 'reachable include content', + }, + }); + + expect(await waitForText('#main', 'Text after missing embed')).toBeTruthy(); + expect(await waitForText('#main', 'reachable include content')).toBeTruthy(); + }); + test('embed file table cell', async () => { await docsifyInit({ markdown: { diff --git a/test/unit/core-util.test.js b/test/unit/core-util.test.js index a1b941271e..34457238f7 100644 --- a/test/unit/core-util.test.js +++ b/test/unit/core-util.test.js @@ -1,12 +1,38 @@ -import { isExternal } from '../../src/core/util/index.js'; +import { cached, isExternal } from '../../src/core/util/index.js'; // Core util // ----------------------------------------------------------------------------- describe('core/util', () => { + describe('cached()', () => { + test('memoizes falsy return values', () => { + let calls = 0; + const fn = cached(() => { + calls += 1; + return ''; + }); + + expect(fn('same-key')).toBe(''); + expect(fn('same-key')).toBe(''); + expect(calls).toBe(1); + }); + + test('memoizes undefined return values', () => { + let calls = 0; + const fn = cached(() => { + calls += 1; + return undefined; + }); + + expect(fn('same-key')).toBeUndefined(); + expect(fn('same-key')).toBeUndefined(); + expect(calls).toBe(1); + }); + }); + // isExternal() // --------------------------------------------------------------------------- describe('isExternal()', () => { - // cases non external + // cases non-external test('non external local url with one /', () => { const result = isExternal(`/${location.host}/docsify/demo.md`); From 0e434ef666afe5ad914329634df19fcd1a784f02 Mon Sep 17 00:00:00 2001 From: Luffy <52o@qq52o.cn> Date: Thu, 11 Jun 2026 12:25:57 +0800 Subject: [PATCH 2/4] fix: format test assertions --- test/integration/embed.test.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/integration/embed.test.js b/test/integration/embed.test.js index 51dd74d3ba..34f217218b 100644 --- a/test/integration/embed.test.js +++ b/test/integration/embed.test.js @@ -281,7 +281,9 @@ Command | Description | Parameters }); expect(await waitForText('#main', 'Text after missing embed')).toBeTruthy(); - expect(await waitForText('#main', 'reachable include content')).toBeTruthy(); + expect( + await waitForText('#main', 'reachable include content'), + ).toBeTruthy(); }); test('embed file table cell', async () => { From 8bc13f3d9d9191da729e1f9553ffd53405950dc0 Mon Sep 17 00:00:00 2001 From: Luffy <52o@qq52o.cn> Date: Thu, 11 Jun 2026 12:45:40 +0800 Subject: [PATCH 3/4] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- test/integration/embed.test.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/integration/embed.test.js b/test/integration/embed.test.js index 34f217218b..546a471b16 100644 --- a/test/integration/embed.test.js +++ b/test/integration/embed.test.js @@ -276,6 +276,11 @@ Command | Description | Parameters `, }, routes: { + '_media/missing.md': { + status: 404, + body: 'Not Found', + contentType: 'text/markdown', + }, '_media/ok.md': 'reachable include content', }, }); From 77d66cd1174e70292dc2c3bb25ebaa0c171370c3 Mon Sep 17 00:00:00 2001 From: Luffy <52o@qq52o.cn> Date: Thu, 11 Jun 2026 13:41:22 +0800 Subject: [PATCH 4/4] Apply suggestion from @sy-records --- src/core/render/embed.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/render/embed.js b/src/core/render/embed.js index 4e6e564d86..47884e3d45 100644 --- a/src/core/render/embed.js +++ b/src/core/render/embed.js @@ -133,7 +133,7 @@ function walkFetchEmbed({ embedTokens, compile, fetch }, cb) { export function prerenderEmbed({ compiler, raw = '', fetch }, done) { const hit = cached[raw]; - if (hit !== undefined) { + if (hit) { const copy = hit.slice(); copy.links = hit.links; return done(copy);