computer-scienceangular-roadmapbackend-roadmapblockchain-roadmapdba-roadmapdeveloper-roadmapdevops-roadmapfrontend-roadmapgo-roadmaphactoberfestjava-roadmapjavascript-roadmapnodejs-roadmappython-roadmapqa-roadmapreact-roadmaproadmapstudy-planvue-roadmapweb3-roadmap
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
167 lines
5.0 KiB
167 lines
5.0 KiB
const fs = require('fs'); |
|
const path = require('path'); |
|
|
|
const CONTENT_DIR = path.join(__dirname, '../content'); |
|
// Directory containing the roadmaps |
|
const ROADMAP_CONTENT_DIR = path.join(__dirname, '../src/data/roadmaps'); |
|
const roadmapId = process.argv[2]; |
|
|
|
const allowedRoadmapIds = fs.readdirSync(ROADMAP_CONTENT_DIR); |
|
if (!roadmapId) { |
|
console.error('roadmapId is required'); |
|
process.exit(1); |
|
} |
|
|
|
if (!allowedRoadmapIds.includes(roadmapId)) { |
|
console.error(`Invalid roadmap key ${roadmapId}`); |
|
console.error(`Allowed keys are ${allowedRoadmapIds.join(', ')}`); |
|
process.exit(1); |
|
} |
|
|
|
// Directory holding the roadmap content files |
|
const roadmapDirName = fs |
|
.readdirSync(ROADMAP_CONTENT_DIR) |
|
.find((dirName) => dirName.replace(/\d+-/, '') === roadmapId); |
|
|
|
if (!roadmapDirName) { |
|
console.error('Roadmap directory not found'); |
|
process.exit(1); |
|
} |
|
|
|
const roadmapDirPath = path.join(ROADMAP_CONTENT_DIR, roadmapDirName); |
|
const roadmapContentDirPath = path.join( |
|
ROADMAP_CONTENT_DIR, |
|
roadmapDirName, |
|
'content' |
|
); |
|
|
|
// If roadmap content already exists do not proceed as it would override the files |
|
if (fs.existsSync(roadmapContentDirPath)) { |
|
console.error(`Roadmap content already exists @ ${roadmapContentDirPath}`); |
|
process.exit(1); |
|
} |
|
|
|
function prepareDirTree(control, dirTree, dirSortOrders) { |
|
// Directories are only created for groups |
|
if (control.typeID !== '__group__') { |
|
return; |
|
} |
|
|
|
// e.g. 104-testing-your-apps:other-options |
|
const controlName = control?.properties?.controlName || ''; |
|
// e.g. 104 |
|
const sortOrder = controlName.match(/^\d+/)?.[0]; |
|
|
|
// No directory for a group without control name |
|
if (!controlName || !sortOrder) { |
|
return; |
|
} |
|
|
|
// e.g. testing-your-apps:other-options |
|
const controlNameWithoutSortOrder = controlName.replace(/^\d+-/, ''); |
|
// e.g. ['testing-your-apps', 'other-options'] |
|
const dirParts = controlNameWithoutSortOrder.split(':'); |
|
|
|
// Nest the dir path in the dirTree |
|
let currDirTree = dirTree; |
|
dirParts.forEach((dirPart) => { |
|
currDirTree[dirPart] = currDirTree[dirPart] || {}; |
|
currDirTree = currDirTree[dirPart]; |
|
}); |
|
|
|
dirSortOrders[controlNameWithoutSortOrder] = Number(sortOrder); |
|
|
|
const childrenControls = control.children.controls.control; |
|
// No more children |
|
if (childrenControls.length) { |
|
childrenControls.forEach((childControl) => { |
|
prepareDirTree(childControl, dirTree, dirSortOrders); |
|
}); |
|
} |
|
|
|
return { dirTree, dirSortOrders }; |
|
} |
|
|
|
const roadmap = require(path.join( |
|
__dirname, |
|
`../src/data/roadmaps/${roadmapId}/${roadmapId}` |
|
)); |
|
|
|
const controls = roadmap.mockup.controls.control; |
|
|
|
// Prepare the dir tree that we will be creating and also calculate the sort orders |
|
const dirTree = {}; |
|
const dirSortOrders = {}; |
|
|
|
controls.forEach((control) => { |
|
prepareDirTree(control, dirTree, dirSortOrders); |
|
}); |
|
|
|
/** |
|
* @param parentDir Parent directory in which directory is to be created |
|
* @param dirTree Nested dir tree to be created |
|
* @param sortOrders Mapping from groupName to sort order |
|
* @param filePaths The mapping from groupName to file path |
|
*/ |
|
function createDirTree(parentDir, dirTree, sortOrders, filePaths = {}) { |
|
const childrenDirNames = Object.keys(dirTree); |
|
const hasChildren = childrenDirNames.length !== 0; |
|
|
|
// @todo write test for this, yolo for now |
|
const groupName = parentDir |
|
.replace(roadmapContentDirPath, '') // Remove base dir path |
|
.replace(/(^\/)|(\/$)/g, '') // Remove trailing slashes |
|
.replace(/(^\d+?-)/g, '') // Remove sorting information |
|
.replaceAll('/', ':') // Replace slashes with `:` |
|
.replace(/:\d+-/, ':'); |
|
|
|
const humanizedGroupName = groupName |
|
.split(':') |
|
.pop() |
|
?.replaceAll('-', ' ') |
|
.replace(/^\w/, ($0) => $0.toUpperCase()); |
|
|
|
const sortOrder = sortOrders[groupName] || ''; |
|
|
|
// Attach sorting information to dirname |
|
// e.g. /roadmaps/100-frontend/content/internet |
|
// ———> /roadmaps/100-frontend/content/103-internet |
|
if (sortOrder) { |
|
parentDir = parentDir.replace(/(.+?)([^\/]+)?$/, `$1${sortOrder}-$2`); |
|
} |
|
|
|
// If no children, create a file for this under the parent directory |
|
if (!hasChildren) { |
|
let fileName = `${parentDir}.md`; |
|
fs.writeFileSync(fileName, `# ${humanizedGroupName}`); |
|
|
|
filePaths[groupName || 'home'] = fileName.replace(CONTENT_DIR, ''); |
|
return filePaths; |
|
} |
|
|
|
// There *are* children, so create the parent as a directory |
|
// and create `index.md` as the content file for this |
|
fs.mkdirSync(parentDir); |
|
|
|
let readmeFilePath = path.join(parentDir, 'index.md'); |
|
fs.writeFileSync(readmeFilePath, `# ${humanizedGroupName}`); |
|
|
|
filePaths[groupName || 'home'] = readmeFilePath.replace(CONTENT_DIR, ''); |
|
|
|
// For each of the directory names, create a |
|
// directory inside the given directory |
|
childrenDirNames.forEach((dirName) => { |
|
createDirTree( |
|
path.join(parentDir, dirName), |
|
dirTree[dirName], |
|
dirSortOrders, |
|
filePaths |
|
); |
|
}); |
|
|
|
return filePaths; |
|
} |
|
|
|
// Create directories and get back the paths for created directories |
|
createDirTree(roadmapContentDirPath, dirTree, dirSortOrders); |
|
console.log('Created roadmap content directory structure');
|
|
|