Move the support line from 0.1.7-alpha.1 to 0.1.7-rc.1 (peer floor `^0.1.7-rc.1`). 0.21.0-alpha.1 never reached npm, and once the code-card labels below are fixed the same build crashes on alpha.1 (its DiffBlock still reads the `labels.files` upstream deleted), so declaring only the host we verify against is the honest floor — and from rc.1 on the host enforces peers at boot anyway: a row whose `@deepseek-ai/dsh-*` peers do not satisfy the runtime is silently disabled. Dependencies: 28 devDependency pins and 14 peers move to 0.1.7-rc.1. rc.1 also raised the vendored runtime (cordis 4.0.4, schemastery 3.18.4, cordis-plugin-group 1.0.4, cordis-plugin-include 1.0.9) and publishes DSH cross-references as exact versions, so bumping schemastery/cordis alone left four unmet transitive peers — the three vendor packages are hoisted into devDependencies, which is what makes `pnpm peers check` clean again. One compile break: rc.1 added `CodeToolbarLabels` (codeLabel / wrapLabel / unwrapLabel, all required), made DiffBlockLabels / ReadBlockLabels extend it and deleted `DiffBlockLabels.files`. Fixing SideChatView then leaving markdown on the legacy banner would render two different code-card styles inside one side-chat view, so the plugin adopts the card everywhere: `markdownTextProps()` now passes `labels.code.toolbarLabels` with the host's own wording (codeBlock.title|wrap|unwrap, added to all 20 dictionaries; `sideChatBlockFiles` retires with the deleted footer). Adopting the card surfaced a silent regression the suite caught: the new toolbar shows `codeLabel` for languages the highlighter does not know, and mermaid has no shiki grammar, so the info string no longer reaches the DOM and the banner-text probe in mermaid.tsx matched nothing — mermaid fences stopped being swapped for diagrams. Detection now matches the FENCE BODY from the source that produced the render. `tests/market-manifest.spec.ts` pins every DSH peer as a floating caret range: an exact pin (the devDependency style) or a comparator without a prerelease would fail rc.1's preflight and silently stop the plugin from loading, and `pnpm peers check` cannot see that — it validates the resolved dev tree, not the range shape the host reads.
98 lines
3.6 KiB
TypeScript
98 lines
3.6 KiB
TypeScript
/**
|
|
* MermaidMarkdown architecture spec: the preview renders the WHOLE document
|
|
* through one MarkdownText pass (cross-fence reference-style links must
|
|
* resolve — the P1 regression from the CR), then swaps every rendered
|
|
* mermaid CodeBlock for a diagram. mermaid is mocked so the swap + semantics
|
|
* are asserted without pulling the real layout engine into jsdom.
|
|
*/
|
|
// @vitest-environment jsdom
|
|
import { describe, expect, it, vi, beforeEach } from 'vitest'
|
|
import { createElement } from 'react'
|
|
import { createRoot, type Root } from 'react-dom/client'
|
|
import { act } from 'react-dom/test-utils'
|
|
|
|
// The act() environment flag (React 18.2 reads it before flushing effects).
|
|
import { setupReactAct } from './test-utils.ts'
|
|
setupReactAct()
|
|
|
|
vi.mock('mermaid', () => ({
|
|
default: {
|
|
initialize: vi.fn(),
|
|
render: async () => ({
|
|
svg: '<svg xmlns="http://www.w3.org/2000/svg"><text>MOCK-DIAGRAM</text></svg>',
|
|
}),
|
|
},
|
|
}))
|
|
|
|
import mermaid from 'mermaid'
|
|
import { MermaidMarkdown } from '../src/client/mermaid.tsx'
|
|
|
|
const codeLabels = {
|
|
copyLabel: 'Copy', copiedLabel: 'Copied', codeLabel: 'Code block', wrapLabel: 'Wrap lines', unwrapLabel: 'Do not wrap lines',
|
|
}
|
|
|
|
async function renderMarkdown(text: string): Promise<{ container: HTMLElement; root: Root }> {
|
|
const container = document.createElement('div')
|
|
document.body.appendChild(container)
|
|
const root = createRoot(container)
|
|
await act(async () => {
|
|
root.render(createElement(MermaidMarkdown, { text, codeLabels }))
|
|
// Let the mocked mermaid.render promise settle inside the act scope.
|
|
await new Promise(resolve => { setTimeout(resolve, 0) })
|
|
})
|
|
return { container, root }
|
|
}
|
|
|
|
async function unmount(root: Root): Promise<void> {
|
|
await act(async () => { root.unmount() })
|
|
}
|
|
|
|
beforeEach(() => {
|
|
document.body.innerHTML = ''
|
|
})
|
|
|
|
describe('MermaidMarkdown', () => {
|
|
it('swaps the mermaid code block for a rendered diagram', async () => {
|
|
const { container, root } = await renderMarkdown('```mermaid\ngraph TD\n A-->B\n```')
|
|
const diagram = container.querySelector('[data-mermaid-diagram] svg')
|
|
expect(diagram, 'the mermaid fence must be swapped for a diagram').not.toBeNull()
|
|
expect(diagram?.textContent).toContain('MOCK-DIAGRAM')
|
|
await unmount(root)
|
|
})
|
|
|
|
it('suppresses Mermaid global error rendering', async () => {
|
|
vi.mocked(mermaid.initialize).mockClear()
|
|
const { root } = await renderMarkdown('```mermaid\ngraph TD\n A-->B\n```')
|
|
expect(mermaid.initialize).toHaveBeenCalledWith(expect.objectContaining({
|
|
suppressErrorRendering: true,
|
|
}))
|
|
await unmount(root)
|
|
})
|
|
|
|
it('resolves cross-fence reference-style links (single markdown parse)', async () => {
|
|
const text = [
|
|
'[before][shared]',
|
|
'',
|
|
'```mermaid',
|
|
'graph TD',
|
|
' A --> B',
|
|
'```',
|
|
'',
|
|
'[shared]: https://example.com',
|
|
].join('\n')
|
|
const { container, root } = await renderMarkdown(text)
|
|
const link = container.querySelector('a[href="https://example.com"]')
|
|
expect(link, 'the definition after the fence must resolve the link before it').not.toBeNull()
|
|
expect(link?.textContent).toContain('before')
|
|
expect(container.querySelector('[data-mermaid-diagram] svg'), 'the diagram must still swap in').not.toBeNull()
|
|
await unmount(root)
|
|
})
|
|
|
|
it('leaves non-mermaid fences untouched', async () => {
|
|
const { container, root } = await renderMarkdown('```ts\nconst a = 1\n```')
|
|
expect(container.querySelector('[data-mermaid-diagram]'), 'no mermaid fence → no swap').toBeNull()
|
|
expect(container.querySelectorAll('.md-code-block').length, 'the ts fence stays a code block').toBe(1)
|
|
await unmount(root)
|
|
})
|
|
})
|