fix: rebuild challenge pages if source is updated (#62056)

This commit is contained in:
Oliver Eyton-Williams
2025-09-05 15:22:38 +02:00
committed by GitHub
parent 49c64a14b3
commit e257c2969e
5 changed files with 107 additions and 13 deletions

View File

@@ -2,7 +2,7 @@ const path = require('path');
const envData = require('./config/env.json');
const {
buildChallenges,
replaceChallengeNode,
replaceChallengeNodes,
localeChallengesRootDir
} = require('./utils/build-challenges');
@@ -57,7 +57,7 @@ module.exports = {
options: {
name: 'challenges',
source: buildChallenges,
onSourceChange: replaceChallengeNode(curriculumLocale),
onSourceChange: replaceChallengeNodes(curriculumLocale),
curriculumPath: localeChallengesRootDir
}
},

View File

@@ -10,6 +10,7 @@ const {
getBlockCreator
} = require('../../curriculum/build-curriculum');
const { getBlockStructure } = require('../../curriculum/file-handler');
const { getSuperblocks } = require('../../curriculum/build-curriculum');
const { curriculumLocale } = envData;
@@ -17,21 +18,27 @@ exports.localeChallengesRootDir = getContentDir(curriculumLocale);
const blockCreator = getBlockCreator(curriculumLocale);
exports.replaceChallengeNode = () => {
return async function replaceChallengeNode(filePath) {
exports.replaceChallengeNodes = () => {
return async function replaceChallengeNodes(filePath) {
const parentDir = path.dirname(filePath);
const block = path.basename(parentDir);
const filename = path.basename(filePath);
console.log(`Replacing challenge node for ${filePath}`);
const meta = getBlockStructure(block);
const superblocks = getSuperblocks(block);
return await blockCreator.createChallenge({
const challenge = await blockCreator.createChallenge({
filename,
block,
meta,
isAudited: true
});
return superblocks.map(superBlock => ({
...challenge,
superBlock
}));
};
};

View File

@@ -244,6 +244,24 @@ function addBlockStructure(
}));
}
/**
* Returns a list of all the superblocks that contain the given block
* @param {string} block
*/
function getSuperblocks(
block,
_addSuperblockStructure = addSuperblockStructure
) {
const { superblocks } = getCurriculumStructure();
const withStructure = _addSuperblockStructure(superblocks);
return withStructure
.filter(({ blocks }) =>
blocks.some(({ dashedName }) => dashedName === block)
)
.map(({ name }) => name);
}
async function buildCurriculum(lang, filters) {
const contentDir = getContentDir(lang);
const builder = new SuperblockCreator({
@@ -289,5 +307,6 @@ module.exports = {
getBlockStructure,
getSuperblockStructure,
createCommentMap,
superBlockToFilename
superBlockToFilename,
getSuperblocks
};

View File

@@ -1,6 +1,15 @@
jest.mock('./file-handler');
const path = require('node:path');
const { createCommentMap, addBlockStructure } = require('./build-curriculum');
const {
createCommentMap,
addBlockStructure,
getSuperblocks
} = require('./build-curriculum');
const { getCurriculumStructure } = require('./file-handler');
const mockGetCurriculumStructure = getCurriculumStructure;
describe('createCommentMap', () => {
const dictionaryDir = path.resolve(__dirname, '__fixtures__', 'dictionaries');
@@ -103,3 +112,58 @@ describe('addBlockStructure', () => {
]);
});
});
describe('getSuperblocks', () => {
it('returns an empty array if no superblocks contain the given block', () => {
mockGetCurriculumStructure.mockReturnValue({
superblocks: ['superblock-1'] // doesn't matter what this is, but must be defined
});
const mockAddSuperblockStructure = () => [
{
blocks: [{ dashedName: 'block-1' }],
name: 'superblock-1'
}
];
expect(
getSuperblocks('nonexistent-block', mockAddSuperblockStructure)
).toEqual([]);
});
it('returns an array with one superblock if one superblock contains the given block', () => {
mockGetCurriculumStructure.mockReturnValue({
superblocks: ['superblock-1'] // doesn't matter what this is, but must be defined
});
const mockAddSuperblockStructure = () => [
{
blocks: [{ dashedName: 'block-1' }, { dashedName: 'block-2' }],
name: 'superblock-1'
}
];
expect(getSuperblocks('block-1', mockAddSuperblockStructure)).toEqual([
'superblock-1'
]);
});
it('returns an array with multiple superblocks if multiple superblocks contain the given block', () => {
mockGetCurriculumStructure.mockReturnValue({
superblocks: ['superblock-1'] // doesn't matter what this is, but must be defined
});
const mockAddSuperblockStructure = () => [
{
blocks: [{ dashedName: 'block-1' }, { dashedName: 'block-2' }],
name: 'superblock-1'
},
{
blocks: [{ dashedName: 'block-3' }, { dashedName: 'block-1' }],
name: 'superblock-2'
}
];
expect(getSuperblocks('block-1', mockAddSuperblockStructure)).toEqual([
'superblock-1',
'superblock-2'
]);
});
});

View File

@@ -39,13 +39,15 @@ exports.sourceNodes = function sourceChallengesSourceNodes(
watcher.on('change', filePath =>
/\.md?$/.test(filePath)
? onSourceChange(filePath)
.then(challenge => {
.then(challenges => {
reporter.info(
`
File changed at ${filePath}, replacing challengeNode id ${challenge.id}
File changed at ${filePath}, replacing challengeNodes with ids ${challenges.map(({ id }) => id).join(', ')}
`
);
createVisibleChallenge(challenge, { isReloading: true });
challenges.forEach(challenge =>
createVisibleChallenge(challenge, { isReloading: true })
);
})
.catch(e =>
reporter.error(`fcc-replace-challenge
@@ -74,13 +76,15 @@ File changed at ${filePath}, replacing challengeNode id ${challenge.id}
const { path: siblingPath } = entry;
const relativePath = path.join(blockPath, siblingPath);
onSourceChange(relativePath)
.then(challenge => {
.then(challenges => {
reporter.info(
`
File changed at ${relativePath}, replacing challengeNode id ${challenge.id}
File changed at ${relativePath}, replacing challengeNodes with ids ${challenges.map(({ id }) => id).join(', ')}
`
);
createVisibleChallenge(challenge);
challenges.forEach(challenge =>
createVisibleChallenge(challenge)
);
})
.catch(e =>
reporter.error(`fcc-replace-challenge