/** * @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} 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 | null | undefined} [mdExtensions] * List of markdown extensions, with dot (default: `['.md', '.markdown', …]`); * affects integrations. * @property {ReadonlyArray | 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 what `evaluate` does in separate steps, 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: `` 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 | 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` properties on `td` and `th` into CSS `style` * properties (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 | 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 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 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 }