-
Notifications
You must be signed in to change notification settings - Fork 10.3k
/
Copy pathcreate-remote-file-node.js
214 lines (192 loc) · 5.55 KB
/
create-remote-file-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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
const fs = require(`fs-extra`)
const {
createContentDigest,
fetchRemoteFile,
createFilePath,
} = require(`gatsby-core-utils`)
const path = require(`path`)
const { isWebUri } = require(`valid-url`)
const { createFileNode } = require(`./create-file-node`)
const { getRemoteFileExtension } = require(`./utils`)
let showFlagWarning = !!process.env.GATSBY_EXPERIMENTAL_REMOTE_FILE_PLACEHOLDER
/********************
* Type Definitions *
********************/
/**
* @typedef {GatsbyCache}
* @see gatsby/packages/gatsby/utils/cache.js
*/
/**
* @typedef {Reporter}
* @see gatsby/packages/gatsby-cli/lib/reporter.js
*/
/**
* @typedef {Auth}
* @type {Object}
* @property {String} htaccess_pass
* @property {String} htaccess_user
*/
/**
* @typedef {CreateRemoteFileNodePayload}
* @typedef {Object}
* @description Create Remote File Node Payload
*
* @param {String} options.url
* @param {GatsbyCache} options.cache
* @param {Function} options.createNode
* @param {Function} options.getCache
* @param {Auth} [options.auth]
* @param {Reporter} [options.reporter]
*/
/******************
* Core Functions *
******************/
/**
* processRemoteNode
* --
* Request the remote file and return the fileNode
*
* @param {CreateRemoteFileNodePayload} options
* @return {Promise<Object>} Resolves with the fileNode
*/
async function processRemoteNode({
url,
cache,
createNode,
parentNodeId,
auth = {},
httpHeaders = {},
createNodeId,
ext,
name,
}) {
let filename
if (process.env.GATSBY_EXPERIMENTAL_REMOTE_FILE_PLACEHOLDER) {
filename = await fetchPlaceholder({
fromPath: process.env.GATSBY_EXPERIMENTAL_REMOTE_FILE_PLACEHOLDER,
url,
cache,
ext,
name,
})
} else {
filename = await fetchRemoteFile({
url,
cache,
auth,
httpHeaders,
ext,
name,
})
}
// Create the file node.
const fileNode = await createFileNode(filename, createNodeId, {})
fileNode.internal.description = `File "${url}"`
fileNode.url = url
fileNode.parent = parentNodeId
// Override the default plugin as gatsby-source-filesystem needs to
// be the owner of File nodes or there'll be conflicts if any other
// File nodes are created through normal usages of
// gatsby-source-filesystem.
await createNode(fileNode, { name: `gatsby-source-filesystem` })
return fileNode
}
async function fetchPlaceholder({ fromPath, url, cache, ext, name }) {
const pluginCacheDir = cache.directory
const digest = createContentDigest(url)
if (!ext) {
ext = getRemoteFileExtension(url)
}
const filename = createFilePath(path.join(pluginCacheDir, digest), name, ext)
fs.copySync(fromPath, filename)
return filename
}
/**
* Index of promises resolving to File node from remote url
*/
const processingCache = {}
/***************
* Entry Point *
***************/
/**
* createRemoteFileNode
* --
*
* Download a remote file
* First checks cache to ensure duplicate requests aren't processed
* Then pushes to a queue
*
* @param {CreateRemoteFileNodePayload} options
* @return {Promise<Object>} Returns the created node
*/
module.exports = function createRemoteFileNode({
url,
cache,
createNode,
getCache,
parentNodeId = null,
auth = {},
httpHeaders = {},
createNodeId,
ext = null,
name = null,
}) {
if (showFlagWarning) {
showFlagWarning = false
// Note: This will use a placeholder image as the default for every file that is downloaded through this API.
// That may break certain cases, in particular when the file is not meant to be an image or when the image
// is expected to be of a particular type that is other than the placeholder. This API is meant to bypass
// the remote download for local testing only.
console.info(
`GATSBY_EXPERIMENTAL_REMOTE_FILE_PLACEHOLDER: Any file downloaded by \`createRemoteFileNode\` will use the same placeholder image and skip the remote fetch. Note: This is an experimental flag that can change/disappear at any point.`
)
console.info(
`GATSBY_EXPERIMENTAL_REMOTE_FILE_PLACEHOLDER: File to use: \`${process.env.GATSBY_EXPERIMENTAL_REMOTE_FILE_PLACEHOLDER}\``
)
}
// validation of the input
// without this it's notoriously easy to pass in the wrong `createNodeId`
// see gatsbyjs/gatsby#6643
if (typeof createNodeId !== `function`) {
throw new Error(
`createNodeId must be a function, was ${typeof createNodeId}`
)
}
if (typeof createNode !== `function`) {
throw new Error(`createNode must be a function, was ${typeof createNode}`)
}
if (typeof getCache === `function`) {
// use cache of this plugin and not cache of function caller
cache = getCache(`gatsby-source-filesystem`)
}
if (typeof cache !== `object`) {
throw new Error(
`Neither "cache" or "getCache" was passed. getCache must be function that return Gatsby cache, "cache" must be the Gatsby cache, was ${typeof cache}`
)
}
// Check if we already requested node for this remote file
// and return stored promise if we did.
if (processingCache[url]) {
return processingCache[url]
}
if (!url || isWebUri(url) === undefined) {
return Promise.reject(
new Error(
`url passed to createRemoteFileNode is either missing or not a proper web uri: ${url}`
)
)
}
const fileDownloadPromise = processRemoteNode({
url,
cache,
createNode,
parentNodeId,
createNodeId,
auth,
httpHeaders,
ext,
name,
})
processingCache[url] = fileDownloadPromise.then(node => node)
return processingCache[url]
}