parent
190c75cebe
commit
dd7c0ec003
54 changed files with 2026 additions and 812 deletions
@ -0,0 +1,163 @@ |
|||||||
|
const fs = require('fs'); |
||||||
|
const path = require('path'); |
||||||
|
|
||||||
|
const CONTENT_DIR = path.join(__dirname, '../content'); |
||||||
|
// Directory containing the best-practices |
||||||
|
const BEST_PRACTICE_CONTENT_DIR = path.join(__dirname, '../src/best-practices'); |
||||||
|
const bestPracticeId = process.argv[2]; |
||||||
|
|
||||||
|
const allowedBestPracticeId = fs.readdirSync(BEST_PRACTICE_CONTENT_DIR); |
||||||
|
if (!bestPracticeId) { |
||||||
|
console.error('bestPractice is required'); |
||||||
|
process.exit(1); |
||||||
|
} |
||||||
|
|
||||||
|
if (!allowedBestPracticeId.includes(bestPracticeId)) { |
||||||
|
console.error(`Invalid best practice key ${bestPracticeId}`); |
||||||
|
console.error(`Allowed keys are ${allowedBestPracticeId.join(', ')}`); |
||||||
|
process.exit(1); |
||||||
|
} |
||||||
|
|
||||||
|
// Directory holding the best parctice content files |
||||||
|
const bestPracticeDirName = fs |
||||||
|
.readdirSync(BEST_PRACTICE_CONTENT_DIR) |
||||||
|
.find((dirName) => dirName.replace(/\d+-/, '') === bestPracticeId); |
||||||
|
|
||||||
|
if (!bestPracticeDirName) { |
||||||
|
console.error('Best practice directory not found'); |
||||||
|
process.exit(1); |
||||||
|
} |
||||||
|
|
||||||
|
const bestPracticeDirPath = path.join(BEST_PRACTICE_CONTENT_DIR, bestPracticeDirName); |
||||||
|
const bestPracticeContentDirPath = path.join( |
||||||
|
BEST_PRACTICE_CONTENT_DIR, |
||||||
|
bestPracticeDirName, |
||||||
|
'content' |
||||||
|
); |
||||||
|
|
||||||
|
// If best practice content already exists do not proceed as it would override the files |
||||||
|
if (fs.existsSync(bestPracticeContentDirPath)) { |
||||||
|
console.error(`Best Practice content already exists @ ${bestPracticeContentDirPath}`); |
||||||
|
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 bestPractice = require(path.join(__dirname, `../public/jsons/best-practices/${bestPracticeId}`)); |
||||||
|
const controls = bestPractice.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(bestPracticeContentDirPath, '') // 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. /best-practices/frontend-performance/content/internet |
||||||
|
// ———> /best-practices/frontend-performance/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(bestPracticeContentDirPath, dirTree, dirSortOrders); |
||||||
|
console.log('Created best practice content directory structure'); |
@ -0,0 +1,28 @@ |
|||||||
|
## CLI Tools |
||||||
|
> A bunch of CLI scripts to make the development easier |
||||||
|
|
||||||
|
## `roadmap-links.cjs` |
||||||
|
|
||||||
|
Generates a list of all the resources links in any roadmap file. |
||||||
|
|
||||||
|
## `compress-jsons.cjs` |
||||||
|
|
||||||
|
Compresses all the JSON files in the `public/jsons` folder |
||||||
|
|
||||||
|
## `roadmap-content.cjs` |
||||||
|
|
||||||
|
This command is used to create the content folders and files for the interactivity of the roadmap. You can use the below command to generate the roadmap skeletons inside a roadmap directory: |
||||||
|
|
||||||
|
```bash |
||||||
|
npm run roadmap-content [frontend|backend|devops|...] |
||||||
|
``` |
||||||
|
|
||||||
|
For the content skeleton to be generated, we should have proper grouping, and the group names in the project files. You can follow the steps listed below in order to add the meta information to the roadmap. |
||||||
|
|
||||||
|
- Remove all the groups from the roadmaps through the project editor. Select all and press `cmd+shift+g` |
||||||
|
- Identify the boxes that should be clickable and group them together with `cmd+shift+g` |
||||||
|
- Assign the name to the groups. |
||||||
|
- Group names have the format of `[sort]-[slug]` e.g. `100-internet`. Each group name should start with a number starting from 100 which helps with sorting of the directories and the files. Groups at the same level have the sequential sorting information. |
||||||
|
- Each groups children have a separate group and have the name similar to `[sort]-[parent-slug]:[child-slug]` where sort refers to the sorting of the `child-slug` and not the parent. Also parent-slug does not need to have the sorting information as a part of slug e.g. if parent was `100-internet` the children would be `100-internet:how-does-the-internet-work`, `101-internet:what-is-http`, `102-internet:browsers`. |
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1 @@ |
|||||||
|
# Minimize iframe count |
@ -0,0 +1 @@ |
|||||||
|
# Minify css |
@ -0,0 +1 @@ |
|||||||
|
# Make css files non blocking |
@ -0,0 +1 @@ |
|||||||
|
# Inline critical css |
@ -0,0 +1 @@ |
|||||||
|
# Avoid inline css |
@ -0,0 +1 @@ |
|||||||
|
# Analyse stylesheets complexity |
@ -0,0 +1 @@ |
|||||||
|
# Compress your images |
@ -0,0 +1 @@ |
|||||||
|
# Choose image format approprietly |
@ -0,0 +1 @@ |
|||||||
|
# Minify your javascript |
@ -0,0 +1 @@ |
|||||||
|
# Use non blocking javascript |
@ -0,0 +1 @@ |
|||||||
|
# Use https on your website |
@ -0,0 +1 @@ |
|||||||
|
# Page weight below 1500 |
@ -0,0 +1 @@ |
|||||||
|
# Page load time below 3s |
@ -0,0 +1 @@ |
|||||||
|
# Keep ttfb less 1 3s |
@ -0,0 +1 @@ |
|||||||
|
# Minimize http requests |
@ -0,0 +1 @@ |
|||||||
|
# Use same protocol |
@ -0,0 +1 @@ |
|||||||
|
# Avoid 404 files |
@ -0,0 +1 @@ |
|||||||
|
# Use http cache headers |
@ -0,0 +1 @@ |
|||||||
|
# Enable compression |
@ -0,0 +1 @@ |
|||||||
|
# Minify html |
@ -0,0 +1 @@ |
|||||||
|
# Use cdn |
@ -0,0 +1 @@ |
|||||||
|
# Prefer vector images |
@ -0,0 +1 @@ |
|||||||
|
# Set width height images |
@ -0,0 +1 @@ |
|||||||
|
# Avoid base64 images |
@ -0,0 +1 @@ |
|||||||
|
# Load offscreen images lazily |
@ -0,0 +1 @@ |
|||||||
|
# Serve exact size images |
@ -0,0 +1 @@ |
|||||||
|
# Avoid multiple inline js snippets |
@ -0,0 +1 @@ |
|||||||
|
# Keep dependencies up to date |
@ -0,0 +1 @@ |
|||||||
|
# Analyze js for perf issues |
@ -0,0 +1 @@ |
|||||||
|
# Use service workers for caching |
@ -0,0 +1 @@ |
|||||||
|
# Cookie size less 4096 bytes |
@ -0,0 +1 @@ |
|||||||
|
# Keep cookie count below 20 |
@ -0,0 +1 @@ |
|||||||
|
# Pre load urls where possible |
@ -0,0 +1 @@ |
|||||||
|
# Concatenate css single file |
@ -0,0 +1 @@ |
|||||||
|
# Remove unused css |
@ -0,0 +1 @@ |
|||||||
|
# Use woff2 font format |
@ -0,0 +1 @@ |
|||||||
|
# Use preconnect to load fonts |
@ -0,0 +1 @@ |
|||||||
|
# Keep web font under 300k |
@ -0,0 +1 @@ |
|||||||
|
# Prevent flash text |
@ -0,0 +1 @@ |
|||||||
|
# Check dependency size |
@ -0,0 +1 @@ |
|||||||
|
# Page speed insights |
@ -0,0 +1 @@ |
|||||||
|
# Lighthouse |
@ -0,0 +1 @@ |
|||||||
|
# Web page test |
@ -0,0 +1 @@ |
|||||||
|
# Chrome dev tools |
@ -0,0 +1 @@ |
|||||||
|
# Bundlephobia |
@ -0,0 +1 @@ |
|||||||
|
# Squoosh ap |
@ -0,0 +1 @@ |
|||||||
|
# Framework guides |
@ -0,0 +1 @@ |
|||||||
|
# Recommended guides |
@ -0,0 +1 @@ |
|||||||
|
# |
@ -1 +0,0 @@ |
|||||||
# Sup |
|
Loading…
Reference in new issue