From d54312bf276e492d0680d9449a66f527e9f50c51 Mon Sep 17 00:00:00 2001 From: Nathan Clevenger Date: Wed, 11 Jun 2025 07:58:56 -0500 Subject: [PATCH] test(mdxld): compile render with real mdx --- packages/mdxld/src/render.test.ts | 139 +++++++++--------------------- packages/mdxld/src/render.ts | 18 ++-- 2 files changed, 50 insertions(+), 107 deletions(-) diff --git a/packages/mdxld/src/render.test.ts b/packages/mdxld/src/render.test.ts index 3d5a53893..ac4333cef 100644 --- a/packages/mdxld/src/render.test.ts +++ b/packages/mdxld/src/render.test.ts @@ -1,115 +1,60 @@ -import { render } from './render' -import { describe, it, expect, vi, beforeEach } from 'vitest' +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest' import React from 'react' -import * as mdx from '@mdx-js/mdx' -import * as ReactDOMServer from 'react-dom/server' -import TurndownService from 'turndown' -vi.mock('@mdx-js/mdx', () => { - const mockCompile = vi.fn().mockResolvedValue('compiled-mdx-content') - const mockEvaluate = vi.fn().mockImplementation(() => { - return Promise.resolve({ - default: () => React.createElement('div', null, 'Mocked MDX Component') - }) - }) - - return { - compile: mockCompile, - evaluate: mockEvaluate - } -}) - -vi.mock('react-dom/server', () => { - return { - renderToString: vi.fn().mockReturnValue('
Mocked MDX Component
') - } -}) - -vi.mock('turndown', () => { - return { - default: vi.fn().mockImplementation(() => ({ - turndown: vi.fn().mockReturnValue('Rendered markdown content') - })) - } -}) - -describe('render', () => { - beforeEach(() => { - vi.clearAllMocks() - }) - - it('should render MDX content to markdown', async () => { - const mdxContent = `--- -title: Test Document -tags: ["mdx", "test"] ---- - -# Hello World - -This is a test document.` +// Real compilation tests using actual @mdx-js/mdx +describe('render - real compilation', () => { + it('renders markdown and parses frontmatter', async () => { + const { render } = await import('./render') + const mdxContent = `---\ntitle: Real Test\n---\n\n# Hello World` const result = await render(mdxContent) - - expect(result.markdown).toBe('Rendered markdown content') - expect(result.frontmatter).toEqual({ - title: 'Test Document', - tags: ['mdx', 'test'], - }) - expect(mdx.compile).toHaveBeenCalled() - expect(mdx.evaluate).toHaveBeenCalled() - expect(ReactDOMServer.renderToString).toHaveBeenCalled() + expect(result.frontmatter).toEqual({ title: 'Real Test' }) + expect(result.markdown).toContain('Hello World') }) - it('should handle MDX content without frontmatter', async () => { - const mdxContent = `# Hello World - -This is a test document without frontmatter.` - + it('handles content without frontmatter', async () => { + const { render } = await import('./render') + const mdxContent = `# Just Heading` const result = await render(mdxContent) - - expect(result.markdown).toBe('Rendered markdown content') expect(result.frontmatter).toEqual({}) - expect(mdx.compile).toHaveBeenCalled() + expect(result.markdown).toContain('Just Heading') }) +}) - it('should pass components and scope to MDX rendering', async () => { - const mdxContent = `# Hello World - -` - - const customComponents = { - CustomComponent: () => React.createElement('div', null, 'Custom content'), - } - - const customScope = { - testVar: 'test value', - } - - const result = await render(mdxContent, { - components: customComponents, - scope: customScope, +describe('render - mocked mdx', () => { + beforeEach(() => { + vi.resetModules() + vi.doMock('@mdx-js/mdx', () => { + const mockEvaluate = vi.fn().mockResolvedValue({ + default: () => React.createElement('div', null, 'Mocked MDX Component'), + }) + return { evaluate: mockEvaluate } }) - - expect(result.markdown).toBe('Rendered markdown content') - expect(mdx.evaluate).toHaveBeenCalledWith(expect.anything(), expect.objectContaining({ - ...customScope + vi.doMock('react-dom/server', () => ({ + renderToString: vi.fn().mockReturnValue('
Mocked MDX Component
'), + })) + vi.doMock('turndown', () => ({ + default: vi.fn().mockImplementation(() => ({ + turndown: vi.fn().mockReturnValue('Rendered markdown content'), + })), })) - expect(ReactDOMServer.renderToString).toHaveBeenCalledWith( - expect.objectContaining({ - props: expect.objectContaining({ - components: customComponents - }) - }) - ) }) - it('should throw an error when MDX compilation fails', async () => { - vi.mocked(mdx.compile).mockRejectedValueOnce(new Error('Compilation error')) - - const mdxContent = `# Invalid MDX + afterEach(() => { + vi.clearAllMocks() + vi.resetModules() + }) -` + it('returns markdown using mocks', async () => { + const { render } = await import('./render') + const mdxContent = `# Test` + const result = await render(mdxContent) + expect(result.markdown).toBe('Rendered markdown content') + }) - await expect(render(mdxContent)).rejects.toThrow('Failed to render MDX: Compilation error') + it('throws an error when evaluation fails', async () => { + vi.mocked((await import('@mdx-js/mdx')).evaluate).mockRejectedValueOnce(new Error('Compilation error')) + const { render } = await import('./render') + await expect(render('# Error')).rejects.toThrow('Failed to render MDX: Compilation error') }) }) diff --git a/packages/mdxld/src/render.ts b/packages/mdxld/src/render.ts index 80a6b09f6..b60cfc245 100644 --- a/packages/mdxld/src/render.ts +++ b/packages/mdxld/src/render.ts @@ -1,7 +1,8 @@ import React from 'react' -import { compile, evaluate } from '@mdx-js/mdx' +import { evaluate } from '@mdx-js/mdx' import { VFile } from 'vfile' import * as runtime from 'react/jsx-runtime' +import * as runtimeDev from 'react/jsx-dev-runtime' import * as ReactDOMServer from 'react-dom/server' import TurndownService from 'turndown' import { parseFrontmatter } from './parser.js' @@ -44,20 +45,17 @@ export async function render(mdxContent: string, options: RenderOptions = {}): P const frontmatterRegex = /^\s*---\s*[\r\n]+([\s\S]*?)[\r\n]+---\s*[\r\n]+/ const contentWithoutFrontmatter = mdxContent.replace(frontmatterRegex, '') - const compiled = await compile(new VFile(contentWithoutFrontmatter), { - jsx: true, - jsxImportSource: 'react', + const dev = process.env.NODE_ENV !== 'production' + const runtimeLib = dev ? runtimeDev : runtime + const { default: Component } = await evaluate(new VFile(contentWithoutFrontmatter), { + ...runtimeLib, remarkPlugins: [remarkGfm], rehypePlugins: [], - development: process.env.NODE_ENV !== 'production', + development: dev, format: 'mdx', recmaPlugins: [], mdExtensions: ['.md', '.mdx'], - elementAttributeNameCase: 'html' - }) - - const { default: Component } = await evaluate(compiled, { - ...runtime, + elementAttributeNameCase: 'html', ...options.scope, })