183 lines
5.1 KiB
JavaScript
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`); |