-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathgatsby-node.js
179 lines (154 loc) · 3.97 KB
/
gatsby-node.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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
// @ts-check
const crypto = require(`crypto`)
const path = require(`path`)
const fs = require(`fs-extra`)
const { fetchRemoteFile } = require(`gatsby-core-utils`)
const svgToMiniDataURI = require('mini-svg-data-uri')
const { default: PQueue } = require('p-queue')
const { optimize } = require('svgo')
const queue = new PQueue({
concurrency: 5
})
const defaultSVGOOptions = {
multipass: true,
floatPrecision: 2,
plugins: [
{
name: 'preset-default',
params: {
overrides: {
removeViewBox: false
}
}
},
'cleanupListOfValues',
'prefixIds',
'removeDimensions',
'removeOffCanvasPaths',
'removeRasterImages',
'removeScriptElement',
'convertStyleToAttrs',
'removeStyleElement',
'reusePaths',
'sortAttrs'
]
}
// do we really need this? :(
const sessionCache = {}
exports.createSchemaCustomization = ({ actions }) => {
actions.createTypes(`
type InlineSvg {
content: String
originalContent: String
dataURI: String
absolutePath: String
relativePath: String
}
`)
}
async function processSVG({ absolutePath, store, reporter }) {
// Read local file
const svg = await fs.readFile(absolutePath, 'utf8')
// Optimize
if (svg.indexOf('base64') !== -1) {
reporter.info(
`${absolutePath}:\nSVG contains pixel data. Pixel data was removed to avoid file size bloat.`
)
}
// @ts-ignore
const result = optimize(svg.toString(), {
...defaultSVGOOptions,
path: absolutePath
})
if ('data' in result) {
// Create mini data URI
const dataURI = svgToMiniDataURI(result.data)
const directory = store.getState().program.directory
return {
content: result.data,
originalContent: svg,
dataURI,
absolutePath,
relativePath: path.relative(directory, absolutePath)
}
}
if ('modernError' in result) {
console.error(result.error)
throw result.modernError
}
throw new Error(
`SVGO returned an invalid result:\n${JSON.stringify(result, null, 2)}`
)
}
async function queueSVG({ absolutePath, cache, store, reporter }) {
const cacheId =
'contentful-svg-content-' +
crypto.createHash(`md5`).update(absolutePath).digest(`hex`)
if (sessionCache[cacheId]) {
return sessionCache[cacheId]
}
return queue.add(async () => {
try {
if (sessionCache[cacheId]) {
return sessionCache[cacheId]
}
const cachedData = await cache.get(cacheId)
if (cachedData) {
return cachedData
}
const processPromise = processSVG({
absolutePath,
store,
reporter
})
sessionCache[cacheId] = processPromise
const result = await processPromise
await cache.set(cacheId, result)
return result
} catch (err) {
reporter.panic(err)
return null
}
})
}
exports.createResolvers = ({ cache, createResolvers, store, reporter }) => {
createResolvers({
File: {
svg: {
type: `InlineSvg`,
resolve: async (source) => {
const { absolutePath } = source
// Ensure to process only svgs
if (source.internal.mediaType !== 'image/svg+xml') {
return null
}
return queueSVG({ absolutePath, store, reporter, cache })
}
}
},
ContentfulAsset: {
svg: {
type: `InlineSvg`,
resolve: async (source) => {
// Catch empty Contentful assets
if (!source.file) {
return null
}
const {
file: { url, contentType }
} = source
// Ensure to process only svgs and files with an url
if (contentType !== 'image/svg+xml' || !url) {
return null
}
// Get remote file
const absolutePath = await fetchRemoteFile({
url: `https:${url}#${source.updatedAt}`,
cache
})
return queueSVG({ absolutePath, store, reporter, cache })
}
}
}
})
}