Skip to content

Commit 9b3b9ce

Browse files
authored
Add support for some popular transformer plugins (#501)
This adds support for the following transformer plugins: - `recma-export-filepath` - `rehype-mdx-title` - `remark-mdx-frontmatter` These plugins all follow the same pattern; they expose some form of metatada as an ESM export. I think these are some of the most popular plugins that actually affect the editor experience. I don’t like that support for these plugins is hardcoded. This is only due to the lack of an alternative. For now I welcome hardcoded support for other plugins. I intend to iterate on this to provide a better editor experience for YAML frontmatter. Refs #297
1 parent fa0a6cf commit 9b3b9ce

File tree

15 files changed

+1380
-50
lines changed

15 files changed

+1380
-50
lines changed

.changeset/lucky-chicken-ring.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@mdx-js/language-service': minor
3+
---
4+
5+
Replace `resolveRemarkPlugins` with `remarkPlugins`

.changeset/sad-melons-play.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
'@mdx-js/typescript-plugin': patch
3+
'@mdx-js/language-service': patch
4+
'@mdx-js/language-server': patch
5+
'vscode-mdx': patch
6+
---
7+
8+
Add support for `recma-export-filepath`, `rehype-mdx-title`, and `remark-mdx-frontmatter`

packages/language-server/lib/index.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#!/usr/bin/env node
22

33
/**
4+
* @import {VirtualCodePlugin} from '@mdx-js/language-service'
45
* @import {PluggableList} from 'unified'
56
*/
67

@@ -10,7 +11,7 @@ import process from 'node:process'
1011
import {
1112
createMdxLanguagePlugin,
1213
createMdxServicePlugin,
13-
resolveRemarkPlugins
14+
resolvePlugins
1415
} from '@mdx-js/language-service'
1516
import {
1617
createConnection,
@@ -82,7 +83,9 @@ connection.onInitialize(async (parameters) => {
8283
*/
8384
function getLanguagePlugins(tsconfig) {
8485
/** @type {PluggableList | undefined} */
85-
let plugins
86+
let remarkPlugins
87+
/** @type {VirtualCodePlugin[] | undefined} */
88+
let virtualCodePlugins
8689
let checkMdx = false
8790
let jsxImportSource = 'react'
8891

@@ -101,7 +104,7 @@ connection.onInitialize(async (parameters) => {
101104
)
102105
const jiti = createJiti(tsconfig)
103106

104-
plugins = resolveRemarkPlugins(
107+
;[remarkPlugins, virtualCodePlugins] = resolvePlugins(
105108
commandLine.raw?.mdx,
106109
(name) => jiti(name).default
107110
)
@@ -111,7 +114,8 @@ connection.onInitialize(async (parameters) => {
111114

112115
return [
113116
createMdxLanguagePlugin(
114-
plugins || defaultPlugins,
117+
remarkPlugins || defaultPlugins,
118+
virtualCodePlugins,
115119
checkMdx,
116120
jsxImportSource
117121
)

packages/language-service/README.md

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
* [API](#api)
2020
* [`createMdxLanguagePlugin([plugins][, checkMdx][, jsxImportSource])`](#createmdxlanguagepluginplugins-checkmdx-jsximportsource)
2121
* [`createMdxServicePlugin(options)`](#createmdxservicepluginoptions)
22-
* [`resolveRemarkPlugins(mdxConfig, resolvePlugin)`](#resolveremarkpluginsmdxconfig-resolveplugin)
22+
* [`resolvePlugins(mdxConfig, resolvePlugin)`](#resolvepluginsmdxconfig-resolveplugin)
2323
* [Compatibility](#compatibility)
2424
* [Types](#types)
2525
* [Security](#security)
@@ -57,7 +57,7 @@ In Deno with [`esm.sh`][esmsh]:
5757
import {
5858
createMdxLanguagePlugin,
5959
createMdxServicePlugin,
60-
resolveRemarkPlugins
60+
resolvePlugins
6161
} from 'https://esm.sh/@mdx-js/language-service'
6262
```
6363

@@ -68,7 +68,7 @@ In browsers with [`esm.sh`][esmsh]:
6868
import {
6969
createMdxLanguagePlugin,
7070
createMdxServicePlugin,
71-
resolveRemarkPlugins
71+
resolvePlugins
7272
} from 'https://esm.sh/@mdx-js/language-service?bundle'
7373
</script>
7474
```
@@ -136,9 +136,9 @@ The following commands are supported:
136136

137137
The Volar service plugin for MDX files.
138138

139-
### `resolveRemarkPlugins(mdxConfig, resolvePlugin)`
139+
### `resolvePlugins(mdxConfig, resolvePlugin)`
140140

141-
Resolve remark plugins from TypeScript’s parsed command line options.
141+
Resolve plugins from TypeScript’s parsed command line options.
142142

143143
#### Parameters
144144

@@ -149,8 +149,14 @@ Resolve remark plugins from TypeScript’s parsed command line options.
149149

150150
#### Returns
151151

152-
An array of resolved plugins, or `undefined` in case of an invalid
153-
configuration.
152+
A tuple where the first item is n array of resolved plugins,
153+
or `undefined` in case of an invalid configuration.
154+
The second item is an array of supported transformer plugins.
155+
The supported transformer plugins are:
156+
157+
* [`recma-export-filepath`](https://github.com/remcohaszing/recma-export-filepath)
158+
* [`rehype-mdx-title`](https://github.com/remcohaszing/rehype-mdx-title)
159+
* [`remark-mdx-frontmatter`](https://github.com/remcohaszing/remark-mdx-frontmatter)
154160

155161
## Compatibility
156162

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
/**
2+
* @typedef {import('./plugins/plugin.js').VirtualCodePlugin} VirtualCodePlugin
3+
*/
4+
15
export {createMdxLanguagePlugin} from './language-plugin.js'
26
export {createMdxServicePlugin} from './service-plugin.js'
3-
export {resolveRemarkPlugins} from './tsconfig.js'
7+
export {resolvePlugins} from './tsconfig.js'

packages/language-service/lib/language-plugin.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* @import {LanguagePlugin} from '@volar/language-service'
55
* @import {PluggableList} from 'unified'
66
* @import {URI} from 'vscode-uri'
7+
* @import {VirtualCodePlugin} from './plugins/plugin.js'
78
*/
89

910
import remarkMdx from 'remark-mdx'
@@ -14,9 +15,10 @@ import {VirtualMdxCode} from './virtual-code.js'
1415
/**
1516
* Create a [Volar](https://volarjs.dev) language plugin to support MDX.
1617
*
17-
* @param {PluggableList} [plugins]
18+
* @param {PluggableList} [remarkPlugins]
1819
* A list of remark syntax plugins. Only syntax plugins are supported.
1920
* Transformers are unused.
21+
* @param {VirtualCodePlugin[]} [virtualCodePlugins]
2022
* @param {boolean} checkMdx
2123
* If true, check MDX files strictly.
2224
* @param {string} jsxImportSource
@@ -25,13 +27,14 @@ import {VirtualMdxCode} from './virtual-code.js'
2527
* A Volar language plugin to support MDX.
2628
*/
2729
export function createMdxLanguagePlugin(
28-
plugins,
30+
remarkPlugins,
31+
virtualCodePlugins,
2932
checkMdx = false,
3033
jsxImportSource = 'react'
3134
) {
3235
const processor = unified().use(remarkParse).use(remarkMdx)
33-
if (plugins) {
34-
processor.use(plugins)
36+
if (remarkPlugins) {
37+
processor.use(remarkPlugins)
3538
}
3639

3740
processor.freeze()
@@ -48,6 +51,7 @@ export function createMdxLanguagePlugin(
4851
return new VirtualMdxCode(
4952
snapshot,
5053
processor,
54+
virtualCodePlugins || [],
5155
checkMdx,
5256
jsxImportSource
5357
)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/**
2+
* @import {Nodes} from 'mdast'
3+
*/
4+
5+
/**
6+
* @typedef VirtualCodePluginObject
7+
* An object returned by a virtual code plugin.
8+
* @property {(node: Nodes) => undefined} [visit]
9+
* Visit an mdast node.
10+
* @property {() => string} finalize
11+
* Generate the JavaScript string to insert into the virtual code.
12+
*/
13+
14+
/**
15+
* @typedef {() => VirtualCodePluginObject} VirtualCodePlugin
16+
* An internal plugin for MDX analyzer that represents an MDX plugin.
17+
*/
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* @import {VirtualCodePlugin} from './plugin.js'
3+
*/
4+
5+
/**
6+
* @param {unknown} options
7+
* @returns {VirtualCodePlugin}
8+
*/
9+
export function recmaExportFilepath(options) {
10+
const opts = typeof options === 'object' && options !== null ? options : {}
11+
const name = 'name' in opts ? String(opts.name) : 'filepath'
12+
13+
return () => {
14+
return {
15+
finalize() {
16+
return (
17+
'/** Generated by [recma-export-filepath](https://github.com/remcohaszing/recma-export-filepath) */\nexport const ' +
18+
name +
19+
" = /** @type {string} */ ('')"
20+
)
21+
}
22+
}
23+
}
24+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/**
2+
* @import {VirtualCodePlugin} from './plugin.js'
3+
*/
4+
5+
/**
6+
* @param {unknown} options
7+
* @returns {VirtualCodePlugin}
8+
*/
9+
export function rehypeMdxTitle(options) {
10+
const opts = typeof options === 'object' && options !== null ? options : {}
11+
const maxRank = 'maxRank' in opts ? Number(opts.maxRank) : 1
12+
const name = 'name' in opts ? String(opts.name) : 'title'
13+
14+
return () => {
15+
let hasTitle = false
16+
17+
return {
18+
visit(node) {
19+
hasTitle ||= node.type === 'heading' && node.depth <= maxRank
20+
},
21+
22+
finalize() {
23+
return (
24+
'/** Generated by [rehype-mdx-title](https://github.com/remcohaszing/rehype-mdx-title) */\nexport const ' +
25+
name +
26+
' = /** @type {string' +
27+
(hasTitle ? '' : ' | undefined') +
28+
"} */ ('')"
29+
)
30+
}
31+
}
32+
}
33+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/**
2+
* @import {VirtualCodePlugin} from './plugin.js'
3+
*/
4+
5+
/**
6+
* @param {unknown} options
7+
* @returns {VirtualCodePlugin}
8+
*/
9+
export function remarkMdxFrontmatter(options) {
10+
const opts = typeof options === 'object' && options !== null ? options : {}
11+
const name = 'name' in opts ? String(opts.name) : 'frontmatter'
12+
13+
return () => {
14+
let hasFrontmatter = false
15+
16+
return {
17+
visit(node) {
18+
hasFrontmatter ||= node.type === 'toml' || node.type === 'yaml'
19+
},
20+
21+
finalize() {
22+
return (
23+
'/** Generated by [remark-mdx-frontmatter](https://github.com/remcohaszing/remark-mdx-frontmatter) */\nexport const ' +
24+
name +
25+
' = /** @type {' +
26+
(hasFrontmatter ? 'any' : 'undefined') +
27+
'} */ (undefined)'
28+
)
29+
}
30+
}
31+
}
32+
}

0 commit comments

Comments
 (0)