feat: update challenge helpers to handle id filenames (#44769)

* refactor: light tweaks for readability

* refactor: simplify metadata functions

* fix: most tests

* test: fix utils tests

* test: simplify mocks

* WIP: update get-last-step-file-content

* feat: finish create-next-step

* fix: type error

* test: provide mock meta.json for test

* refactor: get meta path from project path

* refactor: get project name from path

* refactor: simplify getProjectMetaPath further

Also removes some excessive mocking

* refactor: remove more mocks, always clear .env

* feat: update create-next-step

* feat: update create-empty steps

Also refactors slightly, so it's easier to insert steps into the meta

* docs: update challenge-helper-script docs

* feat: create-step-between

* refactor: allow metadata parse errors to propagate

* fix: convert reorderSteps to renameSteps

* refactor: create-step-between -> insert-step

* feat: update delete-step

* refactor: consolidate commands into commands.ts

* refactor: clean up and consolidation

* refactor: more cleanup

* fix: make cli args consistent

Everything accepts a single integer and nothing else

* refactor: renameSteps -> updateStepTitles

* docs: update with the names and args

* feat: add step validating meta + files are synced
This commit is contained in:
Oliver Eyton-Williams
2022-03-02 16:12:20 +01:00
committed by GitHub
parent 16e7cdedb1
commit 339c6713d2
35 changed files with 535 additions and 724 deletions

View File

@@ -2,120 +2,91 @@ import fs from 'fs';
import path from 'path';
import ObjectID from 'bson-objectid';
import * as matter from 'gray-matter';
import { getMetaData } from '../challenge-helper-scripts/helpers/get-project-path-metadata';
import { parseMDSync } from '../challenge-parser/parser';
import { getProjectMetaPath } from './helpers/get-project-meta-path';
import { getProjectPath } from './helpers/get-project-path';
import { getMetaData, updateMetaData } from './helpers/project-metadata';
import { getProjectPath } from './helpers/get-project-info';
import { ChallengeSeed, getStepTemplate } from './helpers/get-step-template';
import { padWithLeadingZeros } from './helpers/pad-with-leading-zeros';
interface Options {
projectPath: string;
stepNum: number;
projectPath?: string;
challengeSeeds?: Record<string, ChallengeSeed>;
stepBetween?: boolean;
}
const createStepFile = ({
projectPath,
stepNum,
challengeSeeds = {},
stepBetween = false
projectPath = getProjectPath(),
challengeSeeds = {}
}: Options) => {
const challengeId = new ObjectID();
let finalStepNum = padWithLeadingZeros(stepNum);
finalStepNum += stepBetween ? 'a' : '';
const template = getStepTemplate({
challengeId,
challengeSeeds,
stepBetween,
stepNum
});
fs.writeFileSync(`${projectPath}step-${finalStepNum}.md`, template);
fs.writeFileSync(`${projectPath}${challengeId.toString()}.md`, template);
return challengeId;
};
const reorderSteps = () => {
const projectPath = getProjectPath();
interface InsertOptions {
stepNum: number;
stepId: ObjectID;
}
const projectName = process.env.CALLING_DIR
? process.env.CALLING_DIR.split(path.sep).slice(-1).toString()
: process.cwd().split(path.sep).slice(-1).toString();
function insertStepIntoMeta({ stepNum, stepId }: InsertOptions) {
const existingMeta = getMetaData();
const oldOrder = [...existingMeta.challengeOrder];
oldOrder.splice(stepNum - 1, 0, [stepId.toString()]);
// rename all the files in challengeOrder
const challengeOrder = oldOrder.map(([id], index) => [
id,
`Step ${index + 1}`
]);
const curriculumPath = process.env.CALLING_DIR
? ''
: path.join(__dirname, '../');
updateMetaData({ ...existingMeta, challengeOrder });
}
const projectMetaPath = getProjectMetaPath(curriculumPath, projectName);
function deleteStepFromMeta({ stepNum }: { stepNum: number }) {
const existingMeta = getMetaData();
const oldOrder = [...existingMeta.challengeOrder];
oldOrder.splice(stepNum - 1, 1);
// rename all the files in challengeOrder
const challengeOrder = oldOrder.map(([id], index) => [
id,
`Step ${index + 1}`
]);
const parsedData = getMetaData(projectMetaPath);
updateMetaData({ ...existingMeta, challengeOrder });
}
let foundFinal = false;
const filesArr = [];
fs.readdirSync(projectPath).forEach(fileName => {
const updateStepTitles = () => {
const meta = getMetaData();
const fileNames: string[] = [];
fs.readdirSync(getProjectPath()).forEach(fileName => {
if (path.extname(fileName).toLowerCase() === '.md') {
if (!fileName.endsWith('final.md')) {
filesArr.push(fileName);
} else {
foundFinal = true;
}
fileNames.push(fileName);
}
});
if (foundFinal) {
filesArr.push('final.md');
}
const filesToReorder = filesArr.map((fileName, i) => {
const newStepNum = i + 1;
const newFileName =
fileName !== 'final.md'
? `step-${padWithLeadingZeros(newStepNum)}.md`
: 'final.md';
return {
oldFileName: fileName,
newFileName,
newStepNum
};
});
const challengeOrder: string[][] = [];
filesToReorder.forEach(({ oldFileName, newFileName, newStepNum }) => {
fs.renameSync(
`${projectPath}${oldFileName}`,
`${projectPath}${newFileName}.tmp`
);
const filePath = `${projectPath}${newFileName}.tmp`;
fileNames.forEach(fileName => {
const filePath = `${getProjectPath()}${fileName}`;
const frontMatter = matter.read(filePath);
const challengeID =
(frontMatter.data.id as string) || new ObjectID().toString();
const title =
newFileName === 'final.md' ? 'Final Prototype' : `Step ${newStepNum}`;
const newStepNum =
meta.challengeOrder.findIndex(elem => elem[0] === frontMatter.data.id) +
1;
const title = `Step ${newStepNum}`;
const dashedName = `step-${newStepNum}`;
challengeOrder.push(['' + challengeID, title]);
const newData = {
...frontMatter.data,
id: challengeID,
title,
dashedName
};
fs.writeFileSync(filePath, matter.stringify(frontMatter.content, newData));
});
filesToReorder.forEach(({ newFileName }) => {
fs.renameSync(
`${projectPath}${newFileName}.tmp`,
`${projectPath}${newFileName}`
);
});
const newMeta = { ...parsedData, challengeOrder };
fs.writeFileSync(projectMetaPath, JSON.stringify(newMeta, null, 2));
};
const getChallengeSeeds = (
@@ -125,4 +96,10 @@ const getChallengeSeeds = (
return parseMDSync(challengeFilePath).challengeFiles;
};
export { createStepFile, reorderSteps, getChallengeSeeds };
export {
createStepFile,
updateStepTitles,
getChallengeSeeds,
insertStepIntoMeta,
deleteStepFromMeta
};