1212 * @typedef {import('estree-jsx').Property } Property
1313 * @typedef {import('estree-jsx').Statement } Statement
1414 * @typedef {import('estree-jsx').VariableDeclarator } VariableDeclarator
15+ * @typedef {import('estree-jsx').ObjectPattern } ObjectPattern
1516 *
1617 * @typedef {import('estree-walker').SyncHandler } WalkHandler
1718 *
@@ -35,7 +36,7 @@ import {specifiersToDeclarations} from '../util/estree-util-specifiers-to-declar
3536
3637/**
3738 * A plugin that rewrites JSX in functions to accept components as
38- * `props.components` (when the function is called `MDXContent `), or from
39+ * `props.components` (when the function is called `_createMdxContent `), or from
3940 * a provider (if there is one).
4041 * It also makes sure that any undefined components are defined: either from
4142 * received components or as a function that throws an error.
@@ -67,15 +68,23 @@ export function recmaJsxRewrite(options = {}) {
6768 fnStack . push ( { objects : [ ] , components : [ ] , tags : [ ] , node} )
6869 }
6970
70- const fnScope = fnStack [ 0 ]
71+ let fnScope = fnStack [ 0 ]
7172
7273 if (
7374 ! fnScope ||
74- ( ! isMdxContent ( fnScope . node ) && ! providerImportSource )
75+ ( ! isNamedFunction ( fnScope . node , 'MDXContent' ) &&
76+ ! providerImportSource )
7577 ) {
7678 return
7779 }
7880
81+ if (
82+ fnStack [ 1 ] &&
83+ isNamedFunction ( fnStack [ 1 ] . node , '_createMdxContent' )
84+ ) {
85+ fnScope = fnStack [ 1 ]
86+ }
87+
7988 const newScope = /** @type {Scope|undefined } */ (
8089 // @ts -expect-error: periscopic doesn’t support JSX.
8190 scopeInfo . map . get ( node )
@@ -195,8 +204,6 @@ export function recmaJsxRewrite(options = {}) {
195204 }
196205
197206 if ( defaults . length > 0 || actual . length > 0 ) {
198- parameters . push ( { type : 'ObjectExpression' , properties : defaults } )
199-
200207 if ( providerImportSource ) {
201208 importProvider = true
202209 parameters . push ( {
@@ -207,8 +214,12 @@ export function recmaJsxRewrite(options = {}) {
207214 } )
208215 }
209216
210- // Accept `components` as a prop if this is the `MDXContent` function.
211- if ( isMdxContent ( scope . node ) ) {
217+ // Accept `components` as a prop if this is the `MDXContent` or
218+ // `_createMdxContent` function.
219+ if (
220+ isNamedFunction ( scope . node , 'MDXContent' ) ||
221+ isNamedFunction ( scope . node , '_createMdxContent' )
222+ ) {
212223 parameters . push ( {
213224 type : 'MemberExpression' ,
214225 object : { type : 'Identifier' , name : 'props' } ,
@@ -218,22 +229,42 @@ export function recmaJsxRewrite(options = {}) {
218229 } )
219230 }
220231
221- declarations . push ( {
222- type : 'VariableDeclarator' ,
223- id : { type : 'Identifier' , name : '_components' } ,
224- init : {
225- type : 'CallExpression' ,
226- callee : {
227- type : 'MemberExpression' ,
228- object : { type : 'Identifier' , name : 'Object' } ,
229- property : { type : 'Identifier' , name : 'assign' } ,
230- computed : false ,
231- optional : false
232- } ,
233- arguments : parameters ,
234- optional : false
235- }
236- } )
232+ if ( defaults . length > 0 || parameters . length > 1 ) {
233+ parameters . unshift ( {
234+ type : 'ObjectExpression' ,
235+ properties : defaults
236+ } )
237+ }
238+
239+ // If we’re getting components from several sources, merge them.
240+ /** @type {Expression } */
241+ let componentsInit =
242+ parameters . length > 1
243+ ? {
244+ type : 'CallExpression' ,
245+ callee : {
246+ type : 'MemberExpression' ,
247+ object : { type : 'Identifier' , name : 'Object' } ,
248+ property : { type : 'Identifier' , name : 'assign' } ,
249+ computed : false ,
250+ optional : false
251+ } ,
252+ arguments : parameters ,
253+ optional : false
254+ }
255+ : parameters [ 0 ] . type === 'MemberExpression'
256+ ? // If we’re only getting components from `props.components`,
257+ // make sure it’s defined.
258+ {
259+ type : 'LogicalExpression' ,
260+ operator : '||' ,
261+ left : parameters [ 0 ] ,
262+ right : { type : 'ObjectExpression' , properties : [ ] }
263+ }
264+ : parameters [ 0 ]
265+
266+ /** @type {ObjectPattern|undefined } */
267+ let componentsPattern
237268
238269 // Add components to scope.
239270 // For `['MyComponent', 'MDXLayout']` this generates:
@@ -243,24 +274,37 @@ export function recmaJsxRewrite(options = {}) {
243274 // Note that MDXLayout is special as it’s taken from
244275 // `_components.wrapper`.
245276 if ( actual . length > 0 ) {
277+ componentsPattern = {
278+ type : 'ObjectPattern' ,
279+ properties : actual . map ( ( name ) => ( {
280+ type : 'Property' ,
281+ kind : 'init' ,
282+ key : {
283+ type : 'Identifier' ,
284+ name : name === 'MDXLayout' ? 'wrapper' : name
285+ } ,
286+ value : { type : 'Identifier' , name} ,
287+ method : false ,
288+ shorthand : name !== 'MDXLayout' ,
289+ computed : false
290+ } ) )
291+ }
292+ }
293+
294+ if ( scope . tags . length > 0 ) {
246295 declarations . push ( {
247296 type : 'VariableDeclarator' ,
248- id : {
249- type : 'ObjectPattern' ,
250- properties : actual . map ( ( name ) => ( {
251- type : 'Property' ,
252- kind : 'init' ,
253- key : {
254- type : 'Identifier' ,
255- name : name === 'MDXLayout' ? 'wrapper' : name
256- } ,
257- value : { type : 'Identifier' , name} ,
258- method : false ,
259- shorthand : name !== 'MDXLayout' ,
260- computed : false
261- } ) )
262- } ,
263- init : { type : 'Identifier' , name : '_components' }
297+ id : { type : 'Identifier' , name : '_components' } ,
298+ init : componentsInit
299+ } )
300+ componentsInit = { type : 'Identifier' , name : '_components' }
301+ }
302+
303+ if ( componentsPattern ) {
304+ declarations . push ( {
305+ type : 'VariableDeclarator' ,
306+ id : componentsPattern ,
307+ init : componentsInit
264308 } )
265309 }
266310
@@ -328,13 +372,12 @@ function createImportProvider(providerImportSource, outputFormat) {
328372}
329373
330374/**
331- * @param {ESFunction } [node]
375+ * @param {ESFunction } node
376+ * @param {string } name
332377 * @returns {boolean }
333378 */
334- function isMdxContent ( node ) {
335- return Boolean (
336- node && 'id' in node && node . id && node . id . name === 'MDXContent'
337- )
379+ function isNamedFunction ( node , name ) {
380+ return Boolean ( node && 'id' in node && node . id && node . id . name === name )
338381}
339382
340383/**
0 commit comments