Skip to content

Commit

Permalink
Support await expressions (#377)
Browse files Browse the repository at this point in the history
To support await expressions, the `_createMdxContent` function is
generated. It is marked as deprecated to discourage the user from using
it. This pushes the function to the bottom of TypeScript autocompletion
and renders it with a strike through effect.
  • Loading branch information
remcohaszing authored Dec 19, 2023
1 parent 31f1912 commit 000db8c
Show file tree
Hide file tree
Showing 4 changed files with 421 additions and 32 deletions.
7 changes: 7 additions & 0 deletions .changeset/odd-fireants-smell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@mdx-js/language-service": patch
"@mdx-js/language-server": patch
"vscode-mdx": patch
---

Support await expressions.
68 changes: 62 additions & 6 deletions packages/language-service/lib/virtual-file.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* @typedef {import('@volar/language-service').Mapping<CodeInformation>} Mapping
* @typedef {import('@volar/language-service').VirtualFile} VirtualFile
* @typedef {import('estree').ExportDefaultDeclaration} ExportDefaultDeclaration
* @typedef {import('estree').Program} Program
* @typedef {import('mdast').Nodes} Nodes
* @typedef {import('mdast').Root} Root
* @typedef {import('mdast-util-mdxjs-esm').MdxjsEsm} MdxjsEsm
Expand All @@ -11,6 +12,7 @@
* @typedef {import('vfile-message').VFileMessage} VFileMessage
*/

import {walk} from 'estree-walker'
import {ScriptSnapshot} from './script-snapshot.js'

/**
Expand All @@ -31,23 +33,39 @@ const layoutJsDoc = (propsName) => `
* The MDX content wrapped in the layout.
*/`

const componentStart = `
/**
* Render the MDX contents.
* @param {boolean} isAsync
* Whether or not the `_createMdxContent` should be async
*/
const componentStart = (isAsync) => `
/**
* @deprecated
* Do not use.
*
* @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) {
${isAsync ? 'async ' : ''}function _createMdxContent(props) {
return `

const componentEnd = `
}
/**
* Render the MDX contents.
*
* @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) {
return <_createMdxContent {...props} />
}
// @ts-ignore
/** @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 @@ -210,6 +228,38 @@ function processExports(mdx, node, mapping, esm) {
return esm
}

/**
* @param {Program | undefined} expression
* @returns {boolean}
*/
function hasAwaitExpression(expression) {
let awaitExpression = false
if (expression) {
walk(expression, {
enter(node) {
if (
awaitExpression ||
node.type === 'ArrowFunctionExpression' ||
node.type === 'FunctionDeclaration' ||
node.type === 'FunctionExpression'
) {
this.skip()
return
}

if (
node.type === 'AwaitExpression' ||
(node.type === 'ForOfStatement' && node.await)
) {
awaitExpression = true
this.skip()
}
}
})
}
return awaitExpression
}

/**
* @param {string} fileName
* @param {string} mdx
Expand Down Expand Up @@ -280,6 +330,7 @@ function getEmbeddedFiles(fileName, mdx, ast) {
/** @type {VirtualFile[]} */
const virtualFiles = []

let hasAwait = false
let esm = ''
let jsx = ''
let markdown = ''
Expand Down Expand Up @@ -402,12 +453,17 @@ function getEmbeddedFiles(fileName, mdx, ast) {
case 'mdxFlowExpression':
case 'mdxTextExpression': {
updateMarkdownFromNode(node)
const program = node.data?.estree

if (node.data?.estree?.body.length === 0) {
if (program?.body.length === 0) {
jsx = addOffset(jsxMapping, mdx, jsx, start, start + 1)
jsx = addOffset(jsxMapping, mdx, jsx, end - 1, end)
esm = addOffset(esmMapping, mdx, esm, start + 1, end - 1) + '\n'
} else {
if (program) {
hasAwait ||= hasAwaitExpression(program)
}

jsx = addOffset(jsxMapping, mdx, jsx, start, end)
}

Expand Down Expand Up @@ -460,7 +516,7 @@ function getEmbeddedFiles(fileName, mdx, ast) {
)

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

for (let i = 0; i < jsxMapping.generatedOffsets.length; i++) {
jsxMapping.generatedOffsets[i] += esm.length
Expand Down
1 change: 1 addition & 0 deletions packages/language-service/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"@types/mdast": "^4.0.0",
"@types/unist": "^3.0.0",
"@volar/language-service": "2.0.0-alpha.7",
"estree-walker": "^3.0.0",
"mdast-util-mdxjs-esm": "^2.0.0",
"remark-mdx": "^3.0.0",
"remark-parse": "^11.0.0",
Expand Down
Loading

0 comments on commit 000db8c

Please sign in to comment.