Skip to content

Commit

Permalink
fix: correctly handle MDX layouts (#6845)
Browse files Browse the repository at this point in the history
* fix: correctly handle MDX layouts (#6844)

* fix: adjust JSXNode return value (#6844)

* refactor(mdx): use jsx instead of internals

---------

Co-authored-by: Wout Mertens <[email protected]>
  • Loading branch information
danielvaijk and wmertens authored Oct 13, 2024
1 parent fa172c5 commit 5a3e489
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 25 deletions.
5 changes: 5 additions & 0 deletions .changeset/lazy-worms-attack.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@builder.io/qwik-city': patch
---

MDX content no longer ignores Layout components. See [the MDX docs](https://mdxjs.com/docs/using-mdx/#layout) for more information.
5 changes: 5 additions & 0 deletions .changeset/real-garlics-argue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@builder.io/qwik-city': patch
---

Fixed MDX layout default export being ignored by transformer.
13 changes: 3 additions & 10 deletions packages/qwik-city/src/buildtime/markdown/mdx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { rehypeSyntaxHighlight } from './syntax-highlight';
import type { BuildContext } from '../types';
import { parseFrontmatter } from './frontmatter';
import { getExtension } from '../../utils/fs';
import { createHash } from 'node:crypto';
import type { CompileOptions } from '@mdx-js/mdx';

export async function createMdxTransformer(ctx: BuildContext): Promise<MdxTransform> {
Expand Down Expand Up @@ -70,17 +69,11 @@ export async function createMdxTransformer(ctx: BuildContext): Promise<MdxTransf
const file = new VFile({ value: code, path: id });
const compiled = await compile(file, options);
const output = String(compiled.value);
const hasher = createHash('sha256');
const key = hasher
.update(output)
.digest('base64')
.slice(0, 8)
.replace('+', '-')
.replace('/', '_');
const addImport = `import { _jsxC, RenderOnce } from '@builder.io/qwik';\n`;
const addImport = `import { jsx } from '@builder.io/qwik';\n`;
const newDefault = `
const WrappedMdxContent = () => {
return _jsxC(RenderOnce, {children: _jsxC(_createMdxContent, {}, 3, null)}, 3, ${JSON.stringify(key)});
const content = _createMdxContent({});
return typeof MDXLayout === 'function' ? jsx(MDXLayout, {children: content}) : content;
};
export default WrappedMdxContent;
`;
Expand Down
111 changes: 96 additions & 15 deletions packages/qwik-city/src/buildtime/markdown/mdx.unit.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,32 @@
import { describe, expect, test } from 'vitest';
import { createMdxTransformer } from './mdx';

describe('mdx', () => {
test('convert mdx', async () => {
const ctx = {
frontmatter: new Map(),
opts: {
mdx: {
remarkPlugins: [],
rehypePlugins: [],
},
mdxPlugins: {},
// It could be that new MDX versions change the output used for snapshot matching. Change as needed.
describe('mdx', async () => {
const ctx = {
frontmatter: new Map(),
opts: {
mdx: {
remarkPlugins: [],
rehypePlugins: [],
},
};
const transformer = await createMdxTransformer(ctx as any);
mdxPlugins: {},
},
};

const transformer = await createMdxTransformer(ctx as any);

test('convert flat mdx', async () => {
const mdx = `
# Hello
<a href="http://example.com">Hello</a>
<div>World</div>
`;
const result = await transformer(mdx, 'file.mdx');
// It could be that new mdx versions change this output, make sure it still makes sense

expect(result).toMatchInlineSnapshot(`
{
"code": "import { _jsxC, RenderOnce } from '@builder.io/qwik';
"code": "import { jsx } from '@builder.io/qwik';
import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from "@builder.io/qwik/jsx-runtime";
export const headings = [{
"text": "Hello",
Expand Down Expand Up @@ -59,7 +62,8 @@ describe('mdx', () => {
}
const WrappedMdxContent = () => {
return _jsxC(RenderOnce, {children: _jsxC(_createMdxContent, {}, 3, null)}, 3, "eB2HIyA1");
const content = _createMdxContent({});
return typeof MDXLayout === 'function' ? jsx(MDXLayout, {children: content}) : content;
};
export default WrappedMdxContent;
",
Expand All @@ -75,4 +79,81 @@ describe('mdx', () => {
}
`);
});

test('convert layout mdx', async () => {
const mdx = `
# Hello
export default function Layout({ children: content }) {
return <main>{content}</main>;
}
<a href="http://example.com">Hello</a>
<div>World</div>
`;
const result = await transformer(mdx, 'file.mdx');

expect(result).toMatchInlineSnapshot(`
{
"code": "import { jsx } from '@builder.io/qwik';
import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from "@builder.io/qwik/jsx-runtime";
export const headings = [{
"text": "Hello",
"id": "hello",
"level": 1
}];
export const frontmatter = undefined;
const MDXLayout = function Layout({children: content}) {
return _jsx("main", {
children: content
});
};
function _createMdxContent(props) {
const _components = {
a: "a",
h1: "h1",
span: "span",
...props.components
};
return _jsxs(_Fragment, {
children: [_jsxs(_components.h1, {
id: "hello",
children: [_jsx(_components.a, {
"aria-hidden": "true",
tabindex: "-1",
href: "#hello",
children: _jsx(_components.span, {
class: "icon icon-link"
})
}), "Hello"]
}), "\\n", "\\n", _jsx("a", {
href: "http://example.com",
children: "Hello"
}), "\\n", _jsx("div", {
children: "World"
})]
});
}
const WrappedMdxContent = () => {
const content = _createMdxContent({});
return typeof MDXLayout === 'function' ? jsx(MDXLayout, {children: content}) : content;
};
export default WrappedMdxContent;
",
"map": {
"file": "file.mdx",
"mappings": ";;;;;;;kBAGe,iBAAkBA,UAAUC;cACjC;cAAMA;;;;;;;;;;;;;;;;;;;;UAHd;;;gBAM2B;;gBACxB",
"names": [
"children",
"content",
],
"sources": [
"file.mdx",
],
"version": 3,
},
}
`);
});
});

0 comments on commit 5a3e489

Please sign in to comment.