forked from kamranahmedse/developer-roadmap
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcontent-skeleton.ts
255 lines (216 loc) · 7.49 KB
/
content-skeleton.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
import fs from 'fs';
import path from 'path';
const CONTENT_DIR = path.join(__dirname, '../content');
// Directory containing the roadmaps
const ROADMAP_CONTENT_DIR = path.join(__dirname, '../content/roadmaps');
const roadmapKey = process.argv[2];
type ControlType = {
ID: string;
typeID: string;
zOrder: string;
w: string;
h: string;
measuredW: string;
measuredH: string;
x: string;
y: string;
properties: {
controlName: string;
};
children: {
controls: {
control: ControlType[];
};
};
};
type RoadmapType = {
mockup: {
controls: {
control: ControlType[];
};
};
};
const roadmaps: Record<string, RoadmapType> = {
frontend: require('../public/project/frontend.json'),
backend: require('../public/project/backend.json'),
devops: require('../public/project/devops.json'),
android: require('../public/project/android.json'),
golang: require('../public/project/golang.json'),
java: require('../public/project/java.json'),
python: require('../public/project/python.json'),
react: require('../public/project/react.json'),
vue: require('../public/project/vue.json'),
angular: require('../public/project/angular.json'),
blockchain: require('../public/project/blockchain.json'),
javascript: require('../public/project/javascript.json'),
nodejs: require('../public/project/nodejs.json'),
qa: require('../public/project/qa.json'),
'design-system': require('../public/project/design-system.json'),
'software-architect': require('../public/project/software-architect.json'),
'aspnet-core': require('../public/project/aspnet-core.json'),
'flutter': require('../public/project/flutter.json'),
'computer-science': require('../public/project/computer-science.json'),
'graphql': require('../public/project/graphql.json'),
};
if (!roadmapKey || !roadmaps[roadmapKey]) {
console.error(`Invalid roadmap key ${roadmapKey}`);
console.error(`Allowed keys are ${Object.keys(roadmaps).join(', ')}`);
process.exit(1);
}
// Directory holding the roadmap content files
const roadmapDirName = fs
.readdirSync(ROADMAP_CONTENT_DIR)
.find((dirName: string) => dirName.replace(/\d+-/, '') === roadmapKey);
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);
}
// Type representing the nested dir tree
// {
// frontend: {
// internet: {
// dns-how-does-it-work: {}
// what-is-domain-name: {}
// },
// html: {},
// css: {
// making-layouts: {}
// },
// }
// }
type DirTreeType = {
[dirName: string]: DirTreeType;
};
// Hashmap containing the dir key to sort order mapping
// {
// "frontend": 100,
// "frontend:internet": 200,
// "frontend:internet:dns-how-does-it-work": 100
// }
type DirSortOrdersType = Record<string, number>;
function prepareDirTree(
control: ControlType,
dirTree: DirTreeType,
dirSortOrders: DirSortOrdersType
) {
// 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 = roadmaps[roadmapKey];
const controls = roadmap.mockup.controls.control;
// Prepare the dir tree that we will be creating and also calculate the sort orders
const dirTree: DirTreeType = {};
const dirSortOrders: DirSortOrdersType = {};
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: string,
dirTree: DirTreeType,
sortOrders: DirSortOrdersType,
filePaths: Record<string, string> = {}
) {
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 `readme.md` as the content file for this
fs.mkdirSync(parentDir);
let readmeFilePath = path.join(parentDir, 'readme.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
const filePaths = createDirTree(roadmapContentDirPath, dirTree, dirSortOrders);
const contentPathsFilePath = path.join(roadmapDirPath, 'content-paths.json');
fs.writeFileSync(contentPathsFilePath, JSON.stringify(filePaths, null, 2));
const roadmapMetaFilePath = path.join(roadmapDirPath, 'meta.json');
const roadmapMeta = require(roadmapMetaFilePath);
// Put the content paths file path in the roadmap meta
roadmapMeta.contentPathsFilePath = contentPathsFilePath.replace(
roadmapDirPath,
'.'
);
fs.writeFileSync(roadmapMetaFilePath, JSON.stringify(roadmapMeta, null, 2));