122 lines
3.4 KiB
JavaScript
122 lines
3.4 KiB
JavaScript
const path = require('path');
|
|
const chokidar = require('chokidar');
|
|
const readdirp = require('readdirp');
|
|
|
|
const { createChallengeNode } = require('./create-challenge-nodes');
|
|
|
|
exports.sourceNodes = function sourceChallengesSourceNodes(
|
|
{ actions, reporter },
|
|
pluginOptions
|
|
) {
|
|
const { source, onSourceChange, curriculumPath } = pluginOptions;
|
|
if (typeof source !== 'function') {
|
|
reporter.panic(`
|
|
"source" is a required option for fcc-source-challenges. It must be a
|
|
function that delivers challenge objects to the plugin
|
|
`);
|
|
}
|
|
if (typeof onSourceChange !== 'function') {
|
|
reporter.panic(`
|
|
"onSourceChange" is a required option for fcc-source-challenges. It must be
|
|
a function that delivers a new challenge object to the plugin
|
|
`);
|
|
}
|
|
if (typeof curriculumPath !== 'string') {
|
|
reporter.panic(`
|
|
"curriculumPath" is a required option for fcc-source-challenges. It must be
|
|
a path to a curriculum directory
|
|
`);
|
|
}
|
|
const { createNode } = actions;
|
|
const watcher = chokidar.watch(curriculumPath, {
|
|
ignored: /(^|[\/\\])\../,
|
|
ignoreInitial: true,
|
|
persistent: true,
|
|
usePolling: true,
|
|
cwd: curriculumPath
|
|
});
|
|
|
|
watcher.on('change', filePath =>
|
|
/\.md?$/.test(filePath)
|
|
? onSourceChange(filePath)
|
|
.then(challenge => {
|
|
reporter.info(
|
|
`
|
|
File changed at ${filePath}, replacing challengeNode id ${challenge.id}
|
|
`
|
|
);
|
|
createVisibleChallenge(challenge);
|
|
})
|
|
.catch(e =>
|
|
reporter.error(`fcc-replace-challenge
|
|
attempting to replace ${filePath}
|
|
|
|
${e.message}
|
|
|
|
`)
|
|
)
|
|
: null
|
|
);
|
|
|
|
// if a file is added, that might change the order of the challenges in the
|
|
// containing block, so we recreate them all
|
|
watcher.on('add', filePath => {
|
|
if (/\.md?$/.test(filePath)) {
|
|
const blockPath = path.dirname(filePath);
|
|
const fullBlockPath = path.join(
|
|
__dirname,
|
|
'../../../curriculum/challenges/english/',
|
|
blockPath
|
|
);
|
|
readdirp(fullBlockPath, { fileFilter: '*.md' })
|
|
.on('data', entry => {
|
|
const { path: siblingPath } = entry;
|
|
const relativePath = path.join(blockPath, siblingPath);
|
|
onSourceChange(relativePath)
|
|
.then(challenge => {
|
|
reporter.info(
|
|
`
|
|
File changed at ${relativePath}, replacing challengeNode id ${challenge.id}
|
|
`
|
|
);
|
|
createVisibleChallenge(challenge);
|
|
})
|
|
.catch(e =>
|
|
reporter.error(`fcc-replace-challenge
|
|
attempting to replace ${relativePath}
|
|
|
|
${e.message}
|
|
|
|
`)
|
|
);
|
|
})
|
|
.on('warn', error => console.error('non-fatal error', error))
|
|
.on('error', error => console.error('fatal error', error));
|
|
}
|
|
});
|
|
|
|
function sourceAndCreateNodes() {
|
|
return source()
|
|
.then(challenges => Promise.all(challenges))
|
|
.then(challenges =>
|
|
challenges.map(challenge => createVisibleChallenge(challenge))
|
|
)
|
|
.catch(e => {
|
|
console.log(e);
|
|
reporter.panic(`fcc-source-challenges
|
|
|
|
${e.message}
|
|
|
|
`);
|
|
});
|
|
}
|
|
|
|
function createVisibleChallenge(challenge) {
|
|
createNode(createChallengeNode(challenge, reporter));
|
|
}
|
|
|
|
return new Promise((resolve, reject) => {
|
|
watcher.on('ready', () => sourceAndCreateNodes().then(resolve, reject));
|
|
});
|
|
};
|