ronantakizawa/codereview-bench huggingface dataset:
Repository: freeCodeCamp/freeCodeCamp
File: tools/challenge-helper-scripts/create-project.ts (TypeScript)

Code before:
  newIntro[superBlock].blocks[block] = {
    title,
    intro: [title, '']
  };
  void withTrace(
    fs.writeFile,
    introJsonPath,
    await format(JSON.stringify(newIntro), { parser: 'json' })
  );
}

async function createMetaJson(
  superBlock: SuperBlocks,
  block: string,
  title: string,
  helpCategory: string,
  challengeId: ObjectID,
  order?: number,
  blockType?: string,
  blockLayout?: string
) {
  const metaDir = path.resolve(__dirname, '../../curriculum/challenges/_meta');
  let newMeta;
  if (superBlock !== SuperBlocks.FullStackDeveloper) {
    newMeta = getBaseMeta['Step'];
    newMeta.order = order!;
  } else {
    newMeta = getBaseMeta['FullStack'];
    newMeta.blockType = blockType!;
    newMeta.blockLayout = blockLayout!;
  }
  newMeta.name = title;
  newMeta.dashedName = block;
  newMeta.helpCategory = helpCategory;
  newMeta.superBlock = superBlock;
  // eslint-disable-next-line @typescript-eslint/no-base-to-string
  newMeta.challengeOrder = [{ id: challengeId.toString(), title: 'Step 1' }];
  const newMetaDir = path.resolve(metaDir, block);
  if (!existsSync(newMetaDir)) {
    await withTrace(fs.mkdir, newMetaDir);
  }

  void withTrace(
    fs.writeFile,
    path.resolve(metaDir, `${block}/meta.json`),
    await format(JSON.stringify(newMeta), { parser: 'json' })
  );
}

async function createIntroMD(
  superBlock: string,

Review comment (suggestion):
I would recommend a regular assertion over a type assertion. It achieves the same goal, but also immediately tells people if something weird has happened.

Code after:
    newMeta.order = order;
  }
  newMeta.name = title;
  newMeta.dashedName = block;
  newMeta.helpCategory = helpCategory;
  // eslint-disable-next-line @typescript-eslint/no-base-to-string
  newMeta.challengeOrder = [{ id: challengeId.toString(), title: 'Step 1' }];

  await writeBlockStructure(block, newMeta);
}

async function createFirstChallenge(block: string): Promise<ObjectID> {
  const { blockContentDir } = getContentConfig('english') as {
    blockContentDir: string;
  };

  const newChallengeDir = path.resolve(blockContentDir, block);
  await fs.mkdir(newChallengeDir, { recursive: true });

  // TODO: would be nice if the extension made sense for the challenge, but, at
  // least until react I think they're all going to be html anyway.
  const challengeSeeds = {
    indexhtml: {
      contents: '',
      ext: 'html',
      editableRegionBoundaries: [0, 2]
    }
  };
  // including trailing slash for compatibility with createStepFile
  return createStepFile({
    projectPath: newChallengeDir + '/',
    stepNum: 1,
    challengeType: 0,
    challengeSeeds,
    isFirstChallenge: true
  });
}

