-
-
Notifications
You must be signed in to change notification settings - Fork 3
/
index.js
117 lines (106 loc) · 2.94 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
/**
* @import {Image, Link, RootContent, Root} from 'mdast'
*/
/**
* @typedef Options
* Configuration (optional).
* @property {ReadonlyArray<string> | null | undefined} [imageExtensions]
* File extensions (without dot) to treat as images (default:
* `defaultImageExtensions`).
* @property {boolean | null | undefined} [link]
* Whether to wrap the image with a link to it (default: `true`).
*/
import {collapseWhiteSpace} from 'collapse-white-space'
import isUrl from 'is-url'
import {position} from 'unist-util-position'
import {visitParents} from 'unist-util-visit-parents'
/** @type {Readonly<Options>} */
const emptyOptions = {}
/**
* Add a simpler image syntax.
*
* @param {Readonly<Options> | null | undefined} [options]
* Configuration (optional).
* @returns
* Transform.
*/
export default function remarkImages(options) {
const settings = options || emptyOptions
const imageExtensions = settings.imageExtensions || defaultImageExtensions
const imageExtensionRegex = new RegExp(`\\.(${imageExtensions.join('|')})$`)
const link = settings.link !== false
/**
* Transform.
*
* @param {Root} tree
* Tree.
* @returns {undefined}
* Nothing.
*/
return function (tree) {
visitParents(tree, 'text', function (node, parents) {
const value = collapseWhiteSpace(node.value, {
style: 'html',
trim: true
})
if (
// Cannot contain whitespace (collapsed, so there can only be spaces):
!value.includes(' ') &&
// Looks like a URL or path:
(isUrl(value) ||
value.startsWith('/') ||
value.startsWith('./') ||
value.startsWith('../')) &&
// Ends in known extension:
imageExtensionRegex.test(value)
) {
let interactive = false
let length = parents.length
// Check if we’re in interactive content.
while (length--) {
const parent = parents[length]
if (parent.type === 'link' || parent.type === 'linkReference') {
interactive = true
break
}
}
/** @type {Image | Link} */
let replacement = {
type: 'image',
url: value,
title: null,
alt: '',
position: position(node)
}
// Add a link if we’re not already in one.
if (link && !interactive) {
replacement = {
type: 'link',
url: value,
title: null,
children: [replacement],
position: position(node)
}
}
const parent = parents[parents.length - 1]
/** @type {Array<RootContent>} */
const siblings = parent.children
siblings[siblings.indexOf(node)] = replacement
}
})
}
}
/**
* Extensions recognized as images by default.
*
* @type {ReadonlyArray<string>}
*/
export const defaultImageExtensions = [
'avif',
'gif',
'jpeg',
'jpg',
'png',
'svg',
'webp'
]