diff --git a/package-lock.json b/package-lock.json index 0e3114298..e659d5168 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5582,17 +5582,45 @@ } }, "node_modules/axios": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.0.tgz", - "integrity": "sha512-6hp5CwvTPlN2A31g5dxnwAX0orzM7pmCRDLnZSX772mv8WDqICwFjowHuPs04Mc8deIld1+ejhtaMn5vp6b+1w==", + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.18.0.tgz", + "integrity": "sha512-E32NzpYKp++W7XRe52rHiXV2ehxmh3wbdgO7MHeFM+vqxLBYHzt0ElkiImtOBxtOmyp0yoC8C6uESVV84Y2/hw==", "dev": true, "license": "MIT", "dependencies": { "follow-redirects": "^1.16.0", "form-data": "^4.0.5", + "https-proxy-agent": "^5.0.1", "proxy-from-env": "^2.1.0" } }, + "node_modules/axios/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/axios/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/babel-jest": { "version": "30.0.4", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.0.4.tgz", @@ -17728,9 +17756,9 @@ } }, "node_modules/shell-quote": { - "version": "1.8.4", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.4.tgz", - "integrity": "sha512-VsC6n6vz1ihYYyZZwX7YZSF5l5x36ca17OC+a69h94YqB7X6XLwf+5MOgynYir2SLFUbl8gIYvBo8K8RoNQ6bQ==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.10.0.tgz", + "integrity": "sha512-w1aiOKwKuRgtwAReIIj89puqg+I7GvX4IbLrvmhXbzQsj1+Zwi4VO3+fa6ZF91TWSjIxoEkKnMeHcLEODK5ZXA==", "dev": true, "license": "MIT", "engines": { @@ -18554,9 +18582,9 @@ } }, "node_modules/svgo": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.1.tgz", - "integrity": "sha512-XDpWUOPC6FEibaLzjfe0ucaV0YrOjYotGJO1WpF0Zd+n6ZGEQUsSugaoLq9QkEZtAfQIxT42UChcssDVPP3+/w==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.2.tgz", + "integrity": "sha512-ekx94z1rRc5LDi6oSUaeRnYhd0UOJxdtQCL2rF8xpWxD3TPAsISWOrxezqGovqS38GRZOdpDfvQe3ts6F7nsng==", "dev": true, "license": "MIT", "dependencies": { diff --git a/src/core/render/compiler/blockquote.js b/src/core/render/compiler/blockquote.js index 2aab79beb..0b73c6991 100644 --- a/src/core/render/compiler/blockquote.js +++ b/src/core/render/compiler/blockquote.js @@ -15,12 +15,18 @@ export const blockquoteCompiler = ({ renderer, compiler }) => const calloutMark = calloutData[1]; // "[!TIP]" const calloutType = calloutData[2].toLowerCase(); // "tip" + // Avoid mutating tokens that may be reused from the Prerender cache. + tokens = tokens.slice(); + const paragraph = { ...firstParagraph }; + if (firstParagraph.tokens) { + paragraph.tokens = firstParagraph.tokens.map(t => ({ ...t })); + } + tokens[firstParagraphIndex] = paragraph; + // Remove the callout mark from the paragraph raw text - firstParagraph.raw = firstParagraph.raw - .replace(calloutMark, '') - .trimStart(); - if (firstParagraph.tokens && firstParagraph.tokens.length > 0) { - firstParagraph.tokens.forEach(t => { + paragraph.raw = paragraph.raw.replace(calloutMark, '').trimStart(); + if (paragraph.tokens && paragraph.tokens.length > 0) { + paragraph.tokens.forEach(t => { if (t.raw) { t.raw = t.raw.replace(calloutMark, ''); } @@ -31,7 +37,7 @@ export const blockquoteCompiler = ({ renderer, compiler }) => } // If the first paragraph is now empty after removing [!TIP], remove it - if (!firstParagraph.raw.trim()) { + if (!paragraph.raw.trim()) { tokens.splice(firstParagraphIndex, 1); } diff --git a/test/integration/callouts.test.js b/test/integration/callouts.test.js new file mode 100644 index 000000000..da6fee199 --- /dev/null +++ b/test/integration/callouts.test.js @@ -0,0 +1,45 @@ +import { waitForFunction, waitForText } from '../helpers/wait-for.js'; +import docsifyInit from '../helpers/docsify-init.js'; + +describe('callouts', () => { + test('render fully after returning to a previously visited page', async () => { + const calloutText = 'This callout should remain fully rendered.'; + + await docsifyInit({ + testURL: '/docsify-init.html#/custom-navbar', + markdown: { + sidebar: ` + - [Custom Navbar](custom-navbar) + - [Configuration](configuration) + `, + }, + routes: { + 'custom-navbar.md': ` + > [!IMPORTANT] + > ${calloutText} + `, + 'configuration.md': '# Configuration', + }, + }); + + expect(await waitForText('#main', calloutText)).toBeTruthy(); + expect(document.querySelector('#main .callout.important')).toBeTruthy(); + + document.querySelector('a[href="#/configuration"]').click(); + expect( + await waitForFunction(() => + /#\/configuration$/.test(window.location.href), + ), + ).toBeTruthy(); + expect(await waitForText('#main', 'Configuration')).toBeTruthy(); + + document.querySelector('a[href="#/custom-navbar"]').click(); + expect( + await waitForFunction(() => + /#\/custom-navbar$/.test(window.location.href), + ), + ).toBeTruthy(); + expect(await waitForText('#main', calloutText)).toBeTruthy(); + expect(document.querySelector('#main .callout.important')).toBeTruthy(); + }); +});