async function createQuizChallenge(
  block: string,
  title: string,
  questionCount: number
): Promise<ObjectID> {
  const newChallengeDir = path.resolve(
    __dirname,
    `../../curriculum/challenges/english/${block}`
  );
  if (!existsSync(newChallengeDir)) {
    await withTrace(fs.mkdir, newChallengeDir);
  }
  return createQuizFile({
Repository: freeCodeCamp/freeCodeCamp
File: tools/challenge-helper-scripts/create-project.ts (TypeScript)

Code before:
  blockLayout?: string;
  questionCount?: number;
  order?: number;
  chapter?: string;
  position?: number;
  module?: string;
  title?: string;
}

interface FullStackBlock {
  dashedName: string;
}

interface FullStackModule {
  dashedName: string;
  blocks: FullStackBlock[];
}

interface FullStackChapter {
  dashedName: string;
  modules: FullStackModule[];
}

interface FullStackData {
  chapters: FullStackChapter[];
}

async function createProject(
  superBlock: SuperBlocks,
  block: string,
  helpCategory: string,
  blockType?: string,
  blockLayout?: string,
  questionCount?: number,
  title?: string,
  chapter?: string,
  module?: string,
  position?: number,
  order?: number
) {
  if (!title) {
    title = block;
  }

  void updateIntroJson(superBlock, block, title);

  if (blockType === BlockTypes.quiz) {
    const challengeId = await createQuizChallenge(
      superBlock,
      block,
      title,

Review comment (suggestion):
```suggestion
```

Rather than creating these by hand, we can simply import the .json file and let TypeScript figure it out.

Code after:

type IntroJson = Record<SuperBlocks, SuperBlockInfo>;

interface CreateProjectArgs {
  superBlock: SuperBlocks;
  block: string;
  helpCategory: string;
  blockType?: string;
  blockLayout?: string;
  questionCount?: number;
  order?: number;
  chapter?: string;
  position?: number;
  module?: string;
  title?: string;
}

async function createProject(projectArgs: CreateProjectArgs) {
  if (!projectArgs.title) {
    projectArgs.title = projectArgs.block;
  }

  const order = projectArgs.order;
  const chapter = projectArgs.chapter;
  const module = projectArgs.module;
  const position = projectArgs.position;

  const superblockFilename = (
    superBlockToFilename as Record<SuperBlocks, string>
  )[projectArgs.superBlock];

  if (projectArgs.superBlock === SuperBlocks.FullStackDeveloper) {
    if (!chapter || !module || typeof position == 'undefined') {
      throw Error(
        'Missing one of the following arguments: chapter, module, position'
      );
    }
    void updateChapterModuleSuperblockStructure(
      projectArgs.block,
      { order: position, chapter, module },
      superblockFilename
    );
  } else {
    if (typeof order == 'undefined') {
      throw Error('Missing argument: order');
    }
    void updateSimpleSuperblockStructure(
      projectArgs.block,
      { order },
      superblockFilename
    );
Repository: freeCodeCamp/freeCodeCamp
File: tools/challenge-helper-scripts/create-project.ts (TypeScript)

Code before:
import { existsSync } from 'fs';
import fs from 'fs/promises';
import path from 'path';
import { prompt } from 'inquirer';
import { format } from 'prettier';
import ObjectID from 'bson-objectid';
import * as fullStackJson from '../../curriculum/superblock-structure/full-stack.json';
import {
  SuperBlocks,
  superBlockToFolderMap
} from '../../shared/config/curriculum';
import { BlockLayouts, BlockTypes } from '../../shared/config/blocks';
import { createQuizFile, createStepFile, validateBlockName } from './utils';
import { getBaseMeta } from './helpers/get-base-meta';

const helpCategories = [
  'HTML-CSS',
  'JavaScript',
  'Backend Development',
  'Python',
  'English',
  'Odin',
  'Euler',
  'Rosetta'
] as const;

type BlockInfo = {
  title: string;
  intro: string[];
};

type SuperBlockInfo = {

Review comment (suggestion):
```suggestion
import fullStackData from '../../curriculum/superblock-structure/full-stack.json';
```
This has all the types, so is all we need.

Code after:
import { existsSync } from 'fs';
import fs from 'fs/promises';
import path from 'path';
import { prompt } from 'inquirer';
import { format } from 'prettier';
import ObjectID from 'bson-objectid';

import fullStackData from '../../curriculum/structure/superblocks/full-stack-developer.json';
import { SuperBlocks } from '../../shared/config/curriculum';
import { BlockLayouts, BlockTypes } from '../../shared/config/blocks';
import {
  getContentConfig,
  writeBlockStructure
} from '../../curriculum/file-handler';
import { superBlockToFilename } from '../../curriculum/build-curriculum';
import { createQuizFile, createStepFile, validateBlockName } from './utils';
import { getBaseMeta } from './helpers/get-base-meta';
import { createIntroMD } from './helpers/create-intro';
import {
  updateChapterModuleSuperblockStructure,
  updateSimpleSuperblockStructure
} from './helpers/create-project';

const helpCategories = [
  'HTML-CSS',
  'JavaScript',
  'Backend Development',
  'Python',
  'English',
  'Odin',
  'Euler',
  'Rosetta'
Repository: freeCodeCamp/freeCodeCamp
File: tools/challenge-helper-scripts/create-project.ts (TypeScript)

Code before:
    message: 'Choose a block layout',

    default: (answers: { blockType: BlockTypes }) =>
      answers.blockType == BlockTypes.quiz
        ? BlockLayouts.Link
        : BlockLayouts.ChallengeList,
    type: 'list',
    choices: Object.values(BlockLayouts),
    when: (answers: CreateProjectArgs) =>
      answers.superBlock === SuperBlocks.FullStackDeveloper
  },
  {
    name: 'questionCount',
    message: 'Choose a question count',
    default: 20,
    type: 'list',
    choices: [10, 20],
    when: (answers: CreateProjectArgs) => answers.blockType === BlockTypes.quiz
  },
  {
    name: 'chapter',
    message:
      'What chapter in full-stack.json should this full stack project go in?',
    default: 'html',
    type: 'list',
    choices: fullStackJson.chapters.map(x => x.dashedName),
    when: (answers: CreateProjectArgs) =>
      answers.superBlock === SuperBlocks.FullStackDeveloper
  },
  {
    name: 'module',
    message:
      'What module in full-stack.json should this full stack project go in?',
    default: 'html',
    type: 'list',
    choices: (answers: CreateProjectArgs) =>
      fullStackJson.chapters
        .find(x => x.dashedName === answers.chapter)
        ?.modules.map(x => x.dashedName),
    when: (answers: CreateProjectArgs) =>
      answers.superBlock === SuperBlocks.FullStackDeveloper
  },
  {
    name: 'position',
    message: 'Which position in the module does this appear in the module?',
    default: 5,
    validate: (position: string) => {
      return parseInt(position, 10) > 0
        ? true
        : 'Position must be an number greater than zero.';
    },

Review comment (suggestion):
```suggestion
    choices: fullStackData.chapters.map(x => x.dashedName),
```

Code after:
])
  .then(
    async ({
      superBlock,
      block,
      title,
      helpCategory,
      blockType,
      blockLayout,
      questionCount,
      chapter,
      module,
      position,
      order
    }: CreateProjectArgs) =>
      await createProject({
        superBlock,
        block,
        helpCategory,
        blockType,
        blockLayout,
        questionCount,
        title,
        chapter,
        module,
        position,
        order
      })
  )
  .then(() =>
    console.log(
      'All set.  Now use pnpm run clean:client in the root and it should be good to go.'
    )
  );
Repository: freeCodeCamp/freeCodeCamp
File: tools/challenge-helper-scripts/create-project.ts (TypeScript)

Code before:
    name: 'chapter',
    message:
      'What chapter in full-stack.json should this full stack project go in?',
    default: 'html',
    type: 'list',
    choices: fullStackData.chapters.map(x => x.dashedName),
    when: (answers: CreateProjectArgs) =>
      answers.superBlock === SuperBlocks.FullStackDeveloper
  },
  {
    name: 'module',
    message:
      'What module in full-stack.json should this full stack project go in?',
    default: 'html',
    type: 'list',
    choices: (answers: CreateProjectArgs) =>
      fullStackData.chapters
        .find(x => x.dashedName === answers.chapter)
        ?.modules.map(x => x.dashedName),
    when: (answers: CreateProjectArgs) =>
      answers.superBlock === SuperBlocks.FullStackDeveloper
  },
  {
    name: 'position',
    message: 'Which position in the module does this appear in the module?',
    default: 5,
    validate: (position: string) => {
      return parseInt(position, 10) > 0
        ? true
        : 'Position must be an number greater than zero.';
    },
    when: (answers: CreateProjectArgs) =>
      answers.superBlock === SuperBlocks.FullStackDeveloper,
    filter: (position: string) => {
      return parseInt(position, 10);
    }
  },
  {
    name: 'order',
    message: 'Which position does this appear in the certificate?',
    default: 42,
    validate: (order: string) => {
      return parseInt(order, 10) > 0
        ? true
        : 'Order must be an number greater than zero.';
    },
    when: (answers: CreateProjectArgs) =>
      answers.superBlock !== SuperBlocks.FullStackDeveloper,
    filter: (order: string) => {
      return parseInt(order, 10);
    }

Review comment (suggestion):
```suggestion
    default: 1,
```
Best to have a default value that's definitely in the module.

Code after:
    },
    when: (answers: CreateProjectArgs) =>
      answers.superBlock === SuperBlocks.FullStackDeveloper,
    filter: (position: string) => {
      return parseInt(position, 10);
    }
  },
  {
    name: 'order',
    message: 'Which position does this appear in the certificate?',
    default: 42,
    validate: (order: string) => {
      return parseInt(order, 10) > 0
        ? true
        : 'Order must be an number greater than zero.';
    },
    when: (answers: CreateProjectArgs) =>
      answers.superBlock !== SuperBlocks.FullStackDeveloper,
    filter: (order: string) => {
      return parseInt(order, 10);
    }
  }
])
  .then(
    async ({
      superBlock,
      block,
      title,
      helpCategory,
      blockType,
      blockLayout,
      questionCount,
      chapter,
      module,
      position,
      order
    }: CreateProjectArgs) =>
      await createProject({
        superBlock,
        block,
        helpCategory,
        blockType,
        blockLayout,
        questionCount,
        title,
        chapter,
        module,
        position,
        order
      })
  )
Repository: freeCodeCamp/freeCodeCamp
File: tools/challenge-helper-scripts/create-project.ts (TypeScript)

Code before:
  questionCount?: number;
  order?: number;
  chapter?: string;
  position?: number;
  module?: string;
  title?: string;
}

async function createProject(projectArgs: CreateProjectArgs) {
  if (!projectArgs.title) {
    projectArgs.title = projectArgs.block;
  }

  void updateIntroJson(
    projectArgs.superBlock,
    projectArgs.block,
    projectArgs.title
  );

  if (projectArgs.blockType === BlockTypes.quiz) {
    const challengeId = await createQuizChallenge(
      projectArgs.superBlock,
      projectArgs.block,
      projectArgs.title,
      projectArgs.questionCount!
    );
    void createMetaJson(
      projectArgs.superBlock,
      projectArgs.block,
      projectArgs.title,
      projectArgs.helpCategory,
      challengeId
    );
  } else {
    const challengeId = await createFirstChallenge(
      projectArgs.superBlock,
      projectArgs.block
    );
    void createMetaJson(
      projectArgs.superBlock,
      projectArgs.block,
      projectArgs.title,
      projectArgs.helpCategory,
      challengeId,
      projectArgs.order,
      projectArgs.blockType,
      projectArgs.blockLayout
    );
    // TODO: remove once we stop relying on markdown in the client.
  }
  void createIntroMD(

Review comment (bug):
Here's a good place to make use of assert. It's a lot easier to simply assert that this value exists than writing a complicated type to enforce this. Since this whole thing is developer facing, it's totally fine for it to crash if it encounters something unexpected.

Code after:
type IntroJson = Record<SuperBlocks, SuperBlockInfo>;

interface CreateProjectArgs {
  superBlock: SuperBlocks;
  block: string;
  helpCategory: string;
  blockType?: string;
  blockLayout?: string;
  questionCount?: number;
  order?: number;
  chapter?: string;
  position?: number;
  module?: string;
  title?: string;
}

async function createProject(projectArgs: CreateProjectArgs) {
  if (!projectArgs.title) {
    projectArgs.title = projectArgs.block;
  }

  const order = projectArgs.order;
  const chapter = projectArgs.chapter;
  const module = projectArgs.module;
  const position = projectArgs.position;

  const superblockFilename = (
    superBlockToFilename as Record<SuperBlocks, string>
  )[projectArgs.superBlock];

  if (projectArgs.superBlock === SuperBlocks.FullStackDeveloper) {
    if (!chapter || !module || typeof position == 'undefined') {
      throw Error(
        'Missing one of the following arguments: chapter, module, position'
      );
    }
    void updateChapterModuleSuperblockStructure(
      projectArgs.block,
      { order: position, chapter, module },
      superblockFilename
    );
  } else {
    if (typeof order == 'undefined') {
      throw Error('Missing argument: order');
    }
    void updateSimpleSuperblockStructure(
      projectArgs.block,
      { order },
      superblockFilename
    );
  }
Repository: freeCodeCamp/freeCodeCamp
File: tools/challenge-helper-scripts/helpers/project-metadata.ts (TypeScript)

Code before:
      fs.accessSync(filePath);
    } catch (_e) {
      throw new Error(
        `The file
${filePath}
does not exist, but is required by the challengeOrder of
${getProjectMetaPath()}

To fix this, you can rename the file containing id: ${id} to ${id}.md
If there is no file for this id, then either the challengeOrder needs to be updated, or the file needs to be created.
`
      );
    }
  });

  // each file should have a corresponding step in the challengeOrder
  glob.sync(`${getProjectPath()}/*.md`).forEach(file => {
    const id = path.basename(file, '.md');
    if (!challengeOrder.find(({ id: stepId }) => stepId === id))
      throw new Error(
        `File ${file} should be in the ${getProjectPath()}.json's challengeOrder`
      );
  });
}

export { getMetaData, updateMetaData, getBlock, validateMetaData };


Review comment (refactor):
```suggestion
export { getMetaData, updateMetaData, getBlock };
```
Sorry for the mass of conflicts, but there's no need for this any more. The curriculum itself is responsible for validation, so the challenge helper scripts don't need to worry about it.

Code after:
import path from 'path';
import {
  getBlockStructure,
  writeBlockStructure
} from '../../../curriculum/file-handler';
import { getProjectPath } from './get-project-info';

export type Meta = {
  name: string;
  blockLayout: string;
  blockType: string;
  isUpcomingChange: boolean;
  dashedName: string;
  helpCategory: string;
  time: string;
  template: string;
  required: string[];
  challengeOrder: { id: string; title: string }[];
};

function getMetaData() {
  const block = getBlock(getProjectPath());
  return getBlockStructure(block) as Meta;
}

function getBlock(filePath: string) {
  return path.basename(filePath);
}

async function updateMetaData(newMetaData: Record<string, unknown>) {
  const block = getBlock(getProjectPath());
  await writeBlockStructure(block, newMetaData);
}

export { getMetaData, updateMetaData, getBlock };
Repository: freeCodeCamp/freeCodeCamp
File: tools/challenge-helper-scripts/helpers/project-metadata.ts (TypeScript)

Code before:
import fs from 'fs';
import path from 'path';
import glob from 'glob';
import {
  getBlockStructure,
  writeBlockStructure
} from '../../../curriculum/file-handler';
import { getProjectName, getProjectPath } from './get-project-info';

export type Meta = {
  name: string;
  blockLayout: string;
  blockType: string;
  isUpcomingChange: boolean;
  dashedName: string;
  helpCategory: string;
  time: string;
  template: string;
  required: string[];
  challengeOrder: { id: string; title: string }[];
};

function getMetaData() {
  const block = getBlock(getProjectPath());
  return getBlockStructure(block) as Meta;
}

function getBlock(filePath: string) {
  return path.basename(filePath);
}

async function updateMetaData(newMetaData: Record<string, unknown>) {
  const block = getBlock(getProjectPath());

Review comment (suggestion):
```suggestion
import { getProjectPath } from './get-project-info';
```

Code after:
import path from 'path';
import {
  getBlockStructure,
  writeBlockStructure
} from '../../../curriculum/file-handler';
import { getProjectPath } from './get-project-info';

export type Meta = {
  name: string;
  blockLayout: string;
  blockType: string;
  isUpcomingChange: boolean;
  dashedName: string;
  helpCategory: string;
  time: string;
  template: string;
  required: string[];
  challengeOrder: { id: string; title: string }[];
};

function getMetaData() {
  const block = getBlock(getProjectPath());
  return getBlockStructure(block) as Meta;
}

function getBlock(filePath: string) {
  return path.basename(filePath);
}

async function updateMetaData(newMetaData: Record<string, unknown>) {
  const block = getBlock(getProjectPath());
  await writeBlockStructure(block, newMetaData);
}
Repository: freeCodeCamp/freeCodeCamp
File: tools/challenge-helper-scripts/create-project.ts (TypeScript)

Code before:
      projectArgs.blockType) == null
  ) {
    throw new Error('Missing argument: blockType when updating intro markdown');
  }

  void createIntroMD(
    projectArgs.superBlock,
    projectArgs.block,
    projectArgs.title
  );
  if (projectArgs.superBlock === SuperBlocks.FullStackDeveloper) {
    if (
      projectArgs.chapter == null ||
      projectArgs.module == null ||
      projectArgs.position == null
    ) {
      throw new Error(
        'Missing one of the following arguments for updating fullstack.json: chapter,module, position'
      );
    }
    await updateFullStackJson(
      projectArgs.chapter,
      projectArgs.module,
      projectArgs.block,
      projectArgs.position
    );
  }
}

