mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-05-28 18:26:54 +00:00
feat(client): generate challenge data as static files (#50369)
* feat(client): generate challenge data as static files * chore: hopefully this is better types * fix: add declaration file * fix: schema issues --------- Co-authored-by: sembauke <semboot699@gmail.com>
This commit is contained in:
@@ -4,7 +4,8 @@ import path from 'path';
|
||||
import { getChallengesForLang } from '../../../curriculum/get-challenges';
|
||||
import {
|
||||
buildExtCurriculumData,
|
||||
Curriculum
|
||||
Curriculum,
|
||||
CurriculumProps
|
||||
} from './build-external-curricula-data';
|
||||
|
||||
const { CURRICULUM_LOCALE } = process.env;
|
||||
@@ -16,7 +17,7 @@ const globalConfigPath = path.resolve(__dirname, '../../../config');
|
||||
void getChallengesForLang('english')
|
||||
.then((result: Record<string, unknown>) => {
|
||||
if (CURRICULUM_LOCALE === 'english') {
|
||||
buildExtCurriculumData('v1', result as Curriculum);
|
||||
buildExtCurriculumData('v1', result as Curriculum<CurriculumProps>);
|
||||
}
|
||||
return result;
|
||||
})
|
||||
|
||||
@@ -57,7 +57,10 @@ if (envData.clientLocale == 'english' && !envData.showUpcomingChanges) {
|
||||
|
||||
test('the files generated should have the correct schema', async () => {
|
||||
const fileArray = (
|
||||
await readdirp.promise(`${clientStaticPath}/curriculum-data/${VERSION}`)
|
||||
await readdirp.promise(
|
||||
`${clientStaticPath}/curriculum-data/${VERSION}`,
|
||||
{ directoryFilter: ['!challenges'] }
|
||||
)
|
||||
).map(file => file.path);
|
||||
|
||||
fileArray
|
||||
|
||||
@@ -1,25 +1,35 @@
|
||||
import { mkdirSync, writeFileSync, readFileSync } from 'fs';
|
||||
import { resolve, dirname } from 'path';
|
||||
import { submitTypes } from '../../../client/utils/challenge-types';
|
||||
import { type ChallengeNode } from '../../../client/src/redux/prop-types';
|
||||
import { SuperBlocks } from '../../../config/certification-settings';
|
||||
|
||||
type Intro = { [keyValue in SuperBlocks]: IntroProps };
|
||||
export type Curriculum = { [keyValue in SuperBlocks]: CurriculumProps };
|
||||
export type Curriculum<T> = {
|
||||
[keyValue in SuperBlocks]: T extends CurriculumProps
|
||||
? CurriculumProps
|
||||
: GeneratedCurriculumProps;
|
||||
};
|
||||
|
||||
interface IntroProps extends CurriculumProps {
|
||||
title: string;
|
||||
intro: string[];
|
||||
}
|
||||
|
||||
interface CurriculumProps {
|
||||
export interface CurriculumProps {
|
||||
intro: string[];
|
||||
blocks: Record<string, Block>;
|
||||
blocks: Record<string, Block<ChallengeNode['challenge'][]>>;
|
||||
}
|
||||
|
||||
interface Block {
|
||||
interface GeneratedCurriculumProps {
|
||||
intro: string[];
|
||||
blocks: Record<string, Block<Record<string, unknown>>>;
|
||||
}
|
||||
|
||||
interface Block<T> {
|
||||
desc: string[];
|
||||
intro: string[];
|
||||
challenges: Record<string, unknown>;
|
||||
challenges: T;
|
||||
meta: Record<string, unknown>;
|
||||
}
|
||||
|
||||
@@ -44,7 +54,7 @@ const dashedNames = orderedSuperBlockInfo.map(({ dashedName }) => dashedName);
|
||||
|
||||
export function buildExtCurriculumData(
|
||||
ver: string,
|
||||
curriculum: Curriculum
|
||||
curriculum: Curriculum<CurriculumProps>
|
||||
): void {
|
||||
const staticFolderPath = resolve(__dirname, '../../../client/static');
|
||||
const dataPath = `${staticFolderPath}/curriculum-data/`;
|
||||
@@ -71,24 +81,37 @@ export function buildExtCurriculumData(
|
||||
});
|
||||
|
||||
for (const superBlockKey of superBlockKeys) {
|
||||
const superBlock = <Curriculum>{};
|
||||
const superBlock = <Curriculum<GeneratedCurriculumProps>>{};
|
||||
const blockNames = Object.keys(curriculum[superBlockKey].blocks);
|
||||
|
||||
if (blockNames.length === 0) continue;
|
||||
|
||||
superBlock[superBlockKey] = <CurriculumProps>{};
|
||||
superBlock[superBlockKey] = <GeneratedCurriculumProps>{};
|
||||
superBlock[superBlockKey]['intro'] =
|
||||
getSuperBlockDescription(superBlockKey);
|
||||
superBlock[superBlockKey]['blocks'] = {};
|
||||
|
||||
for (let j = 0; j < blockNames.length; j++) {
|
||||
superBlock[superBlockKey]['blocks'][blockNames[j]] = <Block>{};
|
||||
superBlock[superBlockKey]['blocks'][blockNames[j]] = <
|
||||
Block<Record<string, unknown>>
|
||||
>{};
|
||||
|
||||
superBlock[superBlockKey]['blocks'][blockNames[j]]['desc'] =
|
||||
getBlockDescription(superBlockKey, blockNames[j]);
|
||||
|
||||
superBlock[superBlockKey]['blocks'][blockNames[j]]['challenges'] =
|
||||
curriculum[superBlockKey]['blocks'][blockNames[j]]['meta'];
|
||||
|
||||
const blockChallenges =
|
||||
curriculum[superBlockKey]['blocks'][blockNames[j]]['challenges'];
|
||||
|
||||
for (let k = 0; k < blockChallenges.length; k++) {
|
||||
const challenge = blockChallenges[k];
|
||||
const challengeId = challenge['id'];
|
||||
const challengePath = `challenges/${superBlockKey}/${blockNames[j]}/${challengeId}`;
|
||||
|
||||
writeToFile(challengePath, challenge);
|
||||
}
|
||||
}
|
||||
|
||||
writeToFile(superBlockKey, superBlock);
|
||||
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
// this module needs to be present because we are importing types from the client
|
||||
// in build-external-curricula-data.ts
|
||||
|
||||
declare module '@freecodecamp/react-bootstrap';
|
||||
Reference in New Issue
Block a user