fix(dev): preserve step order during hot reload by reloading all chal… (#62048)

This commit is contained in:
Niyati Raiyani
2025-09-16 23:39:40 +05:30
committed by GitHub
parent 3931ed159d
commit 4ad144fc67
2 changed files with 66 additions and 76 deletions

View File

@@ -2,43 +2,69 @@ const path = require('path');
const _ = require('lodash');
const envData = require('../config/env.json');
const { getChallengesForLang } = require('../../curriculum/get-challenges');
const {
getContentDir,
getBlockCreator
getBlockCreator,
getSuperblocks,
superBlockToFilename
} = require('../../curriculum/build-curriculum');
const { getBlockStructure } = require('../../curriculum/file-handler');
const { getSuperblocks } = require('../../curriculum/build-curriculum');
const {
getBlockStructure,
getSuperblockStructure
} = require('../../curriculum/file-handler');
const { transformSuperBlock } = require('../../curriculum/build-superblock');
const { getSuperOrder } = require('../../curriculum/utils');
const { curriculumLocale } = envData;
const curriculumLocale = process.env.CURRICULUM_LOCALE || 'english';
exports.localeChallengesRootDir = getContentDir(curriculumLocale);
const blockCreator = getBlockCreator(curriculumLocale);
function getBlockMetadata(block, superBlock) {
// Compute metadata for the given block in the specified superblock
const sbFilename = superBlockToFilename[superBlock];
const sbData = getSuperblockStructure(sbFilename);
const blocks = transformSuperBlock(sbData, {
showComingSoon: process.env.SHOW_UPCOMING_CHANGES === 'true'
});
const order = blocks.findIndex(b => b.dashedName === block);
const superOrder = getSuperOrder(superBlock);
if (order === -1) {
throw new Error(`Block ${block} not found in superblock ${superBlock}`);
}
return { order, superOrder };
}
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}`);
console.log(`Replacing challenge nodes for ${filePath}`);
const meta = getBlockStructure(block);
const superblocks = getSuperblocks(block);
const challenge = await blockCreator.createChallenge({
filename,
block,
meta,
isAudited: true
});
// Create a challenge for each superblock containing this block
const challenges = await Promise.all(
superblocks.map(async superBlock => {
const { order, superOrder } = getBlockMetadata(block, superBlock);
return blockCreator.createChallenge({
filename,
block,
meta: { ...meta, superBlock, order, superOrder },
isAudited: true
});
})
);
return superblocks.map(superBlock => ({
...challenge,
superBlock
}));
return challenges;
};
};

View File

@@ -1,6 +1,4 @@
const path = require('path');
const chokidar = require('chokidar');
const readdirp = require('readdirp');
const { createChallengeNode } = require('./create-challenge-nodes');
@@ -36,68 +34,34 @@ exports.sourceNodes = function sourceChallengesSourceNodes(
cwd: curriculumPath
});
function handleChallengeUpdate(filePath, action = 'changed') {
return onSourceChange(filePath)
.then(challenges => {
const actionText = action === 'added' ? 'creating' : 'replacing';
reporter.info(
`Challenge file ${action}: ${filePath}, ${actionText} challengeNodes with ids ${challenges.map(({ id }) => id).join(', ')}`
);
challenges.forEach(challenge =>
createVisibleChallenge(challenge, { isReloading: true })
);
})
.catch(e =>
reporter.error(
`fcc-replace-challenge\nattempting to replace ${filePath}\n\n${e.message}\n${e.stack ? ` ${e.stack}` : ''}`
)
);
}
// On file change, replace only the changed challenge. The key is ensuring
// onSourceChange returns a challenge with complete metadata.
watcher.on('change', filePath =>
/\.md?$/.test(filePath)
? onSourceChange(filePath)
.then(challenges => {
reporter.info(
`
File changed at ${filePath}, replacing challengeNodes with ids ${challenges.map(({ id }) => id).join(', ')}
`
);
challenges.forEach(challenge =>
createVisibleChallenge(challenge, { isReloading: true })
);
})
.catch(e =>
reporter.error(`fcc-replace-challenge
attempting to replace ${filePath}
${e.message}
${e.stack}
`)
)
: null
/\.md?$/.test(filePath) ? handleChallengeUpdate(filePath, 'changed') : null
);
// if a file is added, that might change the order of the challenges in the
// containing block, so we recreate them all
// On file add, replace just the new challenge.
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(challenges => {
reporter.info(
`
File changed at ${relativePath}, replacing challengeNodes with ids ${challenges.map(({ id }) => id).join(', ')}
`
);
challenges.forEach(challenge =>
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));
}
if (!/\.md?$/.test(filePath)) return;
handleChallengeUpdate(filePath, 'added');
});
function sourceAndCreateNodes() {