parent
a76b9d9ac0
commit
5d164198d4
8 changed files with 1 additions and 541 deletions
@ -1,120 +0,0 @@ |
||||
const fs = require('fs'); |
||||
const path = require('path'); |
||||
|
||||
// 1 - Renames each readme.md to index.md |
||||
// e.g. |
||||
// before => roadmaps/frontend/content/internet/readme.md |
||||
// after => roadmaps/frontend/content/internet/index.md |
||||
// |
||||
// 2 - Replaces the resource tags with short codes |
||||
// e.g. |
||||
// <ResourceGroupTitle>Free Content</ResourceGroupTitle> |
||||
// <BadgeLink colorScheme='yellow' badgeText='Read' href='https://www.w3schools.com/css/'>W3Schools — Learn CSS</BadgeLink> |
||||
// |
||||
// {% resources %} |
||||
// {% Blog "https://www.w3schools.com/css/", "W3Schools — Learn CSS" %} |
||||
// {% endresources %} |
||||
// |
||||
// 3 - Removes the index.md file from within the content dir i.e. to avoid `/frontend` permalink for `/frontend/index.md` |
||||
// Because we have the `/frontend` permalink serving the actual roadmap and not any content |
||||
const roadmapsDir = path.join(__dirname, '../src/roadmaps'); |
||||
const roadmapDirs = fs.readdirSync(roadmapsDir); |
||||
|
||||
roadmapDirs.forEach((roadmapDirName) => { |
||||
const roadmapDirPath = path.join(roadmapsDir, roadmapDirName); |
||||
const contentDirPath = path.join(roadmapDirPath, 'content'); |
||||
|
||||
console.log(`[Start] == Migrating ${roadmapDirName}`); |
||||
|
||||
if (!fs.existsSync(contentDirPath)) { |
||||
console.log(`Content dir not found ${roadmapDirName}/content`); |
||||
return; |
||||
} |
||||
|
||||
function handleContentDir(parentDirPath) { |
||||
const dirChildrenNames = fs.readdirSync(parentDirPath); |
||||
|
||||
dirChildrenNames.forEach((dirChildName) => { |
||||
let dirChildPath = path.join(parentDirPath, dirChildName); |
||||
|
||||
// If directory, handle the children for it |
||||
if (fs.lstatSync(dirChildPath).isDirectory()) { |
||||
handleContentDir(dirChildPath); |
||||
} |
||||
|
||||
////////////////////////////////////////////////////////// |
||||
// 1 - Rename directories to remove the numbers |
||||
////////////////////////////////////////////////////////// |
||||
// let newDirChildPath = path.join( |
||||
// path.dirname(dirChildPath), |
||||
// path.basename(dirChildPath).replace(/^\d+-/, '') |
||||
// ); |
||||
// fs.renameSync(dirChildPath, dirChildPath); |
||||
|
||||
////////////////////////////////////////////////////////// |
||||
// 1 - Rename readme.md to index.md |
||||
////////////////////////////////////////////////////////// |
||||
if (dirChildPath.endsWith('readme.md')) { |
||||
const newFilePath = path.join(path.dirname(dirChildPath), `index.md`); |
||||
|
||||
fs.renameSync(dirChildPath, newFilePath); |
||||
dirChildPath = newFilePath; |
||||
} |
||||
|
||||
////////////////////////////////////////////////////////// |
||||
// 2 - Replace the resource tags with short codes |
||||
////////////////////////////////////////////////////////// |
||||
if (fs.lstatSync(dirChildPath).isFile()) { |
||||
const fileContent = fs.readFileSync(dirChildPath, 'utf-8'); |
||||
|
||||
let resourceLinks = [...fileContent.matchAll(/<BadgeLink.+<\/BadgeLink>/g)].map(([fullMatch]) => { |
||||
// const resourceType = fullMatch.match(/badgeText=["'](.+?)["']/)[1]; |
||||
const link = fullMatch.match(/href=["'](.+?)["']/)[1]; |
||||
const text = fullMatch.match(/>([^<]+)<\/BadgeLink>$/)[1]; |
||||
|
||||
return `- [${text.replaceAll(/['"]/g, '')}](${link})`; |
||||
}); |
||||
|
||||
////////////////////////////////////////////////////////////////////// |
||||
// Replace the dedicated roadmap tag with the short code |
||||
////////////////////////////////////////////////////////////////////// |
||||
// prettier-ignore |
||||
const dedicatedRegex = /<DedicatedRoadmap\s*href=['"](.+?)['"]\s*title=['"](.+?)['"]\s*description=['"].+?['"]\s*\/>/; |
||||
const dedicatedMatches = fileContent.match(dedicatedRegex); |
||||
|
||||
if (dedicatedMatches) { |
||||
const [, href, title] = dedicatedMatches; |
||||
|
||||
resourceLinks = [`- [Visit Dedicated ${title}](${href})`, ...resourceLinks]; |
||||
} |
||||
|
||||
resourceLinks = ['Visit the following resources to learn more:\n', ...resourceLinks]; |
||||
resourceLinks = resourceLinks.join('\n'); |
||||
|
||||
let newFileContent = fileContent.replace( |
||||
/<ResourceGroupTitle>([^<\/BadgeLink>]|\S|\s)+<\/BadgeLink>/, |
||||
resourceLinks |
||||
); |
||||
|
||||
// In case if the resources were not wrapped in <ResourceGroupTitle> |
||||
newFileContent = newFileContent.replace( |
||||
/<BadgeLink([^<\/BadgeLink>]|\S|\s)+<\/BadgeLink>/, |
||||
resourceLinks |
||||
); |
||||
|
||||
fs.writeFileSync(dirChildPath, newFileContent); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
handleContentDir(contentDirPath); |
||||
|
||||
// 3 - Removes the index.md file from within the content dir i.e. to avoid `/frontend` permalink for `/frontend/index.md` |
||||
// Because we have the `/frontend` permalink serving the actual roadmap and not any content |
||||
const contentRootFile = path.join(contentDirPath, '/index.md'); |
||||
if (fs.existsSync(contentRootFile)) { |
||||
fs.rmSync(contentRootFile); |
||||
} |
||||
|
||||
console.log(` == Migrated ${roadmapDirName}`); |
||||
}); |
@ -1,83 +0,0 @@ |
||||
const fs = require('fs'); |
||||
const path = require('path'); |
||||
const yaml = require('json-to-pretty-yaml'); |
||||
|
||||
const contentDirPath = path.join(__dirname, './developer-roadmap/content'); |
||||
const guides = require('./developer-roadmap/content/guides.json'); |
||||
const authors = require('./developer-roadmap/content/authors.json'); |
||||
|
||||
const guideImagesDirPath = path.join(__dirname, './developer-roadmap/public/guides'); |
||||
const newGuideImagesDirPath = path.join(__dirname, '../public/guides'); |
||||
|
||||
// Remove the guide images directory |
||||
if (fs.existsSync(newGuideImagesDirPath)) { |
||||
fs.rmSync(newGuideImagesDirPath, { recursive: true }); |
||||
} |
||||
|
||||
fs.cpSync(guideImagesDirPath, newGuideImagesDirPath, { recursive: true }); |
||||
|
||||
// Remove the old guides directory |
||||
const newGuidesDirPath = path.join(__dirname, '../src/guides'); |
||||
if (fs.existsSync(newGuidesDirPath)) { |
||||
fs.rmSync(newGuidesDirPath, { recursive: true }); |
||||
} |
||||
|
||||
fs.mkdirSync(newGuidesDirPath); |
||||
|
||||
guides.forEach((guide) => { |
||||
const { id: guideId } = guide; |
||||
|
||||
const originalGuidePath = path.join(contentDirPath, 'guides', `${guideId}.md`); |
||||
const newGuidePath = path.join(__dirname, `../src/guides/${guideId}.md`); |
||||
|
||||
const guideWithoutFrontmatter = fs.readFileSync(originalGuidePath, 'utf8'); |
||||
fs.copyFileSync(originalGuidePath, newGuidePath); |
||||
|
||||
const guideAuthor = authors.find((author) => author.username === guide.authorUsername); |
||||
|
||||
const guideFrontMatter = yaml |
||||
.stringify({ |
||||
title: guide.title, |
||||
description: guide.description, |
||||
author: { |
||||
name: guideAuthor.name, |
||||
url: `https://twitter.com/${guideAuthor.twitter}`, |
||||
imageUrl: `${guideAuthor.picture}`, |
||||
}, |
||||
seo: { |
||||
title: `${guide.title} - roadmap.sh`, |
||||
description: guide.description, |
||||
}, |
||||
isNew: guide.isNew, |
||||
type: guide.type, |
||||
date: guide.createdAt.replace(/T.*/, ''), |
||||
sitemap: { |
||||
priority: 0.7, |
||||
changefreq: 'weekly', |
||||
}, |
||||
tags: ['guide', `${guide.type}-guide`, `guide-sitemap`], |
||||
}) |
||||
.replace(/date: "(.+?)"/, 'date: $1'); |
||||
|
||||
const guideWithUpdatedUrls = guideWithoutFrontmatter |
||||
.replace(/\[\!\[\]\((.+?\.png)\)\]\((.+?\.png)\)/g, '[![]($1)]($2)') |
||||
.replace(/\[\!\[\]\((.+?\.svg)\)\]\((.+?\.svg)\)/g, '[![]($1)]($2)') |
||||
.replace(/\/http/g, 'http') |
||||
.replace(/]\(\/guides\/(.+?)\.png\)/g, '](/guides/$1.png)') |
||||
.replace(/<iframe/g, '<iframe class="w-full aspect-video mb-5"') |
||||
.replace(/<iframe(.+?)\s?\/>/g, '<iframe$1></iframe>'); |
||||
|
||||
const guideWithFrontmatter = `---\n${guideFrontMatter}---\n\n${guideWithUpdatedUrls}`; |
||||
|
||||
console.log(`Writing guide ${guideId} to disk`); |
||||
fs.writeFileSync(newGuidePath, guideWithFrontmatter); |
||||
}); |
||||
|
||||
const oldAuthorAssetsPath = path.join(__dirname, 'developer-roadmap/public/authors'); |
||||
const newAuthorAssetsPath = path.join(__dirname, '../public/authors'); |
||||
|
||||
if (fs.existsSync(newAuthorAssetsPath)) { |
||||
fs.rmSync(newAuthorAssetsPath, { recursive: true }); |
||||
} |
||||
|
||||
fs.cpSync(oldAuthorAssetsPath, newAuthorAssetsPath, { recursive: true }); |
@ -1,116 +0,0 @@ |
||||
module.exports = { |
||||
angular: { |
||||
dimensions: { |
||||
width: 968, |
||||
height: 2277.8, |
||||
}, |
||||
}, |
||||
'aspnet-core': { |
||||
dimensions: { |
||||
width: 968, |
||||
height: 2773.45, |
||||
}, |
||||
}, |
||||
backend: { |
||||
dimensions: { |
||||
width: 968, |
||||
height: 2840.4, |
||||
}, |
||||
}, |
||||
blockchain: { |
||||
dimensions: { |
||||
width: 968, |
||||
height: 2173.87, |
||||
}, |
||||
}, |
||||
'computer-science': { |
||||
dimensions: { |
||||
width: 968, |
||||
height: 3009.05, |
||||
}, |
||||
}, |
||||
'design-system': { |
||||
dimensions: { |
||||
width: 968, |
||||
height: 2309.7, |
||||
}, |
||||
}, |
||||
devops: { |
||||
dimensions: { |
||||
width: 968, |
||||
height: 2527.46, |
||||
}, |
||||
}, |
||||
flutter: { |
||||
dimensions: { |
||||
width: 968, |
||||
height: 2042.2, |
||||
}, |
||||
}, |
||||
frontend: { |
||||
dimensions: { |
||||
width: 968, |
||||
height: 2734.48, |
||||
}, |
||||
}, |
||||
golang: { |
||||
dimensions: { |
||||
width: 968, |
||||
height: 1495.21, |
||||
}, |
||||
}, |
||||
java: { |
||||
dimensions: { |
||||
width: 968, |
||||
height: 1167.29, |
||||
}, |
||||
}, |
||||
javascript: { |
||||
dimensions: { |
||||
width: 968, |
||||
height: 2438.9, |
||||
}, |
||||
}, |
||||
nodejs: { |
||||
dimensions: { |
||||
width: 968, |
||||
height: 2474.06, |
||||
}, |
||||
}, |
||||
python: { |
||||
dimensions: { |
||||
width: 992, |
||||
height: 1259.03, |
||||
}, |
||||
}, |
||||
qa: { |
||||
dimensions: { |
||||
width: 968, |
||||
height: 2107.75, |
||||
}, |
||||
}, |
||||
react: { |
||||
dimensions: { |
||||
width: 968, |
||||
height: 1570.26, |
||||
}, |
||||
}, |
||||
'software-architect': { |
||||
dimensions: { |
||||
width: 968, |
||||
height: 1882.18, |
||||
}, |
||||
}, |
||||
'software-design-architecture': { |
||||
dimensions: { |
||||
width: 968, |
||||
height: 1764.66, |
||||
}, |
||||
}, |
||||
vue: { |
||||
dimensions: { |
||||
width: 968, |
||||
height: 1657.07, |
||||
}, |
||||
}, |
||||
}; |
@ -1,132 +0,0 @@ |
||||
const fs = require('fs'); |
||||
const path = require('path'); |
||||
const yaml = require('json-to-pretty-yaml'); |
||||
const roadmapMetas = require('./roadmap-metas.cjs'); |
||||
|
||||
const oldAssetsPath = path.join(__dirname, 'developer-roadmap/public'); |
||||
const newAssetsPath = path.join(__dirname, '../public/'); |
||||
|
||||
// Create JSONs dir |
||||
const newJsonsPath = path.join(newAssetsPath, 'jsons'); |
||||
if (fs.existsSync(newJsonsPath)) { |
||||
fs.rmSync(newJsonsPath, { recursive: true }); |
||||
} |
||||
|
||||
fs.mkdirSync(newJsonsPath); |
||||
|
||||
// Create PDFs dir |
||||
const newPdfsPath = path.join(newAssetsPath, 'pdfs'); |
||||
if (fs.existsSync(newPdfsPath)) { |
||||
fs.rmSync(newPdfsPath, { recursive: true }); |
||||
} |
||||
|
||||
fs.mkdirSync(newPdfsPath); |
||||
|
||||
const oldRoadmapsDirPath = path.join(__dirname, 'developer-roadmap/content/roadmaps'); |
||||
const newRoadmapsDirPath = path.join(__dirname, '../src/roadmaps'); |
||||
|
||||
if (fs.existsSync(newRoadmapsDirPath)) { |
||||
fs.rmSync(newRoadmapsDirPath, { recursive: true }); |
||||
} |
||||
|
||||
fs.mkdirSync(newRoadmapsDirPath); |
||||
|
||||
const oldRoadmaps = fs |
||||
.readdirSync(oldRoadmapsDirPath) |
||||
.map((roadmapDirName) => path.join(oldRoadmapsDirPath, roadmapDirName)); |
||||
|
||||
const orderInfo = {}; |
||||
const typeCounter = { |
||||
role: 1, |
||||
tool: 1, |
||||
}; |
||||
|
||||
// Calculate the sorting information for the roadmaps |
||||
oldRoadmaps.forEach((oldRoadmapPath) => { |
||||
const roadmapId = path.basename(oldRoadmapPath).replace(/\d+-/g, '').toLowerCase(); |
||||
const oldRoadmapMeta = require(path.join(oldRoadmapPath, 'meta.json')); |
||||
|
||||
orderInfo[roadmapId] = typeCounter[oldRoadmapMeta.type]; |
||||
typeCounter[oldRoadmapMeta.type] += 1; |
||||
}); |
||||
|
||||
// Iterate and create new roadmaps |
||||
oldRoadmaps.forEach((oldRoadmapPath) => { |
||||
const roadmapId = path.basename(oldRoadmapPath).replace(/\d+-/g, '').toLowerCase(); |
||||
|
||||
const metaToMerge = roadmapMetas[roadmapId] ?? {}; |
||||
const oldRoadmapMeta = require(path.join(oldRoadmapPath, 'meta.json')); |
||||
const isTextual = oldRoadmapMeta?.landingPath?.endsWith('.md'); |
||||
|
||||
const hasContentDir = fs.existsSync(path.join(oldRoadmapPath, 'content')); |
||||
|
||||
const roadmapFileContent = isTextual |
||||
? fs.readFileSync(path.join(oldRoadmapPath, oldRoadmapMeta.landingPath), 'utf8') |
||||
: ''; |
||||
|
||||
const roadmapFileContentWithUpdatedUrls = roadmapFileContent |
||||
.replace(/\[\!\[\]\((.+?\.png)\)\]\((.+?\.png)\)/g, '[![](/assets$1)](/assets$2)') |
||||
.replace(/\[\!\[\]\((.+?\.svg)\)\]\((.+?\.svg)\)/g, '[![](/assets$1)](/assets$2)') |
||||
.replace(/\[\!\[\]\((.+?\.svg)\)\]\((.+?\.png)\)/g, '[![](/assets$1)](/assets$2)') |
||||
.replace(/assetshttp\//g, 'http') |
||||
.replace(/assetshttps:\/\//g, 'https://') |
||||
.replace(/\/http/g, 'http') |
||||
.replace(/]\(\/roadmaps\/(.+?)\.png\)/g, '](/assets/roadmaps/$1.png)') |
||||
.replace(/]\(\/roadmaps\/(.+?)\.svg\)/g, '](/assets/roadmaps/$1.svg)') |
||||
.replace(/<iframe/g, '<iframe class="w-full aspect-video mb-5"') |
||||
.replace(/<iframe(.+?)\s?\/>/g, '<iframe$1></iframe>'); |
||||
|
||||
const hasJson = fs.existsSync(path.join(oldAssetsPath, `/project/${roadmapId}.json`)); |
||||
|
||||
const newRoadmapMeta = { |
||||
...( hasJson ? { jsonUrl: `/jsons/${roadmapId}.json`} : {}), |
||||
pdfUrl: `/pdfs/${roadmapId}.pdf`, |
||||
order: orderInfo[roadmapId], |
||||
featuredTitle: |
||||
oldRoadmapMeta.featuredTitle === 'Software Design and Architecture' |
||||
? 'Software Design' |
||||
: oldRoadmapMeta.featuredTitle, |
||||
featuredDescription: oldRoadmapMeta.featuredDescription, |
||||
title: oldRoadmapMeta.title, |
||||
description: oldRoadmapMeta.description, |
||||
isNew: oldRoadmapMeta.isNew, |
||||
hasTopics: hasContentDir, |
||||
...metaToMerge, |
||||
seo: oldRoadmapMeta.seo, |
||||
relatedRoadmaps: oldRoadmapMeta.relatedRoadmaps, |
||||
sitemap: { |
||||
priority: 1, |
||||
changefreq: 'monthly', |
||||
}, |
||||
tags: ['roadmap', 'main-sitemap', `${oldRoadmapMeta.type === 'tool' ? 'skill' : oldRoadmapMeta.type}-roadmap`], |
||||
}; |
||||
|
||||
const frontmatter = yaml.stringify(newRoadmapMeta); |
||||
const newRoadmapDirPath = path.join(newRoadmapsDirPath, roadmapId); |
||||
const newRoadmapFilePath = path.join(newRoadmapDirPath, `/${roadmapId}.md`); |
||||
|
||||
fs.mkdirSync(newRoadmapDirPath); |
||||
fs.writeFileSync(newRoadmapFilePath, `---\n${frontmatter}---\n\n${roadmapFileContentWithUpdatedUrls}`); |
||||
|
||||
const jsonFile = path.join(oldAssetsPath, oldRoadmapMeta.jsonUrl || '/unknown'); |
||||
const pdfFile = path.join(oldAssetsPath, oldRoadmapMeta.pdfUrl || '/unknown'); |
||||
|
||||
if (fs.existsSync(jsonFile)) { |
||||
fs.copyFileSync(jsonFile, path.join(newJsonsPath, `${roadmapId}.json`)); |
||||
} |
||||
|
||||
if (fs.existsSync(pdfFile)) { |
||||
fs.copyFileSync(pdfFile, path.join(newPdfsPath, `${roadmapId}.pdf`)); |
||||
} |
||||
|
||||
// Copy the content directory |
||||
const oldRoadmapContentDir = path.join(oldRoadmapPath, 'content'); |
||||
if (fs.existsSync(oldRoadmapContentDir)) { |
||||
fs.cpSync(oldRoadmapContentDir, path.join(newRoadmapDirPath, 'content'), { recursive: true }); |
||||
} |
||||
}); |
||||
|
||||
const roadmapAssets = path.join(oldAssetsPath, 'roadmaps'); |
||||
if (fs.existsSync(roadmapAssets)) { |
||||
fs.cpSync(roadmapAssets, path.join(newAssetsPath, 'roadmaps'), { recursive: true }); |
||||
} |
@ -1,30 +0,0 @@ |
||||
#!/usr/bin/env bash |
||||
|
||||
set -e |
||||
|
||||
# Change working directory to the directory of this script |
||||
cd "$(dirname "$0")" |
||||
|
||||
if [ ! -d "./developer-roadmap" ]; then |
||||
git clone --depth 1 -b master git@github.com:kamranahmedse/developer-roadmap.git |
||||
fi |
||||
|
||||
echo "Removing old directories" |
||||
rm -rf ../src/videos |
||||
rm -rf ../src/guides |
||||
rm -rf ../src/roadmaps |
||||
|
||||
rm -rf ../public/jsons |
||||
rm -rf ../public/pdfs |
||||
|
||||
echo "=== Migrating Roadmaps ===" |
||||
node roadmap-migrator.cjs |
||||
|
||||
echo "=== Migrating Content ===" |
||||
node content-migrator.cjs |
||||
|
||||
echo "=== Migrating Guides ===" |
||||
node guide-migrator.cjs |
||||
|
||||
echo "=== Migrating Videos ===" |
||||
node video-migrator.cjs |
@ -1,58 +0,0 @@ |
||||
const fs = require('fs'); |
||||
const path = require('path'); |
||||
const yaml = require('json-to-pretty-yaml'); |
||||
|
||||
const contentDirPath = path.join(__dirname, './developer-roadmap/content'); |
||||
const videos = require('./developer-roadmap/content/videos.json'); |
||||
|
||||
// Remove the old videos directory |
||||
const newVideosDirPath = path.join(__dirname, '../src/videos'); |
||||
if (fs.existsSync(newVideosDirPath)) { |
||||
fs.rmSync(newVideosDirPath, { recursive: true }); |
||||
} |
||||
|
||||
fs.mkdirSync(newVideosDirPath); |
||||
|
||||
videos.forEach((video) => { |
||||
const { id: videoId } = video; |
||||
|
||||
const originalVideoPath = path.join( |
||||
contentDirPath, |
||||
'videos', |
||||
`${videoId}.md` |
||||
); |
||||
|
||||
const newVideoPath = path.join(__dirname, `../src/videos/${videoId}.md`); |
||||
|
||||
const videoWithoutFrontmatter = fs.readFileSync(originalVideoPath, 'utf8'); |
||||
fs.copyFileSync(originalVideoPath, newVideoPath); |
||||
|
||||
const videoFrontMatter = yaml |
||||
.stringify({ |
||||
title: video.title, |
||||
description: video.description, |
||||
duration: video.duration, |
||||
isNew: video.isNew, |
||||
date: video.createdAt.replace(/T.*/, ''), |
||||
author: { |
||||
name: 'Kamran Ahmed', |
||||
url: `https://twitter.com/kamranahmedse`, |
||||
imageUrl: `/authors/kamranahmedse.jpeg`, |
||||
}, |
||||
sitemap: { |
||||
priority: 0.7, |
||||
changefreq: 'weekly', |
||||
}, |
||||
tags: ['video', `video-sitemap`], |
||||
}) |
||||
.replace(/date: "(.+?)"/, 'date: $1'); |
||||
|
||||
const videoWithIframeClass = videoWithoutFrontmatter |
||||
.replace(/<iframe/g, '<iframe class="w-full aspect-video mb-5"') |
||||
.replace(/<iframe(.+?)\s?\/>/g, '<iframe$1></iframe>'); |
||||
|
||||
const videoWithFrontmatter = `---\n${videoFrontMatter}---\n\n${videoWithIframeClass}`; |
||||
|
||||
console.log(`Writing video ${videoId} to disk`); |
||||
fs.writeFileSync(newVideoPath, videoWithFrontmatter); |
||||
}); |
File diff suppressed because one or more lines are too long
Loading…
Reference in new issue