From e60c1ca06f3bfac3c122320e8ead8f305ef66ba5 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Tue, 9 Nov 2021 08:17:21 +0100 Subject: [PATCH 1/6] Update dev-dependencies --- package.json | 6 +++--- readme.md | 4 ++-- test/core.js | 10 ++++------ test/evaluate.js | 1 + 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 94bea52..59a140d 100644 --- a/package.json +++ b/package.json @@ -73,8 +73,8 @@ "@babel/core": "^7.0.0", "@babel/plugin-transform-react-jsx": "^7.0.0", "@emotion/react": "^11.0.0", - "@mdx-js/react": "2.0.0-next.8", - "@theme-ui/preset-base": "^0.11.0", + "@mdx-js/react": "2.0.0-rc.1", + "@theme-ui/preset-base": "^0.12.0", "@types/babel__core": "^7.0.0", "@types/node": "^16.0.0", "@types/react": "^17.0.0", @@ -109,7 +109,7 @@ "typescript": "^4.0.0", "vue": "^3.0.0", "webpack": "^5.0.0", - "xo": "^0.45.0" + "xo": "^0.46.0" }, "scripts": { "prepack": "npm run build && npm run format", diff --git a/readme.md b/readme.md index c9d6bd1..85204d6 100644 --- a/readme.md +++ b/readme.md @@ -938,7 +938,7 @@ These do not adhere to semver and could break at any time! ### Importing `.mdx` files directly -[ESM loaders](https://nodejs.org/api/esm.html#esm\_loaders) are an experimental +[ESM loaders](https://nodejs.org/api/esm.html#esm_loaders) are an experimental feature in Node, slated to change. Still, they let projects “hijack” imports, to do all sorts of fancy things! **xdm** comes with experimental support for importing `.mdx` files with @@ -986,7 +986,7 @@ multiple loaders with ### Requiring `.mdx` files directly -[`require.extensions`](https://nodejs.org/api/modules.html#modules\_require\_extensions) +[`require.extensions`](https://nodejs.org/api/modules.html#modules_require_extensions) is a deprecated feature in Node. Still, it lets projects “hijack” `require` calls to do fancy things. **xdm** comes with support for requiring `.mdx` files with on-the-fly diff --git a/test/core.js b/test/core.js index 31167f1..0937ed7 100644 --- a/test/core.js +++ b/test/core.js @@ -117,9 +117,7 @@ test('xdm', async (t) => { render( h( // @ts-expect-error: React and Preact interferring. - await run(compileSync('?', {jsxImportSource: 'preact'}), { - keepImport: true - }), + await run(compileSync('?', {jsxImportSource: 'preact'})), {}, [] ) @@ -151,9 +149,7 @@ test('xdm', async (t) => { render( h( // @ts-expect-error: Preact types do not accept `JSX.Element`. - await run(compileSync('<>1', {jsxImportSource: 'preact'}), { - keepImport: true - }), + await run(compileSync('<>1', {jsxImportSource: 'preact'})), {} ) ), @@ -303,6 +299,7 @@ test('xdm', async (t) => { MDXProvider, { components: { + // @ts-expect-error: React and Preact interferring. Y() { return React.createElement('span', {}, '!') } @@ -545,6 +542,7 @@ export default function Layout({children}) { /** * @param {Object.} props */ + // @ts-expect-error: React and Preact interferring. em(props) { return React.createElement('i', props) } diff --git a/test/evaluate.js b/test/evaluate.js index ea83548..88c1e10 100644 --- a/test/evaluate.js +++ b/test/evaluate.js @@ -357,6 +357,7 @@ test('xdm (evaluate)', async (t) => { provider.MDXProvider, { components: { + // @ts-expect-error: React and Preact interferring. X() { return React.createElement('span', {}, '!') } From ec01bb8e6ffae8e46f21c4900d8a4f172c41b687 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Tue, 9 Nov 2021 08:34:28 +0100 Subject: [PATCH 2/6] Remove `deasync` test for now --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 59a140d..f8cd441 100644 --- a/package.json +++ b/package.json @@ -115,7 +115,8 @@ "prepack": "npm run build && npm run format", "build": "rimraf \"{lib/**/**,test/**/**,script/**}*.d.ts\" \"{esbuild,esm-loader,index,rollup}.d.ts\" && tsc && type-coverage", "format": "remark . -qfo && prettier . -w --loglevel warn && xo --fix", - "test-api": "node --experimental-loader=./test/react-18-esm-loader.js test/index.js && node test/register.cjs", + "#": "to do: test `node test/register.cjs` when `deasync` works on Node 17", + "test-api": "node --experimental-loader=./test/react-18-esm-loader.js test/index.js", "test-coverage": "c8 --check-coverage --branches 100 --functions 100 --lines 100 --statements 100 --reporter lcov node --experimental-loader=./test/react-18-esm-loader.js test/index.js", "test": "npm run build && npm run format && npm run test-coverage" }, From 710ef5f6feea320381742982a60d42e8888eaa67 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Wed, 10 Nov 2021 13:33:57 +0100 Subject: [PATCH 3/6] Add exports of `run`, `runSync` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add exports * Adds docs on how to compile on “the server” and run on “the client” --- index.js | 1 + readme.md | 82 +++++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 71 insertions(+), 12 deletions(-) diff --git a/index.js b/index.js index a4a5f84..175ff60 100644 --- a/index.js +++ b/index.js @@ -2,4 +2,5 @@ export {createProcessor} from './lib/core.js' export {compile, compileSync} from './lib/compile.js' export {evaluate, evaluateSync} from './lib/evaluate.js' +export {run, runSync} from './lib/run.js' export {nodeTypes} from './lib/node-types.js' diff --git a/readme.md b/readme.md index 85204d6..c7740e7 100644 --- a/readme.md +++ b/readme.md @@ -47,6 +47,8 @@ Node 12+ is needed to use it and it must be `import`ed instead of `require`d. * [`compileSync(file, options?)`](#compilesyncfile-options) * [`evaluate(file, options)`](#evaluatefile-options) * [`evaluateSync(file, options)`](#evaluatesyncfile-options) + * [`run(functionBody, options)`](#runfunctionbody-options) + * [`runSync(functionBody, options)`](#runsyncfunctionbody-options) * [`createProcessor(options)`](#createprocessoroptions) * [👩‍🔬 Lab](#-lab) * [Importing `.mdx` files directly](#importing-mdx-files-directly) @@ -203,7 +205,9 @@ See [§ MDX content][mdx-content] below on how to use the result. [`compile`][compile], [`compileSync`](#compilesyncfile-options), [`evaluate`][eval], -[`evaluateSync`](#evaluatesyncfile-options), and +[`evaluateSync`](#evaluatesyncfile-options), +[`run`][run], +[`runSync`](#runsyncfunctionbody-options), and [`createProcessor`](#createprocessoroptions). There is no default export. @@ -342,7 +346,7 @@ it affects *which* files are “registered”: Output format to generate (`'program' | 'function-body'`, default: `'program'`). In most cases `'program'` should be used, as it results in a whole program. Internally, [`evaluate`][eval] uses `outputFormat: 'function-body'` to compile -to code that can be `eval`ed. +to code that can be `eval`ed with [`run`][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. @@ -827,16 +831,13 @@ When possible please use the async `compile`. ### `evaluate(file, options)` -Compile and run MDX. -☢️ It’s called **evaluate** because it `eval`s JavaScript. +> ☢️ **Danger**: It’s called **evaluate** because it `eval`s JavaScript. + +Compile and [run][] MDX. When possible, please use `compile`, write to a file, and then run with Node or bundle with [esbuild][]/[Rollup][]/[webpack][]. But if you trust your content, `evaluate` can work. -`evaluate` wraps code in an [`AsyncFunction`][async-function], `evaluateSync` -uses a normal [`Function`][function]. -That means that `evaluate` also supports top-level await. - Typically, `import` (or `export … from`) do not work here. They can be compiled to dynamic `import()` by passing [`options.useDynamicImport`][usedynamicimport]. @@ -918,10 +919,69 @@ console.log(await evaluate(file, {...runtime})) ### `evaluateSync(file, options)` +> ☢️ **Danger**: It’s called **evaluate** because it `eval`s JavaScript. + Compile and run MDX. Synchronous version of [`evaluate`][eval]. When possible please use the async `evaluate`. +### `run(functionBody, options)` + +> ☢️ **Danger**: This `eval`s JavaScript. + +Run MDX compiled as [`options.outputFormat: 'function-body'`][outputformat]. + +###### `options` + +You can pass `jsx`, `jsxs`, and `Fragment` from an automatic JSX runtime as +`options`. +You can pass `useMDXComponents` from a provider in options as well if the MDX +is compiled with `options.providerImportSource: '#'` (the exact value of this +option doesn’t matter). +All other options have to be passed to `compile` instead. + +###### Returns + +`Promise.` — See `evaluate` + +
+Example + +On the server: + +```js +import {compile} from 'xdm' + +const code = String(await compile('# hi', {outputFormat: 'function-body'})) +``` + +On the client: + +```js +import {run} from 'xdm' +import * as runtime from 'react/jsx-runtime' + +const code = '' // To do: get `code` from server somehow. + +const {default: Content} = await run(code, runtime) +``` + +…yields: + +```js +[Function: MDXContent] +``` + +
+ +### `runSync(functionBody, options)` + +> ☢️ **Danger**: This `eval`s JavaScript. + +Run MDX. +Synchronous version of [`run`][run]. +When possible please use the async `run`. + ### `createProcessor(options)` Create a unified processor to compile MDX to JS. @@ -2439,14 +2499,12 @@ Most of the work is done by: [gfm]: https://github.com/remarkjs/remark-gfm -[async-function]: https://developer.mozilla.org/docs/JavaScript/Reference/Global_Objects/AsyncFunction - -[function]: https://developer.mozilla.org/docs/JavaScript/Reference/Global_Objects/Function - [compile]: #compilefile-options [eval]: #evaluatefile-options +[run]: #runfunctionbody-options + [integrations]: #integrations [mdx-syntax]: #mdx-syntax From 27a6a3a732d52addd52ae8799ee99bc3fae17de5 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Wed, 10 Nov 2021 13:39:10 +0100 Subject: [PATCH 4/6] Refactor --- lib/integration/esbuild.js | 8 +++--- lib/plugin/recma-document.js | 8 +++--- lib/plugin/recma-jsx-rewrite.js | 26 +++++++++---------- lib/util/estree-util-to-binary-addition.js | 2 +- .../estree-util-to-id-or-member-expression.js | 4 +-- readme.md | 8 +++--- test/context/components.js | 4 +-- test/core.js | 12 ++++----- 8 files changed, 36 insertions(+), 36 deletions(-) diff --git a/lib/integration/esbuild.js b/lib/integration/esbuild.js index 73c3ccb..c935775 100644 --- a/lib/integration/esbuild.js +++ b/lib/integration/esbuild.js @@ -109,7 +109,7 @@ export function esbuild(options = {}) { } /** - * @param {Omit. & {pluginData?: {contents?: string|Uint8Array}}} data + * @param {Omit & {pluginData?: {contents?: string|Uint8Array}}} data * @returns {Promise} */ async function onload(data) { @@ -123,11 +123,11 @@ export function esbuild(options = {}) { let file = new VFile({value: doc, path: data.path}) /** @type {VFileValue|undefined} */ let value - /** @type {(VFileMessage|Error)[]} */ + /** @type {Array} */ let messages = [] - /** @type {Message[]} */ + /** @type {Array} */ const errors = [] - /** @type {Message[]} */ + /** @type {Array} */ const warnings = [] try { diff --git a/lib/plugin/recma-document.js b/lib/plugin/recma-document.js index b24abe1..dcc4c16 100644 --- a/lib/plugin/recma-document.js +++ b/lib/plugin/recma-document.js @@ -54,11 +54,11 @@ export function recmaDocument(options = {}) { } = options return (tree, file) => { - /** @type {Array.} */ + /** @type {Array} */ const exportedIdentifiers = [] - /** @type {Array.} */ + /** @type {Array} */ const replacement = [] - /** @type {Array.} */ + /** @type {Array} */ const pragmas = [] let exportAllCount = 0 /** @type {ExportDefaultDeclaration|ExportSpecifier|undefined} */ @@ -417,7 +417,7 @@ export function recmaDocument(options = {}) { } else if (node.declaration) { replace = node.declaration } else { - /** @type {Array.} */ + /** @type {Array} */ const declarators = node.specifiers .filter( (specifier) => specifier.local.name !== specifier.exported.name diff --git a/lib/plugin/recma-jsx-rewrite.js b/lib/plugin/recma-jsx-rewrite.js index b83ea29..a815e24 100644 --- a/lib/plugin/recma-jsx-rewrite.js +++ b/lib/plugin/recma-jsx-rewrite.js @@ -24,10 +24,10 @@ * @property {string} [providerImportSource] Place to import a provider from * * @typedef StackEntry - * @property {Array.} objects - * @property {Array.} components - * @property {Array.} tags - * @property {Record.} references + * @property {Array} objects + * @property {Array} components + * @property {Array} tags + * @property {Record} references * @property {ESFunction} node */ @@ -60,7 +60,7 @@ export function recmaJsxRewrite(options = {}) { return (tree, file) => { // Find everything that’s defined in the top-level scope. const scopeInfo = analyze(tree) - /** @type {Array.} */ + /** @type {Array} */ const fnStack = [] /** @type {boolean|undefined} */ let importProvider @@ -191,13 +191,13 @@ export function recmaJsxRewrite(options = {}) { } }, leave(node) { - /** @type {Array.} */ + /** @type {Array} */ const defaults = [] - /** @type {Array.} */ + /** @type {Array} */ const actual = [] - /** @type {Array.} */ + /** @type {Array} */ const parameters = [] - /** @type {Array.} */ + /** @type {Array} */ const declarations = [] if (currentScope && currentScope.node === node) { @@ -358,7 +358,7 @@ export function recmaJsxRewrite(options = {}) { } } - /** @type {Statement[]} */ + /** @type {Array} */ const statements = [ { type: 'VariableDeclaration', @@ -373,7 +373,7 @@ export function recmaJsxRewrite(options = {}) { const id = references[index] const info = scope.references[id] const place = stringifyPosition(positionFromEstree(info.node)) - /** @type {Expression[]} */ + /** @type {Array} */ const parameters = [ {type: 'Literal', value: id}, {type: 'Literal', value: info.component} @@ -423,7 +423,7 @@ export function recmaJsxRewrite(options = {}) { // If potentially missing components are used. if (createErrorHelper) { - /** @type {Expression[]} */ + /** @type {Array} */ const message = [ {type: 'Literal', value: 'Expected '}, { @@ -441,7 +441,7 @@ export function recmaJsxRewrite(options = {}) { } ] - /** @type {Identifier[]} */ + /** @type {Array} */ const parameters = [ {type: 'Identifier', name: 'id'}, {type: 'Identifier', name: 'component'} diff --git a/lib/util/estree-util-to-binary-addition.js b/lib/util/estree-util-to-binary-addition.js index f1496da..6d36b28 100644 --- a/lib/util/estree-util-to-binary-addition.js +++ b/lib/util/estree-util-to-binary-addition.js @@ -3,7 +3,7 @@ */ /** - * @param {Expression[]} expressions + * @param {Array} expressions */ export function toBinaryAddition(expressions) { let index = -1 diff --git a/lib/util/estree-util-to-id-or-member-expression.js b/lib/util/estree-util-to-id-or-member-expression.js index e1f6122..0d26845 100644 --- a/lib/util/estree-util-to-id-or-member-expression.js +++ b/lib/util/estree-util-to-id-or-member-expression.js @@ -15,7 +15,7 @@ export const toIdOrMemberExpression = toIdOrMemberExpressionFactory( export const toJsxIdOrMemberExpression = // @ts-expect-error: fine - /** @type {(ids: Array.) => JSXIdentifier|JSXMemberExpression)} */ + /** @type {(ids: Array) => JSXIdentifier|JSXMemberExpression)} */ (toIdOrMemberExpressionFactory('JSXIdentifier', 'JSXMemberExpression')) /** @@ -25,7 +25,7 @@ export const toJsxIdOrMemberExpression = function toIdOrMemberExpressionFactory(idType, memberType) { return toIdOrMemberExpression /** - * @param {Array.} ids + * @param {Array} ids * @returns {Identifier|MemberExpression} */ function toIdOrMemberExpression(ids) { diff --git a/readme.md b/readme.md index c7740e7..296fd0a 100644 --- a/readme.md +++ b/readme.md @@ -795,7 +795,7 @@ See `options.pragma` for an example. ###### Returns -`Promise.` — Promise that resolves to the compiled JS as a [vfile][]. +`Promise` — Promise that resolves to the compiled JS as a [vfile][].
Example @@ -893,7 +893,7 @@ const {default: Content} = await evaluate('# hi', {...provider, ...runtime, ...o ###### Returns -`Promise.` — Promise that resolves to something that looks a bit like a +`Promise` — Promise that resolves to something that looks a bit like a module: an object with a `default` field set to the component and anything else that was exported from the MDX file available too. @@ -942,7 +942,7 @@ All other options have to be passed to `compile` instead. ###### Returns -`Promise.` — See `evaluate` +`Promise` — See `evaluate`
Example @@ -1490,7 +1490,7 @@ You do not need to pass `options.SourceMapGenerator`. ###### `options.exclude` List of [`picomatch`][pico] patterns to include and/or exclude -(`string`, `RegExp`, `(string|RegExp)[]`, default: `[]`). +(`string`, `RegExp`, `Array`, default: `[]`). #### Webpack diff --git a/test/context/components.js b/test/context/components.js index 6f51515..d3fcfb3 100644 --- a/test/context/components.js +++ b/test/context/components.js @@ -1,14 +1,14 @@ import React from 'react' /** - * @param {Object.} props + * @param {Record} props */ export function Pill(props) { return React.createElement('span', {...props, style: {color: 'red'}}) } /** - * @param {Object.} props + * @param {Record} props */ export function Layout(props) { return React.createElement('div', {...props, style: {color: 'red'}}) diff --git a/test/core.js b/test/core.js index 0937ed7..84f60a9 100644 --- a/test/core.js +++ b/test/core.js @@ -190,7 +190,7 @@ test('xdm', async (t) => { renderToStaticMarkup( React.createElement(await run(compileSync('')), { components: { - /** @param {Object.} props */ + /** @param {Record} props */ // @ts-expect-error: React and Preact interfering. X(props) { return React.createElement('span', props, '!') @@ -226,10 +226,10 @@ test('xdm', async (t) => { components: { // @ts-expect-error: React and Preact interfering. X: Object.assign( - /** @param {Object.} props */ + /** @param {Record} props */ (props) => React.createElement('span', props, '!'), { - /** @param {Object.} props */ + /** @param {Record} props */ Y(props) { return React.createElement('span', props, '?') } @@ -364,7 +364,7 @@ test('xdm', async (t) => { React.createElement(await run(compileSync('a')), { components: { /** - * @param {Object.} props + * @param {Record} props */ // @ts-expect-error: React and Preact interfering. wrapper(props) { @@ -424,7 +424,7 @@ export default function Layout({children}) { { components: { /** - * @param {Object.} props + * @param {Record} props */ // @ts-expect-error: React and Preact interfering. wrapper(props) { @@ -540,7 +540,7 @@ export default function Layout({children}) { { components: { /** - * @param {Object.} props + * @param {Record} props */ // @ts-expect-error: React and Preact interferring. em(props) { From e6727b88d3d74c75078178a8fc78ca4d3f220f83 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Wed, 10 Nov 2021 13:41:50 +0100 Subject: [PATCH 5/6] Update dev-dependencies --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f8cd441..93b8d3d 100644 --- a/package.json +++ b/package.json @@ -104,7 +104,7 @@ "rimraf": "^3.0.0", "rollup": "^2.0.0", "tape": "^5.0.0", - "theme-ui": "^0.11.0", + "theme-ui": "^0.12.0", "type-coverage": "^2.0.0", "typescript": "^4.0.0", "vue": "^3.0.0", From ec01e1894520cf4e7f398003ffdfe6167ba4aef6 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Wed, 10 Nov 2021 13:50:25 +0100 Subject: [PATCH 6/6] 3.3.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 93b8d3d..929d773 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "xdm", - "version": "3.2.0", + "version": "3.3.0", "description": "an MDX compiler", "license": "MIT", "keywords": [