Repository: freeCodeCamp/freeCodeCamp
File: curriculum/src/build-curriculum.ts (TypeScript)

Code before:
export async function parseCurriculumStructure(filter?: Filter) {
  const curriculum = getCurriculumStructure();
  const blockStructureDir = getBlockStructureDir();
  if (isEmpty(curriculum.superblocks))
    throw Error('No superblocks found in curriculum.json');
  if (isEmpty(curriculum.certifications))
    throw Error('No certifications found in curriculum.json');
  log(`Found ${curriculum.superblocks.length} superblocks to build`);
  log(`Found ${curriculum.certifications.length} certifications to build`);

  validateBlocks(curriculum.superblocks, blockStructureDir);

  const superblockList = addBlockStructure(
    addSuperblockStructure(curriculum.superblocks)
  );
  const refinedFilter = closestFilters(superblockList, filter);
  const fullSuperblockList = applyFilters(superblockList, refinedFilter);
  return {
    fullSuperblockList,
    certifications: curriculum.certifications
  };
}

// When in selective mode, we need to ensure that GraphQL is satisfied with basic query data,
// so we build a minimal full-stack-developer challenge if it wasn't included above.
async function ensureGraphQLSchema(
  selectedSuperblockEnums: SuperBlocks[],
  buildSuperblocks: string | undefined,
  fullCurriculum: Record<string, unknown>,
  builder: SuperblockCreator
) {
  if (
    selectedSuperblockEnums.length > 0 &&
    !buildSuperblocks?.includes('full-stack-developer') &&
    !fullCurriculum[SuperBlocks.FullStackDeveloper]
  ) {
    log(
      'Building minimal full-stack-developer challenge for GraphQL schema validation.'
    );

    const { fullSuperblockList: allSuperblocks } =
      await parseCurriculumStructure();
    const fsdSuperblock = allSuperblocks.find(
      sb => sb.name === SuperBlocks.FullStackDeveloper
    );

    if (fsdSuperblock && fsdSuperblock.blocks.length > 0) {
      const firstBlock = fsdSuperblock.blocks[0];
      if (firstBlock) {
        log(`Building minimal challenge from block: ${firstBlock.dashedName}`);
        const blockResult = await builder.blockCreator.processBlock(

Review comment (suggestion):
We use [createSchemaCustomization](https://github.com/freeCodeCamp/freeCodeCamp/blob/main/client/gatsby-node.js#L295) to define the expected schema. It should take care of that and, if it isn't, building a single challenge doesn't guarantee that Gatsby will be able to build. Reason being: it can't build if it tries a GraphQL query for data that neither exists nor is specified by `createSchemaCustomization`. A single challenge won't include all the data, so is not always going to fix the issue.

Code after:
export async function parseCurriculumStructure(filter?: Filter) {
  const curriculum = getCurriculumStructure();
  const blockStructureDir = getBlockStructureDir();
  if (isEmpty(curriculum.superblocks))
    throw Error('No superblocks found in curriculum.json');
  if (isEmpty(curriculum.certifications))
    throw Error('No certifications found in curriculum.json');
  log(`Found ${curriculum.superblocks.length} superblocks to build`);
  log(`Found ${curriculum.certifications.length} certifications to build`);

  validateBlocks(curriculum.superblocks, blockStructureDir);

  const superblockList = addBlockStructure(
    addSuperblockStructure(curriculum.superblocks)
  );
  const refinedFilter = closestFilters(superblockList, filter);
  const fullSuperblockList = applyFilters(superblockList, refinedFilter);
  return {
    fullSuperblockList,
    certifications: curriculum.certifications
  };
}

export async function buildCurriculum(lang: string, filters?: Filter) {
  const contentDir = getContentDir(lang);
  const fccSuperblock = process.env.FCC_SUPERBLOCK;

  const combinedFilters: Filter | undefined = fccSuperblock
    ? { ...filters, superBlock: fccSuperblock }
    : filters;

  const builder = new SuperblockCreator(
    getBlockCreator(lang, !isEmpty(combinedFilters))
  );

  const { fullSuperblockList, certifications } =
    await parseCurriculumStructure(combinedFilters);

  const fullCurriculum: {
    [key: string]: unknown;
    certifications: { blocks: { [key: string]: unknown } };
  } = {
    certifications: { blocks: {} }
  };

  const liveSuperblocks = fullSuperblockList.filter(({ name }) => {
    const superOrder = getSuperOrder(name);
    const upcomingSuperOrder = getSuperOrder(name, true);

    if (isUndefined(superOrder) && isUndefined(upcomingSuperOrder)) {
      throw Error(`Invalid superBlock: ${name}`);