5959 * Options to pass through to `remark-rehype`.
6060 * @property {boolean | null | undefined } [skipHtml=false]
6161 * Ignore HTML in markdown completely (default: `false`).
62- * @property {TransformLink | false | null | undefined } [transformLinkUri]
63- * Change URLs on images (default: `uriTransformer`);
64- * pass `false` to allow all URLs, which is unsafe
65- * @property {TransformImage | false | null | undefined } [transformImageUri]
66- * Change URLs on links (default: `uriTransformer`);
67- * pass `false` to allow all URLs, which is unsafe
6862 * @property {boolean | null | undefined } [unwrapDisallowed=false]
6963 * Extract (unwrap) the children of not allowed elements (default: `false`);
7064 * normally when say `strong` is disallowed, it and it’s children are dropped,
7165 * with `unwrapDisallowed` the element itself is replaced by its children.
66+ * @property {UrlTransform | null | undefined } [urlTransform]
67+ * Change URLs (default: `defaultUrlTransform`)
7268 *
73- * @callback TransformImage
74- * Transform URLs on images .
75- * @param {string } src
69+ * @callback UrlTransform
70+ * Transform URLs.
71+ * @param {string } url
7672 * URL to transform.
77- * @param {string } alt
78- * Alt text.
79- * @param {string | null } title
80- * Title.
81- * To do: pass `undefined`.
73+ * @param {string } key
74+ * Property name (example: `'href'`).
75+ * @param {Readonly<Element> } node
76+ * Node.
8277 * @returns {string | null | undefined }
8378 * Transformed URL (optional).
84- *
85- * @callback TransformLink
86- * Transform URLs on links.
87- * @param {string } href
88- * URL to transform.
89- * @param {ReadonlyArray<ElementContent> } children
90- * Content.
91- * @param {string | null } title
92- * Title.
93- * To do: pass `undefined`.
94- * @returns {string }
95- * Transformed URL (optional).
9679 */
9780
9881import { unreachable } from 'devlop'
9982import { toJsxRuntime } from 'hast-util-to-jsx-runtime'
83+ import { urlAttributes } from 'html-url-attributes'
10084// @ts -expect-error: untyped.
10185import { Fragment , jsx , jsxs } from 'react/jsx-runtime'
10286import remarkParse from 'remark-parse'
@@ -146,7 +130,9 @@ const deprecations = [
146130 { from : 'rawSourcePos' , id : '#remove-rawsourcepos-option' } ,
147131 { from : 'renderers' , id : 'change-renderers-to-components' , to : 'components' } ,
148132 { from : 'source' , id : 'change-source-to-children' , to : 'children' } ,
149- { from : 'sourcePos' , id : '#remove-sourcepos-option' }
133+ { from : 'sourcePos' , id : '#remove-sourcepos-option' } ,
134+ { from : 'transformImageUri' , id : '#add-urltransform' , to : 'urlTransform' } ,
135+ { from : 'transformLinkUri' , id : '#add-urltransform' , to : 'urlTransform' }
150136]
151137
152138/**
@@ -171,15 +157,8 @@ export function Markdown(options) {
171157 ? { ...options . remarkRehypeOptions , ...emptyRemarkRehypeOptions }
172158 : emptyRemarkRehypeOptions
173159 const skipHtml = options . skipHtml
174- const transformImageUri =
175- options . transformImageUri === undefined
176- ? uriTransformer
177- : options . transformImageUri
178- const transformLinkUri =
179- options . transformLinkUri === undefined
180- ? uriTransformer
181- : options . transformLinkUri
182160 const unwrapDisallowed = options . unwrapDisallowed
161+ const urlTransform = options . urlTransform || defaultUrlTransform
183162
184163 const processor = unified ( )
185164 . use ( remarkParse )
@@ -265,26 +244,19 @@ export function Markdown(options) {
265244 return index
266245 }
267246
268- if ( transformLinkUri && node . type === 'element' && node . tagName === 'a' ) {
269- node . properties . href = transformLinkUri (
270- String ( node . properties . href || '' ) ,
271- node . children ,
272- // To do: pass `undefined`.
273- typeof node . properties . title === 'string' ? node . properties . title : null
274- )
275- }
276-
277- if (
278- transformImageUri &&
279- node . type === 'element' &&
280- node . tagName === 'img'
281- ) {
282- node . properties . src = transformImageUri (
283- String ( node . properties . src || '' ) ,
284- String ( node . properties . alt || '' ) ,
285- // To do: pass `undefined`.
286- typeof node . properties . title === 'string' ? node . properties . title : null
287- )
247+ if ( node . type === 'element' ) {
248+ /** @type {string } */
249+ let key
250+
251+ for ( key in urlAttributes ) {
252+ if ( own . call ( urlAttributes , key ) && own . call ( node . properties , key ) ) {
253+ const value = node . properties [ key ]
254+ const test = urlAttributes [ key ]
255+ if ( test === null || test . includes ( node . tagName ) ) {
256+ node . properties [ key ] = urlTransform ( String ( value || '' ) , key , node )
257+ }
258+ }
259+ }
288260 }
289261
290262 if ( node . type === 'element' ) {
@@ -316,13 +288,14 @@ export function Markdown(options) {
316288/**
317289 * Make a URL safe.
318290 *
291+ * @satisfies {UrlTransform }
319292 * @param {string } value
320293 * URL.
321294 * @returns {string }
322295 * Safe URL.
323296 */
324- export function uriTransformer ( value ) {
325- const url = ( value || '' ) . trim ( )
297+ export function defaultUrlTransform ( value ) {
298+ const url = value . trim ( )
326299 const first = url . charAt ( 0 )
327300
328301 if ( first === '#' || first === '/' ) {
0 commit comments