diff --git a/package-lock.json b/package-lock.json index 39cc887d3..19f0b29b6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -95,7 +95,6 @@ "remark-squeeze-paragraphs": "^5.0.0", "remark-strip-badges": "^6.0.0", "remark-toc": "^8.0.0", - "rimraf": "^3.0.0", "rodemirror": "^2.0.0", "type-coverage": "^2.0.0", "typescript": "^4.0.0", diff --git a/package.json b/package.json index bb798eb05..d934dd588 100644 --- a/package.json +++ b/package.json @@ -98,7 +98,6 @@ "remark-squeeze-paragraphs": "^5.0.0", "remark-strip-badges": "^6.0.0", "remark-toc": "^8.0.0", - "rimraf": "^3.0.0", "rodemirror": "^2.0.0", "type-coverage": "^2.0.0", "typescript": "^4.0.0", @@ -119,7 +118,6 @@ "scripts": { "postinstall": "patch-package", "prepare": "husky install", - "clean": "npm exec -c \"rimraf node_modules\" --workspaces", "docs-prep": "node --unhandled-rejections=strict website/prep.js && postcss docs/_asset/index.css -o public/index.css", "docs-bundle-dev": "cross-env NODE_ENV=development node --unhandled-rejections=strict website/bundle.js", "docs-bundle-prod": "cross-env NODE_ENV=production node --unhandled-rejections=strict website/bundle.js", diff --git a/packages/esbuild/lib/index.js b/packages/esbuild/lib/index.js index dadec2238..d14b25131 100644 --- a/packages/esbuild/lib/index.js +++ b/packages/esbuild/lib/index.js @@ -7,10 +7,12 @@ * @typedef {import('esbuild').Message} Message * @typedef {import('vfile').VFileValue} VFileValue * @typedef {import('vfile-message').VFileMessage} VFileMessage - * @typedef {import('unist').Point} Point * @typedef {import('@mdx-js/mdx/lib/core.js').ProcessorOptions} ProcessorOptions - * - * @typedef {ProcessorOptions & {allowDangerousRemoteMdx?: boolean}} Options + */ + +/** + * @typedef {ProcessorOptions & {allowDangerousRemoteMdx?: boolean | null | undefined}} Options + * Configuration. */ import assert from 'node:assert' @@ -32,11 +34,11 @@ const p = process /** * Compile MDX w/ esbuild. * - * @param {Options} [options] + * @param {Options | null | undefined} [options] * @return {Plugin} */ -export function esbuild(options = {}) { - const {allowDangerousRemoteMdx, ...rest} = options +export function esbuild(options) { + const {allowDangerousRemoteMdx, ...rest} = options || {} const name = '@mdx-js/esbuild' const remoteNamespace = name + '-remote' const {extnames, process} = createFormatAwareProcessors(rest) @@ -123,22 +125,24 @@ export function esbuild(options = {}) { } /** - * @param {Omit & {pluginData?: {contents?: string|Buffer}}} data + * @param {Omit & {pluginData?: {contents?: Buffer | string | null | undefined}}} data * @returns {Promise} */ async function onload(data) { /** @type {string} */ const doc = String( - data.pluginData && data.pluginData.contents !== undefined + data.pluginData && + data.pluginData.contents !== null && + data.pluginData.contents !== undefined ? data.pluginData.contents : /* eslint-disable-next-line security/detect-non-literal-fs-filename */ await fs.readFile(data.path) ) let file = new VFile({value: doc, path: data.path}) - /** @type {VFileValue|undefined} */ + /** @type {VFileValue | undefined} */ let value - /** @type {Array} */ + /** @type {Array} */ let messages = [] /** @type {Array} */ const errors = [] @@ -150,7 +154,7 @@ export function esbuild(options = {}) { value = file.value messages = file.messages } catch (error_) { - const error = /** @type {VFileMessage|Error} */ (error_) + const error = /** @type {Error | VFileMessage} */ (error_) if ('fatal' in error) error.fatal = true messages.push(error) } @@ -225,7 +229,7 @@ export function esbuild(options = {}) { // V8 on Erbium. /* c8 ignore next 9 */ return { - contents: value, + contents: value || '', errors, warnings, resolveDir: http.test(file.path) diff --git a/packages/esbuild/package.json b/packages/esbuild/package.json index 7bf05294e..b6b59b7bd 100644 --- a/packages/esbuild/package.json +++ b/packages/esbuild/package.json @@ -56,7 +56,7 @@ }, "scripts": { "prepack": "npm run build", - "build": "rimraf \"lib/**/*.d.ts\" \"test/**/*.d.ts\" \"*.d.ts\" && tsc && type-coverage", + "build": "tsc --build --clean && tsc --build && type-coverage", "test-api": "uvu test \"\\.js$\"", "test-coverage": "c8 --check-coverage --100 --reporter lcov npm run test-api", "test": "npm run build && npm run test-coverage" diff --git a/packages/esbuild/test/index.test.js b/packages/esbuild/test/index.test.js index 99e96d4ce..3099773c6 100644 --- a/packages/esbuild/test/index.test.js +++ b/packages/esbuild/test/index.test.js @@ -1,11 +1,10 @@ /** * @typedef {import('esbuild').BuildFailure} BuildFailure - * @typedef {import('esbuild').Message} Message * @typedef {import('hast').Root} Root * @typedef {import('vfile').VFile} VFile * @typedef {import('mdx/types.js').MDXContent} MDXContent * - * @typedef {import('remark-mdx')} + * @typedef {import('remark-mdx')} DoNotTouchIncludeMathInTree */ import {promises as fs} from 'fs' @@ -251,7 +250,6 @@ test('@mdx-js/esbuild', async () => { assert.ok(text && text.type === 'text') const jsx = head.children[1] // JSX in heading assert.ok(jsx && jsx.type === 'mdxJsxTextElement') - console.log(head) file.message('1') file.message('2', eol) file.message('3', tree) diff --git a/packages/esbuild/tsconfig.json b/packages/esbuild/tsconfig.json index 94d319f4f..6cac7d0aa 100644 --- a/packages/esbuild/tsconfig.json +++ b/packages/esbuild/tsconfig.json @@ -1,4 +1,5 @@ { "extends": "../../tsconfig.json", - "include": ["lib/**/*.js", "test/**/*.js", "index.js"] + "include": ["**/*.cjs", "**/*.js", "**/*.jsx"], + "exclude": ["coverage/", "node_modules/"] } diff --git a/packages/loader/index.cjs b/packages/loader/index.cjs index 0acb30bfa..60ad1a5cc 100644 --- a/packages/loader/index.cjs +++ b/packages/loader/index.cjs @@ -1,3 +1,7 @@ +/** + * @typedef {import('webpack').LoaderContext} LoaderContext + */ + 'use strict' /** @@ -5,6 +9,7 @@ * * @todo once webpack supports ESM loaders, remove this wrapper. * + * @this {LoaderContext} * @param {string} code */ module.exports = function (code) { diff --git a/packages/loader/index.d.cts b/packages/loader/index.d.cts new file mode 100644 index 000000000..34362abf8 --- /dev/null +++ b/packages/loader/index.d.cts @@ -0,0 +1,7 @@ +// Some TS versions use this file, some `index.d.ts`. +type LoaderContext = import('webpack').LoaderContext + +declare function mdxLoader(this: LoaderContext, code: string): void +export = mdxLoader + +export type Options = import('@mdx-js/mdx/lib/core.js').ProcessorOptions diff --git a/packages/loader/index.d.ts b/packages/loader/index.d.ts index c9d05356e..ee1fe0ee9 100644 --- a/packages/loader/index.d.ts +++ b/packages/loader/index.d.ts @@ -1,3 +1,4 @@ +// Some TS versions use this file, some `index.d.cts`. import type {ProcessorOptions} from '@mdx-js/mdx/lib/core.js' import type {LoaderContext} from 'webpack' diff --git a/packages/loader/lib/index.js b/packages/loader/lib/index.js index eedc24887..9869d7bfe 100644 --- a/packages/loader/lib/index.js +++ b/packages/loader/lib/index.js @@ -1,13 +1,23 @@ /** + * @typedef {import('@mdx-js/mdx').CompileOptions} CompileOptions * @typedef {import('vfile').VFileCompatible} VFileCompatible * @typedef {import('vfile').VFile} VFile * @typedef {import('vfile-message').VFileMessage} VFileMessage - * @typedef {import('@mdx-js/mdx').CompileOptions} CompileOptions - * @typedef {Pick} Defaults - * @typedef {Omit} Options * @typedef {import('webpack').LoaderContext} LoaderContext * @typedef {import('webpack').Compiler} WebpackCompiler - * @typedef {(vfileCompatible: VFileCompatible) => Promise} Process + */ + +/** + * @typedef {Pick} Defaults + * @typedef {Omit} Options + * Configuration. + * + * @callback Process + * Process. + * @param {VFileCompatible} vfileCompatible + * Input. + * @returns {Promise} + * File. */ import {createHash} from 'node:crypto' @@ -30,7 +40,7 @@ const cache = new WeakMap() * * @this {LoaderContext} * @param {string} value - * @param {(error: Error|null|undefined, content?: string|Buffer, map?: Object) => void} callback + * @param {LoaderContext['callback']} callback */ export function loader(value, callback) { /** @type {Defaults} */ @@ -69,7 +79,9 @@ export function loader(value, callback) { process({value, path: this.resourcePath}).then( (file) => { - callback(null, file.value, file.map || undefined) + // @ts-expect-error: `webpack` is not compiled with `exactOptionalPropertyTypes`, + // so it does not allow `file.map` to be `undefined` here. + callback(null, file.value, file.map) }, (/** @type VFileMessage */ error) => { const fpath = path.relative(this.context, this.resourcePath) diff --git a/packages/loader/package.json b/packages/loader/package.json index f595e6051..a2ff7e601 100644 --- a/packages/loader/package.json +++ b/packages/loader/package.json @@ -39,8 +39,9 @@ "types": "index.d.ts", "files": [ "lib/", - "index.d.ts", - "index.cjs" + "index.cjs", + "index.d.cts", + "index.d.ts" ], "dependencies": { "@mdx-js/mdx": "^2.0.0", @@ -65,7 +66,7 @@ }, "scripts": { "prepack": "npm run build", - "build": "rimraf \"lib/**/*.d.ts\" \"test/**/*.d.ts\" && tsc && type-coverage", + "build": "tsc --build --clean && tsc --build && type-coverage", "test-api": "uvu test \"\\.js$\"", "test-coverage": "c8 --check-coverage --100 --reporter lcov npm run test-api", "test": "npm run build && npm run test-coverage" diff --git a/packages/loader/test/index.test.js b/packages/loader/test/index.test.js index f3165dd45..bf9a2be87 100644 --- a/packages/loader/test/index.test.js +++ b/packages/loader/test/index.test.js @@ -2,7 +2,6 @@ * @typedef {import('mdx/types.js').MDXContent} MDXContent * @typedef {import('preact').FunctionComponent} PreactComponent * @typedef {import('vue').Component} VueComponent - * @typedef {import('vue').SetupContext} SetupContext */ import {promises as fs} from 'fs' diff --git a/packages/loader/tsconfig.json b/packages/loader/tsconfig.json index 115ac6895..1a560ac14 100644 --- a/packages/loader/tsconfig.json +++ b/packages/loader/tsconfig.json @@ -1,4 +1,5 @@ { "extends": "../../tsconfig.json", - "include": ["lib/**/*.js", "test/**/*.js"] + "include": ["**/*.cjs", "**/*.js", "**/*.jsx", "index.d.ts", "index.d.cts"], + "exclude": ["coverage/", "node_modules/"] } diff --git a/packages/mdx/lib/compile.js b/packages/mdx/lib/compile.js index ce1e17034..6d54d2879 100644 --- a/packages/mdx/lib/compile.js +++ b/packages/mdx/lib/compile.js @@ -1,14 +1,21 @@ /** - * @typedef {import('vfile').VFileCompatible} VFileCompatible * @typedef {import('vfile').VFile} VFile + * @typedef {import('vfile').VFileCompatible} VFileCompatible * @typedef {import('./core.js').PluginOptions} PluginOptions * @typedef {import('./core.js').BaseProcessorOptions} BaseProcessorOptions + */ + +/** * @typedef {Omit} CoreProcessorOptions + * Core configuration. * * @typedef ExtraOptions - * @property {'detect'|'mdx'|'md'} [format='detect'] Format of `file` + * Extra configuration. + * @property {'detect' | 'mdx' | 'md' | null | undefined} [format='detect'] + * Format of `file`. * * @typedef {CoreProcessorOptions & PluginOptions & ExtraOptions} CompileOptions + * Configuration. */ import {createProcessor} from './core.js' @@ -20,8 +27,10 @@ import {resolveFileAndOptions} from './util/resolve-file-and-options.js' * @param {VFileCompatible} vfileCompatible * MDX document to parse (`string`, `Buffer`, `vfile`, anything that can be * given to `vfile`). - * @param {CompileOptions} [compileOptions] + * @param {CompileOptions | null | undefined} [compileOptions] + * Compile configuration. * @return {Promise} + * File. */ export function compile(vfileCompatible, compileOptions) { const {file, options} = resolveFileAndOptions(vfileCompatible, compileOptions) @@ -34,8 +43,10 @@ export function compile(vfileCompatible, compileOptions) { * @param {VFileCompatible} vfileCompatible * MDX document to parse (`string`, `Buffer`, `vfile`, anything that can be * given to `vfile`). - * @param {CompileOptions} [compileOptions] + * @param {CompileOptions | null | undefined} [compileOptions] + * Compile configuration. * @return {VFile} + * File. */ export function compileSync(vfileCompatible, compileOptions) { const {file, options} = resolveFileAndOptions(vfileCompatible, compileOptions) diff --git a/packages/mdx/lib/core.js b/packages/mdx/lib/core.js index 1d3aec731..f028dbfec 100644 --- a/packages/mdx/lib/core.js +++ b/packages/mdx/lib/core.js @@ -1,33 +1,39 @@ /** - * @typedef {import('unified').Processor} Processor + * @typedef {import('remark-rehype').Options} RemarkRehypeOptions * @typedef {import('unified').PluggableList} PluggableList + * @typedef {import('unified').Processor} Processor * @typedef {import('./plugin/recma-document.js').RecmaDocumentOptions} RecmaDocumentOptions * @typedef {import('./plugin/recma-stringify.js').RecmaStringifyOptions} RecmaStringifyOptions * @typedef {import('./plugin/recma-jsx-rewrite.js').RecmaJsxRewriteOptions} RecmaJsxRewriteOptions - * @typedef {import('remark-rehype').Options} RemarkRehypeOptions - * + */ + +/** * @typedef BaseProcessorOptions - * @property {boolean} [jsx=false] + * Base configuration. + * @property {boolean | null | undefined} [jsx=false] * Whether to keep JSX. - * @property {'mdx'|'md'} [format='mdx'] + * @property {'mdx' | 'md' | null | undefined} [format='mdx'] * Format of the files to be processed. - * @property {'program'|'function-body'} [outputFormat='program'] + * @property {'function-body' | 'program'} [outputFormat='program'] * Whether to compile to a whole program or a function body.. - * @property {Array} [mdExtensions] + * @property {Array | null | undefined} [mdExtensions] * Extensions (with `.`) for markdown. - * @property {Array} [mdxExtensions] + * @property {Array | null | undefined} [mdxExtensions] * Extensions (with `.`) for MDX. - * @property {PluggableList} [recmaPlugins] + * @property {PluggableList | null | undefined} [recmaPlugins] * List of recma (esast, JavaScript) plugins. - * @property {PluggableList} [remarkPlugins] + * @property {PluggableList | null | undefined} [remarkPlugins] * List of remark (mdast, markdown) plugins. - * @property {PluggableList} [rehypePlugins] + * @property {PluggableList | null | undefined} [rehypePlugins] * List of rehype (hast, HTML) plugins. - * @property {RemarkRehypeOptions} [remarkRehypeOptions] + * @property {RemarkRehypeOptions | null | undefined} [remarkRehypeOptions] * Options to pass through to `remark-rehype`. * * @typedef {Omit} PluginOptions + * Configuration for internal plugins. + * * @typedef {BaseProcessorOptions & PluginOptions} ProcessorOptions + * Configuration for processor. */ import {unified} from 'unified' @@ -60,12 +66,14 @@ const removedOptions = [ * 2. Transform through remark (mdast), rehype (hast), and recma (esast) * 3. Serialize as JavaScript * - * @param {ProcessorOptions} [options] + * @param {ProcessorOptions | null | undefined} [options] + * Configuration. * @return {Processor} + * Processor. */ -export function createProcessor(options = {}) { +export function createProcessor(options) { const { - development = defaultDevelopment, + development, jsx, format, outputFormat, @@ -73,15 +81,19 @@ export function createProcessor(options = {}) { recmaPlugins, rehypePlugins, remarkPlugins, - remarkRehypeOptions = {}, + remarkRehypeOptions, SourceMapGenerator, ...rest - } = options + } = options || {} + const dev = + development === null || development === undefined + ? defaultDevelopment + : development let index = -1 while (++index < removedOptions.length) { const key = removedOptions[index] - if (key in options) { + if (options && key in options) { throw new Error( '`options.' + key + @@ -104,14 +116,18 @@ export function createProcessor(options = {}) { pipeline.use(remarkMdx) } + const extraNodeTypes = remarkRehypeOptions + ? /* c8 ignore next */ + remarkRehypeOptions.passThrough || [] + : [] + pipeline .use(remarkMarkAndUnravel) .use(remarkPlugins || []) .use(remarkRehype, { ...remarkRehypeOptions, allowDangerousHtml: true, - /* c8 ignore next */ - passThrough: [...(remarkRehypeOptions.passThrough || []), ...nodeTypes] + passThrough: [...extraNodeTypes, ...nodeTypes] }) .use(rehypePlugins || []) @@ -122,10 +138,14 @@ export function createProcessor(options = {}) { pipeline .use(rehypeRecma) .use(recmaDocument, {...rest, outputFormat}) - .use(recmaJsxRewrite, {development, providerImportSource, outputFormat}) + .use(recmaJsxRewrite, { + development: dev, + providerImportSource, + outputFormat + }) if (!jsx) { - pipeline.use(recmaJsxBuild, {development, outputFormat}) + pipeline.use(recmaJsxBuild, {development: dev, outputFormat}) } pipeline.use(recmaStringify, {SourceMapGenerator}).use(recmaPlugins || []) diff --git a/packages/mdx/lib/evaluate.js b/packages/mdx/lib/evaluate.js index 3a17b98d7..72e0bc7a9 100644 --- a/packages/mdx/lib/evaluate.js +++ b/packages/mdx/lib/evaluate.js @@ -1,8 +1,7 @@ /** + * @typedef {import('mdx/types.js').MDXModule} ExportMap * @typedef {import('vfile').VFileCompatible} VFileCompatible * @typedef {import('./util/resolve-evaluate-options.js').EvaluateOptions} EvaluateOptions - * - * @typedef {import('mdx/types.js').MDXModule} ExportMap */ import {compile, compileSync} from './compile.js' @@ -16,7 +15,9 @@ import {resolveEvaluateOptions} from './util/resolve-evaluate-options.js' * MDX document to parse (`string`, `Buffer`, `vfile`, anything that can be * given to `vfile`). * @param {EvaluateOptions} evaluateOptions + * Configuration for evaluation. * @return {Promise} + * Export map. */ export async function evaluate(vfileCompatible, evaluateOptions) { const {compiletime, runtime} = resolveEvaluateOptions(evaluateOptions) @@ -32,7 +33,9 @@ export async function evaluate(vfileCompatible, evaluateOptions) { * MDX document to parse (`string`, `Buffer`, `vfile`, anything that can be * given to `vfile`). * @param {EvaluateOptions} evaluateOptions + * Configuration for evaluation. * @return {ExportMap} + * Export map. */ export function evaluateSync(vfileCompatible, evaluateOptions) { const {compiletime, runtime} = resolveEvaluateOptions(evaluateOptions) diff --git a/packages/mdx/lib/plugin/recma-document.js b/packages/mdx/lib/plugin/recma-document.js index 82996bb90..156b57153 100644 --- a/packages/mdx/lib/plugin/recma-document.js +++ b/packages/mdx/lib/plugin/recma-document.js @@ -1,9 +1,9 @@ /** * @typedef {import('estree-jsx').Directive} Directive + * @typedef {import('estree-jsx').ExportAllDeclaration} ExportAllDeclaration * @typedef {import('estree-jsx').ExportDefaultDeclaration} ExportDefaultDeclaration - * @typedef {import('estree-jsx').ExportSpecifier} ExportSpecifier * @typedef {import('estree-jsx').ExportNamedDeclaration} ExportNamedDeclaration - * @typedef {import('estree-jsx').ExportAllDeclaration} ExportAllDeclaration + * @typedef {import('estree-jsx').ExportSpecifier} ExportSpecifier * @typedef {import('estree-jsx').Expression} Expression * @typedef {import('estree-jsx').FunctionDeclaration} FunctionDeclaration * @typedef {import('estree-jsx').ImportDeclaration} ImportDeclaration @@ -11,32 +11,35 @@ * @typedef {import('estree-jsx').ModuleDeclaration} ModuleDeclaration * @typedef {import('estree-jsx').Node} Node * @typedef {import('estree-jsx').Program} Program + * @typedef {import('estree-jsx').Property} Property * @typedef {import('estree-jsx').SimpleLiteral} SimpleLiteral + * @typedef {import('estree-jsx').SpreadElement} SpreadElement * @typedef {import('estree-jsx').Statement} Statement * @typedef {import('estree-jsx').VariableDeclarator} VariableDeclarator - * @typedef {import('estree-jsx').SpreadElement} SpreadElement - * @typedef {import('estree-jsx').Property} Property - * + */ + +/** * @typedef RecmaDocumentOptions - * @property {'program'|'function-body'} [outputFormat='program'] + * Configuration for internal plugin `recma-document`. + * @property {'function-body' | 'program' | null | undefined} [outputFormat='program'] * Whether to use either `import` and `export` statements to get the runtime * (and optionally provider) and export the content, or get values from * `arguments` and return things. - * @property {boolean} [useDynamicImport=false] + * @property {boolean | null | undefined} [useDynamicImport=false] * Whether to keep `import` (and `export … from`) statements or compile them * to dynamic `import()` instead. - * @property {string} [baseUrl] + * @property {string | null | undefined} [baseUrl] * Resolve `import`s (and `export … from`, and `import.meta.url`) relative to * this URL. - * @property {string} [pragma='React.createElement'] + * @property {string | null | undefined} [pragma='React.createElement'] * Pragma for JSX (used in classic runtime). - * @property {string} [pragmaFrag='React.Fragment'] + * @property {string | null | undefined} [pragmaFrag='React.Fragment'] * Pragma for JSX fragments (used in classic runtime). - * @property {string} [pragmaImportSource='react'] + * @property {string | null | undefined} [pragmaImportSource='react'] * Where to import the identifier of `pragma` from (used in classic runtime). - * @property {string} [jsxImportSource='react'] + * @property {string | null | undefined} [jsxImportSource='react'] * Place to import automatic JSX runtimes from (used in automatic runtime). - * @property {'automatic'|'classic'} [jsxRuntime='automatic'] + * @property {'automatic' | 'classic' | null | undefined} [jsxRuntime='automatic'] * JSX runtime to use. */ @@ -52,31 +55,34 @@ import {isDeclaration} from '../util/estree-util-is-declaration.js' /** * A plugin to wrap the estree in `MDXContent`. * - * @type {import('unified').Plugin<[RecmaDocumentOptions]|[], Program>} + * @type {import('unified').Plugin<[RecmaDocumentOptions | null | undefined] | [], Program>} */ -export function recmaDocument(options = {}) { - const { - baseUrl, - useDynamicImport, - outputFormat = 'program', - pragma = 'React.createElement', - pragmaFrag = 'React.Fragment', - pragmaImportSource = 'react', - jsxImportSource = 'react', - jsxRuntime = 'automatic' - } = options +export function recmaDocument(options) { + // Always given inside `@mdx-js/mdx` + /* c8 ignore next */ + const options_ = options || {} + const baseUrl = options_.baseUrl || undefined + const useDynamicImport = options_.useDynamicImport || undefined + const outputFormat = options_.outputFormat || 'program' + const pragma = + options_.pragma === undefined ? 'React.createElement' : options_.pragma + const pragmaFrag = + options_.pragmaFrag === undefined ? 'React.Fragment' : options_.pragmaFrag + const pragmaImportSource = options_.pragmaImportSource || 'react' + const jsxImportSource = options_.jsxImportSource || 'react' + const jsxRuntime = options_.jsxRuntime || 'automatic' return (tree, file) => { - /** @type {Array} */ + /** @type {Array<[string, string] | string>} */ const exportedIdentifiers = [] - /** @type {Array} */ + /** @type {Array} */ const replacement = [] /** @type {Array} */ const pragmas = [] let exportAllCount = 0 - /** @type {ExportDefaultDeclaration|ExportSpecifier|undefined} */ + /** @type {ExportDefaultDeclaration | ExportSpecifier | undefined} */ let layout - /** @type {boolean|undefined} */ + /** @type {boolean | undefined} */ let content /** @type {Node} */ let child @@ -298,9 +304,7 @@ export function recmaDocument(options = {}) { if (baseUrl) { walk(tree, { - enter(_node) { - const node = /** @type {Node} */ (_node) - + enter(node) { if ( node.type === 'MemberExpression' && 'object' in node && @@ -319,7 +323,7 @@ export function recmaDocument(options = {}) { } /** - * @param {ExportNamedDeclaration|ExportAllDeclaration} node + * @param {ExportAllDeclaration | ExportNamedDeclaration} node * @returns {void} */ function handleExport(node) { @@ -348,7 +352,7 @@ export function recmaDocument(options = {}) { } /** - * @param {ImportDeclaration|ExportNamedDeclaration|ExportAllDeclaration} node + * @param {ExportAllDeclaration | ExportNamedDeclaration | ImportDeclaration} node * @returns {void} */ function handleEsm(node) { @@ -375,7 +379,7 @@ export function recmaDocument(options = {}) { node.source = create(node.source, {type: 'Literal', value}) } - /** @type {Statement|ModuleDeclaration|undefined} */ + /** @type {ModuleDeclaration | Statement | undefined} */ let replace /** @type {Expression} */ let init @@ -478,9 +482,9 @@ export function recmaDocument(options = {}) { } /** - * @param {Expression} [content] - * @param {boolean} [hasInternalLayout] - * @returns {FunctionDeclaration[]} + * @param {Expression | undefined} [content] + * @param {boolean | undefined} [hasInternalLayout] + * @returns {Array} */ function createMdxContent(content, hasInternalLayout) { /** @type {JSXElement} */ @@ -575,12 +579,7 @@ export function recmaDocument(options = {}) { ], body: { type: 'BlockStatement', - body: [ - { - type: 'ReturnStatement', - argument: result - } - ] + body: [{type: 'ReturnStatement', argument: result}] } } ] diff --git a/packages/mdx/lib/plugin/recma-jsx-build.js b/packages/mdx/lib/plugin/recma-jsx-build.js index f7e14b021..630c6230e 100644 --- a/packages/mdx/lib/plugin/recma-jsx-build.js +++ b/packages/mdx/lib/plugin/recma-jsx-build.js @@ -1,11 +1,16 @@ /** * @typedef {import('estree-jsx').Program} Program * @typedef {import('estree-util-build-jsx').BuildJsxOptions} BuildJsxOptions - * - * @typedef RecmaJsxBuildOptions - * @property {'program'|'function-body'} [outputFormat='program'] + */ + +/** + * @typedef ExtraOptions + * Configuration for internal plugin `recma-jsx-build`. + * @property {'function-body' | 'program' | null | undefined} [outputFormat='program'] * Whether to keep the import of the automatic runtime or get it from * `arguments[0]` instead. + * + * @typedef {BuildJsxOptions & ExtraOptions} RecmaJsxBuildOptions */ import {buildJsx} from 'estree-util-build-jsx' @@ -16,10 +21,12 @@ import {toIdOrMemberExpression} from '../util/estree-util-to-id-or-member-expres * A plugin to build JSX into function calls. * `estree-util-build-jsx` does all the work for us! * - * @type {import('unified').Plugin<[BuildJsxOptions & RecmaJsxBuildOptions?], Program>} + * @type {import('unified').Plugin<[RecmaJsxBuildOptions | null | undefined] | [], Program>} */ -export function recmaJsxBuild(options = {}) { - const {development, outputFormat} = options +export function recmaJsxBuild(options) { + // Always given inside `@mdx-js/mdx` + /* c8 ignore next */ + const {development, outputFormat} = options || {} return (tree, file) => { buildJsx(tree, {development, filePath: file.history[0]}) diff --git a/packages/mdx/lib/plugin/recma-jsx-rewrite.js b/packages/mdx/lib/plugin/recma-jsx-rewrite.js index 35aeca805..50dd37a19 100644 --- a/packages/mdx/lib/plugin/recma-jsx-rewrite.js +++ b/packages/mdx/lib/plugin/recma-jsx-rewrite.js @@ -1,30 +1,30 @@ /** - * @typedef {import('estree-jsx').Node} Node * @typedef {import('estree-jsx').Expression} Expression - * @typedef {import('estree-jsx').Function} ESFunction + * @typedef {import('estree-jsx').Function} EstreeFunction + * @typedef {import('estree-jsx').Identifier} Identifier * @typedef {import('estree-jsx').ImportSpecifier} ImportSpecifier * @typedef {import('estree-jsx').JSXElement} JSXElement - * @typedef {import('estree-jsx').JSXIdentifier} JSXIdentifier - * @typedef {import('estree-jsx').JSXNamespacedName} JSXNamespacedName * @typedef {import('estree-jsx').ModuleDeclaration} ModuleDeclaration + * @typedef {import('estree-jsx').Node} Node + * @typedef {import('estree-jsx').ObjectPattern} ObjectPattern * @typedef {import('estree-jsx').Program} Program * @typedef {import('estree-jsx').Property} Property * @typedef {import('estree-jsx').Statement} Statement * @typedef {import('estree-jsx').VariableDeclarator} VariableDeclarator - * @typedef {import('estree-jsx').ObjectPattern} ObjectPattern - * @typedef {import('estree-jsx').Identifier} Identifier - * - * @typedef {import('estree-walker').SyncHandler} WalkHandler * * @typedef {import('periscopic').Scope & {node: Node}} Scope - * + */ + +/** * @typedef RecmaJsxRewriteOptions - * @property {'program'|'function-body'} [outputFormat='program'] + * Configuration for internal plugin `recma-jsx-rewrite`. + * @property {'function-body' | 'program' | null | undefined} [outputFormat='program'] * Whether to use an import statement or `arguments[0]` to get the provider. - * @property {string} [providerImportSource] + * @property {string | null | undefined} [providerImportSource] * Place to import a provider from. - * @property {boolean} [development=false] + * @property {boolean | null | undefined} [development=false] * Whether to add extra info to error messages in generated code. + * * This also results in the development automatic JSX runtime * (`/jsx-dev-runtime`, `jsxDEV`) being used, which passes positional info to * nodes. @@ -37,7 +37,7 @@ * @property {Array} tags * @property {Record} references * @property {Map} idToInvalidComponentName - * @property {ESFunction} node + * @property {EstreeFunction} node */ import {stringifyPosition} from 'unist-util-stringify-position' @@ -61,27 +61,26 @@ const own = {}.hasOwnProperty * It also makes sure that any undefined components are defined: either from * received components or as a function that throws an error. * - * @type {import('unified').Plugin<[RecmaJsxRewriteOptions]|[], Program>} + * @type {import('unified').Plugin<[RecmaJsxRewriteOptions | null | undefined] | [], Program>} */ -export function recmaJsxRewrite(options = {}) { - const {development, providerImportSource, outputFormat} = options +export function recmaJsxRewrite(options) { + // Always given inside `@mdx-js/mdx` + /* c8 ignore next */ + const {development, providerImportSource, outputFormat} = options || {} return (tree, file) => { // Find everything that’s defined in the top-level scope. const scopeInfo = analyze(tree) /** @type {Array} */ const fnStack = [] - /** @type {boolean|undefined} */ - let importProvider - /** @type {boolean|undefined} */ - let createErrorHelper - /** @type {Scope|null} */ + let importProvider = false + let createErrorHelper = false + /** @type {Scope | undefined} */ let currentScope walk(tree, { - enter(_node) { - const node = /** @type {Node} */ (_node) - const newScope = /** @type {Scope|undefined} */ ( + enter(node) { + const newScope = /** @type {Scope | undefined} */ ( scopeInfo.map.get(node) ) @@ -144,7 +143,7 @@ export function recmaJsxRewrite(options = {}) { const isInScope = inScope(currentScope, id) if (!own.call(fnScope.references, fullId)) { - const parentScope = /** @type {Scope|null} */ ( + const parentScope = /** @type {Scope | undefined} */ ( currentScope.parent ) if ( @@ -242,7 +241,7 @@ export function recmaJsxRewrite(options = {}) { node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression' ) { - const fn = /** @type {ESFunction} */ (node) + const fn = node const scope = fnStack[fnStack.length - 1] /** @type {string} */ let name @@ -326,7 +325,7 @@ export function recmaJsxRewrite(options = {}) { } : parameters[0] - /** @type {ObjectPattern|undefined} */ + /** @type {ObjectPattern | undefined} */ let componentsPattern // Add components to scope. @@ -558,7 +557,7 @@ export function recmaJsxRewrite(options = {}) { /** * @param {string} providerImportSource * @param {RecmaJsxRewriteOptions['outputFormat']} outputFormat - * @returns {Statement|ModuleDeclaration} + * @returns {Statement | ModuleDeclaration} */ function createImportProvider(providerImportSource, outputFormat) { /** @type {Array} */ @@ -587,7 +586,7 @@ function createImportProvider(providerImportSource, outputFormat) { } /** - * @param {ESFunction} node + * @param {EstreeFunction} node * @param {string} name * @returns {boolean} */ @@ -598,9 +597,10 @@ function isNamedFunction(node, name) { /** * @param {Scope} scope * @param {string} id + * @returns {boolean} */ function inScope(scope, id) { - /** @type {Scope|null} */ + /** @type {Scope | undefined} */ let currentScope = scope while (currentScope) { diff --git a/packages/mdx/lib/plugin/recma-stringify.js b/packages/mdx/lib/plugin/recma-stringify.js index 9f8e6477f..b1c99bfa9 100644 --- a/packages/mdx/lib/plugin/recma-stringify.js +++ b/packages/mdx/lib/plugin/recma-stringify.js @@ -3,7 +3,8 @@ * @typedef {typeof import('source-map').SourceMapGenerator} SourceMapGenerator * * @typedef RecmaStringifyOptions - * @property {SourceMapGenerator} [SourceMapGenerator] + * Configuration for internal plugin `recma-stringify`. + * @property {SourceMapGenerator | null | undefined} [SourceMapGenerator] * Generate a source map by passing a `SourceMapGenerator` from `source-map` * in. */ @@ -15,10 +16,12 @@ import {toJs, jsx} from 'estree-util-to-js' * support for serializing JSX. * * @this {import('unified').Processor} - * @type {import('unified').Plugin<[RecmaStringifyOptions]|[], Program, string>} + * @type {import('unified').Plugin<[RecmaStringifyOptions | null | undefined] | [], Program, string>} */ -export function recmaStringify(options = {}) { - const {SourceMapGenerator} = options +export function recmaStringify(options) { + // Always given inside `@mdx-js/mdx` + /* c8 ignore next */ + const {SourceMapGenerator} = options || {} Object.assign(this, {Compiler: compiler}) diff --git a/packages/mdx/lib/plugin/rehype-recma.js b/packages/mdx/lib/plugin/rehype-recma.js index 4acec9a2e..a55abbc31 100644 --- a/packages/mdx/lib/plugin/rehype-recma.js +++ b/packages/mdx/lib/plugin/rehype-recma.js @@ -9,7 +9,7 @@ import {toEstree} from 'hast-util-to-estree' * A plugin to transform an HTML (hast) tree to a JS (estree). * `hast-util-to-estree` does all the work for us! * - * @type {import('unified').Plugin, Root, Program>} + * @type {import('unified').Plugin<[], Root, Program>} */ export function rehypeRecma() { return (tree) => toEstree(tree) diff --git a/packages/mdx/lib/plugin/rehype-remove-raw.js b/packages/mdx/lib/plugin/rehype-remove-raw.js index bf660b69f..098f4c0d2 100644 --- a/packages/mdx/lib/plugin/rehype-remove-raw.js +++ b/packages/mdx/lib/plugin/rehype-remove-raw.js @@ -7,10 +7,11 @@ import {visit} from 'unist-util-visit' /** * A tiny plugin that removes raw HTML. + * * This is needed if the format is `md` and `rehype-raw` was not used to parse * dangerous HTML into nodes. * - * @type {import('unified').Plugin, Root>} + * @type {import('unified').Plugin<[], Root>} */ export function rehypeRemoveRaw() { return (tree) => { diff --git a/packages/mdx/lib/plugin/remark-mark-and-unravel.js b/packages/mdx/lib/plugin/remark-mark-and-unravel.js index 53c3f929e..68e6eb76a 100644 --- a/packages/mdx/lib/plugin/remark-mark-and-unravel.js +++ b/packages/mdx/lib/plugin/remark-mark-and-unravel.js @@ -1,8 +1,5 @@ /** * @typedef {import('mdast').Root} Root - * @typedef {import('mdast').Content} Content - * @typedef {Root|Content} Node - * @typedef {Extract} Parent * * @typedef {import('remark-mdx')} DoNotTouchAsThisImportItIncludesMdxInTree */ @@ -12,19 +9,18 @@ import {visit} from 'unist-util-visit' /** * A tiny plugin that unravels `

x

` but also * `

` (so it has no knowledge of “HTML”). + * * It also marks JSX as being explicitly JSX, so when a user passes a `h1` * component, it is used for `# heading` but not for `

heading

`. * - * @type {import('unified').Plugin, Root>} + * @type {import('unified').Plugin<[], Root>} */ export function remarkMarkAndUnravel() { return (tree) => { - visit(tree, (node, index, parent_) => { - const parent = /** @type {Parent} */ (parent_) + visit(tree, (node, index, parent) => { let offset = -1 let all = true - /** @type {boolean|undefined} */ - let oneOrMore + let oneOrMore = false if (parent && typeof index === 'number' && node.type === 'paragraph') { const children = node.children diff --git a/packages/mdx/lib/run.js b/packages/mdx/lib/run.js index 80ecfec9b..aab96cbb2 100644 --- a/packages/mdx/lib/run.js +++ b/packages/mdx/lib/run.js @@ -7,7 +7,9 @@ const AsyncFunction = Object.getPrototypeOf(run).constructor * @param {{toString(): string}} file * JS document to run. * @param {unknown} options - * @return {Promise<*>} + * Parameter. + * @return {Promise} + * Anthing. */ export async function run(file, options) { // V8 on Erbium. @@ -21,7 +23,9 @@ export async function run(file, options) { * @param {{toString(): string}} file * JS document to run. * @param {unknown} options - * @return {*} + * Parameter. + * @return {any} + * Anthing. */ export function runSync(file, options) { // eslint-disable-next-line no-new-func diff --git a/packages/mdx/lib/util/create-format-aware-processors.js b/packages/mdx/lib/util/create-format-aware-processors.js index c17d01d78..ed7abbeb3 100644 --- a/packages/mdx/lib/util/create-format-aware-processors.js +++ b/packages/mdx/lib/util/create-format-aware-processors.js @@ -1,7 +1,7 @@ /** - * @typedef {import('vfile').VFileCompatible} VFileCompatible - * @typedef {import('vfile').VFile} VFile * @typedef {import('unified').Processor} Processor + * @typedef {import('vfile').VFile} VFile + * @typedef {import('vfile').VFileCompatible} VFileCompatible * @typedef {import('../compile.js').CompileOptions} CompileOptions */ @@ -12,12 +12,15 @@ import {resolveFileAndOptions} from './resolve-file-and-options.js' /** * Create smart processors to handle different formats. * - * @param {CompileOptions} [compileOptions] + * @param {CompileOptions | null | undefined} [compileOptions] + * configuration. * @return {{extnames: Array, process: process, processSync: processSync}} + * Smart processor. */ -export function createFormatAwareProcessors(compileOptions = {}) { - const mdExtensions = compileOptions.mdExtensions || md - const mdxExtensions = compileOptions.mdxExtensions || mdx +export function createFormatAwareProcessors(compileOptions) { + const compileOptions_ = compileOptions || {} + const mdExtensions = compileOptions_.mdExtensions || md + const mdxExtensions = compileOptions_.mdxExtensions || mdx /** @type {Processor} */ let cachedMarkdown /** @type {Processor} */ @@ -25,9 +28,9 @@ export function createFormatAwareProcessors(compileOptions = {}) { return { extnames: - compileOptions.format === 'md' + compileOptions_.format === 'md' ? mdExtensions - : compileOptions.format === 'mdx' + : compileOptions_.format === 'mdx' ? mdxExtensions : mdExtensions.concat(mdxExtensions), process, @@ -40,6 +43,7 @@ export function createFormatAwareProcessors(compileOptions = {}) { * @param {VFileCompatible} vfileCompatible * MDX or markdown document. * @return {Promise} + * File. */ function process(vfileCompatible) { const {file, processor} = split(vfileCompatible) @@ -52,6 +56,7 @@ export function createFormatAwareProcessors(compileOptions = {}) { * @param {VFileCompatible} vfileCompatible * MDX or markdown document. * @return {VFile} + * File. */ // C8 does not cover `.cjs` files (this is only used for the require hook, // which has to be CJS). @@ -70,11 +75,12 @@ export function createFormatAwareProcessors(compileOptions = {}) { * @param {VFileCompatible} vfileCompatible * MDX or markdown document. * @return {{file: VFile, processor: Processor}} + * File and corresponding processor. */ function split(vfileCompatible) { const {file, options} = resolveFileAndOptions( vfileCompatible, - compileOptions + compileOptions_ ) const processor = options.format === 'md' diff --git a/packages/mdx/lib/util/estree-util-declaration-to-expression.js b/packages/mdx/lib/util/estree-util-declaration-to-expression.js index 2f3496194..fa0c2e312 100644 --- a/packages/mdx/lib/util/estree-util-declaration-to-expression.js +++ b/packages/mdx/lib/util/estree-util-declaration-to-expression.js @@ -5,12 +5,15 @@ /** * Turn a declaration into an expression. + * * Doesn’t work for variable declarations, but that’s fine for our use case * because currently we’re using this utility for export default declarations, * which can’t contain variable declarations. * * @param {Declaration} declaration + * Declaration. * @returns {Expression} + * Expression. */ export function declarationToExpression(declaration) { if (declaration.type === 'FunctionDeclaration') { diff --git a/packages/mdx/lib/util/estree-util-is-declaration.js b/packages/mdx/lib/util/estree-util-is-declaration.js index d2f4254c3..cf2188456 100644 --- a/packages/mdx/lib/util/estree-util-is-declaration.js +++ b/packages/mdx/lib/util/estree-util-is-declaration.js @@ -1,18 +1,20 @@ /** + * @typedef {import('estree-jsx').Node} Node * @typedef {import('estree-jsx').Declaration} Declaration */ /** - * @param {unknown} node + * Check if `node` is a declaration. + * + * @param {Node} node + * Node to check. * @returns {node is Declaration} + * Whether `node` is a declaration. */ export function isDeclaration(node) { - /** @type {string} */ - // @ts-expect-error Hush typescript, looks like `type` is available. - const type = node && typeof node === 'object' && node.type return Boolean( - type === 'FunctionDeclaration' || - type === 'ClassDeclaration' || - type === 'VariableDeclaration' + node.type === 'FunctionDeclaration' || + node.type === 'ClassDeclaration' || + node.type === 'VariableDeclaration' ) } diff --git a/packages/mdx/lib/util/estree-util-specifiers-to-declarations.js b/packages/mdx/lib/util/estree-util-specifiers-to-declarations.js index 2f75a2c03..25fac38f1 100644 --- a/packages/mdx/lib/util/estree-util-specifiers-to-declarations.js +++ b/packages/mdx/lib/util/estree-util-specifiers-to-declarations.js @@ -1,18 +1,17 @@ /** + * @typedef {import('estree-jsx').ExportSpecifier} ExportSpecifier + * @typedef {import('estree-jsx').Expression} Expression * @typedef {import('estree-jsx').Identifier} Identifier * @typedef {import('estree-jsx').ImportSpecifier} ImportSpecifier * @typedef {import('estree-jsx').ImportDefaultSpecifier} ImportDefaultSpecifier * @typedef {import('estree-jsx').ImportNamespaceSpecifier} ImportNamespaceSpecifier - * @typedef {import('estree-jsx').ExportSpecifier} ExportSpecifier - * @typedef {import('estree-jsx').ObjectPattern} ObjectPattern * @typedef {import('estree-jsx').VariableDeclarator} VariableDeclarator - * @typedef {import('estree-jsx').Expression} Expression */ import {create} from './estree-util-create.js' /** - * @param {Array} specifiers + * @param {Array} specifiers * @param {Expression} init * @returns {Array} */ @@ -20,10 +19,10 @@ export function specifiersToDeclarations(specifiers, init) { let index = -1 /** @type {Array} */ const declarations = [] - /** @type {Array} */ + /** @type {Array} */ const otherSpecifiers = [] // Can only be one according to JS syntax. - /** @type {ImportNamespaceSpecifier|undefined} */ + /** @type {ImportNamespaceSpecifier | undefined} */ let importNamespaceSpecifier while (++index < specifiers.length) { diff --git a/packages/mdx/lib/util/estree-util-to-binary-addition.js b/packages/mdx/lib/util/estree-util-to-binary-addition.js index 6d36b28b5..1f5744f99 100644 --- a/packages/mdx/lib/util/estree-util-to-binary-addition.js +++ b/packages/mdx/lib/util/estree-util-to-binary-addition.js @@ -7,7 +7,7 @@ */ export function toBinaryAddition(expressions) { let index = -1 - /** @type {Expression|undefined} */ + /** @type {Expression | undefined} */ let left while (++index < expressions.length) { diff --git a/packages/mdx/lib/util/estree-util-to-id-or-member-expression.js b/packages/mdx/lib/util/estree-util-to-id-or-member-expression.js index 63fe3f1fd..41b0d4aad 100644 --- a/packages/mdx/lib/util/estree-util-to-id-or-member-expression.js +++ b/packages/mdx/lib/util/estree-util-to-id-or-member-expression.js @@ -1,9 +1,9 @@ /** * @typedef {import('estree-jsx').Identifier} Identifier - * @typedef {import('estree-jsx').Literal} Literal * @typedef {import('estree-jsx').JSXIdentifier} JSXIdentifier - * @typedef {import('estree-jsx').MemberExpression} MemberExpression * @typedef {import('estree-jsx').JSXMemberExpression} JSXMemberExpression + * @typedef {import('estree-jsx').Literal} Literal + * @typedef {import('estree-jsx').MemberExpression} MemberExpression */ import { @@ -20,7 +20,7 @@ export const toIdOrMemberExpression = toIdOrMemberExpressionFactory( export const toJsxIdOrMemberExpression = // @ts-expect-error: fine - /** @type {(ids: Array) => JSXIdentifier|JSXMemberExpression)} */ + /** @type {(ids: Array) => JSXIdentifier | JSXMemberExpression)} */ ( toIdOrMemberExpressionFactory( 'JSXIdentifier', @@ -37,12 +37,12 @@ export const toJsxIdOrMemberExpression = function toIdOrMemberExpressionFactory(idType, memberType, isIdentifier) { return toIdOrMemberExpression /** - * @param {Array} ids - * @returns {Identifier|MemberExpression} + * @param {Array} ids + * @returns {Identifier | MemberExpression} */ function toIdOrMemberExpression(ids) { let index = -1 - /** @type {Identifier|Literal|MemberExpression|undefined} */ + /** @type {Identifier | Literal | MemberExpression | undefined} */ let object while (++index < ids.length) { @@ -56,7 +56,7 @@ function toIdOrMemberExpressionFactory(idType, memberType, isIdentifier) { throw new Error('Cannot turn `' + name + '` into a JSX identifier') } - /** @type {Identifier|Literal} */ + /** @type {Identifier | Literal} */ // @ts-expect-error: JSX is fine. const id = valid ? {type: idType, name} : {type: 'Literal', value: name} // @ts-expect-error: JSX is fine. diff --git a/packages/mdx/lib/util/resolve-evaluate-options.js b/packages/mdx/lib/util/resolve-evaluate-options.js index de73f412e..355cf7efd 100644 --- a/packages/mdx/lib/util/resolve-evaluate-options.js +++ b/packages/mdx/lib/util/resolve-evaluate-options.js @@ -2,26 +2,29 @@ * @typedef {import('../core.js').ProcessorOptions} ProcessorOptions * * @typedef RunnerOptions - * @property {*} Fragment + * Configuration with JSX runtime. + * @property {any} Fragment * Symbol to use for fragments. - * @property {*} [jsx] + * @property {any} [jsx] * Function to generate an element with static children in production mode. - * @property {*} [jsxs] + * @property {any} [jsxs] * Function to generate an element with dynamic children in production mode. - * @property {*} [jsxDEV] + * @property {any} [jsxDEV] * Function to generate an element in development mode. - * @property {*} [useMDXComponents] + * @property {any} [useMDXComponents] * Function to get `MDXComponents` from context. * * @typedef {Omit } EvaluateProcessorOptions + * Compile configuration without JSX options for evaluation. * * @typedef {EvaluateProcessorOptions & RunnerOptions} EvaluateOptions + * Configuration for evaluation. */ /** * Split compiletime options from runtime options. * - * @param {EvaluateOptions} options + * @param {EvaluateOptions | null | undefined} options * @returns {{compiletime: ProcessorOptions, runtime: RunnerOptions}} */ export function resolveEvaluateOptions(options) { diff --git a/packages/mdx/lib/util/resolve-file-and-options.js b/packages/mdx/lib/util/resolve-file-and-options.js index c5d08331d..143d54c97 100644 --- a/packages/mdx/lib/util/resolve-file-and-options.js +++ b/packages/mdx/lib/util/resolve-file-and-options.js @@ -12,7 +12,7 @@ import {md} from './extnames.js' * might contain `format: 'detect'`. * * @param {VFileCompatible} vfileCompatible - * @param {CompileOptions} [options] + * @param {CompileOptions | null | undefined} [options] * @returns {{file: VFile, options: ProcessorOptions}} */ export function resolveFileAndOptions(vfileCompatible, options) { @@ -35,7 +35,7 @@ export function resolveFileAndOptions(vfileCompatible, options) { } /** - * @param {VFileCompatible} [value] + * @param {VFileCompatible | null | undefined} [value] * @returns {value is VFile} */ function looksLikeAVFile(value) { diff --git a/packages/mdx/package.json b/packages/mdx/package.json index 4e4f9aa3b..7848cd239 100644 --- a/packages/mdx/package.json +++ b/packages/mdx/package.json @@ -83,7 +83,7 @@ }, "scripts": { "prepack": "npm run build", - "build": "rimraf \"lib/**/*.d.ts\" \"test/**/*.d.ts\" \"*.d.ts\" && tsc && type-coverage", + "build": "tsc --build --clean && tsc --build && type-coverage", "test-api": "uvu test \"^(compile|evaluate)\\.js$\"", "test-coverage": "c8 --check-coverage --100 --reporter lcov npm run test-api", "test": "npm run build && npm run test-coverage" diff --git a/packages/mdx/tsconfig.json b/packages/mdx/tsconfig.json index 5fec52d2b..6cac7d0aa 100644 --- a/packages/mdx/tsconfig.json +++ b/packages/mdx/tsconfig.json @@ -1,4 +1,5 @@ { "extends": "../../tsconfig.json", - "include": ["lib/**/*.js", "test/**/*.js", "*.js"] + "include": ["**/*.cjs", "**/*.js", "**/*.jsx"], + "exclude": ["coverage/", "node_modules/"] } diff --git a/packages/node-loader/lib/index.js b/packages/node-loader/lib/index.js index ccff13325..0dd9e09b6 100644 --- a/packages/node-loader/lib/index.js +++ b/packages/node-loader/lib/index.js @@ -2,13 +2,15 @@ * @typedef {import('@mdx-js/mdx/lib/compile.js').CompileOptions} CompileOptions * * @typedef LoaderOptions - * @property {boolean} [fixRuntimeWithoutExportMap=true] + * Extra configuration. + * @property {boolean | null | undefined} [fixRuntimeWithoutExportMap=true] * Several JSX runtimes, notably React below 18 and Emotion below 11.10.0, * don’t yet have a proper export map set up. * Export maps are needed to map `xxx/jsx-runtime` to an actual file in ESM. * This option fixes React et al by turning those into `xxx/jsx-runtime.js`. * * @typedef {CompileOptions & LoaderOptions} Options + * Configuration. */ import fs from 'node:fs/promises' @@ -20,11 +22,12 @@ import {createFormatAwareProcessors} from '@mdx-js/mdx/lib/util/create-format-aw /** * Create smart processors to handle different formats. * - * @param {Options} [options] + * @param {Options | null | undefined} [options] */ -export function createLoader(options = {}) { - const {extnames, process} = createFormatAwareProcessors(options) - let fixRuntimeWithoutExportMap = options.fixRuntimeWithoutExportMap +export function createLoader(options) { + const options_ = options || {} + const {extnames, process} = createFormatAwareProcessors(options_) + let fixRuntimeWithoutExportMap = options_.fixRuntimeWithoutExportMap if ( fixRuntimeWithoutExportMap === null || diff --git a/packages/node-loader/package.json b/packages/node-loader/package.json index 00085b9a5..fecc4fab2 100644 --- a/packages/node-loader/package.json +++ b/packages/node-loader/package.json @@ -48,7 +48,7 @@ }, "scripts": { "prepack": "npm run build", - "build": "rimraf \"lib/**/*.d.ts\" \"test/**/*.d.ts\" \"*.d.ts\" && tsc && type-coverage", + "build": "tsc --build --clean && tsc --build && type-coverage", "test-api": "node --no-warnings --experimental-loader=./test/react-18-node-loader.js ../../node_modules/uvu/bin.js test \"\\.js$\"", "test-coverage": "c8 --check-coverage --100 --reporter lcov npm run test-api", "test": "npm run build && npm run test-coverage" diff --git a/packages/node-loader/tsconfig.json b/packages/node-loader/tsconfig.json index 5fec52d2b..6cac7d0aa 100644 --- a/packages/node-loader/tsconfig.json +++ b/packages/node-loader/tsconfig.json @@ -1,4 +1,5 @@ { "extends": "../../tsconfig.json", - "include": ["lib/**/*.js", "test/**/*.js", "*.js"] + "include": ["**/*.cjs", "**/*.js", "**/*.jsx"], + "exclude": ["coverage/", "node_modules/"] } diff --git a/packages/preact/lib/index.js b/packages/preact/lib/index.js index ee4c5fa7d..6c739dd29 100644 --- a/packages/preact/lib/index.js +++ b/packages/preact/lib/index.js @@ -4,14 +4,15 @@ * * @typedef Props * Configuration. - * @property {Components} [components] + * @property {Components | MergeComponents | null | undefined} [components] * Mapping of names for JSX components to Preact components. - * @property {boolean} [disableParentContext=false] + * @property {boolean | null | undefined} [disableParentContext=false] * Turn off outer component context. - * @property {ComponentChildren} [children] + * @property {ComponentChildren | null | undefined} [children] * Children. * * @callback MergeComponents + * Custom merge function. * @param {Components} currentComponents * Current components from the context. * @returns {Components} @@ -34,7 +35,7 @@ import {useContext} from 'preact/hooks' export const MDXContext = createContext({}) /** - * @param {import('react').ComponentType} Component + * @param {import('preact').ComponentType} Component * @deprecated * This export is marked as a legacy feature. * That means it’s no longer recommended for use as it might be removed @@ -46,12 +47,11 @@ export function withMDXComponents(Component) { return boundMDXComponent /** - * @param {Record & {components?: Components}} props + * @param {Record & {components?: Components | null | undefined}} props * @returns {JSX.Element} */ function boundMDXComponent(props) { const allComponents = useMDXComponents(props.components) - // @ts-expect-error: React + Preact in this repo mess with TS. return h(Component, {...props, allComponents}) } } @@ -59,7 +59,7 @@ export function withMDXComponents(Component) { /** * Get current components from the MDX Context. * - * @param {Components|MergeComponents} [components] + * @param {Components | MergeComponents | null | undefined} [components] * Additional components to use or a function that takes the current * components and filters/merges/changes them. * @returns {Components} @@ -76,6 +76,9 @@ export function useMDXComponents(components) { return {...contextComponents, ...components} } +/** @type {Components} */ +const emptyObject = {} + /** * Provider for MDX context * @@ -83,12 +86,21 @@ export function useMDXComponents(components) { * @returns {JSX.Element} */ export function MDXProvider({components, children, disableParentContext}) { - let allComponents = useMDXComponents(components) + /** @type {Components} */ + let allComponents if (disableParentContext) { - allComponents = components || {} + allComponents = + typeof components === 'function' + ? components({}) + : components || emptyObject + } else { + allComponents = useMDXComponents(components) } - // @ts-expect-error: preact types are wrong. - return h(MDXContext.Provider, {value: allComponents}, children) + return h( + MDXContext.Provider, + {value: allComponents, children: undefined}, + children + ) } diff --git a/packages/preact/package.json b/packages/preact/package.json index 84f683abf..f20ccc7c7 100644 --- a/packages/preact/package.json +++ b/packages/preact/package.json @@ -54,7 +54,7 @@ }, "scripts": { "prepack": "npm run build", - "build": "rimraf \"lib/**/*.d.ts\" \"test/**/*.d.ts\" \"index.d.ts\" && tsc && type-coverage", + "build": "tsc --build --clean && tsc --build && type-coverage", "test-api": "node --no-warnings --experimental-loader=../../script/jsx-loader.js ../../node_modules/uvu/bin.js test \"\\.jsx?$\"", "test-coverage": "c8 --check-coverage --100 --reporter lcov npm run test-api", "test": "npm run build && npm run test-coverage" diff --git a/packages/preact/test/test.jsx b/packages/preact/test/test.jsx index 66dd10a83..83bcfd258 100644 --- a/packages/preact/test/test.jsx +++ b/packages/preact/test/test.jsx @@ -1,8 +1,11 @@ -/* @jsx h */ -/* @jsxFrag Fragment */ +/* @jsxRuntime automatic @jsxImportSource preact */ + +// * @jsx h +// * @jsxFrag Fragment +// Note: this is unused by otherwise `xo` or so seems to fail? +import {h} from 'preact' import {test} from 'uvu' import * as assert from 'uvu/assert' -import {h} from 'preact' import * as runtime from 'preact/jsx-runtime' import {render} from 'preact-render-to-string' import {evaluate} from '@mdx-js/mdx' @@ -18,7 +21,10 @@ test('should support `components` with `MDXProvider`', async () => { render(

+ h1(props) { + // @ts-expect-error: Something wrong with Preact + React maybe? + return

+ } }} > @@ -38,7 +44,9 @@ test('should support `wrapper` in `components`', async () => { render(
+ wrapper(/** @type {Record} */ props) { + return
+ } }} > @@ -58,13 +66,22 @@ test('should combine components in nested `MDXProvider`s', async () => { render(

, - h2: (props) =>

+ h1(props) { + // @ts-expect-error: Something wrong with Preact + React maybe? + return

+ }, + h2(props) { + // @ts-expect-error: Something wrong with Preact + React maybe? + return

+ } }} >

+ h2(props) { + // @ts-expect-error: Something wrong with Preact + React maybe? + return

+ } }} > @@ -85,13 +102,22 @@ test('should support components as a function', async () => { render(

, - h2: (props) =>

+ h1(props) { + // @ts-expect-error: Something wrong with Preact + React maybe? + return

+ }, + h2(props) { + // @ts-expect-error: Something wrong with Preact + React maybe? + return

+ } }} > ({ - h2: (props) =>

+ h2(props) { + // @ts-expect-error: Something wrong with Preact + React maybe? + return

+ } })} > @@ -112,7 +138,10 @@ test('should support a `disableParentContext` prop (sandbox)', async () => { render(

+ h1(props) { + // @ts-expect-error: Something wrong with Preact + React maybe? + return

+ } }} > @@ -124,11 +153,46 @@ test('should support a `disableParentContext` prop (sandbox)', async () => { ) }) +test('should support a `disableParentContext` *and* `components as a function', async () => { + const {default: Content} = await evaluate('# hi\n## hello', { + ...runtime, + useMDXComponents + }) + + assert.equal( + render( + + } + }} + > + ({ + h2(props) { + // @ts-expect-error: Something wrong with Preact + React maybe? + return

+ } + })} + > + + + + ), + '

hi

\n

hello

' + ) +}) + test('should support `withComponents`', async () => { const {default: Content} = await evaluate('# hi\n## hello', { ...runtime, useMDXComponents }) + // Unknown props. + // type-coverage:ignore-next-line const With = withMDXComponents((props) => props.children) // Bug: this should use the `h2` component too, logically? @@ -138,12 +202,18 @@ test('should support `withComponents`', async () => { render(

+ h1(props) { + // @ts-expect-error: Something wrong with Preact + React maybe? + return

+ } }} >

+ h2(props) { + // @ts-expect-error: Something wrong with Preact + React maybe? + return

+ } }} > diff --git a/packages/preact/tsconfig.json b/packages/preact/tsconfig.json index 94d319f4f..027bb5b04 100644 --- a/packages/preact/tsconfig.json +++ b/packages/preact/tsconfig.json @@ -1,4 +1,9 @@ { "extends": "../../tsconfig.json", - "include": ["lib/**/*.js", "test/**/*.js", "index.js"] + "include": ["**/*.cjs", "**/*.js", "**/*.jsx"], + "exclude": ["coverage/", "node_modules/"], + "compilerOptions": { + "jsx": "react-jsx", + "jsxImportSource": "preact" + } } diff --git a/packages/react/lib/index.js b/packages/react/lib/index.js index 421fc2d6a..3411d0957 100644 --- a/packages/react/lib/index.js +++ b/packages/react/lib/index.js @@ -4,14 +4,15 @@ * * @typedef Props * Configuration. - * @property {Components} [components] + * @property {Components | MergeComponents | null | undefined} [components] * Mapping of names for JSX components to React components. - * @property {boolean} [disableParentContext=false] + * @property {boolean | null | undefined} [disableParentContext=false] * Turn off outer component context. - * @property {ReactNode} [children] + * @property {ReactNode | null | undefined} [children] * Children. * * @callback MergeComponents + * Custom merge function. * @param {Components} currentComponents * Current components from the context. * @returns {Components} @@ -45,7 +46,7 @@ export function withMDXComponents(Component) { return boundMDXComponent /** - * @param {Record & {components?: Components}} props + * @param {Record & {components?: Components | null | undefined}} props * @returns {JSX.Element} */ function boundMDXComponent(props) { @@ -57,7 +58,7 @@ export function withMDXComponents(Component) { /** * Get current components from the MDX Context. * - * @param {Components|MergeComponents} [components] + * @param {Components | MergeComponents | null | undefined} [components] * Additional components to use or a function that takes the current * components and filters/merges/changes them. * @returns {Components} @@ -65,6 +66,7 @@ export function withMDXComponents(Component) { */ export function useMDXComponents(components) { const contextComponents = React.useContext(MDXContext) + // Memoize to avoid unnecessary top-level context changes return React.useMemo(() => { // Custom merge via a function prop @@ -86,10 +88,16 @@ const emptyObject = {} * @returns {JSX.Element} */ export function MDXProvider({components, children, disableParentContext}) { - let allComponents = useMDXComponents(components) + /** @type {Components} */ + let allComponents if (disableParentContext) { - allComponents = components || emptyObject + allComponents = + typeof components === 'function' + ? components({}) + : components || emptyObject + } else { + allComponents = useMDXComponents(components) } return React.createElement( diff --git a/packages/react/package.json b/packages/react/package.json index b97450b45..51abe2f7f 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -56,7 +56,7 @@ }, "scripts": { "prepack": "npm run build", - "build": "rimraf \"lib/**/*.d.ts\" \"test/**/*.d.ts\" \"index.d.ts\" && tsc && type-coverage", + "build": "tsc --build --clean && tsc --build && type-coverage", "test-api": "node --no-warnings --experimental-loader=../../script/jsx-loader.js ../../node_modules/uvu/bin.js test \"\\.jsx?$\"", "test-coverage": "c8 --check-coverage --100 --reporter lcov npm run test-api", "test": "npm run build && npm run test-coverage" diff --git a/packages/react/test/test.jsx b/packages/react/test/test.jsx index a0107ab77..24f8a5256 100644 --- a/packages/react/test/test.jsx +++ b/packages/react/test/test.jsx @@ -1,11 +1,19 @@ +/** + * @typedef {import('react').ReactNode} ReactNode + */ + import {test} from 'uvu' import * as assert from 'uvu/assert' import {evaluate} from '@mdx-js/mdx' import React from 'react' -import * as runtime from 'react/jsx-runtime' +import * as runtimeRaw from 'react/jsx-runtime' import {renderToString} from 'react-dom/server' import {MDXProvider, useMDXComponents, withMDXComponents} from '../index.js' +/** @type {{Fragment: unknown, jsx: unknown, jsxs: unknown}} */ +// @ts-expect-error: React types are wrong. +const runtime = runtimeRaw + test('should support `components` with `MDXProvider`', async () => { const {default: Content} = await evaluate('# hi', { ...runtime, @@ -16,7 +24,9 @@ test('should support `components` with `MDXProvider`', async () => { renderToString(

+ h1(props) { + return

+ } }} > @@ -36,7 +46,9 @@ test('should support `wrapper` in `components`', async () => { renderToString(
+ wrapper(/** @type {Record} */ props) { + return
+ } }} > @@ -56,13 +68,19 @@ test('should combine components in nested `MDXProvider`s', async () => { renderToString(

, - h2: (props) =>

+ h1(props) { + return

+ }, + h2(props) { + return

+ } }} >

+ h2(props) { + return

+ } }} > @@ -83,13 +101,19 @@ test('should support components as a function', async () => { renderToString(

, - h2: (props) =>

+ h1(props) { + return

+ }, + h2(props) { + return

+ } }} > ({ - h2: (props) =>

+ h2(props) { + return

+ } })} > @@ -110,7 +134,9 @@ test('should support a `disableParentContext` prop (sandbox)', async () => { renderToString(

+ h1(props) { + return

+ } }} > @@ -122,11 +148,44 @@ test('should support a `disableParentContext` prop (sandbox)', async () => { ) }) +test('should support a `disableParentContext` *and* `components as a function', async () => { + const {default: Content} = await evaluate('# hi\n## hello', { + ...runtime, + useMDXComponents + }) + + assert.equal( + renderToString( + + } + }} + > + ({ + h2(props) { + return

+ } + })} + > + + + + ), + '

hi

\n

hello

' + ) +}) + test('should support `withComponents`', async () => { const {default: Content} = await evaluate('# hi\n## hello', { ...runtime, useMDXComponents }) + // Unknown props. + // type-coverage:ignore-next-line const With = withMDXComponents((props) => props.children) // Bug: this should use the `h2` component too, logically? @@ -136,12 +195,16 @@ test('should support `withComponents`', async () => { renderToString(

+ h1(props) { + return

+ } }} >

