diff --git a/__tests__/unit/node/markdown/plugins/image-url.test.ts b/__tests__/unit/node/markdown/plugins/image-url.test.ts new file mode 100644 index 000000000..f6a104863 --- /dev/null +++ b/__tests__/unit/node/markdown/plugins/image-url.test.ts @@ -0,0 +1,73 @@ +import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises' +import { tmpdir } from 'node:os' +import path from 'node:path' + +import { + createMarkdownRenderer, + disposeMdItInstance, + type MarkdownRenderer +} from 'node/markdown/markdown' + +describe('image dimensions with URL suffixes', () => { + let root: string + let md: MarkdownRenderer + + beforeAll(async () => { + root = await mkdtemp(path.join(tmpdir(), 'vitepress-image-url-')) + await mkdir(path.join(root, 'public')) + for (const file of [ + 'diagram.svg', + 'diagram space.svg', + 'diagram#hash.svg', + 'public/diagram.svg' + ]) { + await writeFile( + path.join(root, file), + '' + ) + } + disposeMdItInstance() + md = await createMarkdownRenderer(root, { highlight: (code) => code }) + }) + + afterAll(async () => { + disposeMdItInstance() + await rm(root, { recursive: true, force: true }) + }) + + describe.each([ + './diagram.svg', + '/diagram.svg', + './diagram%20space.svg', + './diagram%23hash.svg' + ])('%s', (pathname) => { + test.each(['?v=1', '#layer', '?v=1#layer'])( + 'reads dimensions while preserving %s', + async (suffix) => { + const src = pathname + suffix + const html = await md.renderAsync(`![Diagram](${src})`, { + path: path.join(root, 'index.md') + }) + + expect(html).toContain(`src="${decodeURIComponent(src)}"`) + expect(html).toContain('width="120"') + expect(html).toContain('height="80"') + } + ) + }) + + test.each([ + ['width=240', '240', '160'], + ['height=40', '60', '40'], + ['width=240 height=90', '240', '90'] + ])('respects explicit dimensions: %s', async (attrs, width, height) => { + const html = await md.renderAsync( + `![Diagram](/diagram.svg?v=1#layer){${attrs}}`, + { path: path.join(root, 'index.md') } + ) + + expect(html).toContain('src="/diagram.svg?v=1#layer"') + expect(html).toContain(`width="${width}"`) + expect(html).toContain(`height="${height}"`) + }) +}) diff --git a/src/node/markdown/plugins/image.ts b/src/node/markdown/plugins/image.ts index f2c4fe6fa..0acdebdb9 100644 --- a/src/node/markdown/plugins/image.ts +++ b/src/node/markdown/plugins/image.ts @@ -36,10 +36,12 @@ export const imagePlugin = ( url = './' + url } + // Strip URL suffixes before decoding so encoded hashes stay in filenames. + const pathname = decodeURIComponent(url.replace(/[?#].*$/, '')) url = decodeURIComponent(url) token.attrSet('src', url) - addImageDimensions(token, url, publicDir, env) + addImageDimensions(token, pathname, publicDir, env) } if (lazyLoad && token.attrIndex('loading') < 0) {