diff --git a/lib/index.js b/lib/index.js index a8b73c7..e96409d 100644 --- a/lib/index.js +++ b/lib/index.js @@ -288,6 +288,102 @@ function transform(state, node) { return text(state, unsafe) } + case 'mdxJsxFlowElement': { + /** + * A typedef for the MDX node shape. + * (Adjust property types as needed for your use case.) + * + * @typedef {Object} MdxJsxFlowElement + * @property {string} name + * @property {Record} [properties] + * @property {Array<{ + * type: 'mdxJsxAttribute', + * name: string, + * value: unknown + * }>} [attributes] + */ + + /** @type {Record} */ + const attributesMap = {} + + /** + * Cast `unsafe` to our `MdxJsxFlowElement`. + * If `unsafe` truly may be something else, you may want extra runtime checks. + * + * @type {MdxJsxFlowElement} + */ + const typedUnsafe = /** @type {MdxJsxFlowElement} */ (unsafe) + + if (Array.isArray(typedUnsafe.attributes)) { + for (const attribute of typedUnsafe.attributes) { + if ( + attribute && + typeof attribute === 'object' && + 'name' in attribute && + 'value' in attribute + ) { + attributesMap[attribute.name] = attribute.value + } + } + } + + /** + * Here we define `mdxProps` to include everything from `typedUnsafe` + * plus a new `tagName` field. + * + * @typedef {MdxJsxFlowElement & { + * tagName: string, + * }} MdxProps + */ + + /** @type {MdxProps} */ + const mdxProperties = { + ...typedUnsafe, + tagName: typedUnsafe.name, + properties: { + ...typedUnsafe.properties, + ...attributesMap + } + } + + return element(state, mdxProperties) + } + + case 'mdxJsxTextElement': { + /** @type {Record} */ + const attributesMap = {} + + /** @type {{ attributes?: Array<{ type: 'mdxJsxAttribute', name: string, value: unknown }> }} */ + const typedUnsafe = unsafe + + if (Array.isArray(typedUnsafe.attributes)) { + for (const attribute of typedUnsafe.attributes) { + if ( + attribute && + typeof attribute === 'object' && + 'name' in attribute && + 'value' in attribute + ) { + attributesMap[attribute.name] = attribute.value + } + } + } + + const mdxProperties = { + .../** @type {{ name: string, properties?: Record }} */ ( + unsafe + ), + tagName: /** @type {{ name: string }} */ (unsafe).name, + properties: { + .../** @type {{ properties?: Record }} */ (unsafe) + .properties, + ...attributesMap + } + } + + return element(state, mdxProperties) + } + default: } } @@ -503,17 +599,25 @@ function properties(state, properties) { : undefined const defaults = attributes && own.call(attributes, '*') ? attributes['*'] : undefined + + // Handle both traditional properties and MDX attributes const properties_ = - /** @type {Readonly>>} */ ( - properties && typeof properties === 'object' ? properties : {} - ) + properties && typeof properties === 'object' ? properties : {} + /** @type {Array<{ type: 'mdxJsxAttribute' } & Record>} */ + const mdxAttributes = + /** @type {{ attributes?: Array<{ type: 'mdxJsxAttribute' } & Record> }} */ ( + properties_ + )?.attributes || [] + /** @type {Properties} */ const result = {} /** @type {string} */ let key + // Process traditional properties for (key in properties_) { if (own.call(properties_, key)) { + // @ts-ignore const unsafe = properties_[key] let safe = propertyValue( state, @@ -532,9 +636,30 @@ function properties(state, properties) { } } + // Process MDX attributes + for (key in mdxAttributes) { + if (own.call(mdxAttributes, key)) { + const unsafe = mdxAttributes[key] + let safe = propertyValue( + state, + findDefinition(specific, key), + key, + unsafe + ) + + if (safe === null || safe === undefined) { + safe = propertyValue(state, findDefinition(defaults, key), key, unsafe) + } + + if (safe !== null && safe !== undefined) { + result[key] = safe + } + } + } + + // Handle required properties if (required && own.call(required, tagName)) { const properties = required[tagName] - for (key in properties) { if (own.call(properties, key) && !own.call(result, key)) { result[key] = properties[key]