+ h2(props) { + return

+ } }} > diff --git a/packages/react/tsconfig.json b/packages/react/tsconfig.json index 94d319f4f..159a2649d 100644 --- a/packages/react/tsconfig.json +++ b/packages/react/tsconfig.json @@ -1,4 +1,8 @@ { "extends": "../../tsconfig.json", - "include": ["lib/**/*.js", "test/**/*.js", "index.js"] + "include": ["**/*.cjs", "**/*.js", "**/*.jsx"], + "exclude": ["coverage/", "node_modules/"], + "compilerOptions": { + "jsx": "preserve" + } } diff --git a/packages/register/tsconfig.json b/packages/register/tsconfig.json index e0270b1f1..6cac7d0aa 100644 --- a/packages/register/tsconfig.json +++ b/packages/register/tsconfig.json @@ -1,4 +1,5 @@ { "extends": "../../tsconfig.json", - "include": ["lib/**/*.cjs", "test/**/*.cjs", "*.cjs"] + "include": ["**/*.cjs", "**/*.js", "**/*.jsx"], + "exclude": ["coverage/", "node_modules/"] } diff --git a/packages/remark-mdx/index.js b/packages/remark-mdx/index.js index 7517b3ceb..f56d5ab6a 100644 --- a/packages/remark-mdx/index.js +++ b/packages/remark-mdx/index.js @@ -2,9 +2,12 @@ * @typedef {import('mdast').Root} Root * @typedef {import('micromark-extension-mdxjs').Options} MicromarkOptions * @typedef {import('mdast-util-mdx').ToMarkdownOptions} ToMarkdownOptions + * @typedef {import('mdast-util-mdx')} DoNotTouchAsThisIncludesMdxInTree + */ + +/** * @typedef {MicromarkOptions & ToMarkdownOptions} Options - * - * @typedef {import('mdast-util-mdx')} DoNotTouchAsThisImportItIncludesMdxInTree + * Configuration. */ import {mdxjs} from 'micromark-extension-mdxjs' @@ -15,9 +18,9 @@ import {mdxFromMarkdown, mdxToMarkdown} from 'mdast-util-mdx' * `{1 + 1}`; and JSX: `