refactor: (slightly) decentralize type checking (#64163)

This commit is contained in:
Oliver Eyton-Williams
2025-12-01 12:43:53 +01:00
committed by GitHub
parent 176bba6c15
commit 37ce134123
46 changed files with 214 additions and 226 deletions
+3 -3
View File
@@ -1,13 +1,13 @@
import fs from 'fs';
import { getProjectPath } from './helpers/get-project-info';
import { getMetaData } from './helpers/project-metadata';
import { getProjectPath } from './helpers/get-project-info.js';
import { getMetaData } from './helpers/project-metadata.js';
import {
createStepFile,
deleteStepFromMeta,
getChallenge,
insertStepIntoMeta,
updateStepTitles
} from './utils';
} from './utils.js';
async function deleteStep(stepNum: number): Promise<void> {
if (stepNum < 1) {
@@ -4,15 +4,14 @@
*/
import { readFileSync, writeFileSync } from 'fs';
import path, { join } from 'path';
import { fileURLToPath } from 'url';
import ObjectID from 'bson-objectid';
import { Meta } from './helpers/project-metadata';
import { getArgValue } from './helpers/get-arg-value';
import { join } from 'path';
import { ObjectId } from 'bson';
import { Meta } from './helpers/project-metadata.js';
import { getArgValue } from './helpers/get-arg-value.js';
import {
getDailyJavascriptChallengeTemplate,
getDailyPythonChallengeTemplate
} from './helpers/get-challenge-template';
} from './helpers/get-challenge-template.js';
const numberOfChallengesToCreate = getArgValue(process.argv);
@@ -20,9 +19,6 @@ if (numberOfChallengesToCreate > 10) {
throw new Error('Are you sure you want to create that many challenges?');
}
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const curriculumPath = join(__dirname, '../../curriculum');
const structureBlocksPath = join(curriculumPath, '/structure/blocks');
@@ -58,7 +54,7 @@ for (let i = 0; i < numberOfChallengesToCreate; i++) {
);
}
const challengeId = new ObjectID();
const challengeId = new ObjectId();
const newChallengeNumber = numberOfJsChallenges + 1;
createDailyJsChallenge({
@@ -74,7 +70,7 @@ for (let i = 0; i < numberOfChallengesToCreate; i++) {
}
interface CreateDailyChallengeOptions {
challengeId: ObjectID;
challengeId: ObjectId;
challengeNumber: number;
meta: Meta;
}
@@ -89,7 +85,6 @@ function createDailyJsChallenge({
challengeOrder: [
...meta.challengeOrder,
{
// eslint-disable-next-line @typescript-eslint/no-base-to-string
id: challengeId.toString(),
title: `Challenge ${challengeNumber}: Placeholder`
}
@@ -105,7 +100,7 @@ function createDailyJsChallenge({
const jsChallengePath = join(
jsChallengesPath,
// eslint-disable-next-line @typescript-eslint/no-base-to-string
`${challengeId.toString()}.md`
);
@@ -122,7 +117,6 @@ function createDailyPyChallenge({
challengeOrder: [
...meta.challengeOrder,
{
// eslint-disable-next-line @typescript-eslint/no-base-to-string
id: challengeId.toString(),
title: `Challenge ${challengeNumber}: Placeholder`
}
@@ -138,7 +132,7 @@ function createDailyPyChallenge({
const pyChallengePath = join(
pyChallengesPath,
// eslint-disable-next-line @typescript-eslint/no-base-to-string
`${challengeId.toString()}.md`
);
@@ -1,4 +1,4 @@
import { getArgValue } from './helpers/get-arg-value';
import { createEmptySteps } from './commands';
import { getArgValue } from './helpers/get-arg-value.js';
import { createEmptySteps } from './commands.js';
void createEmptySteps(getArgValue(process.argv));
@@ -2,35 +2,35 @@ import fs from 'fs/promises';
import path from 'path';
import { prompt } from 'inquirer';
import { format } from 'prettier';
import ObjectID from 'bson-objectid';
import { ObjectId } from 'bson';
import {
SuperBlocks,
languageSuperBlocks,
chapterBasedSuperBlocks
} from '../../shared/config/curriculum';
} from '../../shared-dist/config/curriculum.js';
import { BlockLayouts, BlockLabel } from '../../shared/config/blocks';
import { BlockLayouts, BlockLabel } from '../../shared-dist/config/blocks.js';
import {
getContentConfig,
writeBlockStructure,
getSuperblockStructure,
createBlockFolder
} from '../../curriculum/src/file-handler';
import { superBlockToFilename } from '../../curriculum/src/build-curriculum';
import { getBaseMeta } from './helpers/get-base-meta';
import { createIntroMD } from './helpers/create-intro';
createBlockFolder,
getSuperblockStructure
} from '../../curriculum/src/file-handler.js';
import { superBlockToFilename } from '../../curriculum/src/build-curriculum.js';
import { getBaseMeta } from './helpers/get-base-meta.js';
import { createIntroMD } from './helpers/create-intro.js';
import {
createDialogueFile,
createQuizFile,
getAllBlocks,
validateBlockName
} from './utils';
} from './utils.js';
import {
updateSimpleSuperblockStructure,
updateChapterModuleSuperblockStructure
} from './helpers/create-project';
import { getLangFromSuperBlock } from './helpers/get-lang-from-superblock';
} from './helpers/create-project.js';
import { getLangFromSuperBlock } from './helpers/get-lang-from-superblock.js';
const helpCategories = [
'English',
@@ -80,7 +80,7 @@ async function createLanguageBlock(
await updateIntroJson(superBlock, block, title);
const challengeLang = getLangFromSuperBlock(superBlock);
let challengeId: ObjectID;
let challengeId: ObjectId;
if (blockLabel === BlockLabel.quiz) {
challengeId = await createQuizChallenge(
@@ -157,7 +157,7 @@ async function createMetaJson(
block: string,
title: string,
helpCategory: string,
challengeId: ObjectID,
challengeId: ObjectId,
blockLabel?: BlockLabel,
blockLayout?: string
) {
@@ -178,7 +178,6 @@ async function createMetaJson(
newMeta.challengeOrder = [
{
// eslint-disable-next-line @typescript-eslint/no-base-to-string
id: challengeId.toString(),
title: challengeTitle
}
@@ -191,7 +190,7 @@ async function createDialogueChallenge(
superBlock: SuperBlocks,
block: string,
challengeLang: string
): Promise<ObjectID> {
): Promise<ObjectId> {
const { blockContentDir } = getContentConfig('english') as {
blockContentDir: string;
};
@@ -210,7 +209,7 @@ async function createQuizChallenge(
title: string,
questionCount: number,
challengeLang: string
): Promise<ObjectID> {
): Promise<ObjectId> {
return createQuizFile({
projectPath: await createBlockFolder(block),
title: title,
@@ -1,9 +1,9 @@
import ObjectID from 'bson-objectid';
import { getTemplate } from './helpers/get-challenge-template';
import { newChallengePrompts } from './helpers/new-challenge-prompts';
import { getProjectPath } from './helpers/get-project-info';
import { getMetaData, updateMetaData } from './helpers/project-metadata';
import { createChallengeFile } from './utils';
import { ObjectId } from 'bson';
import { getTemplate } from './helpers/get-challenge-template.js';
import { newChallengePrompts } from './helpers/new-challenge-prompts.js';
import { getProjectPath } from './helpers/get-project-info.js';
import { getMetaData, updateMetaData } from './helpers/project-metadata.js';
import { createChallengeFile } from './utils.js';
const createNextChallenge = async () => {
const path = getProjectPath();
@@ -11,14 +11,13 @@ const createNextChallenge = async () => {
const options = await newChallengePrompts();
const template = getTemplate(options.challengeType);
const challengeId = new ObjectID();
const challengeId = new ObjectId();
const challengeText = template({ ...options, challengeId });
createChallengeFile(options.dashedName, challengeText, path);
const meta = getMetaData();
meta.challengeOrder.push({
// eslint-disable-next-line @typescript-eslint/no-base-to-string
id: challengeId.toString(),
title: options.title
});
@@ -1,4 +1,4 @@
import { getLastStep } from './helpers/get-last-step-file-number';
import { insertStep } from './commands';
import { getLastStep } from './helpers/get-last-step-file-number.js';
import { insertStep } from './commands.js';
void insertStep(getLastStep().stepNum + 1);
@@ -1,15 +1,15 @@
import ObjectID from 'bson-objectid';
import { getTemplate } from './helpers/get-challenge-template';
import { newTaskPrompts } from './helpers/new-task-prompts';
import { getProjectPath } from './helpers/get-project-info';
import { getMetaData, updateMetaData } from './helpers/project-metadata';
import { ObjectId } from 'bson';
import { getTemplate } from './helpers/get-challenge-template.js';
import { newTaskPrompts } from './helpers/new-task-prompts.js';
import { getProjectPath } from './helpers/get-project-info.js';
import { getMetaData, updateMetaData } from './helpers/project-metadata.js';
import {
createChallengeFile,
getChallenge,
updateTaskMeta,
updateTaskMarkdownFiles
} from './utils';
import { getInputType } from './helpers/get-input-type';
} from './utils.js';
import { getInputType } from './helpers/get-input-type.js';
const createNextTask = async () => {
const { challengeType } = await newTaskPrompts();
@@ -32,9 +32,9 @@ const createNextTask = async () => {
const path = getProjectPath();
const template = getTemplate(options.challengeType);
const challengeId = new ObjectID();
const challengeId = new ObjectId();
const challengeText = template({ ...options, challengeId });
// eslint-disable-next-line @typescript-eslint/no-base-to-string
const challengeIdString = challengeId.toString();
createChallengeFile(challengeIdString, challengeText, path);
@@ -2,31 +2,31 @@ import fs from 'fs/promises';
import path from 'path';
import { prompt } from 'inquirer';
import { format } from 'prettier';
import ObjectID from 'bson-objectid';
import { ObjectId } from 'bson';
import {
SuperBlocks,
chapterBasedSuperBlocks
} from '../../shared/config/curriculum';
import { BlockLayouts, BlockLabel } from '../../shared/config/blocks';
} from '../../shared-dist/config/curriculum.js';
import { BlockLayouts, BlockLabel } from '../../shared-dist/config/blocks.js';
import {
createBlockFolder,
writeBlockStructure
} from '../../curriculum/src/file-handler';
import { superBlockToFilename } from '../../curriculum/src/build-curriculum';
} from '../../curriculum/src/file-handler.js';
import { superBlockToFilename } from '../../curriculum/src/build-curriculum.js';
import {
createQuizFile,
createStepFile,
validateBlockName,
getAllBlocks
} from './utils';
import { getBaseMeta } from './helpers/get-base-meta';
import { createIntroMD } from './helpers/create-intro';
} from './utils.js';
import { getBaseMeta } from './helpers/get-base-meta.js';
import { createIntroMD } from './helpers/create-intro.js';
import {
ChapterModuleSuperblockStructure,
updateChapterModuleSuperblockStructure,
updateSimpleSuperblockStructure
} from './helpers/create-project';
} from './helpers/create-project.js';
const helpCategories = [
'HTML-CSS',
@@ -181,7 +181,7 @@ async function createMetaJson(
block: string,
title: string,
helpCategory: string,
challengeId: ObjectID,
challengeId: ObjectId,
order?: number,
blockLabel?: string,
blockLayout?: string
@@ -201,13 +201,13 @@ async function createMetaJson(
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> {
async function createFirstChallenge(block: string): Promise<ObjectId> {
// 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 = [
@@ -231,7 +231,7 @@ async function createQuizChallenge(
block: string,
title: string,
questionCount: number
): Promise<ObjectID> {
): Promise<ObjectId> {
return createQuizFile({
projectPath: await createBlockFolder(block),
title: title,
+11 -11
View File
@@ -2,18 +2,18 @@ import fs from 'fs/promises';
import path from 'path';
import { prompt } from 'inquirer';
import { format } from 'prettier';
import ObjectID from 'bson-objectid';
import { ObjectId } from 'bson';
import { SuperBlocks } from '../../shared/config/curriculum';
import { SuperBlocks } from '../../shared-dist/config/curriculum.js';
import {
createBlockFolder,
writeBlockStructure
} from '../../curriculum/src/file-handler';
import { superBlockToFilename } from '../../curriculum/src/build-curriculum';
import { createQuizFile, getAllBlocks, validateBlockName } from './utils';
import { getBaseMeta } from './helpers/get-base-meta';
import { createIntroMD } from './helpers/create-intro';
import { updateSimpleSuperblockStructure } from './helpers/create-project';
} from '../../curriculum/src/file-handler.js';
import { superBlockToFilename } from '../../curriculum/src/build-curriculum.js';
import { createQuizFile, getAllBlocks, validateBlockName } from './utils.js';
import { getBaseMeta } from './helpers/get-base-meta.js';
import { createIntroMD } from './helpers/create-intro.js';
import { updateSimpleSuperblockStructure } from './helpers/create-project.js';
const helpCategories = [
'HTML-CSS',
@@ -93,13 +93,13 @@ async function createMetaJson(
block: string,
title: string,
helpCategory: string,
challengeId: ObjectID
challengeId: ObjectId
) {
const newMeta = getBaseMeta('Quiz');
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: title }];
await writeBlockStructure(block, newMeta);
@@ -110,7 +110,7 @@ async function createQuizChallenge(
block: string,
title: string,
questionCount: number
): Promise<ObjectID> {
): Promise<ObjectId> {
return createQuizFile({
projectPath: await createBlockFolder(block),
title: title,
@@ -6,18 +6,17 @@
* filename. Change the `challengeId` at the bottom to use the dashed name if
* you want that.
*/
import ObjectID from 'bson-objectid';
import { ObjectId } from 'bson';
import {
getBlockStructure,
writeBlockStructure
} from '../../curriculum/src/file-handler';
import { createChallengeFile } from './utils';
import { getProjectPath } from './helpers/get-project-info';
import { getBlock, type Meta } from './helpers/project-metadata';
} from '../../curriculum/src/file-handler.js';
import { createChallengeFile } from './utils.js';
import { getProjectPath } from './helpers/get-project-info.js';
import { getBlock, type Meta } from './helpers/project-metadata.js';
// eslint-disable-next-line @typescript-eslint/no-base-to-string
const challengeId = new ObjectID().toString();
const challengeId = new ObjectId().toString();
/***** Only change code below this line *****/
@@ -1,8 +1,8 @@
import { unlink } from 'fs/promises';
import { prompt } from 'inquirer';
import { getProjectPath } from './helpers/get-project-info';
import { getMetaData, updateMetaData } from './helpers/project-metadata';
import { getFileName } from './helpers/get-file-name';
import { getProjectPath } from './helpers/get-project-info.js';
import { getMetaData, updateMetaData } from './helpers/project-metadata.js';
import { getFileName } from './helpers/get-file-name.js';
const deleteChallenge = async () => {
const path = getProjectPath();
@@ -1,4 +1,4 @@
import { deleteStep } from './commands';
import { getArgValue } from './helpers/get-arg-value';
import { deleteStep } from './commands.js';
import { getArgValue } from './helpers/get-arg-value.js';
void deleteStep(getArgValue(process.argv));
@@ -1,14 +1,14 @@
import { unlink } from 'fs/promises';
import { prompt } from 'inquirer';
import { getProjectPath } from './helpers/get-project-info';
import { getFileName } from './helpers/get-file-name';
import { getProjectPath } from './helpers/get-project-info.js';
import { getFileName } from './helpers/get-file-name.js';
import {
deleteChallengeFromMeta,
updateTaskMarkdownFiles,
updateTaskMeta
} from './utils';
import { isTaskChallenge } from './helpers/task-helpers';
import { getMetaData } from './helpers/project-metadata';
} from './utils.js';
import { isTaskChallenge } from './helpers/task-helpers.js';
import { getMetaData } from './helpers/project-metadata.js';
const deleteTask = async () => {
const path = getProjectPath();
@@ -2,11 +2,11 @@ import { describe, it, expect, beforeEach, vi } from 'vitest';
import {
getSuperblockStructure,
writeSuperblockStructure
} from '../../../curriculum/src/file-handler';
} from '../../../curriculum/src/file-handler.js';
import {
updateChapterModuleSuperblockStructure,
updateSimpleSuperblockStructure
} from './create-project';
} from './create-project.js';
vi.mock('../../../curriculum/src/file-handler');
@@ -3,8 +3,8 @@
import {
getSuperblockStructure,
writeSuperblockStructure
} from '../../../curriculum/src/file-handler';
import { insertInto } from './utils';
} from '../../../curriculum/src/file-handler.js';
import { insertInto } from './utils.js';
export async function updateSimpleSuperblockStructure(
block: string,
@@ -1,5 +1,5 @@
import { describe, it, expect } from 'vitest';
import { getArgValue } from './get-arg-value';
import { getArgValue } from './get-arg-value.js';
describe('getArgValue helper', () => {
it('should throw if there no arguments', () => {
@@ -1,12 +1,11 @@
/* eslint-disable @typescript-eslint/no-base-to-string */
import ObjectID from 'bson-objectid';
import { ObjectId } from 'bson';
const sanitizeTitle = (title: string) => {
return title.includes(':') || title.includes("'") ? `"${title}"` : title;
};
interface ChallengeOptions {
challengeId: ObjectID;
challengeId: ObjectId;
title: string;
dashedName: string;
challengeType: string;
@@ -359,7 +358,7 @@ Do the assignment.
`;
interface DailyCodingChallengeOptions {
challengeId: ObjectID;
challengeId: ObjectId;
challengeNumber: number;
}
@@ -2,7 +2,7 @@ import fs from 'fs';
import { join } from 'path';
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { getFileName } from './get-file-name';
import { getFileName } from './get-file-name.js';
const basePath = join(process.cwd(), '__fixtures__');
const commonPath = join(basePath, 'curriculum', 'challenges');
@@ -1,6 +1,6 @@
import { readdir } from 'fs/promises';
import matter from 'gray-matter';
import { getProjectPath } from './get-project-info';
import { getProjectPath } from './get-project-info.js';
export const getFileName = async (id: string): Promise<string | null> => {
const path = getProjectPath();
@@ -1,6 +1,6 @@
import { prompt } from 'inquirer';
import { ChallengeLang } from '../../../shared/config/curriculum';
import { challengeTypes } from '../../../shared/config/challenge-types';
import { ChallengeLang } from '../../../shared-dist/config/curriculum.js';
import { challengeTypes } from '../../../shared-dist/config/challenge-types.js';
export const getInputType = async (
challengeType: string,
@@ -2,7 +2,7 @@ import {
ChallengeLang,
SuperBlocks,
superBlockToSpeechLang
} from '../../../shared/config/curriculum';
} from '../../../shared-dist/config/curriculum.js';
export const getLangFromSuperBlock = (
superBlock: SuperBlocks
@@ -1,5 +1,5 @@
import { last } from 'lodash';
import { getMetaData } from './project-metadata';
import { getMetaData } from './project-metadata.js';
function getLastStep(): { stepNum: number } {
const meta = getMetaData();
@@ -1,5 +1,5 @@
import { describe, it, expect } from 'vitest';
import { getProjectName, getProjectPath } from './get-project-info';
import { getProjectName, getProjectPath } from './get-project-info.js';
describe('getProjectPath helper', () => {
it('should return the calling dir path', () => {
@@ -1,9 +1,9 @@
import { describe, it, expect } from 'vitest';
import ObjectID from 'bson-objectid';
import { getStepTemplate } from './get-step-template';
import { ObjectId } from 'bson';
import { getStepTemplate } from './get-step-template.js';
const props = {
challengeId: new ObjectID('60d4ebe4801158d1abe1b18f'),
challengeId: new ObjectId('60d4ebe4801158d1abe1b18f'),
challengeSeeds: [
{
contents: '',
@@ -1,6 +1,5 @@
/* eslint-disable @typescript-eslint/no-base-to-string */
import ObjectID from 'bson-objectid';
import { insertErms } from './insert-erms';
import { ObjectId } from 'bson';
import { insertErms } from './insert-erms.js';
// Builds a block
function getCodeBlock(label: string, content?: string) {
@@ -21,7 +20,7 @@ ${content}`
}
type StepOptions = {
challengeId: ObjectID;
challengeId: ObjectId;
challengeSeeds: ChallengeSeed[];
stepNum: number;
challengeType?: number;
@@ -1,5 +1,5 @@
import { describe, it, expect } from 'vitest';
import { insertErms } from './insert-erms';
import { insertErms } from './insert-erms.js';
describe('insertErms helper', () => {
const code = `<h1>Hello World</h1>
@@ -1,6 +1,6 @@
import { prompt } from 'inquirer';
import { challengeTypes } from '../../../shared/config/challenge-types';
import { getLastStep } from './get-last-step-file-number';
import { challengeTypes } from '../../../shared-dist/config/challenge-types.js';
import { getLastStep } from './get-last-step-file-number.js';
export const newChallengePrompts = async (): Promise<{
title: string;
@@ -1,5 +1,5 @@
import { prompt } from 'inquirer';
import { challengeTypes } from '../../../shared/config/challenge-types';
import { challengeTypes } from '../../../shared-dist/config/challenge-types.js';
const taskChallenges = [
challengeTypes.multipleChoice,
@@ -1,7 +1,7 @@
import { join } from 'path';
import { describe, it, expect, vi } from 'vitest';
import { getBlockStructure } from '../../../curriculum/src/file-handler';
import { getMetaData } from './project-metadata';
import { getBlockStructure } from '../../../curriculum/src/file-handler.js';
import { getMetaData } from './project-metadata.js';
vi.mock('../../../curriculum/src/file-handler');
@@ -2,8 +2,8 @@ import path from 'path';
import {
getBlockStructure,
writeBlockStructure
} from '../../../curriculum/src/file-handler';
import { getProjectPath } from './get-project-info';
} from '../../../curriculum/src/file-handler.js';
import { getProjectPath } from './get-project-info.js';
export type Meta = {
name: string;
@@ -1,5 +1,5 @@
import { describe, it, expect } from 'vitest';
import { insertInto } from './utils';
import { insertInto } from './utils.js';
describe('insertInto', () => {
it('should not modify the original array', () => {
@@ -1,10 +1,10 @@
import ObjectID from 'bson-objectid';
import { ObjectId } from 'bson';
import { prompt } from 'inquirer';
import { getTemplate } from './helpers/get-challenge-template';
import { newChallengePrompts } from './helpers/new-challenge-prompts';
import { getProjectPath } from './helpers/get-project-info';
import { getMetaData, updateMetaData } from './helpers/project-metadata';
import { createChallengeFile } from './utils';
import { getTemplate } from './helpers/get-challenge-template.js';
import { newChallengePrompts } from './helpers/new-challenge-prompts.js';
import { getProjectPath } from './helpers/get-project-info.js';
import { getMetaData, updateMetaData } from './helpers/project-metadata.js';
import { createChallengeFile } from './utils.js';
const insertChallenge = async () => {
const path = getProjectPath();
@@ -27,13 +27,12 @@ const insertChallenge = async () => {
);
const template = getTemplate(options.challengeType);
const challengeId = new ObjectID();
const challengeId = new ObjectId();
const challengeText = template({ ...options, challengeId });
createChallengeFile(options.dashedName, challengeText, path);
const meta = getMetaData();
meta.challengeOrder.splice(indexToInsert, 0, {
// eslint-disable-next-line @typescript-eslint/no-base-to-string
id: challengeId.toString(),
title: options.title
});
@@ -1,4 +1,4 @@
import { getArgValue } from './helpers/get-arg-value';
import { insertStep } from './commands';
import { getArgValue } from './helpers/get-arg-value.js';
import { insertStep } from './commands.js';
void insertStep(getArgValue(process.argv));
@@ -1,17 +1,17 @@
import ObjectID from 'bson-objectid';
import { ObjectId } from 'bson';
import { prompt } from 'inquirer';
import { getTemplate } from './helpers/get-challenge-template';
import { newTaskPrompts } from './helpers/new-task-prompts';
import { getProjectPath } from './helpers/get-project-info';
import { getTemplate } from './helpers/get-challenge-template.js';
import { newTaskPrompts } from './helpers/new-task-prompts.js';
import { getProjectPath } from './helpers/get-project-info.js';
import {
createChallengeFile,
getChallenge,
insertChallengeIntoMeta,
updateTaskMeta,
updateTaskMarkdownFiles
} from './utils';
import { getMetaData } from './helpers/project-metadata';
import { getInputType } from './helpers/get-input-type';
} from './utils.js';
import { getMetaData } from './helpers/project-metadata.js';
import { getInputType } from './helpers/get-input-type.js';
const insertChallenge = async () => {
const challenges = getMetaData().challengeOrder;
@@ -45,9 +45,9 @@ const insertChallenge = async () => {
const path = getProjectPath();
const template = getTemplate(challengeType);
const challengeId = new ObjectID();
const challengeId = new ObjectId();
const challengeText = template({ ...options, challengeId });
// eslint-disable-next-line @typescript-eslint/no-base-to-string
const challengeIdString = challengeId.toString();
createChallengeFile(challengeIdString, challengeText, path);
+2 -1
View File
@@ -28,10 +28,11 @@
},
"devDependencies": {
"@freecodecamp/eslint-config": "workspace:*",
"@total-typescript/ts-reset": "^0.6.1",
"@types/glob": "^8.0.1",
"@types/inquirer": "^8.2.5",
"@vitest/ui": "^3.2.4",
"bson-objectid": "2.0.4",
"bson": "^7.0.0",
"eslint": "^9.39.1",
"glob": "^8.1.0",
"gray-matter": "4.0.3",
@@ -1,4 +1,4 @@
import { updateTaskMeta, updateTaskMarkdownFiles } from './utils';
import { updateTaskMeta, updateTaskMarkdownFiles } from './utils.js';
const reorderTasks = async () => {
await updateTaskMeta();
+1
View File
@@ -0,0 +1 @@
import '@total-typescript/ts-reset';
+6 -1
View File
@@ -1,3 +1,8 @@
{
"extends": "../../tsconfig-base.json"
"extends": "../../tsconfig-base.json",
"include": ["**/*.ts"],
"compilerOptions": {
"module": "node16",
"moduleResolution": "node16"
}
}
@@ -1,6 +1,6 @@
import { prompt } from 'inquirer';
import { getMetaData, updateMetaData } from './helpers/project-metadata';
import { getMetaData, updateMetaData } from './helpers/project-metadata.js';
const updateChallengeOrder = async () => {
const oldChallengeOrder = getMetaData().challengeOrder;
@@ -1,3 +1,3 @@
import { updateStepTitles } from './utils';
import { updateStepTitles } from './utils.js';
updateStepTitles();
+8 -9
View File
@@ -1,7 +1,7 @@
import fs from 'fs';
import path, { join } from 'path';
import matter from 'gray-matter';
import ObjectID from 'bson-objectid';
import { ObjectId } from 'bson';
import { vi, describe, it, expect, afterEach } from 'vitest';
vi.mock('fs', () => {
@@ -22,9 +22,9 @@ vi.mock('gray-matter', () => {
};
});
vi.mock('bson-objectid', () => {
vi.mock('bson', () => {
return {
default: vi.fn(() => ({ toString: () => mockChallengeId }))
ObjectId: vi.fn(() => ({ toString: () => mockChallengeId }))
};
});
@@ -46,15 +46,15 @@ vi.mock('./helpers/project-metadata', () => {
});
const mockChallengeId = '60d35cf3fe32df2ce8e31b03';
import { getStepTemplate } from './helpers/get-step-template';
import { getStepTemplate } from './helpers/get-step-template.js';
import {
createChallengeFile,
createStepFile,
insertStepIntoMeta,
updateStepTitles,
validateBlockName
} from './utils';
import { updateMetaData } from './helpers/project-metadata';
} from './utils.js';
import { updateMetaData } from './helpers/project-metadata.js';
const block = 'utils-project';
const projectPath = join(
@@ -81,9 +81,8 @@ describe('Challenge utils helper scripts', () => {
challengeType: 0
});
// eslint-disable-next-line @typescript-eslint/no-base-to-string
expect(step.toString()).toEqual(mockChallengeId);
expect(ObjectID).toHaveBeenCalledTimes(1);
expect(ObjectId).toHaveBeenCalledTimes(1);
// Internal tasks
// - Should generate a template for the step that is being created
@@ -144,7 +143,7 @@ describe('Challenge utils helper scripts', () => {
await insertStepIntoMeta({
stepNum: 3,
stepId: new ObjectID(mockChallengeId)
stepId: new ObjectId(mockChallengeId)
});
expect(updateMetaData).toHaveBeenCalledWith({
+20 -22
View File
@@ -1,20 +1,20 @@
import fs from 'fs';
import path from 'path';
import ObjectID from 'bson-objectid';
import { ObjectId } from 'bson';
import matter from 'gray-matter';
import { uniq } from 'lodash';
import { challengeTypes } from '../../shared/config/challenge-types';
import { parseCurriculumStructure } from '../../curriculum/src/build-curriculum';
import { parseMDSync } from '../challenge-parser/parser';
import { getMetaData, updateMetaData } from './helpers/project-metadata';
import { getProjectPath } from './helpers/get-project-info';
import { ChallengeSeed, getStepTemplate } from './helpers/get-step-template';
import { challengeTypes } from '../../shared-dist/config/challenge-types.js';
import { parseCurriculumStructure } from '../../curriculum/src/build-curriculum.js';
import { parseMDSync } from '../challenge-parser/parser/index.js';
import { getMetaData, updateMetaData } from './helpers/project-metadata.js';
import { getProjectPath } from './helpers/get-project-info.js';
import { ChallengeSeed, getStepTemplate } from './helpers/get-step-template.js';
import {
isTaskChallenge,
getTaskNumberFromTitle
} from './helpers/task-helpers';
import { getTemplate } from './helpers/get-challenge-template';
} from './helpers/task-helpers.js';
import { getTemplate } from './helpers/get-challenge-template.js';
interface Options {
stepNum: number;
@@ -53,8 +53,8 @@ const createStepFile = ({
challengeSeeds = [],
isFirstChallenge = false,
challengeLang
}: Options): ObjectID => {
const challengeId = new ObjectID();
}: Options): ObjectId => {
const challengeId = new ObjectId();
const template = getStepTemplate({
challengeId,
@@ -65,7 +65,6 @@ const createStepFile = ({
challengeLang
});
// eslint-disable-next-line @typescript-eslint/no-base-to-string
fs.writeFileSync(`${projectPath}${challengeId.toString()}.md`, template);
return challengeId;
@@ -85,8 +84,8 @@ const createQuizFile = ({
dashedName,
questionCount,
challengeLang
}: QuizOptions): ObjectID => {
const challengeId = new ObjectID();
}: QuizOptions): ObjectId => {
const challengeId = new ObjectId();
const challengeType = challengeTypes.quiz.toString();
const template = getTemplate(challengeType);
@@ -98,7 +97,7 @@ const createQuizFile = ({
questionCount,
challengeLang
});
// eslint-disable-next-line @typescript-eslint/no-base-to-string
fs.writeFileSync(`${projectPath}${challengeId.toString()}.md`, quizText);
return challengeId;
};
@@ -109,8 +108,8 @@ const createDialogueFile = ({
}: {
projectPath: string;
challengeLang: string;
}): ObjectID => {
const challengeId = new ObjectID();
}): ObjectId => {
const challengeId = new ObjectId();
const challengeType = challengeTypes.dialogue.toString();
const template = getTemplate(challengeType);
@@ -121,19 +120,19 @@ const createDialogueFile = ({
dashedName: 'dialogue-1-im-tom',
challengeLang
});
// eslint-disable-next-line @typescript-eslint/no-base-to-string
fs.writeFileSync(`${projectPath}${challengeId.toString()}.md`, dialogueText);
return challengeId;
};
interface InsertOptions {
stepNum: number;
stepId: ObjectID;
stepId: ObjectId;
}
interface InsertChallengeOptions {
index: number;
id: ObjectID;
id: ObjectId;
title: string;
}
@@ -145,7 +144,6 @@ async function insertChallengeIntoMeta({
const existingMeta = getMetaData();
const challengeOrder = [...existingMeta.challengeOrder];
// eslint-disable-next-line @typescript-eslint/no-base-to-string
challengeOrder.splice(index, 0, { id: id.toString(), title });
await updateMetaData({ ...existingMeta, challengeOrder });
}
@@ -153,7 +151,7 @@ async function insertChallengeIntoMeta({
async function insertStepIntoMeta({ stepNum, stepId }: InsertOptions) {
const existingMeta = getMetaData();
const oldOrder = [...existingMeta.challengeOrder];
// eslint-disable-next-line @typescript-eslint/no-base-to-string
oldOrder.splice(stepNum - 1, 0, { id: stepId.toString(), title: '' });
// rename all the files in challengeOrder
const challengeOrder = oldOrder.map(({ id }, index) => ({