parent
79e7c10ad9
commit
1ac8a86f1c
47 changed files with 180 additions and 52 deletions
@ -0,0 +1,179 @@ |
|||||||
|
const fs = require('node:fs'); |
||||||
|
const path = require('node:path'); |
||||||
|
|
||||||
|
const roadmapId = 'frontend'; |
||||||
|
|
||||||
|
const roadmapDir = path.join( |
||||||
|
__dirname, |
||||||
|
`../src/data/roadmaps/${roadmapId}/content`, |
||||||
|
); |
||||||
|
|
||||||
|
function getHostNameWithoutTld(hostname) { |
||||||
|
const parts = hostname.split('.'); |
||||||
|
return parts.slice(0, parts.length - 1).join('.'); |
||||||
|
} |
||||||
|
|
||||||
|
function isOfficialWebsite(hostname, fileName, roadmapId) { |
||||||
|
if (hostname === 'javascript.info') { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
fileName = fileName.replace('/index.md', '').replace('.md', ''); |
||||||
|
|
||||||
|
const parts = fileName.split('/'); |
||||||
|
const lastPart = parts[parts.length - 1]; |
||||||
|
|
||||||
|
const normalizedFilename = lastPart.replace(/\d+/g, '').replace(/-/g, ''); |
||||||
|
const normalizedHostname = getHostNameWithoutTld(hostname); |
||||||
|
|
||||||
|
if (normalizedFilename === normalizedHostname) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
if (normalizedFilename.includes(normalizedHostname)) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
return !!roadmapId.includes(normalizedHostname); |
||||||
|
} |
||||||
|
|
||||||
|
// websites are educational websites that are of following types: |
||||||
|
// - @official@ |
||||||
|
// - @article@ |
||||||
|
// - @course@ |
||||||
|
// - @opensource@ |
||||||
|
// - @podcast@ |
||||||
|
// - @video@ |
||||||
|
// - @website@ |
||||||
|
// content is only educational websites |
||||||
|
function getTypeFromHostname(hostname) { |
||||||
|
hostname = hostname.replace('www.', ''); |
||||||
|
|
||||||
|
const videoHostnames = ['youtube.com', 'vimeo.com']; |
||||||
|
const courseHostnames = ['coursera.org', 'udemy.com', 'edx.org']; |
||||||
|
const podcastHostnames = ['spotify.com', 'apple.com']; |
||||||
|
const opensourceHostnames = ['github.com', 'gitlab.com']; |
||||||
|
const articleHostnames = [ |
||||||
|
'docs.gitlab.com', |
||||||
|
'docs.github.com', |
||||||
|
'skills.github.com', |
||||||
|
'cloudflare.com', |
||||||
|
'w3schools.com', |
||||||
|
'medium.com', |
||||||
|
'dev.to', |
||||||
|
'web.dev', |
||||||
|
'css-tricks.com', |
||||||
|
'developer.mozilla.org', |
||||||
|
'smashingmagazine.com', |
||||||
|
'freecodecamp.org', |
||||||
|
'cs.fyi', |
||||||
|
'thenewstack.io', |
||||||
|
'html5rocks.com', |
||||||
|
'html.com', |
||||||
|
'javascript.info', |
||||||
|
'css-tricks.com', |
||||||
|
'developer.apple.com', |
||||||
|
]; |
||||||
|
|
||||||
|
if (articleHostnames.includes(hostname)) { |
||||||
|
return 'article'; |
||||||
|
} |
||||||
|
|
||||||
|
if (videoHostnames.includes(hostname)) { |
||||||
|
return 'video'; |
||||||
|
} |
||||||
|
|
||||||
|
if (courseHostnames.includes(hostname)) { |
||||||
|
return 'course'; |
||||||
|
} |
||||||
|
|
||||||
|
if (podcastHostnames.includes(hostname)) { |
||||||
|
return 'podcast'; |
||||||
|
} |
||||||
|
|
||||||
|
if (opensourceHostnames.includes(hostname)) { |
||||||
|
return 'opensource'; |
||||||
|
} |
||||||
|
|
||||||
|
if (hostname === 'roadmap.sh') { |
||||||
|
return 'website'; |
||||||
|
} |
||||||
|
|
||||||
|
return ''; |
||||||
|
} |
||||||
|
|
||||||
|
function readNestedMarkdownFiles(dir, files = []) { |
||||||
|
const dirEnts = fs.readdirSync(dir, { withFileTypes: true }); |
||||||
|
|
||||||
|
for (const dirent of dirEnts) { |
||||||
|
const fullPath = path.join(dir, dirent.name); |
||||||
|
if (dirent.isDirectory()) { |
||||||
|
readNestedMarkdownFiles(fullPath, files); |
||||||
|
} else { |
||||||
|
if (path.extname(fullPath) === '.md') { |
||||||
|
files.push(fullPath); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return files; |
||||||
|
} |
||||||
|
|
||||||
|
const files = readNestedMarkdownFiles(roadmapDir); |
||||||
|
|
||||||
|
// for each of the files, assign the type of link to the beginning of each markdown link |
||||||
|
// i.e. - [@article@abc](xyz) where @article@ is the type of link. Possible types: |
||||||
|
// - @article@ |
||||||
|
// - @course@ |
||||||
|
// - @opensource@ |
||||||
|
// - @podcast@ |
||||||
|
// - @video@ |
||||||
|
// - @website@ |
||||||
|
files.forEach((file) => { |
||||||
|
const content = fs.readFileSync(file, 'utf-8'); |
||||||
|
const lines = content.split('\n'); |
||||||
|
|
||||||
|
const newContent = lines.map((line) => { |
||||||
|
if (line.startsWith('- [')) { |
||||||
|
const type = line.match(/@(\w+)@/); |
||||||
|
if (type) { |
||||||
|
return line; |
||||||
|
} |
||||||
|
|
||||||
|
let fullUrl = line.match(/\((https?:\/\/[^)]+)\)/)?.[1]; |
||||||
|
if (!fullUrl) { |
||||||
|
// is it slashed URL i.e. - [abc](/xyz) |
||||||
|
fullUrl = line.match(/\((\/[^)]+)\)/)?.[1]; |
||||||
|
if (fullUrl) { |
||||||
|
fullUrl = `https://roadmap.sh${fullUrl}`; |
||||||
|
} |
||||||
|
|
||||||
|
if (!fullUrl) { |
||||||
|
console.error('No URL found in line:', line); |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
const url = new URL(fullUrl); |
||||||
|
const hostname = url.hostname; |
||||||
|
|
||||||
|
const urlType = |
||||||
|
getTypeFromHostname(hostname) || |
||||||
|
(isOfficialWebsite(hostname, file, roadmapId) ? 'official' : ''); |
||||||
|
|
||||||
|
if (urlType === 'official') { |
||||||
|
console.log('Official:', hostname); |
||||||
|
process.exit(0); |
||||||
|
} |
||||||
|
|
||||||
|
if (!urlType) { |
||||||
|
console.error('Missing type:', hostname); |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return line; |
||||||
|
}); |
||||||
|
|
||||||
|
console.log(file); |
||||||
|
}); |
Loading…
Reference in new issue