* 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
		
			
				
	
	
		
			106 lines
		
	
	
		
			2.8 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			106 lines
		
	
	
		
			2.8 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import fs from 'fs';
 | |
| import path from 'path';
 | |
| import ObjectID from 'bson-objectid';
 | |
| import * as matter from 'gray-matter';
 | |
| 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';
 | |
| 
 | |
| interface Options {
 | |
|   stepNum: number;
 | |
|   projectPath?: string;
 | |
|   challengeSeeds?: Record<string, ChallengeSeed>;
 | |
| }
 | |
| 
 | |
| const createStepFile = ({
 | |
|   stepNum,
 | |
|   projectPath = getProjectPath(),
 | |
|   challengeSeeds = {}
 | |
| }: Options) => {
 | |
|   const challengeId = new ObjectID();
 | |
| 
 | |
|   const template = getStepTemplate({
 | |
|     challengeId,
 | |
|     challengeSeeds,
 | |
|     stepNum
 | |
|   });
 | |
| 
 | |
|   fs.writeFileSync(`${projectPath}${challengeId.toString()}.md`, template);
 | |
| 
 | |
|   return challengeId;
 | |
| };
 | |
| 
 | |
| interface InsertOptions {
 | |
|   stepNum: number;
 | |
|   stepId: ObjectID;
 | |
| }
 | |
| 
 | |
| 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}`
 | |
|   ]);
 | |
| 
 | |
|   updateMetaData({ ...existingMeta, challengeOrder });
 | |
| }
 | |
| 
 | |
| 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}`
 | |
|   ]);
 | |
| 
 | |
|   updateMetaData({ ...existingMeta, challengeOrder });
 | |
| }
 | |
| 
 | |
| const updateStepTitles = () => {
 | |
|   const meta = getMetaData();
 | |
| 
 | |
|   const fileNames: string[] = [];
 | |
|   fs.readdirSync(getProjectPath()).forEach(fileName => {
 | |
|     if (path.extname(fileName).toLowerCase() === '.md') {
 | |
|       fileNames.push(fileName);
 | |
|     }
 | |
|   });
 | |
| 
 | |
|   fileNames.forEach(fileName => {
 | |
|     const filePath = `${getProjectPath()}${fileName}`;
 | |
|     const frontMatter = matter.read(filePath);
 | |
|     const newStepNum =
 | |
|       meta.challengeOrder.findIndex(elem => elem[0] === frontMatter.data.id) +
 | |
|       1;
 | |
|     const title = `Step ${newStepNum}`;
 | |
|     const dashedName = `step-${newStepNum}`;
 | |
|     const newData = {
 | |
|       ...frontMatter.data,
 | |
|       title,
 | |
|       dashedName
 | |
|     };
 | |
|     fs.writeFileSync(filePath, matter.stringify(frontMatter.content, newData));
 | |
|   });
 | |
| };
 | |
| 
 | |
| const getChallengeSeeds = (
 | |
|   challengeFilePath: string
 | |
| ): Record<string, ChallengeSeed> => {
 | |
|   // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access
 | |
|   return parseMDSync(challengeFilePath).challengeFiles;
 | |
| };
 | |
| 
 | |
| export {
 | |
|   createStepFile,
 | |
|   updateStepTitles,
 | |
|   getChallengeSeeds,
 | |
|   insertStepIntoMeta,
 | |
|   deleteStepFromMeta
 | |
| };
 |