chore: move to tools/challenge-helper-scripts
This commit is contained in:
committed by
Mrugesh Mohapatra
parent
258bb3a10f
commit
85925b5a30
85
tools/challenge-helper-scripts/README.md
Normal file
85
tools/challenge-helper-scripts/README.md
Normal file
@@ -0,0 +1,85 @@
|
||||
# freeCodeCamp Project-based Curriculum Tools
|
||||
|
||||
This folder contains tools to help facilitate the creation and maintenance of the freeCodeCamp project-based curriculum.
|
||||
|
||||
## [create-next-step.js](create-next-step.js)
|
||||
A one-off script that will automatically add the next step based on the last step numbered as `part-xxx.md` where `xxx` represents the 3-digit step number of the last step. The challenge seed code will use the previous step's challenge seed code with the editable region markers (ERMs) removed.
|
||||
|
||||
**Note:** This script also runs [reorder-steps.js](reorder-steps.js).
|
||||
|
||||
### How to run script:
|
||||
1. Change to the directory of the project.
|
||||
2. Run the following npm command:
|
||||
```bash
|
||||
npm run create-next-step
|
||||
```
|
||||
|
||||
## [create-empty-steps.js](create-empty-steps.js)
|
||||
A one-off script that automatically adds a specified number of steps at a specific starting step number. The challenge seed code for all steps created will be empty.
|
||||
|
||||
**Note:** This script also runs [reorder-steps.js](reorder-steps.js).
|
||||
|
||||
### How to run script:
|
||||
1. Change to the directory of the project.
|
||||
2. Run the following npm command:
|
||||
```bash
|
||||
npm run create-empty-steps start=X num=Y # where X is the starting step number and Y is the number of steps to create.
|
||||
```
|
||||
|
||||
## [create-step-between.js](create-step-between.js)
|
||||
A one-off script that automatically adds a new step between two existing consecutive steps. The challenge seed code will use the existing starting step's challenge seed code with the editable region markers (ERMs) removed.
|
||||
|
||||
**Note:** This script also runs [reorder-steps.js](reorder-steps.js).
|
||||
|
||||
### How to run script:
|
||||
1. Change to the directory of the project.
|
||||
2. Run the following npm command:
|
||||
```bash
|
||||
npm run create-step-between start=X end=Y # where X is the starting step number and Y is the following step number.
|
||||
```
|
||||
## [reorder-steps.js](reorder-steps.js)
|
||||
A one-off script that automatically reorders the step files in a project's markdown files based on the filename. It also updates the `challengeOrder` property array in the project's `meta.json` with the new order of the steps.
|
||||
|
||||
### Working Example
|
||||
Let's say you start with the following project structure:
|
||||
|
||||
```bash
|
||||
part-1.md
|
||||
part-2.md
|
||||
part-3.md
|
||||
part-4.md
|
||||
part-5.md
|
||||
part-6.md
|
||||
```
|
||||
At some point you decide you need to delete `part-2.md`, because that step is no longer needed. Also, you decide to break down `part-4.md` into three steps instead of just one.
|
||||
|
||||
To accomplish the this restructure, you would need to delete `part-2.md` and then add a `part-4a.md` and a `part=5b.md`. The new folder structure would look like the following:
|
||||
```bash
|
||||
part-001.md
|
||||
part-003.md
|
||||
part-004.md
|
||||
part-004a.md
|
||||
part-004b.md
|
||||
part-005.md
|
||||
part-006.md
|
||||
```
|
||||
You now need the file names to be `part-1.md` through `part-7.md`, because you removed one but gained two more for a net difference of one file. Also, the frontmatter of each file below a deleted step or added step will need to be modified by making the `title` key value match the new step number. For example, after renaming `part-3.md` to `part-2.md`, you would need to change `part-2.md`'s title from `Part 03` to `Part 02`.
|
||||
|
||||
See below for the actual project folder changes needed:
|
||||
```bash
|
||||
part-001.md
|
||||
part-003.md renamed to part-002.md and title changes to "Part 2"
|
||||
part-004.md renames to part-003.md and title changes to "Part 3"
|
||||
part-004a.md renames to part-004.md and title changes to "Part 4"
|
||||
part-004b.md renames to part-005.md and title changes to "Part 5"
|
||||
part-005.md renames to part-006.md and title changes to "Part 6"
|
||||
part-006.md renames to part-007.md and title changes to "Part 7"
|
||||
```
|
||||
Along with the above changes, the `challengeOrder` key in the project's `meta.json` file needs to reflect the new step order. This is needed because each step below a step deletion and/or step addtion changes the `title` assoiciated with each of the affected step's challenge `id`.
|
||||
|
||||
### Solution Steps
|
||||
1. Change to the directory of the project.
|
||||
2. Run the following npm command:
|
||||
```bash
|
||||
npm run reorder-steps
|
||||
```
|
48
tools/challenge-helper-scripts/create-empty-steps.js
Normal file
48
tools/challenge-helper-scripts/create-empty-steps.js
Normal file
@@ -0,0 +1,48 @@
|
||||
const {
|
||||
reorderSteps,
|
||||
createStepFile,
|
||||
getExistingStepNums,
|
||||
getProjectPath
|
||||
} = require('./utils');
|
||||
|
||||
const anyStepExists = (steps, stepsToFind) =>
|
||||
stepsToFind.some(num => steps.includes(num));
|
||||
|
||||
const projectPath = getProjectPath();
|
||||
const argValuePairs = process.argv.slice(2);
|
||||
|
||||
const args = argValuePairs.reduce((argsObj, arg) => {
|
||||
const [argument, value] = arg.replace(/\s/g, '').split('=');
|
||||
if (!argument || !value) {
|
||||
throw `Invalid argument/value specified: ${arg}`;
|
||||
}
|
||||
return { ...argsObj, [argument]: value };
|
||||
}, {});
|
||||
|
||||
let { num, start } = args;
|
||||
if (!start) {
|
||||
throw `No steps created. start arg val must be specified.`;
|
||||
}
|
||||
if (!num) {
|
||||
throw `No steps created. num arg val must be specified.`;
|
||||
}
|
||||
num = parseInt(num, 10);
|
||||
const stepStart = parseInt(start, 10);
|
||||
|
||||
if (num > 0 && num < 20) {
|
||||
throw `No steps created. num arg val must be greater than 1 and less than or equal to 20.`;
|
||||
}
|
||||
|
||||
const maxStepNum = stepStart + num - 1;
|
||||
|
||||
const existingSteps = getExistingStepNums(projectPath);
|
||||
|
||||
if (anyStepExists(existingSteps, [start, maxStepNum])) {
|
||||
throw `Step not created. At least one of the steps specified between ${start} and ${maxStepNum} already exists.`;
|
||||
}
|
||||
|
||||
for (let stepNum = stepStart; stepNum <= maxStepNum; stepNum++) {
|
||||
createStepFile({ stepNum, projectPath });
|
||||
}
|
||||
console.log(`Sucessfully added ${num} steps`);
|
||||
reorderSteps();
|
41
tools/challenge-helper-scripts/create-next-step.js
Normal file
41
tools/challenge-helper-scripts/create-next-step.js
Normal file
@@ -0,0 +1,41 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const {
|
||||
reorderSteps,
|
||||
createStepFile,
|
||||
getChallengeSeed,
|
||||
getProjectPath
|
||||
} = require('./utils');
|
||||
|
||||
const getLastStepFileContent = () => {
|
||||
const filesArr = [];
|
||||
fs.readdirSync(projectPath).forEach(fileName => {
|
||||
if (
|
||||
path.extname(fileName).toLowerCase() === '.md' &&
|
||||
!fileName.endsWith('final.md')
|
||||
) {
|
||||
filesArr.push(fileName);
|
||||
}
|
||||
});
|
||||
|
||||
const fileName = filesArr[filesArr.length - 1];
|
||||
let lastStepFileNum = fileName.split('.')[0].split('-')[1];
|
||||
lastStepFileNum = parseInt(lastStepFileNum, 10);
|
||||
if (filesArr.length !== lastStepFileNum) {
|
||||
throw `Error: The last file step is ${lastStepFileNum} and there are ${filesArr.length} files.`;
|
||||
}
|
||||
|
||||
return {
|
||||
nextStepNum: lastStepFileNum + 1,
|
||||
challengeSeed: getChallengeSeed(projectPath + fileName)
|
||||
};
|
||||
};
|
||||
|
||||
const projectPath = getProjectPath();
|
||||
|
||||
const { nextStepNum, challengeSeed } = getLastStepFileContent();
|
||||
|
||||
createStepFile({ stepNum: nextStepNum, projectPath, challengeSeed });
|
||||
console.log(`Sucessfully added step #${nextStepNum}`);
|
||||
reorderSteps();
|
53
tools/challenge-helper-scripts/create-step-between.js
Normal file
53
tools/challenge-helper-scripts/create-step-between.js
Normal file
@@ -0,0 +1,53 @@
|
||||
const {
|
||||
reorderSteps,
|
||||
createStepFile,
|
||||
getChallengeSeed,
|
||||
padWithLeadingZeros,
|
||||
getExistingStepNums,
|
||||
getProjectPath
|
||||
} = require('./utils');
|
||||
|
||||
const allStepsExist = (steps, stepsToFind) =>
|
||||
stepsToFind.every(num => steps.includes(num));
|
||||
|
||||
const projectPath = getProjectPath();
|
||||
const argValuePairs = process.argv.slice(2);
|
||||
|
||||
const args = argValuePairs.reduce((argsObj, arg) => {
|
||||
const [argument, value] = arg.replace(/\s/g, '').split('=');
|
||||
if (!argument || !value) {
|
||||
throw `Invalid argument/value specified: ${arg}`;
|
||||
}
|
||||
return { ...argsObj, [argument]: value };
|
||||
}, {});
|
||||
|
||||
let { start, end } = args;
|
||||
start = parseInt(start, 10);
|
||||
end = parseInt(end, 10);
|
||||
|
||||
if (
|
||||
!Number.isInteger(start) ||
|
||||
!Number.isInteger(end) ||
|
||||
start < 1 ||
|
||||
start !== end - 1
|
||||
) {
|
||||
throw 'Step not created. Steps specified must be' +
|
||||
' consecutive numbers and start step must be greater than 0.';
|
||||
}
|
||||
|
||||
const existingSteps = getExistingStepNums(projectPath);
|
||||
if (!allStepsExist(existingSteps, [start, end])) {
|
||||
throw 'Step not created. At least one of the steps specified does not exist.';
|
||||
}
|
||||
|
||||
const challengeSeed = getChallengeSeed(
|
||||
`${projectPath}part-${padWithLeadingZeros(start)}.md`
|
||||
);
|
||||
createStepFile({
|
||||
stepNum: start,
|
||||
projectPath,
|
||||
challengeSeed,
|
||||
stepBetween: true
|
||||
});
|
||||
console.log(`Sucessfully added step between step #${start} and step #${end}`);
|
||||
reorderSteps();
|
150
tools/challenge-helper-scripts/package-lock.json
generated
Normal file
150
tools/challenge-helper-scripts/package-lock.json
generated
Normal file
@@ -0,0 +1,150 @@
|
||||
{
|
||||
"name": "@freecodecamp/curriculum-helper-scripts",
|
||||
"version": "0.0.0-next.1",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"argparse": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
||||
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"sprintf-js": "~1.0.2"
|
||||
}
|
||||
},
|
||||
"bson-objectid": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/bson-objectid/-/bson-objectid-1.3.1.tgz",
|
||||
"integrity": "sha512-eQBNQXsisEAXlwiSy8zRNZdW2xDBJaEVkTPbodYR9hGxxtE548Qq7ilYOd8WAQ86xF7NRUdiWSQ1pa/TkKiE2A==",
|
||||
"dev": true
|
||||
},
|
||||
"cross-env": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.2.tgz",
|
||||
"integrity": "sha512-KZP/bMEOJEDCkDQAyRhu3RL2ZO/SUVrxQVI0G3YEQ+OLbRA3c6zgixe8Mq8a/z7+HKlNEjo8oiLUs8iRijY2Rw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cross-spawn": "^7.0.1"
|
||||
}
|
||||
},
|
||||
"cross-spawn": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
||||
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"path-key": "^3.1.0",
|
||||
"shebang-command": "^2.0.0",
|
||||
"which": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"esprima": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
|
||||
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
|
||||
"dev": true
|
||||
},
|
||||
"extend-shallow": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
|
||||
"integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-extendable": "^0.1.0"
|
||||
}
|
||||
},
|
||||
"gray-matter": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.2.tgz",
|
||||
"integrity": "sha512-7hB/+LxrOjq/dd8APlK0r24uL/67w7SkYnfwhNFwg/VDIGWGmduTDYf3WNstLW2fbbmRwrDGCVSJ2isuf2+4Hw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"js-yaml": "^3.11.0",
|
||||
"kind-of": "^6.0.2",
|
||||
"section-matter": "^1.0.0",
|
||||
"strip-bom-string": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"is-extendable": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
|
||||
"integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
|
||||
"dev": true
|
||||
},
|
||||
"isexe": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||
"integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
|
||||
"dev": true
|
||||
},
|
||||
"js-yaml": {
|
||||
"version": "3.14.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz",
|
||||
"integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"argparse": "^1.0.7",
|
||||
"esprima": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"kind-of": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
|
||||
"integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
|
||||
"dev": true
|
||||
},
|
||||
"path-key": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
||||
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
|
||||
"dev": true
|
||||
},
|
||||
"section-matter": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz",
|
||||
"integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"extend-shallow": "^2.0.1",
|
||||
"kind-of": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"shebang-command": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"shebang-regex": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"shebang-regex": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
|
||||
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
|
||||
"dev": true
|
||||
},
|
||||
"sprintf-js": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
||||
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
|
||||
"dev": true
|
||||
},
|
||||
"strip-bom-string": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz",
|
||||
"integrity": "sha1-5SEekiQ2n7uB1jOi8ABE3IztrZI=",
|
||||
"dev": true
|
||||
},
|
||||
"which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"isexe": "^2.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
33
tools/challenge-helper-scripts/package.json
Normal file
33
tools/challenge-helper-scripts/package.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "@freecodecamp/curriculum-helper-scripts",
|
||||
"description": "freeCodeCamp's project-based curriculum scripts",
|
||||
"license": "(BSD-3-Clause AND CC-BY-SA-4.0)",
|
||||
"author": "freeCodeCamp <team@freecodecamp.org>",
|
||||
"homepage": "https://github.com/freeCodeCamp/freeCodeCamp#readme",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/freeCodeCamp/freeCodeCamp.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/freeCodeCamp/freeCodeCamp/issues"
|
||||
},
|
||||
"version": "0.0.0-next.1",
|
||||
"main": "utils.js",
|
||||
"scripts": {
|
||||
"test": "mocha --delay --reporter progress --bail"
|
||||
},
|
||||
"devDependencies": {
|
||||
"bson-objectid": "^1.3.1",
|
||||
"cross-env": "^7.0.2",
|
||||
"gray-matter": "^4.0.2"
|
||||
},
|
||||
"keywords": [
|
||||
"challenges",
|
||||
"curriculum",
|
||||
"freecodecamp",
|
||||
"javascript"
|
||||
],
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
3
tools/challenge-helper-scripts/reorder-steps.js
Normal file
3
tools/challenge-helper-scripts/reorder-steps.js
Normal file
@@ -0,0 +1,3 @@
|
||||
const { reorderSteps } = require('./utils');
|
||||
|
||||
reorderSteps();
|
216
tools/challenge-helper-scripts/utils.js
Normal file
216
tools/challenge-helper-scripts/utils.js
Normal file
@@ -0,0 +1,216 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const matter = require('gray-matter');
|
||||
const ObjectID = require('bson-objectid');
|
||||
|
||||
const padWithLeadingZeros = originalNum => {
|
||||
/* always want file step numbers 3 digits */
|
||||
const maxDigits = 3;
|
||||
let paddedNum = '' + originalNum;
|
||||
while (paddedNum.length < maxDigits) {
|
||||
paddedNum = '0' + paddedNum;
|
||||
}
|
||||
return paddedNum;
|
||||
};
|
||||
|
||||
const removeErms = seedCode => {
|
||||
const lines = seedCode.split('\n');
|
||||
return lines
|
||||
.filter(line => !line.includes('--fcc-editable-region--'))
|
||||
.join('\n');
|
||||
};
|
||||
|
||||
const createStepFile = ({
|
||||
projectPath,
|
||||
stepNum,
|
||||
challengeSeed = '',
|
||||
stepBetween = false
|
||||
}) => {
|
||||
if (challengeSeed) {
|
||||
challengeSeed = removeErms(challengeSeed);
|
||||
}
|
||||
|
||||
const descStepNum = stepBetween ? stepNum + 1 : stepNum;
|
||||
const stepDescription = `${
|
||||
stepBetween ? 'new' : ''
|
||||
} step ${descStepNum} instructions`;
|
||||
const challengeSeedSection = `<section id='challengeSeed'>
|
||||
|
||||
${challengeSeed.trim()}
|
||||
|
||||
</section>`;
|
||||
|
||||
const template = `---
|
||||
id: ${ObjectID.generate()}
|
||||
title: Part ${stepNum}
|
||||
challengeType: 0
|
||||
isHidden: true
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
|
||||
${stepDescription}
|
||||
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
<section id='tests'>
|
||||
|
||||
\`\`\`yml
|
||||
tests:
|
||||
- text: Test 1
|
||||
testString: ''
|
||||
|
||||
\`\`\`
|
||||
|
||||
</section>
|
||||
|
||||
## Challenge Seed
|
||||
${challengeSeedSection}
|
||||
`;
|
||||
|
||||
let finalStepNum = padWithLeadingZeros(stepNum);
|
||||
finalStepNum += stepBetween ? 'a' : '';
|
||||
fs.writeFileSync(`${projectPath}part-${finalStepNum}.md`, template);
|
||||
};
|
||||
|
||||
const reorderSteps = () => {
|
||||
const projectPath = getProjectPath();
|
||||
|
||||
const projectName = process.env.CALLING_DIR
|
||||
? process.env.CALLING_DIR.split(path.sep)
|
||||
.slice(-1)
|
||||
.toString()
|
||||
: process
|
||||
.cwd()
|
||||
.split(path.sep)
|
||||
.slice(-1)
|
||||
.toString();
|
||||
|
||||
const curriculumPath = process.env.CALLING_DIR
|
||||
? ''
|
||||
: path.join(__dirname, '../');
|
||||
|
||||
const projectMetaPath = path.resolve(
|
||||
curriculumPath,
|
||||
'challenges',
|
||||
'_meta',
|
||||
projectName,
|
||||
'meta.json'
|
||||
);
|
||||
|
||||
let metaData;
|
||||
try {
|
||||
metaData = fs.readFileSync(projectMetaPath);
|
||||
} catch (err) {
|
||||
throw `No _meta.json file exists at ${projectMetaPath}`;
|
||||
}
|
||||
|
||||
let foundFinal = false;
|
||||
const filesArr = [];
|
||||
fs.readdirSync(projectPath).forEach(fileName => {
|
||||
if (path.extname(fileName).toLowerCase() === '.md') {
|
||||
if (!fileName.endsWith('final.md')) {
|
||||
filesArr.push(fileName);
|
||||
} else {
|
||||
foundFinal = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (foundFinal) {
|
||||
filesArr.push('final.md');
|
||||
}
|
||||
|
||||
const filesToReorder = filesArr.map((fileName, i) => {
|
||||
const newStepNum = i + 1;
|
||||
const newFileName =
|
||||
fileName !== 'final.md'
|
||||
? `part-${padWithLeadingZeros(newStepNum)}.md`
|
||||
: 'final.md';
|
||||
return {
|
||||
oldFileName: fileName,
|
||||
newFileName,
|
||||
newStepNum
|
||||
};
|
||||
});
|
||||
|
||||
const challengeOrder = [];
|
||||
const parsedData = JSON.parse(metaData);
|
||||
|
||||
filesToReorder.forEach(({ oldFileName, newFileName, newStepNum }) => {
|
||||
fs.renameSync(
|
||||
`${projectPath}${oldFileName}`,
|
||||
`${projectPath}${newFileName}.tmp`
|
||||
);
|
||||
const filePath = `${projectPath}${newFileName}.tmp`;
|
||||
const frontMatter = matter.read(filePath);
|
||||
const challengeID = frontMatter.data.id || ObjectID.generate();
|
||||
const title =
|
||||
newFileName === 'final.md' ? 'Final Prototype' : `Part ${newStepNum}`;
|
||||
challengeOrder.push(['' + challengeID, title]);
|
||||
const newData = {
|
||||
...frontMatter.data,
|
||||
id: challengeID,
|
||||
title
|
||||
};
|
||||
fs.writeFileSync(filePath, frontMatter.stringify(newData));
|
||||
});
|
||||
|
||||
filesToReorder.forEach(({ newFileName }) => {
|
||||
fs.renameSync(
|
||||
`${projectPath}${newFileName}.tmp`,
|
||||
`${projectPath}${newFileName}`
|
||||
);
|
||||
});
|
||||
|
||||
const newMeta = { ...parsedData, challengeOrder };
|
||||
fs.writeFileSync(projectMetaPath, JSON.stringify(newMeta, null, 2));
|
||||
console.log('Reordered steps');
|
||||
};
|
||||
|
||||
const getChallengeSeed = challengeFilePath => {
|
||||
const fileContent = fs.readFileSync(challengeFilePath, 'utf8');
|
||||
const matchedSection = fileContent
|
||||
.toString()
|
||||
.match(/<section id='challengeSeed'>(?<challengeSeed>[\s\S]+)<\/section>/);
|
||||
let finalChallengeSeed = '';
|
||||
if (matchedSection) {
|
||||
let {
|
||||
groups: { challengeSeed }
|
||||
} = matchedSection;
|
||||
finalChallengeSeed = challengeSeed ? challengeSeed : '';
|
||||
}
|
||||
return finalChallengeSeed;
|
||||
};
|
||||
|
||||
const getExistingStepNums = projectPath => {
|
||||
return fs.readdirSync(projectPath).reduce((stepNums, fileName) => {
|
||||
if (
|
||||
path.extname(fileName).toLowerCase() === '.md' &&
|
||||
!fileName.endsWith('final.md')
|
||||
) {
|
||||
let stepNum = fileName.split('.')[0].split('-')[1];
|
||||
if (!/^\d{3}$/.test(stepNum)) {
|
||||
throw `Step not created. File ${fileName} has a step number containing non-digits.` +
|
||||
' Please run reorder-steps script first.';
|
||||
}
|
||||
stepNum = parseInt(stepNum, 10);
|
||||
stepNums.push(stepNum);
|
||||
}
|
||||
return stepNums;
|
||||
}, []);
|
||||
};
|
||||
|
||||
const getProjectPath = () =>
|
||||
(process.env.CALLING_DIR || process.cwd()) + path.sep;
|
||||
|
||||
module.exports = {
|
||||
createStepFile,
|
||||
getChallengeSeed,
|
||||
padWithLeadingZeros,
|
||||
reorderSteps,
|
||||
getExistingStepNums,
|
||||
getProjectPath
|
||||
};
|
Reference in New Issue
Block a user