/
static-image.ts
128 lines (120 loc) · 3.46 KB
/
static-image.ts
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
118
119
120
121
122
123
124
125
126
127
128
import { visit } from 'unist-util-visit'
import { Plugin } from 'unified'
import { Root } from 'mdast'
import path from 'node:path'
import { truthy } from '../utils'
import { existsSync } from '../file-system'
import { EXTERNAL_URL_REGEX, PUBLIC_DIR } from '../constants'
const getASTNodeImport = (name: string, from: string) => ({
type: 'mdxjsEsm',
value: `import ${name} from "${from}"`,
data: {
estree: {
type: 'Program',
sourceType: 'module',
body: [
{
type: 'ImportDeclaration',
specifiers: [
{
type: 'ImportDefaultSpecifier',
local: { type: 'Identifier', name }
}
],
source: {
type: 'Literal',
value: from,
raw: `"${from}"`
}
}
]
}
}
})
// Based on the remark-embed-images project
// https://github.com/remarkjs/remark-embed-images
export const remarkStaticImage: Plugin<
[{ allowFutureImage?: boolean; filePath: string }],
Root
> =
({ allowFutureImage, filePath }) =>
(tree, _file, done) => {
const importsToInject: any[] = []
visit(tree, 'image', node => {
let { url } = node
if (!url) {
console.warn(
`[nextra] File "${filePath}" contain image with empty "src" property, skipping…`
)
return
}
if (EXTERNAL_URL_REGEX.test(url)) {
// do nothing with images with external url
return
}
if (url.startsWith('/')) {
const urlPath = path.join(PUBLIC_DIR, url)
if (!existsSync(urlPath)) {
console.error(
`[nextra] File "${filePath}" contain image with url "${url}" that not found in "/public" directory, skipping…`
)
return
}
url = urlPath
}
// Unique variable name for the given static image URL.
const tempVariableName = `$nextraImage${importsToInject.length}`
// Replace the image node with a MDX component node (Next.js Image).
Object.assign(node, {
type: 'mdxJsxFlowElement',
name: '$NextImageNextra',
children: [],
attributes: [
// do not render empty alt in html markup
node.alt && {
type: 'mdxJsxAttribute',
name: 'alt',
value: node.alt
},
!url.endsWith('.svg') && {
type: 'mdxJsxAttribute',
name: 'placeholder',
value: 'blur'
},
{
type: 'mdxJsxAttribute',
name: 'src',
value: {
type: 'mdxJsxAttributeValueExpression',
value: tempVariableName,
data: {
estree: {
type: 'Program',
sourceType: 'module',
body: [
{
type: 'ExpressionStatement',
expression: {
type: 'Identifier',
name: tempVariableName
}
}
]
}
}
}
}
].filter(truthy)
})
// Inject the static image import into the root node.
importsToInject.push(getASTNodeImport(tempVariableName, url))
})
tree.children.unshift(
getASTNodeImport(
'$NextImageNextra',
allowFutureImage ? 'next/future/image' : 'next/image'
) as any,
...importsToInject
)
done()
}