From c20420574b216a31bda91235ebfbc6cc6e54590d Mon Sep 17 00:00:00 2001 From: cobbvanth <80018012+cobbvanth@users.noreply.github.com> Date: Sat, 4 Jan 2025 22:38:18 +1030 Subject: [PATCH 1/3] Added ability for mdxJSX elements to not be stripped out, and also added the ability for attributes whitelisted on those elements to not be stripped --- lib/index.js | 88 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 84 insertions(+), 4 deletions(-) diff --git a/lib/index.js b/lib/index.js index a8b73c7..935851b 100644 --- a/lib/index.js +++ b/lib/index.js @@ -288,6 +288,62 @@ function transform(state, node) { return text(state, unsafe) } + case 'mdxJsxFlowElement': { + // Map the attributes array into name/value pairs + const attributesMap = {} + if (Array.isArray(unsafe.attributes)) { + unsafe.attributes.forEach((attr) => { + if ( + attr && + typeof attr === 'object' && + 'name' in attr && + 'value' in attr + ) { + attributesMap[attr.name] = attr.value + } + }) + } + + const mdxProps = { + ...unsafe, + tagName: unsafe.name, + properties: { + ...unsafe.properties, + ...attributesMap // Spread the mapped attributes directly into properties + } + } + + return element(state, mdxProps) + } + + case 'mdxJsxTextElement': { + // Handle text elements the same way + const attributesMap = {} + if (Array.isArray(unsafe.attributes)) { + unsafe.attributes.forEach((attr) => { + if ( + attr && + typeof attr === 'object' && + 'name' in attr && + 'value' in attr + ) { + attributesMap[attr.name] = attr.value + } + }) + } + + const mdxProps = { + ...unsafe, + tagName: unsafe.name, + properties: { + ...unsafe.properties, + ...attributesMap + } + } + + return element(state, mdxProps) + } + default: } } @@ -503,15 +559,18 @@ 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 : {} + const mdxAttributes = properties_?.attributes || {} // Add support for MDX attributes + /** @type {Properties} */ const result = {} /** @type {string} */ let key + // Process traditional properties for (key in properties_) { if (own.call(properties_, key)) { const unsafe = properties_[key] @@ -532,9 +591,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] From c31c83f50f85bb5b967721fc50c90872f8d7506e Mon Sep 17 00:00:00 2001 From: cobbvanth <80018012+cobbvanth@users.noreply.github.com> Date: Sat, 4 Jan 2025 23:03:26 +1030 Subject: [PATCH 2/3] fixed type errors --- lib/index.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/index.js b/lib/index.js index 935851b..4dfe3e4 100644 --- a/lib/index.js +++ b/lib/index.js @@ -290,7 +290,9 @@ function transform(state, node) { case 'mdxJsxFlowElement': { // Map the attributes array into name/value pairs + /** @type {Record} */ const attributesMap = {} + if (Array.isArray(unsafe.attributes)) { unsafe.attributes.forEach((attr) => { if ( @@ -317,7 +319,7 @@ function transform(state, node) { } case 'mdxJsxTextElement': { - // Handle text elements the same way + /** @type {Record} */ const attributesMap = {} if (Array.isArray(unsafe.attributes)) { unsafe.attributes.forEach((attr) => { @@ -563,7 +565,11 @@ function properties(state, properties) { // Handle both traditional properties and MDX attributes const properties_ = properties && typeof properties === 'object' ? properties : {} - const mdxAttributes = properties_?.attributes || {} // Add support for MDX attributes + /** @type {Array<{ type: 'mdxJsxAttribute' } & Record>} */ + const mdxAttributes = + /** @type {{ attributes?: Array<{ type: 'mdxJsxAttribute' } & Record> }} */ ( + properties_ + )?.attributes || [] /** @type {Properties} */ const result = {} @@ -573,6 +579,7 @@ function properties(state, properties) { // Process traditional properties for (key in properties_) { if (own.call(properties_, key)) { + // @ts-ignore const unsafe = properties_[key] let safe = propertyValue( state, From 3e4793b86264a69ab79e6e748ac9a31e6b32520a Mon Sep 17 00:00:00 2001 From: cobbvanth <80018012+cobbvanth@users.noreply.github.com> Date: Sat, 4 Jan 2025 23:29:09 +1030 Subject: [PATCH 3/3] fixed typed code coverage --- lib/index.js | 94 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 66 insertions(+), 28 deletions(-) diff --git a/lib/index.js b/lib/index.js index 4dfe3e4..e96409d 100644 --- a/lib/index.js +++ b/lib/index.js @@ -289,61 +289,99 @@ function transform(state, node) { } case 'mdxJsxFlowElement': { - // Map the attributes array into name/value pairs + /** + * 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 = {} - if (Array.isArray(unsafe.attributes)) { - unsafe.attributes.forEach((attr) => { + /** + * 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 ( - attr && - typeof attr === 'object' && - 'name' in attr && - 'value' in attr + attribute && + typeof attribute === 'object' && + 'name' in attribute && + 'value' in attribute ) { - attributesMap[attr.name] = attr.value + attributesMap[attribute.name] = attribute.value } - }) + } } - const mdxProps = { - ...unsafe, - tagName: unsafe.name, + /** + * 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: { - ...unsafe.properties, - ...attributesMap // Spread the mapped attributes directly into properties + ...typedUnsafe.properties, + ...attributesMap } } - return element(state, mdxProps) + return element(state, mdxProperties) } case 'mdxJsxTextElement': { /** @type {Record} */ const attributesMap = {} - if (Array.isArray(unsafe.attributes)) { - unsafe.attributes.forEach((attr) => { + + /** @type {{ attributes?: Array<{ type: 'mdxJsxAttribute', name: string, value: unknown }> }} */ + const typedUnsafe = unsafe + + if (Array.isArray(typedUnsafe.attributes)) { + for (const attribute of typedUnsafe.attributes) { if ( - attr && - typeof attr === 'object' && - 'name' in attr && - 'value' in attr + attribute && + typeof attribute === 'object' && + 'name' in attribute && + 'value' in attribute ) { - attributesMap[attr.name] = attr.value + attributesMap[attribute.name] = attribute.value } - }) + } } - const mdxProps = { - ...unsafe, - tagName: unsafe.name, + const mdxProperties = { + .../** @type {{ name: string, properties?: Record }} */ ( + unsafe + ), + tagName: /** @type {{ name: string }} */ (unsafe).name, properties: { - ...unsafe.properties, + .../** @type {{ properties?: Record }} */ (unsafe) + .properties, ...attributesMap } } - return element(state, mdxProps) + return element(state, mdxProperties) } default: