gatsby-source-filesystem 4.12.0 → 4.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/create-file-node-from-buffer.js +192 -0
- package/create-file-node.js +89 -0
- package/create-file-path.js +48 -0
- package/create-remote-file-node.js +172 -0
- package/error-utils.js +33 -0
- package/extend-file-node.js +55 -0
- package/gatsby-node.js +277 -0
- package/index.js +12 -0
- package/package.json +4 -4
- package/utils.js +60 -0
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const fs = require(`fs-extra`);
|
|
4
|
+
|
|
5
|
+
const path = require(`path`);
|
|
6
|
+
|
|
7
|
+
const fileType = require(`file-type`);
|
|
8
|
+
|
|
9
|
+
const {
|
|
10
|
+
createFileNode
|
|
11
|
+
} = require(`./create-file-node`);
|
|
12
|
+
|
|
13
|
+
const {
|
|
14
|
+
createContentDigest,
|
|
15
|
+
createFilePath
|
|
16
|
+
} = require(`gatsby-core-utils`);
|
|
17
|
+
|
|
18
|
+
const cacheId = hash => `create-file-node-from-buffer-${hash}`;
|
|
19
|
+
/********************
|
|
20
|
+
* Type Definitions *
|
|
21
|
+
********************/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @typedef {GatsbyCache}
|
|
25
|
+
* @see gatsby/packages/gatsby/utils/cache.js
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @typedef {CreateFileNodeFromBufferPayload}
|
|
30
|
+
* @typedef {Object}
|
|
31
|
+
* @description Create File Node From Buffer Payload
|
|
32
|
+
*
|
|
33
|
+
* @param {Buffer} options.buffer
|
|
34
|
+
* @param {String} options.hash
|
|
35
|
+
* @param {GatsbyCache} options.cache
|
|
36
|
+
* @param {Function} options.getCache
|
|
37
|
+
* @param {Function} options.createNode
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* writeBuffer
|
|
42
|
+
* --
|
|
43
|
+
* Write the contents of `buffer` to `filename`
|
|
44
|
+
*
|
|
45
|
+
*
|
|
46
|
+
* @param {String} filename
|
|
47
|
+
* @param {Buffer} buffer
|
|
48
|
+
* @returns {Promise<void>}
|
|
49
|
+
*/
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
const writeBuffer = (filename, buffer) => new Promise((resolve, reject) => {
|
|
53
|
+
fs.writeFile(filename, buffer, err => err ? reject(err) : resolve());
|
|
54
|
+
});
|
|
55
|
+
/**
|
|
56
|
+
* processBufferNode
|
|
57
|
+
* --
|
|
58
|
+
* Write the buffer contents out to disk and return the fileNode
|
|
59
|
+
*
|
|
60
|
+
* @param {CreateFileNodeFromBufferPayload} options
|
|
61
|
+
* @return {Promise<Object>} Resolves with the fileNode
|
|
62
|
+
*/
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
async function processBufferNode({
|
|
66
|
+
buffer,
|
|
67
|
+
hash,
|
|
68
|
+
cache,
|
|
69
|
+
createNode,
|
|
70
|
+
parentNodeId,
|
|
71
|
+
createNodeId,
|
|
72
|
+
ext,
|
|
73
|
+
name
|
|
74
|
+
}) {
|
|
75
|
+
const pluginCacheDir = cache.directory; // See if there's a cache file for this buffer's contents from
|
|
76
|
+
// a previous run
|
|
77
|
+
|
|
78
|
+
let filename = await cache.get(cacheId(hash));
|
|
79
|
+
|
|
80
|
+
if (!filename) {
|
|
81
|
+
// If the user did not provide an extension and we couldn't get
|
|
82
|
+
// one from remote file, try and guess one
|
|
83
|
+
if (typeof ext === `undefined`) {
|
|
84
|
+
const filetype = await fileType.fromBuffer(buffer);
|
|
85
|
+
ext = filetype ? `.${filetype.ext}` : `.bin`;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
filename = createFilePath(path.join(pluginCacheDir, hash), name, ext);
|
|
89
|
+
await fs.ensureDir(path.dirname(filename)); // Cache the buffer contents
|
|
90
|
+
|
|
91
|
+
await writeBuffer(filename, buffer); // Save the cache file path for future use
|
|
92
|
+
|
|
93
|
+
await cache.set(cacheId(hash), filename);
|
|
94
|
+
} // Create the file node.
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
const fileNode = await createFileNode(filename, createNodeId, {});
|
|
98
|
+
fileNode.internal.description = `File "Buffer<${hash}>"`;
|
|
99
|
+
fileNode.hash = hash;
|
|
100
|
+
fileNode.parent = parentNodeId; // Override the default plugin as gatsby-source-filesystem needs to
|
|
101
|
+
// be the owner of File nodes or there'll be conflicts if any other
|
|
102
|
+
// File nodes are created through normal usages of
|
|
103
|
+
// gatsby-source-filesystem.
|
|
104
|
+
|
|
105
|
+
await createNode(fileNode, {
|
|
106
|
+
name: `gatsby-source-filesystem`
|
|
107
|
+
});
|
|
108
|
+
return fileNode;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Index of promises resolving to File node from buffer cache
|
|
112
|
+
*/
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
const processingCache = {};
|
|
116
|
+
/***************
|
|
117
|
+
* Entry Point *
|
|
118
|
+
***************/
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* createFileNodeFromBuffer
|
|
122
|
+
* --
|
|
123
|
+
*
|
|
124
|
+
* Cache a buffer's contents to disk
|
|
125
|
+
* First checks cache to ensure duplicate buffers aren't processed
|
|
126
|
+
*
|
|
127
|
+
* @param {CreateFileNodeFromBufferPayload} options
|
|
128
|
+
* @return {Promise<Object>} Returns the created node
|
|
129
|
+
*/
|
|
130
|
+
|
|
131
|
+
module.exports = ({
|
|
132
|
+
buffer,
|
|
133
|
+
hash,
|
|
134
|
+
cache,
|
|
135
|
+
createNode,
|
|
136
|
+
getCache,
|
|
137
|
+
parentNodeId = null,
|
|
138
|
+
createNodeId,
|
|
139
|
+
ext,
|
|
140
|
+
name
|
|
141
|
+
}) => {
|
|
142
|
+
// validation of the input
|
|
143
|
+
// without this it's notoriously easy to pass in the wrong `createNodeId`
|
|
144
|
+
// see gatsbyjs/gatsby#6643
|
|
145
|
+
if (typeof createNodeId !== `function`) {
|
|
146
|
+
throw new Error(`createNodeId must be a function, was ${typeof createNodeId}`);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (typeof createNode !== `function`) {
|
|
150
|
+
throw new Error(`createNode must be a function, was ${typeof createNode}`);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (typeof getCache === `function`) {
|
|
154
|
+
// use cache of this plugin and not cache of function caller
|
|
155
|
+
cache = getCache(`gatsby-source-filesystem`);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (typeof cache !== `object`) {
|
|
159
|
+
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}`);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (!buffer) {
|
|
163
|
+
return Promise.reject(`bad buffer: ${buffer}`);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (!hash) {
|
|
167
|
+
hash = createContentDigest(buffer);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (!name) {
|
|
171
|
+
name = hash;
|
|
172
|
+
} // Check if we already requested node for this remote file
|
|
173
|
+
// and return stored promise if we did.
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
if (processingCache[hash]) {
|
|
177
|
+
return processingCache[hash];
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const bufferCachePromise = processBufferNode({
|
|
181
|
+
buffer,
|
|
182
|
+
hash,
|
|
183
|
+
cache,
|
|
184
|
+
createNode,
|
|
185
|
+
parentNodeId,
|
|
186
|
+
createNodeId,
|
|
187
|
+
ext,
|
|
188
|
+
name
|
|
189
|
+
});
|
|
190
|
+
processingCache[hash] = bufferCachePromise;
|
|
191
|
+
return processingCache[hash];
|
|
192
|
+
};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const path = require(`path`);
|
|
4
|
+
|
|
5
|
+
const fs = require(`fs-extra`);
|
|
6
|
+
|
|
7
|
+
const mime = require(`mime`);
|
|
8
|
+
|
|
9
|
+
const prettyBytes = require(`pretty-bytes`);
|
|
10
|
+
|
|
11
|
+
const md5File = require(`md5-file`);
|
|
12
|
+
|
|
13
|
+
const {
|
|
14
|
+
createContentDigest,
|
|
15
|
+
slash
|
|
16
|
+
} = require(`gatsby-core-utils`);
|
|
17
|
+
|
|
18
|
+
exports.createFileNode = async (pathToFile, createNodeId, pluginOptions = {}) => {
|
|
19
|
+
const slashed = slash(pathToFile);
|
|
20
|
+
const parsedSlashed = path.parse(slashed);
|
|
21
|
+
const slashedFile = { ...parsedSlashed,
|
|
22
|
+
absolutePath: slashed,
|
|
23
|
+
// Useful for limiting graphql query with certain parent directory
|
|
24
|
+
relativeDirectory: slash(path.relative(pluginOptions.path || process.cwd(), parsedSlashed.dir))
|
|
25
|
+
};
|
|
26
|
+
const stats = await fs.stat(slashedFile.absolutePath);
|
|
27
|
+
let internal;
|
|
28
|
+
|
|
29
|
+
if (stats.isDirectory()) {
|
|
30
|
+
const contentDigest = createContentDigest({
|
|
31
|
+
stats: stats,
|
|
32
|
+
absolutePath: slashedFile.absolutePath
|
|
33
|
+
});
|
|
34
|
+
internal = {
|
|
35
|
+
contentDigest,
|
|
36
|
+
type: `Directory`,
|
|
37
|
+
description: `Directory "${path.relative(process.cwd(), slashed)}"`
|
|
38
|
+
};
|
|
39
|
+
} else {
|
|
40
|
+
const contentDigest = await md5File(slashedFile.absolutePath);
|
|
41
|
+
const mediaType = mime.getType(slashedFile.ext);
|
|
42
|
+
internal = {
|
|
43
|
+
contentDigest,
|
|
44
|
+
type: `File`,
|
|
45
|
+
mediaType: mediaType ? mediaType : `application/octet-stream`,
|
|
46
|
+
description: `File "${path.relative(process.cwd(), slashed)}"`
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
// Don't actually make the File id the absolute path as otherwise
|
|
52
|
+
// people will use the id for that and ids shouldn't be treated as
|
|
53
|
+
// useful information.
|
|
54
|
+
id: createNodeId(pathToFile),
|
|
55
|
+
children: [],
|
|
56
|
+
parent: null,
|
|
57
|
+
internal,
|
|
58
|
+
sourceInstanceName: pluginOptions.name || `__PROGRAMMATIC__`,
|
|
59
|
+
relativePath: slash(path.relative(pluginOptions.path || process.cwd(), slashedFile.absolutePath)),
|
|
60
|
+
extension: slashedFile.ext.slice(1).toLowerCase(),
|
|
61
|
+
prettySize: prettyBytes(stats.size),
|
|
62
|
+
modifiedTime: stats.mtime.toJSON(),
|
|
63
|
+
accessTime: stats.atime.toJSON(),
|
|
64
|
+
changeTime: stats.ctime.toJSON(),
|
|
65
|
+
birthTime: stats.birthtime.toJSON(),
|
|
66
|
+
// Note: deprecate splatting the slashedFile object
|
|
67
|
+
// Note: the object may contain different properties depending on File or Dir
|
|
68
|
+
...slashedFile,
|
|
69
|
+
// TODO: deprecate copying the entire object
|
|
70
|
+
// Note: not splatting for perf reasons (make sure Date objects are serialized)
|
|
71
|
+
dev: stats.dev,
|
|
72
|
+
mode: stats.mode,
|
|
73
|
+
nlink: stats.nlink,
|
|
74
|
+
uid: stats.uid,
|
|
75
|
+
rdev: stats.rdev,
|
|
76
|
+
blksize: stats.blksize,
|
|
77
|
+
ino: stats.ino,
|
|
78
|
+
size: stats.size,
|
|
79
|
+
blocks: stats.blocks,
|
|
80
|
+
atimeMs: stats.atimeMs,
|
|
81
|
+
mtimeMs: stats.mtimeMs,
|
|
82
|
+
ctimeMs: stats.ctimeMs,
|
|
83
|
+
birthtimeMs: stats.birthtimeMs,
|
|
84
|
+
atime: stats.atime.toJSON(),
|
|
85
|
+
mtime: stats.mtime.toJSON(),
|
|
86
|
+
ctime: stats.ctime.toJSON(),
|
|
87
|
+
birthtime: stats.birthtime.toJSON()
|
|
88
|
+
};
|
|
89
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const path = require(`path`);
|
|
4
|
+
|
|
5
|
+
const {
|
|
6
|
+
slash
|
|
7
|
+
} = require(`gatsby-core-utils`);
|
|
8
|
+
|
|
9
|
+
function findFileNode({
|
|
10
|
+
node,
|
|
11
|
+
getNode
|
|
12
|
+
}) {
|
|
13
|
+
// Find the file node.
|
|
14
|
+
let fileNode = node;
|
|
15
|
+
let whileCount = 0;
|
|
16
|
+
|
|
17
|
+
while (fileNode.internal.type !== `File` && fileNode.parent && getNode(fileNode.parent) !== undefined && whileCount < 101) {
|
|
18
|
+
fileNode = getNode(fileNode.parent);
|
|
19
|
+
whileCount += 1;
|
|
20
|
+
|
|
21
|
+
if (whileCount > 100) {
|
|
22
|
+
console.log(`It looks like you have a node that's set its parent as itself`, fileNode);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return fileNode;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
module.exports = ({
|
|
30
|
+
node,
|
|
31
|
+
getNode,
|
|
32
|
+
basePath = `src/pages`,
|
|
33
|
+
trailingSlash = true
|
|
34
|
+
}) => {
|
|
35
|
+
// Find the File node
|
|
36
|
+
const fileNode = findFileNode({
|
|
37
|
+
node,
|
|
38
|
+
getNode
|
|
39
|
+
});
|
|
40
|
+
if (!fileNode) return undefined;
|
|
41
|
+
const relativePath = path.posix.relative(slash(basePath), slash(fileNode.relativePath));
|
|
42
|
+
const {
|
|
43
|
+
dir = ``,
|
|
44
|
+
name
|
|
45
|
+
} = path.parse(relativePath);
|
|
46
|
+
const parsedName = name === `index` ? `` : name;
|
|
47
|
+
return path.posix.join(`/`, dir, parsedName, trailingSlash ? `/` : ``);
|
|
48
|
+
};
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
fetchRemoteFile
|
|
5
|
+
} = require(`gatsby-core-utils/fetch-remote-file`);
|
|
6
|
+
|
|
7
|
+
const {
|
|
8
|
+
isWebUri
|
|
9
|
+
} = require(`valid-url`);
|
|
10
|
+
|
|
11
|
+
const {
|
|
12
|
+
createFileNode
|
|
13
|
+
} = require(`./create-file-node`);
|
|
14
|
+
/********************
|
|
15
|
+
* Type Definitions *
|
|
16
|
+
********************/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @typedef {GatsbyCache}
|
|
20
|
+
* @see gatsby/packages/gatsby/utils/cache.js
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @typedef {Reporter}
|
|
25
|
+
* @see gatsby/packages/gatsby-cli/lib/reporter.js
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @typedef {Auth}
|
|
30
|
+
* @type {Object}
|
|
31
|
+
* @property {String} htaccess_pass
|
|
32
|
+
* @property {String} htaccess_user
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @typedef {CreateRemoteFileNodePayload}
|
|
37
|
+
* @typedef {Object}
|
|
38
|
+
* @description Create Remote File Node Payload
|
|
39
|
+
*
|
|
40
|
+
* @param {String} options.url
|
|
41
|
+
* @param {GatsbyCache} options.cache
|
|
42
|
+
* @param {Function} options.createNode
|
|
43
|
+
* @param {Function} options.getCache
|
|
44
|
+
* @param {Auth} [options.auth]
|
|
45
|
+
* @param {Reporter} [options.reporter]
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
/******************
|
|
49
|
+
* Core Functions *
|
|
50
|
+
******************/
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* processRemoteNode
|
|
54
|
+
* --
|
|
55
|
+
* Request the remote file and return the fileNode
|
|
56
|
+
*
|
|
57
|
+
* @param {CreateRemoteFileNodePayload} options
|
|
58
|
+
* @return {Promise<Object>} Resolves with the fileNode
|
|
59
|
+
*/
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
async function processRemoteNode({
|
|
63
|
+
url,
|
|
64
|
+
cache,
|
|
65
|
+
createNode,
|
|
66
|
+
parentNodeId,
|
|
67
|
+
auth = {},
|
|
68
|
+
httpHeaders = {},
|
|
69
|
+
createNodeId,
|
|
70
|
+
ext,
|
|
71
|
+
name
|
|
72
|
+
}) {
|
|
73
|
+
const filename = await fetchRemoteFile({
|
|
74
|
+
url,
|
|
75
|
+
cache,
|
|
76
|
+
auth,
|
|
77
|
+
httpHeaders,
|
|
78
|
+
ext,
|
|
79
|
+
name
|
|
80
|
+
}); // Create the file node.
|
|
81
|
+
|
|
82
|
+
const fileNode = await createFileNode(filename, createNodeId, {});
|
|
83
|
+
fileNode.internal.description = `File "${url}"`;
|
|
84
|
+
fileNode.url = url;
|
|
85
|
+
fileNode.parent = parentNodeId; // Override the default plugin as gatsby-source-filesystem needs to
|
|
86
|
+
// be the owner of File nodes or there'll be conflicts if any other
|
|
87
|
+
// File nodes are created through normal usages of
|
|
88
|
+
// gatsby-source-filesystem.
|
|
89
|
+
|
|
90
|
+
await createNode(fileNode, {
|
|
91
|
+
name: `gatsby-source-filesystem`
|
|
92
|
+
});
|
|
93
|
+
return fileNode;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Index of promises resolving to File node from remote url
|
|
97
|
+
*/
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
const processingCache = {};
|
|
101
|
+
/***************
|
|
102
|
+
* Entry Point *
|
|
103
|
+
***************/
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* createRemoteFileNode
|
|
107
|
+
* --
|
|
108
|
+
*
|
|
109
|
+
* Download a remote file
|
|
110
|
+
* First checks cache to ensure duplicate requests aren't processed
|
|
111
|
+
* Then pushes to a queue
|
|
112
|
+
*
|
|
113
|
+
* @param {CreateRemoteFileNodePayload} options
|
|
114
|
+
* @return {Promise<Object>} Returns the created node
|
|
115
|
+
*/
|
|
116
|
+
|
|
117
|
+
module.exports = function createRemoteFileNode({
|
|
118
|
+
url,
|
|
119
|
+
cache,
|
|
120
|
+
createNode,
|
|
121
|
+
getCache,
|
|
122
|
+
parentNodeId = null,
|
|
123
|
+
auth = {},
|
|
124
|
+
httpHeaders = {},
|
|
125
|
+
createNodeId,
|
|
126
|
+
ext = null,
|
|
127
|
+
name = null
|
|
128
|
+
}) {
|
|
129
|
+
// validation of the input
|
|
130
|
+
// without this it's notoriously easy to pass in the wrong `createNodeId`
|
|
131
|
+
// see gatsbyjs/gatsby#6643
|
|
132
|
+
if (typeof createNodeId !== `function`) {
|
|
133
|
+
throw new Error(`createNodeId must be a function, was ${typeof createNodeId}`);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (typeof createNode !== `function`) {
|
|
137
|
+
throw new Error(`createNode must be a function, was ${typeof createNode}`);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (typeof getCache === `function`) {
|
|
141
|
+
// use cache of this plugin and not cache of function caller
|
|
142
|
+
cache = getCache(`gatsby-source-filesystem`);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (typeof cache !== `object`) {
|
|
146
|
+
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}`);
|
|
147
|
+
} // Check if we already requested node for this remote file
|
|
148
|
+
// and return stored promise if we did.
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
if (processingCache[url]) {
|
|
152
|
+
return processingCache[url];
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (!url || isWebUri(url) === undefined) {
|
|
156
|
+
throw new Error(`url passed to createRemoteFileNode is either missing or not a proper web uri: ${url}`);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const fileDownloadPromise = processRemoteNode({
|
|
160
|
+
url,
|
|
161
|
+
cache,
|
|
162
|
+
createNode,
|
|
163
|
+
parentNodeId,
|
|
164
|
+
createNodeId,
|
|
165
|
+
auth,
|
|
166
|
+
httpHeaders,
|
|
167
|
+
ext,
|
|
168
|
+
name
|
|
169
|
+
});
|
|
170
|
+
processingCache[url] = fileDownloadPromise.then(node => node);
|
|
171
|
+
return processingCache[url];
|
|
172
|
+
};
|
package/error-utils.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
exports.__esModule = true;
|
|
4
|
+
exports.prefixId = prefixId;
|
|
5
|
+
exports.ERROR_MAP = exports.pluginPrefix = exports.CODES = void 0;
|
|
6
|
+
const CODES = {
|
|
7
|
+
Generic: `10000`,
|
|
8
|
+
MissingResource: `10001`
|
|
9
|
+
};
|
|
10
|
+
exports.CODES = CODES;
|
|
11
|
+
const pluginPrefix = `gatsby-source-filesystem`;
|
|
12
|
+
exports.pluginPrefix = pluginPrefix;
|
|
13
|
+
|
|
14
|
+
function prefixId(id) {
|
|
15
|
+
return `${pluginPrefix}_${id}`;
|
|
16
|
+
} // TODO: Refactor to use contextual data instead of only context.sourceMessage
|
|
17
|
+
// once reporter.setErrorMap is guaranteed to be available
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
const ERROR_MAP = {
|
|
21
|
+
[CODES.Generic]: {
|
|
22
|
+
text: context => context.sourceMessage,
|
|
23
|
+
level: `ERROR`,
|
|
24
|
+
type: `PLUGIN`
|
|
25
|
+
},
|
|
26
|
+
[CODES.MissingResource]: {
|
|
27
|
+
text: context => context.sourceMessage,
|
|
28
|
+
level: `ERROR`,
|
|
29
|
+
type: `PLUGIN`,
|
|
30
|
+
category: `USER`
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
exports.ERROR_MAP = ERROR_MAP;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
GraphQLString
|
|
5
|
+
} = require(`gatsby/graphql`);
|
|
6
|
+
|
|
7
|
+
const fs = require(`fs-extra`);
|
|
8
|
+
|
|
9
|
+
const path = require(`path`);
|
|
10
|
+
|
|
11
|
+
const {
|
|
12
|
+
prefixId,
|
|
13
|
+
CODES
|
|
14
|
+
} = require(`./error-utils`);
|
|
15
|
+
|
|
16
|
+
module.exports = ({
|
|
17
|
+
type,
|
|
18
|
+
getNodeAndSavePathDependency,
|
|
19
|
+
pathPrefix = ``,
|
|
20
|
+
reporter
|
|
21
|
+
}) => {
|
|
22
|
+
if (type.name !== `File`) {
|
|
23
|
+
return {};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
publicURL: {
|
|
28
|
+
type: GraphQLString,
|
|
29
|
+
args: {},
|
|
30
|
+
description: `Copy file to static directory and return public url to it`,
|
|
31
|
+
resolve: (file, fieldArgs, context) => {
|
|
32
|
+
const details = getNodeAndSavePathDependency(file.id, context.path);
|
|
33
|
+
const fileName = `${file.internal.contentDigest}/${details.base}`;
|
|
34
|
+
const publicPath = path.join(process.cwd(), `public`, `static`, fileName);
|
|
35
|
+
|
|
36
|
+
if (!fs.existsSync(publicPath)) {
|
|
37
|
+
fs.copySync(details.absolutePath, publicPath, {
|
|
38
|
+
dereference: true
|
|
39
|
+
}, err => {
|
|
40
|
+
if (err) {
|
|
41
|
+
reporter.panic({
|
|
42
|
+
id: prefixId(CODES.MissingResource),
|
|
43
|
+
context: {
|
|
44
|
+
sourceMessage: `error copying file from ${details.absolutePath} to ${publicPath}`
|
|
45
|
+
}
|
|
46
|
+
}, err);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return `${pathPrefix}/static/${fileName}`;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
};
|
package/gatsby-node.js
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const chokidar = require(`chokidar`);
|
|
4
|
+
|
|
5
|
+
const fs = require(`fs`);
|
|
6
|
+
|
|
7
|
+
const path = require(`path`);
|
|
8
|
+
|
|
9
|
+
const {
|
|
10
|
+
Machine,
|
|
11
|
+
interpret
|
|
12
|
+
} = require(`xstate`);
|
|
13
|
+
|
|
14
|
+
const {
|
|
15
|
+
createFileNode
|
|
16
|
+
} = require(`./create-file-node`);
|
|
17
|
+
|
|
18
|
+
const {
|
|
19
|
+
ERROR_MAP
|
|
20
|
+
} = require(`./error-utils`);
|
|
21
|
+
|
|
22
|
+
exports.onPreInit = ({
|
|
23
|
+
reporter
|
|
24
|
+
}) => {
|
|
25
|
+
if (reporter.setErrorMap) {
|
|
26
|
+
reporter.setErrorMap(ERROR_MAP);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Create a state machine to manage Chokidar's not-ready/ready states.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
const createFSMachine = ({
|
|
35
|
+
actions: {
|
|
36
|
+
createNode,
|
|
37
|
+
deleteNode
|
|
38
|
+
},
|
|
39
|
+
getNode,
|
|
40
|
+
createNodeId,
|
|
41
|
+
reporter
|
|
42
|
+
}, pluginOptions) => {
|
|
43
|
+
const createAndProcessNode = path => {
|
|
44
|
+
const fileNodePromise = createFileNode(path, createNodeId, pluginOptions).then(fileNode => {
|
|
45
|
+
createNode(fileNode);
|
|
46
|
+
return null;
|
|
47
|
+
});
|
|
48
|
+
return fileNodePromise;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const deletePathNode = path => {
|
|
52
|
+
const node = getNode(createNodeId(path)); // It's possible the node was never created as sometimes tools will
|
|
53
|
+
// write and then immediately delete temporary files to the file system.
|
|
54
|
+
|
|
55
|
+
if (node) {
|
|
56
|
+
deleteNode(node);
|
|
57
|
+
}
|
|
58
|
+
}; // For every path that is reported before the 'ready' event, we throw them
|
|
59
|
+
// into a queue and then flush the queue when 'ready' event arrives.
|
|
60
|
+
// After 'ready', we handle the 'add' event without putting it into a queue.
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
let pathQueue = [];
|
|
64
|
+
|
|
65
|
+
const flushPathQueue = () => {
|
|
66
|
+
const queue = pathQueue.slice();
|
|
67
|
+
pathQueue = null;
|
|
68
|
+
return Promise.all( // eslint-disable-next-line consistent-return
|
|
69
|
+
queue.map(({
|
|
70
|
+
op,
|
|
71
|
+
path
|
|
72
|
+
}) => {
|
|
73
|
+
switch (op) {
|
|
74
|
+
case `delete`:
|
|
75
|
+
return deletePathNode(path);
|
|
76
|
+
|
|
77
|
+
case `upsert`:
|
|
78
|
+
return createAndProcessNode(path);
|
|
79
|
+
}
|
|
80
|
+
}));
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const log = expr => (ctx, action, meta) => {
|
|
84
|
+
if (meta.state.matches(`BOOTSTRAP.BOOTSTRAPPED`)) {
|
|
85
|
+
reporter.info(expr(ctx, action, meta));
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const fsMachine = Machine({
|
|
90
|
+
id: `fs`,
|
|
91
|
+
type: `parallel`,
|
|
92
|
+
states: {
|
|
93
|
+
BOOTSTRAP: {
|
|
94
|
+
initial: `BOOTSTRAPPING`,
|
|
95
|
+
states: {
|
|
96
|
+
BOOTSTRAPPING: {
|
|
97
|
+
on: {
|
|
98
|
+
BOOTSTRAP_FINISHED: `BOOTSTRAPPED`
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
BOOTSTRAPPED: {
|
|
102
|
+
type: `final`
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
CHOKIDAR: {
|
|
107
|
+
initial: `NOT_READY`,
|
|
108
|
+
states: {
|
|
109
|
+
NOT_READY: {
|
|
110
|
+
on: {
|
|
111
|
+
CHOKIDAR_READY: `READY`,
|
|
112
|
+
CHOKIDAR_ADD: {
|
|
113
|
+
actions: `queueNodeProcessing`
|
|
114
|
+
},
|
|
115
|
+
CHOKIDAR_CHANGE: {
|
|
116
|
+
actions: `queueNodeProcessing`
|
|
117
|
+
},
|
|
118
|
+
CHOKIDAR_UNLINK: {
|
|
119
|
+
actions: `queueNodeDeleting`
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
exit: `flushPathQueue`
|
|
123
|
+
},
|
|
124
|
+
READY: {
|
|
125
|
+
on: {
|
|
126
|
+
CHOKIDAR_ADD: {
|
|
127
|
+
actions: [`createAndProcessNode`, log((_, {
|
|
128
|
+
pathType,
|
|
129
|
+
path
|
|
130
|
+
}) => `added ${pathType} at ${path}`)]
|
|
131
|
+
},
|
|
132
|
+
CHOKIDAR_CHANGE: {
|
|
133
|
+
actions: [`createAndProcessNode`, log((_, {
|
|
134
|
+
pathType,
|
|
135
|
+
path
|
|
136
|
+
}) => `changed ${pathType} at ${path}`)]
|
|
137
|
+
},
|
|
138
|
+
CHOKIDAR_UNLINK: {
|
|
139
|
+
actions: [`deletePathNode`, log((_, {
|
|
140
|
+
pathType,
|
|
141
|
+
path
|
|
142
|
+
}) => `deleted ${pathType} at ${path}`)]
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}, {
|
|
150
|
+
actions: {
|
|
151
|
+
createAndProcessNode(_, {
|
|
152
|
+
pathType,
|
|
153
|
+
path
|
|
154
|
+
}) {
|
|
155
|
+
createAndProcessNode(path).catch(err => reporter.error(err));
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
deletePathNode(_, {
|
|
159
|
+
pathType,
|
|
160
|
+
path
|
|
161
|
+
}, {
|
|
162
|
+
state
|
|
163
|
+
}) {
|
|
164
|
+
deletePathNode(path);
|
|
165
|
+
},
|
|
166
|
+
|
|
167
|
+
flushPathQueue(_, {
|
|
168
|
+
resolve,
|
|
169
|
+
reject
|
|
170
|
+
}) {
|
|
171
|
+
flushPathQueue().then(resolve, reject);
|
|
172
|
+
},
|
|
173
|
+
|
|
174
|
+
queueNodeDeleting(_, {
|
|
175
|
+
path
|
|
176
|
+
}) {
|
|
177
|
+
pathQueue.push({
|
|
178
|
+
op: `delete`,
|
|
179
|
+
path
|
|
180
|
+
});
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
queueNodeProcessing(_, {
|
|
184
|
+
path
|
|
185
|
+
}) {
|
|
186
|
+
pathQueue.push({
|
|
187
|
+
op: `upsert`,
|
|
188
|
+
path
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
return interpret(fsMachine).start();
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
exports.pluginOptionsSchema = ({
|
|
198
|
+
Joi
|
|
199
|
+
}) => Joi.object({
|
|
200
|
+
name: Joi.string(),
|
|
201
|
+
path: Joi.string(),
|
|
202
|
+
ignore: Joi.array().items(Joi.string(), Joi.object().regex(), Joi.function())
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
exports.sourceNodes = (api, pluginOptions) => {
|
|
206
|
+
// Validate that the path exists.
|
|
207
|
+
if (!fs.existsSync(pluginOptions.path)) {
|
|
208
|
+
api.reporter.panic(`
|
|
209
|
+
The path passed to gatsby-source-filesystem does not exist on your file system:
|
|
210
|
+
${pluginOptions.path}
|
|
211
|
+
Please pick a path to an existing directory.
|
|
212
|
+
See docs here - https://www.gatsbyjs.com/plugins/gatsby-source-filesystem/
|
|
213
|
+
`);
|
|
214
|
+
} // Validate that the path is absolute.
|
|
215
|
+
// Absolute paths are required to resolve images correctly.
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
if (!path.isAbsolute(pluginOptions.path)) {
|
|
219
|
+
pluginOptions.path = path.resolve(process.cwd(), pluginOptions.path);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const fsMachine = createFSMachine(api, pluginOptions); // Once bootstrap is finished, we only let one File node update go through
|
|
223
|
+
// the system at a time.
|
|
224
|
+
|
|
225
|
+
api.emitter.on(`BOOTSTRAP_FINISHED`, () => {
|
|
226
|
+
fsMachine.send(`BOOTSTRAP_FINISHED`);
|
|
227
|
+
});
|
|
228
|
+
const watcher = chokidar.watch(pluginOptions.path, {
|
|
229
|
+
ignored: [`**/*.un~`, `**/.DS_Store`, `**/.gitignore`, `**/.npmignore`, `**/.babelrc`, `**/yarn.lock`, `**/bower_components`, `**/node_modules`, `../**/dist/**`, ...(pluginOptions.ignore || [])]
|
|
230
|
+
});
|
|
231
|
+
watcher.on(`add`, path => {
|
|
232
|
+
fsMachine.send({
|
|
233
|
+
type: `CHOKIDAR_ADD`,
|
|
234
|
+
pathType: `file`,
|
|
235
|
+
path
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
watcher.on(`change`, path => {
|
|
239
|
+
fsMachine.send({
|
|
240
|
+
type: `CHOKIDAR_CHANGE`,
|
|
241
|
+
pathType: `file`,
|
|
242
|
+
path
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
watcher.on(`unlink`, path => {
|
|
246
|
+
fsMachine.send({
|
|
247
|
+
type: `CHOKIDAR_UNLINK`,
|
|
248
|
+
pathType: `file`,
|
|
249
|
+
path
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
watcher.on(`addDir`, path => {
|
|
253
|
+
fsMachine.send({
|
|
254
|
+
type: `CHOKIDAR_ADD`,
|
|
255
|
+
pathType: `directory`,
|
|
256
|
+
path
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
watcher.on(`unlinkDir`, path => {
|
|
260
|
+
fsMachine.send({
|
|
261
|
+
type: `CHOKIDAR_UNLINK`,
|
|
262
|
+
pathType: `directory`,
|
|
263
|
+
path
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
return new Promise((resolve, reject) => {
|
|
267
|
+
watcher.on(`ready`, () => {
|
|
268
|
+
fsMachine.send({
|
|
269
|
+
type: `CHOKIDAR_READY`,
|
|
270
|
+
resolve,
|
|
271
|
+
reject
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
exports.setFieldsOnGraphQLNodeType = require(`./extend-file-node`);
|
package/index.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const fs = require(`fs-extra`);
|
|
4
|
+
|
|
5
|
+
function loadNodeContent(fileNode) {
|
|
6
|
+
return fs.readFile(fileNode.absolutePath, `utf-8`);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
exports.createFilePath = require(`./create-file-path`);
|
|
10
|
+
exports.createRemoteFileNode = require(`./create-remote-file-node`);
|
|
11
|
+
exports.createFileNodeFromBuffer = require(`./create-file-node-from-buffer`);
|
|
12
|
+
exports.loadNodeContent = loadNodeContent;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gatsby-source-filesystem",
|
|
3
3
|
"description": "Gatsby source plugin for building websites from local data. Markdown, JSON, images, YAML, CSV, and dozens of other data types supported.",
|
|
4
|
-
"version": "4.12.
|
|
4
|
+
"version": "4.12.1",
|
|
5
5
|
"author": "Kyle Mathews <mathews.kyle@gmail.com>",
|
|
6
6
|
"bugs": {
|
|
7
7
|
"url": "https://github.com/gatsbyjs/gatsby/issues"
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"chokidar": "^3.5.2",
|
|
12
12
|
"file-type": "^16.5.3",
|
|
13
13
|
"fs-extra": "^10.0.0",
|
|
14
|
-
"gatsby-core-utils": "^3.12.
|
|
14
|
+
"gatsby-core-utils": "^3.12.1",
|
|
15
15
|
"got": "^9.6.0",
|
|
16
16
|
"md5-file": "^5.0.0",
|
|
17
17
|
"mime": "^2.5.2",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@babel/cli": "^7.15.4",
|
|
25
25
|
"@babel/core": "^7.15.5",
|
|
26
|
-
"babel-preset-gatsby-package": "^2.12.
|
|
26
|
+
"babel-preset-gatsby-package": "^2.12.1",
|
|
27
27
|
"cross-env": "^7.0.3"
|
|
28
28
|
},
|
|
29
29
|
"homepage": "https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-source-filesystem#readme",
|
|
@@ -49,5 +49,5 @@
|
|
|
49
49
|
"engines": {
|
|
50
50
|
"node": ">=14.15.0"
|
|
51
51
|
},
|
|
52
|
-
"gitHead": "
|
|
52
|
+
"gitHead": "4765c4514325cf1d4bd209854fd974ba56e8a751"
|
|
53
53
|
}
|
package/utils.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
exports.__esModule = true;
|
|
4
|
+
exports.getRemoteFileExtension = getRemoteFileExtension;
|
|
5
|
+
exports.getRemoteFileName = getRemoteFileName;
|
|
6
|
+
exports.createFilePath = void 0;
|
|
7
|
+
|
|
8
|
+
const path = require(`path`);
|
|
9
|
+
|
|
10
|
+
const Url = require(`url`);
|
|
11
|
+
|
|
12
|
+
const {
|
|
13
|
+
createFilePath
|
|
14
|
+
} = require(`gatsby-core-utils`);
|
|
15
|
+
/**
|
|
16
|
+
* getParsedPath
|
|
17
|
+
* --
|
|
18
|
+
* Parses remote url to a path object
|
|
19
|
+
*
|
|
20
|
+
*
|
|
21
|
+
* @param {String} url
|
|
22
|
+
* @return {Object} path
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
exports.createFilePath = createFilePath;
|
|
27
|
+
|
|
28
|
+
function getParsedPath(url) {
|
|
29
|
+
return path.parse(Url.parse(url).pathname);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* getRemoteFileExtension
|
|
33
|
+
* --
|
|
34
|
+
* Parses remote url to retrieve remote file extension
|
|
35
|
+
*
|
|
36
|
+
*
|
|
37
|
+
* @param {String} url
|
|
38
|
+
* @return {String} extension
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
function getRemoteFileExtension(url) {
|
|
43
|
+
return getParsedPath(url).ext;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* getRemoteFileName
|
|
47
|
+
* --
|
|
48
|
+
* Parses remote url to retrieve remote file name
|
|
49
|
+
*
|
|
50
|
+
*
|
|
51
|
+
* @param {String} url
|
|
52
|
+
* @return {String} filename
|
|
53
|
+
*/
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
function getRemoteFileName(url) {
|
|
57
|
+
return getParsedPath(url).name;
|
|
58
|
+
} // createFilePath should be imported from `gatsby-core-utils`
|
|
59
|
+
// but some plugins already do import it from `gatsby-source-filesystem/utils`
|
|
60
|
+
// so just keeping re-export here for backward compatibility
|