async function updateFullStackJson(
  chapterName: string,
  moduleName: string,
  block: string,
  position: number
) {
  // Get the index of the correct chapter
  const chapterIndex = fullStackData['chapters'].findIndex(
    chapter => chapter.dashedName === chapterName
  );
  const moduleIndex = fullStackData['chapters'][chapterIndex][
    'modules'
  ].findIndex(module => module.dashedName === moduleName);
  fullStackData['chapters'][chapterIndex]['modules'][moduleIndex][
    'blocks'
  ].splice(position - 1, 0, block);
  // Insert the new block into the already present module
  // Write the new changes to the file
  const newData = JSON.stringify(fullStackData, null, 2);
  await fs.writeFile(
    '../../curriculum/structure/superblocks/full-stack-developer.json',
    newData

Review comment (suggestion):
```suggestion
```
This should be taken care of by `updateChapterModuleSuperblockStructure`

Code after:
      projectArgs.order,
      projectArgs.blockType,
      projectArgs.blockLayout
    );
    // TODO: remove once we stop relying on markdown in the client.
  }

  if (
    (projectArgs.superBlock === SuperBlocks.FullStackDeveloper &&
      projectArgs.blockType) == null
  ) {
    throw new Error('Missing argument: blockType when updating intro markdown');
  }

  void createIntroMD(
    projectArgs.superBlock,
    projectArgs.block,
    projectArgs.title
  );
}

async function updateIntroJson(
  superBlock: SuperBlocks,
  block: string,
  title: string
) {
  const introJsonPath = path.resolve(
    __dirname,
    '../../client/i18n/locales/english/intro.json'
  );
  const newIntro = await parseJson<IntroJson>(introJsonPath);
  newIntro[superBlock].blocks[block] = {
    title,
    intro: [title, '']
  };
  void withTrace(
    fs.writeFile,
    introJsonPath,
    await format(JSON.stringify(newIntro), { parser: 'json' })
  );
}

async function createMetaJson(
  superBlock: SuperBlocks,
  block: string,
  title: string,
  helpCategory: string,
  challengeId: ObjectID,
  order?: number,
  blockType?: string,
  blockLayout?: string