mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-05-28 18:26:54 +00:00
chore: move challenge-auditor out of root (#64280)
Co-authored-by: Sem Bauke <semboot699@gmail.com>
This commit is contained in:
committed by
GitHub
parent
9c40394914
commit
8ef4620e0b
@@ -1,118 +0,0 @@
|
||||
import { readdir } from 'fs/promises';
|
||||
import { join, resolve } from 'path';
|
||||
|
||||
import { flatten } from 'lodash/fp';
|
||||
import { config } from 'dotenv';
|
||||
|
||||
const envPath = resolve(__dirname, '../../.env');
|
||||
config({ path: envPath });
|
||||
|
||||
import { availableLangs } from '../../shared/config/i18n';
|
||||
import { getChallengesForLang } from '../../curriculum/src/get-challenges';
|
||||
import {
|
||||
SuperBlocks,
|
||||
getAuditedSuperBlocks
|
||||
} from '../../shared/config/curriculum';
|
||||
|
||||
// TODO: re-organise the types to a common 'types' folder that can be shared
|
||||
// between the workspaces so we don't have to declare ChallengeNode here and in
|
||||
// the client.
|
||||
|
||||
// This cannot be imported from the client, without causing tsc to attempt to
|
||||
// compile the client (something it cannot do)
|
||||
type ChallengeNode = {
|
||||
block: string;
|
||||
dashedName: string;
|
||||
superBlock: SuperBlocks;
|
||||
id: string;
|
||||
challengeType: number;
|
||||
};
|
||||
|
||||
// Adding types for getChallengesForLang is possible, but not worth the effort
|
||||
// at this time.
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
|
||||
const getChallenges = async (lang: string) => {
|
||||
const curriculum = await getChallengesForLang(lang);
|
||||
return (
|
||||
Object.keys(curriculum)
|
||||
// @ts-expect-error - curriculum comes from a JS file.
|
||||
.map(key => curriculum[key].blocks)
|
||||
.reduce((challengeArray, superBlock) => {
|
||||
const challengesForBlock = Object.keys(superBlock).map(
|
||||
key => superBlock[key].challenges
|
||||
);
|
||||
return [...challengeArray, ...flatten(challengesForBlock)];
|
||||
}, []) as unknown as ChallengeNode[]
|
||||
);
|
||||
};
|
||||
|
||||
/* eslint-enable @typescript-eslint/no-unsafe-return */
|
||||
/* eslint-enable @typescript-eslint/no-unsafe-argument */
|
||||
/* eslint-enable @typescript-eslint/no-unsafe-assignment */
|
||||
/* eslint-enable @typescript-eslint/no-unsafe-member-access */
|
||||
|
||||
void (async () => {
|
||||
let actionShouldFail = false;
|
||||
const englishCurriculumDirectory = join(
|
||||
process.cwd(),
|
||||
'curriculum',
|
||||
'challenges',
|
||||
'english'
|
||||
);
|
||||
const englishFilePaths: string[] = [];
|
||||
const englishBlocks = await readdir(englishCurriculumDirectory);
|
||||
for (const englishBlock of englishBlocks) {
|
||||
if (englishBlock.endsWith('.txt')) {
|
||||
continue;
|
||||
}
|
||||
const englishChallenges = await readdir(
|
||||
join(englishCurriculumDirectory, englishBlock)
|
||||
);
|
||||
for (const englishChallenge of englishChallenges) {
|
||||
englishFilePaths.push(join(englishBlock, englishChallenge));
|
||||
}
|
||||
}
|
||||
const langsToCheck = availableLangs.curriculum.filter(
|
||||
lang => String(lang) !== 'english'
|
||||
);
|
||||
for (const language of langsToCheck) {
|
||||
console.log(`\n=== ${language} ===`);
|
||||
const certs = getAuditedSuperBlocks({ language });
|
||||
|
||||
const noDuplicateSlugs = await auditSlugs(language, certs);
|
||||
if (noDuplicateSlugs) {
|
||||
console.log(`All challenges pass.`);
|
||||
} else {
|
||||
actionShouldFail = true;
|
||||
}
|
||||
}
|
||||
process.exit(actionShouldFail ? 1 : 0);
|
||||
})();
|
||||
|
||||
async function auditSlugs(lang: string, certs: SuperBlocks[]) {
|
||||
let auditPassed = true;
|
||||
const slugs = new Map<string, string>();
|
||||
const challenges = await getChallenges(lang);
|
||||
|
||||
for (const challenge of challenges) {
|
||||
const { block, dashedName, superBlock } = challenge;
|
||||
const slug = `/learn/${superBlock}/${block}/${dashedName}`;
|
||||
// Skipping certifications
|
||||
const isCertification = challenge.challengeType === 7;
|
||||
if (certs.includes(superBlock) && !isCertification && slugs.has(slug)) {
|
||||
console.log(
|
||||
`${slug} appears more than once: ${slugs.get(slug) ?? ''} and ${
|
||||
challenge.id
|
||||
}`
|
||||
);
|
||||
auditPassed = false;
|
||||
}
|
||||
slugs.set(slug, challenge.id);
|
||||
}
|
||||
|
||||
return auditPassed;
|
||||
}
|
||||
Reference in New Issue
Block a user