Skip to content

Commit

Permalink
Support MDX layout
Browse files Browse the repository at this point in the history
Closes #362
  • Loading branch information
remcohaszing committed Dec 11, 2023
1 parent 7c00fc3 commit ac1b212
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 7 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
],
"rules": {
"@typescript-eslint/prefer-nullish-coalescing": "off",
"max-depth": "off",
"max-params": "off"
}
}
Expand Down
53 changes: 46 additions & 7 deletions packages/language-service/lib/language-module.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,40 @@ import remarkMdx from 'remark-mdx'
import remarkParse from 'remark-parse'
import {unified} from 'unified'

const componentStart = `
const layoutJsDoc = `
/** @typedef {MDXContentProps & { children: JSX.Element }} MDXLayoutProps */
/**
* There is one special component: [MDX layout](https://mdxjs.com/docs/using-mdx/#layout).
* If it is defined, it’s used to wrap all content.
* A layout can be defined from within MDX using a default export.
*
* @param {{readonly [K in keyof MDXLayoutProps]: MDXLayoutProps[K]}} props
* The [props](https://mdxjs.com/docs/using-mdx/#props) that have been passed to the MDX component.
* In addition, the MDX layout receives the \`children\` prop, which contains the rendered MDX content.
*/
`

/**
* @param {boolean} hasLayout
*/
const componentStart = (hasLayout) => `
/**
* Render the MDX contents.
*
* @param {MDXContentProps} props
* The props that have been passed to the MDX component.
* @param {{readonly [K in keyof MDXContentProps]: MDXContentProps[K]}} props
* The [props](https://mdxjs.com/docs/using-mdx/#props) that have been passed to the MDX component.
*/
export default function MDXContent(props) {
${hasLayout ? '' : 'export default '}function MDXContent(props) {
return `
const componentEnd = `
}
// @ts-ignore
/** @typedef {Props} MDXContentProps */
/** @typedef {0 extends 1 & Props ? {} : Props} MDXContentProps */
`

const fallback = componentStart + componentEnd
const fallback = componentStart(false) + componentEnd

/**
* Visit an mdast tree with and enter and exit callback.
Expand Down Expand Up @@ -165,6 +182,7 @@ function getVirtualFiles(fileName, snapshot, ts, processor) {
/** @type {VirtualFile[]} */
const virtualFiles = []

let hasLayout = false
let esm = ''
let jsx = ''
let markdown = ''
Expand Down Expand Up @@ -250,6 +268,27 @@ function getVirtualFiles(fileName, snapshot, ts, processor) {

case 'mdxjsEsm': {
updateMarkdownFromNode(node)
const body = node.data?.estree?.body

if (body) {
for (const child of body) {
if (child.type === 'ExportNamedDeclaration') {
for (const specifier of child.specifiers) {
if (specifier.local.name === 'default') {
hasLayout = true
break
}
}
}

if (child.type === 'ExportDefaultDeclaration') {
esm += layoutJsDoc
hasLayout = true
break
}
}
}

addOffset(esmMapping, start, esm.length, end - start)
esm += mdx.slice(start, end) + '\n'
break
Expand Down Expand Up @@ -334,7 +373,7 @@ function getVirtualFiles(fileName, snapshot, ts, processor) {
)

updateMarkdownFromOffsets(mdx.length, mdx.length)
esm += componentStart
esm += componentStart(hasLayout)

for (let i = 0; i < jsxMapping.generatedOffsets.length; i++) {
jsxMapping.generatedOffsets[i] += esm.length
Expand Down

0 comments on commit ac1b212

Please sign in to comment.