59
59
* Options to pass through to `remark-rehype`.
60
60
* @property {boolean | null | undefined } [skipHtml=false]
61
61
* 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
68
62
* @property {boolean | null | undefined } [unwrapDisallowed=false]
69
63
* Extract (unwrap) the children of not allowed elements (default: `false`);
70
64
* normally when say `strong` is disallowed, it and it’s children are dropped,
71
65
* with `unwrapDisallowed` the element itself is replaced by its children.
66
+ * @property {UrlTransform | null | undefined } [urlTransform]
67
+ * Change URLs (default: `defaultUrlTransform`)
72
68
*
73
- * @callback TransformImage
74
- * Transform URLs on images .
75
- * @param {string } src
69
+ * @callback UrlTransform
70
+ * Transform URLs.
71
+ * @param {string } url
76
72
* 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.
82
77
* @returns {string | null | undefined }
83
78
* 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).
96
79
*/
97
80
98
81
import { unreachable } from 'devlop'
99
82
import { toJsxRuntime } from 'hast-util-to-jsx-runtime'
83
+ import { urlAttributes } from 'html-url-attributes'
100
84
// @ts -expect-error: untyped.
101
85
import { Fragment , jsx , jsxs } from 'react/jsx-runtime'
102
86
import remarkParse from 'remark-parse'
@@ -146,7 +130,9 @@ const deprecations = [
146
130
{ from : 'rawSourcePos' , id : '#remove-rawsourcepos-option' } ,
147
131
{ from : 'renderers' , id : 'change-renderers-to-components' , to : 'components' } ,
148
132
{ 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' }
150
136
]
151
137
152
138
/**
@@ -171,15 +157,8 @@ export function Markdown(options) {
171
157
? { ...options . remarkRehypeOptions , ...emptyRemarkRehypeOptions }
172
158
: emptyRemarkRehypeOptions
173
159
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
182
160
const unwrapDisallowed = options . unwrapDisallowed
161
+ const urlTransform = options . urlTransform || defaultUrlTransform
183
162
184
163
const processor = unified ( )
185
164
. use ( remarkParse )
@@ -265,26 +244,19 @@ export function Markdown(options) {
265
244
return index
266
245
}
267
246
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
+ }
288
260
}
289
261
290
262
if ( node . type === 'element' ) {
@@ -316,13 +288,14 @@ export function Markdown(options) {
316
288
/**
317
289
* Make a URL safe.
318
290
*
291
+ * @satisfies {UrlTransform }
319
292
* @param {string } value
320
293
* URL.
321
294
* @returns {string }
322
295
* Safe URL.
323
296
*/
324
- export function uriTransformer ( value ) {
325
- const url = ( value || '' ) . trim ( )
297
+ export function defaultUrlTransform ( value ) {
298
+ const url = value . trim ( )
326
299
const first = url . charAt ( 0 )
327
300
328
301
if ( first === '#' || first === '/' ) {
0 commit comments