Files
30-seconds-of-code/node_modules/gatsby-source-filesystem/gatsby-node.js
2019-08-20 15:52:05 +02:00

183 lines
5.1 KiB
JavaScript

"use strict";
const chokidar = require(`chokidar`);
const fs = require(`fs`);
const path = require(`path`);
const {
Machine
} = require(`xstate`);
const {
createFileNode
} = require(`./create-file-node`);
/**
* Create a state machine to manage Chokidar's not-ready/ready states.
*/
const createFSMachine = () => Machine({
key: `emitFSEvents`,
parallel: true,
strict: true,
states: {
CHOKIDAR: {
initial: `CHOKIDAR_NOT_READY`,
states: {
CHOKIDAR_NOT_READY: {
on: {
CHOKIDAR_READY: `CHOKIDAR_WATCHING`,
BOOTSTRAP_FINISHED: `CHOKIDAR_WATCHING_BOOTSTRAP_FINISHED`
}
},
CHOKIDAR_WATCHING: {
on: {
BOOTSTRAP_FINISHED: `CHOKIDAR_WATCHING_BOOTSTRAP_FINISHED`,
CHOKIDAR_READY: `CHOKIDAR_WATCHING`
}
},
CHOKIDAR_WATCHING_BOOTSTRAP_FINISHED: {
on: {
CHOKIDAR_READY: `CHOKIDAR_WATCHING_BOOTSTRAP_FINISHED`
}
}
}
}
}
});
exports.sourceNodes = ({
actions,
getNode,
createNodeId,
reporter,
emitter
}, pluginOptions) => {
const {
createNode,
createTypes,
deleteNode
} = actions;
const typeDefs = `
type File implements Node @infer {
birthtime: Date @deprecated(reason: "Use \`birthTime\` instead")
birthtimeMs: Float @deprecated(reason: "Use \`birthTime\` instead")
}
`;
createTypes(typeDefs); // Validate that the path exists.
if (!fs.existsSync(pluginOptions.path)) {
reporter.panic(`
The path passed to gatsby-source-filesystem does not exist on your file system:
${pluginOptions.path}
Please pick a path to an existing directory.
See docs here - https://www.gatsbyjs.org/packages/gatsby-source-filesystem/
`);
} // Validate that the path is absolute.
// Absolute paths are required to resolve images correctly.
if (!path.isAbsolute(pluginOptions.path)) {
pluginOptions.path = path.resolve(process.cwd(), pluginOptions.path);
}
const fsMachine = createFSMachine();
let currentState = fsMachine.initialState; // Once bootstrap is finished, we only let one File node update go through
// the system at a time.
emitter.on(`BOOTSTRAP_FINISHED`, () => {
currentState = fsMachine.transition(currentState.value, `BOOTSTRAP_FINISHED`);
});
const watcher = chokidar.watch(pluginOptions.path, {
ignored: [`**/*.un~`, `**/.DS_Store`, `**/.gitignore`, `**/.npmignore`, `**/.babelrc`, `**/yarn.lock`, `**/bower_components`, `**/node_modules`, `../**/dist/**`, ...(pluginOptions.ignore || [])]
});
const createAndProcessNode = path => {
const fileNodePromise = createFileNode(path, createNodeId, pluginOptions).then(fileNode => {
createNode(fileNode);
return null;
});
return fileNodePromise;
}; // For every path that is reported before the 'ready' event, we throw them
// into a queue and then flush the queue when 'ready' event arrives.
// After 'ready', we handle the 'add' event without putting it into a queue.
let pathQueue = [];
const flushPathQueue = () => {
let queue = pathQueue.slice();
pathQueue = [];
return Promise.all(queue.map(createAndProcessNode));
};
watcher.on(`add`, path => {
if (currentState.value.CHOKIDAR !== `CHOKIDAR_NOT_READY`) {
if (currentState.value.CHOKIDAR === `CHOKIDAR_WATCHING_BOOTSTRAP_FINISHED`) {
reporter.info(`added file at ${path}`);
}
createAndProcessNode(path).catch(err => reporter.error(err));
} else {
pathQueue.push(path);
}
});
watcher.on(`change`, path => {
if (currentState.value.CHOKIDAR === `CHOKIDAR_WATCHING_BOOTSTRAP_FINISHED`) {
reporter.info(`changed file at ${path}`);
}
createAndProcessNode(path).catch(err => reporter.error(err));
});
watcher.on(`unlink`, path => {
if (currentState.value.CHOKIDAR === `CHOKIDAR_WATCHING_BOOTSTRAP_FINISHED`) {
reporter.info(`file deleted at ${path}`);
}
const node = getNode(createNodeId(path)); // It's possible the file node was never created as sometimes tools will
// write and then immediately delete temporary files to the file system.
if (node) {
deleteNode({
node
});
}
});
watcher.on(`addDir`, path => {
if (currentState.value.CHOKIDAR !== `CHOKIDAR_NOT_READY`) {
if (currentState.value.CHOKIDAR === `CHOKIDAR_WATCHING_BOOTSTRAP_FINISHED`) {
reporter.info(`added directory at ${path}`);
}
createAndProcessNode(path).catch(err => reporter.error(err));
} else {
pathQueue.push(path);
}
});
watcher.on(`unlinkDir`, path => {
if (currentState.value.CHOKIDAR === `CHOKIDAR_WATCHING_BOOTSTRAP_FINISHED`) {
reporter.info(`directory deleted at ${path}`);
}
const node = getNode(createNodeId(path));
if (node) {
deleteNode({
node
});
}
});
return new Promise((resolve, reject) => {
watcher.on(`ready`, () => {
currentState = fsMachine.transition(currentState.value, `CHOKIDAR_READY`);
flushPathQueue().then(resolve, reject);
});
});
};
exports.setFieldsOnGraphQLNodeType = require(`./extend-file-node`);