-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
core.js
234 lines (217 loc) · 10.1 KB
/
core.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
/**
* @typedef {import('estree-jsx').Program} Program
* @typedef {import('hast-util-to-estree').ElementAttributeNameCase} ElementAttributeNameCase
* @typedef {import('hast-util-to-estree').StylePropertyNameCase} StylePropertyNameCase
* @typedef {import('mdast').Root} Root
* @typedef {import('remark-rehype').Options} RemarkRehypeOptions
* @typedef {typeof import('source-map').SourceMapGenerator} SourceMapGenerator
* @typedef {import('unified').PluggableList} PluggableList
* @typedef {import('unified').Processor<Root, Program, Program, Program, string>} Processor
*/
/**
* @typedef ProcessorOptions
* Configuration for `createProcessor`.
* @property {SourceMapGenerator | null | undefined} [SourceMapGenerator]
* Add a source map (object form) as the `map` field on the resulting file
* (optional).
* @property {URL | string | null | undefined} [baseUrl]
* Use this URL as `import.meta.url` and resolve `import` and `export … from`
* relative to it (optional, example: `import.meta.url`).
* @property {boolean | null | undefined} [development=false]
* Whether to add extra info to error messages in generated code and use the
* development automatic JSX runtime (`Fragment` and `jsxDEV` from
* `/jsx-dev-runtime`) (default: `false`);
* when using the webpack loader (`@mdx-js/loader`) or the Rollup integration
* (`@mdx-js/rollup`) through Vite, this is automatically inferred from how
* you configure those tools.
* @property {ElementAttributeNameCase | null | undefined} [elementAttributeNameCase='react']
* Casing to use for attribute names (default: `'react'`);
* HTML casing is for example `class`, `stroke-linecap`, `xml:lang`;
* React casing is for example `className`, `strokeLinecap`, `xmlLang`;
* for JSX components written in MDX, the author has to be aware of which
* framework they use and write code accordingly;
* for AST nodes generated by this project, this option configures it
* @property {'md' | 'mdx' | null | undefined} [format='mdx']
* format of the file (default: `'mdx'`);
* `'md'` means treat as markdown and `'mdx'` means treat as MDX.
* @property {boolean | null | undefined} [jsx=false]
* Whether to keep JSX (default: `false`);
* the default is to compile JSX away so that the resulting file is
* immediately runnable.
* @property {string | null | undefined} [jsxImportSource='react']
* Place to import automatic JSX runtimes from (default: `'react'`);
* when in the `automatic` runtime, this is used to define an import for
* `Fragment`, `jsx`, `jsxDEV`, and `jsxs`.
* @property {'automatic' | 'classic' | null | undefined} [jsxRuntime='automatic']
* JSX runtime to use (default: `'automatic'`);
* the automatic runtime compiles to `import _jsx from
* '$importSource/jsx-runtime'\n_jsx('p')`;
* the classic runtime compiles to calls such as `h('p')`.
*
* > 👉 **Note**: support for the classic runtime is deprecated and will
* > likely be removed in the next major version.
* @property {ReadonlyArray<string> | null | undefined} [mdExtensions]
* List of markdown extensions, with dot (default: `['.md', '.markdown', …]`);
* affects integrations.
* @property {ReadonlyArray<string> | null | undefined} [mdxExtensions]
* List of MDX extensions, with dot (default: `['.mdx']`);
* affects integrations.
* @property {'function-body' | 'program' | null | undefined} [outputFormat='program']
* Output format to generate (default: `'program'`);
* in most cases `'program'` should be used, it results in a whole program;
* internally `evaluate` uses `'function-body'` to compile to
* code that can be passed to `run`;
* in some cases, you might want to do what `evaluate` does in separate steps
* yourself, such as when compiling on the server and running on the client.
* @property {string | null | undefined} [pragma='React.createElement']
* Pragma for JSX, used in the classic runtime as an identifier for function
* calls: `<x />` to `React.createElement('x')` (default:
* `'React.createElement'`);
* when changing this, you should also define `pragmaFrag` and
* `pragmaImportSource` too.
*
* > 👉 **Note**: support for the classic runtime is deprecated and will
* > likely be removed in the next major version.
* @property {string | null | undefined} [pragmaFrag='React.Fragment']
* Pragma for fragment symbol, used in the classic runtime as an identifier
* for unnamed calls: `<>` to `React.createElement(React.Fragment)` (default:
* `'React.Fragment'`);
* when changing this, you should also define `pragma` and
* `pragmaImportSource` too.
*
* > 👉 **Note**: support for the classic runtime is deprecated and will
* > likely be removed in the next major version.
* @property {string | null | undefined} [pragmaImportSource='react']
* Where to import the identifier of `pragma` from, used in the classic
* runtime (default: `'react'`);
* to illustrate, when `pragma` is `'a.b'` and `pragmaImportSource` is `'c'`
* the following will be generated: `import a from 'c'` and things such as
* `a.b('h1', {})`.
* when changing this, you should also define `pragma` and `pragmaFrag` too.
*
* > 👉 **Note**: support for the classic runtime is deprecated and will
* > likely be removed in the next major version.
* @property {string | null | undefined} [providerImportSource]
* Place to import a provider from (optional, example: `'@mdx-js/react'`);
* normally it’s used for runtimes that support context (React, Preact), but
* it can be used to inject components into the compiled code;
* the module must export and identifier `useMDXComponents` which is called
* without arguments to get an object of components (`MDXComponents` from
* `mdx/types.js`).
* @property {PluggableList | null | undefined} [recmaPlugins]
* List of recma plugins (optional);
* this is a new ecosystem, currently in beta, to transform esast trees
* (JavaScript)
* @property {PluggableList | null | undefined} [remarkPlugins]
* List of remark plugins (optional).
* @property {PluggableList | null | undefined} [rehypePlugins]
* List of rehype plugins (optional).
* @property {Readonly<RemarkRehypeOptions> | null | undefined} [remarkRehypeOptions]
* Options to pass through to `remark-rehype` (optional);
* the option `allowDangerousHtml` will always be set to `true` and the MDX
* nodes (see `nodeTypes`) are passed through;
* In particular, you might want to pass configuration for footnotes if your
* content is not in English.
* @property {StylePropertyNameCase | null | undefined} [stylePropertyNameCase='dom']
* Casing to use for property names in `style` objects (default: `'dom'`);
* CSS casing is for example `background-color` and `-webkit-line-clamp`;
* DOM casing is for example `backgroundColor` and `WebkitLineClamp`;
* for JSX components written in MDX, the author has to be aware of which
* framework they use and write code accordingly;
* for AST nodes generated by this project, this option configures it
* @property {boolean | null | undefined} [tableCellAlignToStyle=true]
* Turn obsolete `align` props on `td` and `th` into CSS `style` props
* (default: `true`).
*/
import {unreachable} from 'devlop'
import remarkMdx from 'remark-mdx'
import remarkParse from 'remark-parse'
import remarkRehype from 'remark-rehype'
import {unified} from 'unified'
import {recmaDocument} from './plugin/recma-document.js'
import {recmaJsxBuild} from './plugin/recma-jsx-build.js'
import {recmaJsxRewrite} from './plugin/recma-jsx-rewrite.js'
import {recmaStringify} from './plugin/recma-stringify.js'
import {rehypeRecma} from './plugin/rehype-recma.js'
import {rehypeRemoveRaw} from './plugin/rehype-remove-raw.js'
import {remarkMarkAndUnravel} from './plugin/remark-mark-and-unravel.js'
import {nodeTypes} from './node-types.js'
const removedOptions = [
'compilers',
'filepath',
'hastPlugins',
'mdPlugins',
'skipExport',
'wrapExport'
]
let warned = false
/**
* Create a processor to compile markdown or MDX to JavaScript.
*
* > **Note**: `format: 'detect'` is not allowed in `ProcessorOptions`.
*
* @param {Readonly<ProcessorOptions> | null | undefined} [options]
* Configuration (optional).
* @return {Processor}
* Processor.
*/
export function createProcessor(options) {
const settings = options || {}
let index = -1
while (++index < removedOptions.length) {
const key = removedOptions[index]
if (key in settings) {
unreachable(
'Unexpected removed option `' +
key +
'`; see <https://mdxjs.com/migrating/v2/> on how to migrate'
)
}
}
// @ts-expect-error: throw an error for a runtime value which is not allowed
// by the types.
if (settings.format === 'detect') {
unreachable(
"Unexpected `format: 'detect'`, which is not supported by `createProcessor`, expected `'mdx'` or `'md'`"
)
}
if (
(settings.jsxRuntime === 'classic' ||
settings.pragma ||
settings.pragmaFrag ||
settings.pragmaImportSource) &&
!warned
) {
warned = true
console.warn(
"Unexpected deprecated option `jsxRuntime: 'classic'`, `pragma`, `pragmaFrag`, or `pragmaImportSource`; see <https://mdxjs.com/migrating/v3/> on how to migrate"
)
}
const pipeline = unified().use(remarkParse)
if (settings.format !== 'md') {
pipeline.use(remarkMdx)
}
const remarkRehypeOptions = settings.remarkRehypeOptions || {}
pipeline
.use(remarkMarkAndUnravel)
.use(settings.remarkPlugins || [])
.use(remarkRehype, {
...remarkRehypeOptions,
allowDangerousHtml: true,
passThrough: [...(remarkRehypeOptions.passThrough || []), ...nodeTypes]
})
.use(settings.rehypePlugins || [])
if (settings.format === 'md') {
pipeline.use(rehypeRemoveRaw)
}
pipeline
.use(rehypeRecma, settings)
.use(recmaDocument, settings)
.use(recmaJsxRewrite, settings)
if (!settings.jsx) {
pipeline.use(recmaJsxBuild, settings)
}
pipeline.use(recmaStringify, settings).use(settings.recmaPlugins || [])
// @ts-expect-error: we added plugins with if-checks, which TS doesn’t get.
return pipeline
}