Compare commits

...

108 Commits

Author SHA1 Message Date
jhbirddotdev
2cc780e08c Fix typo in SignalR (#3309)
* 'singalr' typo to 'signalr' 'singlar-core'
typo to 'signalr-core'
Renamed content file 'signlar-core' to 'signalr-core'

* Update aspnet-core.json

Co-authored-by: Jay <jhbird.dev>
Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
2023-01-18 17:02:58 +04:00
mouaaz
9bec0cb30d Adding content to 114-microservices 2023-01-15 13:45:16 +05:00
mouaaz
817c2f2bb2 Adding content to 113-testing 2023-01-14 21:05:27 +05:00
mouaaz
25ea9aeb00 Adding content to 118-good-to-know-libraries 2023-01-14 17:58:26 +05:00
mouaaz
0274d9eddb Adding content to 117-template-engines 2023-01-14 16:48:56 +05:00
mouaaz
8e181d26cb Adding content to 116-client-side-libraries 2023-01-14 16:21:27 +05:00
mouaaz
177f34404b Adding content to 115-ci-cd 2023-01-14 16:08:18 +05:00
Kamran Ahmed
85206085df Update src/roadmaps/aspnet-core/content/109-api-clients/101-grpc.md 2023-01-14 15:05:48 +04:00
Kamran Ahmed
6f9d586415 Update src/roadmaps/aspnet-core/content/109-api-clients/102-graphql/100-graphql-dotnet.md 2023-01-14 15:05:40 +04:00
Kamran Ahmed
277d7815b9 Update src/roadmaps/aspnet-core/content/109-api-clients/100-rest/index.md 2023-01-14 15:05:33 +04:00
Kamran Ahmed
45ed8d3ec1 Update src/roadmaps/aspnet-core/content/109-api-clients/100-rest/101-odata.md 2023-01-14 15:05:20 +04:00
Kamran Ahmed
fc37b42ff1 Update src/roadmaps/aspnet-core/content/108-log-frameworks/index.md 2023-01-14 15:05:12 +04:00
Kamran Ahmed
16de89c832 Update src/roadmaps/aspnet-core/content/108-log-frameworks/102-log-management-system/104-elmah.md 2023-01-14 15:05:04 +04:00
Kamran Ahmed
b5566c2984 Update src/roadmaps/aspnet-core/content/108-log-frameworks/102-log-management-system/100-elk-stack.md 2023-01-14 15:04:56 +04:00
Kamran Ahmed
c3bf2716d4 Update src/roadmaps/aspnet-core/content/108-log-frameworks/100-serilog.md 2023-01-14 15:04:47 +04:00
Kamran Ahmed
aaf4b61845 Update src/roadmaps/aspnet-core/content/108-log-frameworks/101-nlog.md 2023-01-14 15:04:40 +04:00
Kamran Ahmed
025e4895ca Update src/roadmaps/aspnet-core/content/107-databases/103-nosql/101-mongodb.md 2023-01-14 15:04:29 +04:00
Kamran Ahmed
20d06d929e Update src/roadmaps/aspnet-core/content/107-databases/102-relational/103-mysql.md 2023-01-14 15:04:20 +04:00
Kamran Ahmed
e74d3b5e2e Update src/roadmaps/aspnet-core/content/107-databases/102-relational/101-postgresql.md 2023-01-14 15:03:41 +04:00
Kamran Ahmed
efecc43523 Update src/roadmaps/aspnet-core/content/107-databases/100-search-engines/100-elasticsearch.md 2023-01-14 15:03:19 +04:00
Kamran Ahmed
fd40850d0c Update src/roadmaps/aspnet-core/content/111-object-mapping/100-atuo-mapper.md 2023-01-14 15:02:24 +04:00
Kamran Ahmed
09718af33f Update src/roadmaps/aspnet-core/content/110-real-time-communication/101-singlar-core.md 2023-01-14 15:02:11 +04:00
Kamran Ahmed
3af67ec742 Update src/roadmaps/aspnet-core/content/109-api-clients/102-graphql/index.md 2023-01-14 15:02:05 +04:00
Kamran Ahmed
2a4985f8c4 Update src/roadmaps/aspnet-core/content/109-api-clients/102-graphql/100-graphql-dotnet.md 2023-01-14 15:02:00 +04:00
Kamran Ahmed
74edb3d2fd Update src/roadmaps/aspnet-core/content/105-dependency-injection/index.md 2023-01-14 15:01:55 +04:00
Kamran Ahmed
6ce6ce36d7 Update src/roadmaps/aspnet-core/content/105-dependency-injection/102-life-cycles/index.md 2023-01-14 15:01:47 +04:00
Kamran Ahmed
001f0d31a3 Update src/roadmaps/aspnet-core/content/104-orm/102-repodb.md 2023-01-14 15:01:03 +04:00
Kamran Ahmed
0262e7486b Update src/roadmaps/aspnet-core/content/104-orm/102-repodb.md 2023-01-14 15:00:51 +04:00
Kamran Ahmed
27810a2c39 Update src/roadmaps/aspnet-core/content/104-orm/100-entity-framework-core/102-change-tracker-api.md 2023-01-14 15:00:10 +04:00
Kamran Ahmed
fa1ec112b1 Update src/roadmaps/aspnet-core/content/103-basics-of-aspnet-core/index.md 2023-01-14 14:59:57 +04:00
Kamran Ahmed
71a2b3712c Update src/roadmaps/aspnet-core/content/109-api-clients/102-graphql/100-graphql-dotnet.md 2023-01-14 14:52:37 +04:00
Kamran Ahmed
28a87feb92 Update src/roadmaps/aspnet-core/content/103-basics-of-aspnet-core/101-rest.md 2023-01-14 14:52:30 +04:00
Kamran Ahmed
0ca7d23cbd Update src/roadmaps/aspnet-core/content/100-basics-of-csharp/101-dotnet.md 2023-01-14 14:52:25 +04:00
Kamran Ahmed
04d19d3d6d Update src/roadmaps/aspnet-core/content/104-orm/103-nhibernate.md 2023-01-14 14:52:20 +04:00
Kamran Ahmed
b0630a34ee Fix lazy loading heading 2023-01-14 14:52:15 +04:00
Kamran Ahmed
a850e3785c Fix Eager Loading heading 2023-01-14 14:52:07 +04:00
mouaaz
5fc79810ff Content added in aspnet-core/content/112-task-scheduling 2023-01-14 13:58:46 +05:00
mouaaz
4f84f35bcf Content added in aspnet-core/content/111-object-mapping 2023-01-14 13:12:20 +05:00
mouaaz
16b1ec96b1 Content added in aspnet-core/content/110-real-time-communication 2023-01-14 12:52:25 +05:00
mouaaz
04019d1b27 Content added in aspnet-core/content/109-api-clients 2023-01-14 12:32:20 +05:00
mouaaz
68f4b2ec11 Content added in aspnet-core/content/108-log-frameworks 2023-01-14 12:09:56 +05:00
mouaaz
fa0bcb7ef1 Committing 107-databases 2023-01-13 22:54:25 +05:00
mouaaz
7ad3089d13 Initial commit 2023-01-13 22:21:31 +05:00
mouaaz
aa55a6dac0 Initial commit 2023-01-13 21:54:16 +05:00
mouaaz
44239647ed Initital commit 2023-01-13 21:35:05 +05:00
mouaaz
5d66bf786e Initial commit 2023-01-13 20:38:18 +05:00
mouaaz
fb098550b4 Initial commit 2023-01-13 19:45:41 +05:00
mouaaz
2793e2b13a Initial commit 2023-01-13 18:14:15 +05:00
mouaaz
ce3ff223aa Initial commit 2023-01-13 03:47:25 +05:00
mouaaz
167419b70a Initial commit 2023-01-12 20:47:21 +05:00
mouaaz
48d6e320bc Initial commit 2023-01-12 19:57:39 +05:00
mouaaz
e583e3499f Initial commit 2023-01-12 19:42:09 +05:00
mouaaz
76b1562c51 Initial commit 2023-01-12 19:13:18 +05:00
Kamran Ahmed
ba86e8a6b1 Update content headings 2023-01-12 14:41:54 +04:00
syedmouaazfarrukh
5f23d4c7eb Add content to Spring Boot roadmap (#3285)
* Initial commit

* Update content files

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
2023-01-12 14:38:50 +04:00
Kamran Ahmed
8264c4509f Update canonicals 2023-01-11 22:28:41 +04:00
Kamran Ahmed
6c8aea98da Rename Software Design and Architecture Roadmap 2023-01-11 20:12:49 +04:00
Kamran Ahmed
64ccd02d53 Fix broken popup 2023-01-11 17:12:19 +04:00
Kamran Ahmed
f8c1c6278b Refactor HTML event handlers 2023-01-11 00:08:49 +04:00
Kamran Ahmed
4786265e04 Refactor event tracking implementation 2023-01-10 23:57:41 +04:00
Kamran Ahmed
8badf383b2 Responsiveness changes 2023-01-10 23:26:29 +04:00
Kamran Ahmed
c4406b7649 Add meta text below roadmap topic for contribution 2023-01-10 19:39:43 +04:00
Kamran Ahmed
1e878069bc Add free eBook link 2023-01-10 19:11:49 +04:00
Kamran Ahmed
8234de2b8c Fix capitalization of word 2023-01-10 18:35:57 +04:00
Kamran Ahmed
3466708ed4 Add FAQs to the backend roadmap 2023-01-10 18:33:19 +04:00
Kamran Ahmed
b440fd9787 Add Spring Boot terminology content 2023-01-10 16:47:56 +04:00
Kamran Ahmed
9bc73ab738 Update FAQs on frontend developer roadmap 2023-01-10 16:10:56 +04:00
Kamran Ahmed
91c16a5e32 Minor FAQ change 2023-01-10 15:49:52 +04:00
Kamran Ahmed
1768150fb1 Fix color of the code items 2023-01-10 15:42:03 +04:00
Kamran Ahmed
4a1374c978 Add FAQs to the frontend roadmap 2023-01-10 15:39:10 +04:00
Kamran Ahmed
43df31b312 Remove duplicate go roadmap 2023-01-10 11:28:16 +04:00
Kamran Ahmed
2037edb2da Add content for spring configuration 2023-01-10 03:22:03 +04:00
Kamran Ahmed
de8a4d4acf Add build script 2023-01-10 03:12:25 +04:00
Kamran Ahmed
a67a27299e Add functionality to create content directory for a roadmap 2023-01-10 03:11:51 +04:00
Kamran Ahmed
5d164198d4 Delete migration scripts and grouping on roadmap 2023-01-10 02:58:07 +04:00
Kamran Ahmed
a76b9d9ac0 Add roadmap link to PDFs page 2023-01-09 19:56:24 +04:00
Kamran Ahmed
6ed83349ba Add spring boot roadmap link 2023-01-09 19:55:01 +04:00
Kamran Ahmed
1b21550e48 Add spring boot roadmap pdf 2023-01-09 19:53:08 +04:00
Kamran Ahmed
c1d0ff7ea2 Add spring boot roadmap 2023-01-09 19:51:47 +04:00
Kamran Ahmed
26125fc6d7 Add software architect roadmap 2023-01-09 19:40:44 +04:00
Kamran Ahmed
aff7d8eece Add events to the subscription forms 2023-01-09 19:36:45 +04:00
Kamran Ahmed
fd939f198a Fix popup not working on roadmaps 2023-01-09 18:01:49 +04:00
Kamran Ahmed
18e4804a51 Add command to collect links from roadmaps 2023-01-09 15:37:10 +04:00
Sricharan Krishnan
ed8bf11150 Add resources for React (#3264)
* [Build] React Roadmap

Additional Suggestions for 103 Refs
1. Web Dev Simplied - a youtube video that explains the use of refs and what it can do for us
2. A content blog from 'Dmitri Pavlutin' website

* [Build] 104-React Events

I've added an additional description text in the main markdown file to help other learners/readers
keep in mind how important it is to make sure that we write code that is easy to understand. Do take
a look.

* Update src/roadmaps/react/content/103-rendering/104-events.md

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
2023-01-09 14:02:22 +04:00
Fred Vasquez
61f088d42a Add content for GraphQL Introduction (#3268)
Adding introduction to topic and useful links.
2023-01-09 14:01:30 +04:00
Tilen Pogačnik
faee01b22d Fix: remove text highlight in links (#3273) 2023-01-09 14:00:52 +04:00
Kamran Ahmed
dc56ef6190 Remove broken link 2023-01-09 14:00:15 +04:00
Kamran Ahmed
f393a23994 Add script for upgrading dependencies 2023-01-09 13:59:34 +04:00
Kamran Ahmed
8e61330080 Rename sha to commitUrl 2023-01-08 06:48:51 +04:00
Kamran Ahmed
2c18529429 Add commit url meta attribute 2023-01-08 06:38:50 +04:00
Kamran Ahmed
88ff836bfb Fix canonicals 2023-01-08 06:29:38 +04:00
Kamran Ahmed
66cb4f9a06 Fix canonicals 2023-01-08 06:25:34 +04:00
Ansat
d9697b74fd fix: remove text highlight in links (#3265) 2023-01-08 00:16:21 +04:00
Kamran Ahmed
863b7fa08b Make signup page no-index 2023-01-07 13:45:31 +04:00
Kamran Ahmed
64078f9d1a Update homepage title 2023-01-07 13:44:10 +04:00
Kamran Ahmed
5f8ead3d2f Fix long titles, multiple headings and redirect links 2023-01-07 13:38:23 +04:00
Kamran Ahmed
cb16abc8e1 Add alt attributes to all images 2023-01-07 13:23:43 +04:00
Kamran Ahmed
52d00a0654 Add canonicals and fix og:url on all pages 2023-01-06 22:08:49 +04:00
Kamran Ahmed
d5495f7280 Reduce file sizes 2023-01-06 21:45:11 +04:00
Kamran Ahmed
564c9fdd4f Fix broken URLs 2023-01-06 21:42:58 +04:00
Kamran Ahmed
e75df0ef9e Fix broken roadmap URL 2023-01-06 21:41:13 +04:00
Kamran Ahmed
642cbbf6d3 Fix star count is displaying NaN 2023-01-06 18:51:13 +04:00
Hossein zare
032602ad3b Rename raywenderlich to Kodeco (#3243)
raywenderlich's team has changed their name to Kodeco.

https://www.kodeco.com/36641071-introducing-kodeco-the-new-raywenderlich-com
2023-01-06 15:54:36 +04:00
Benson Arafat
522f16957a Update content in Dart (#3244)
* Updated 100 Dart Basics 

Dart can also be used to build server and desktop applications.

* Updated 103 Functions

Explanation about dart functions
2023-01-06 15:52:59 +04:00
Sricharan Krishnan
1f3bf761cd Add resources for React (#3245)
* [Build] React Roadmap

1. Came across two other interesting topics related to props and state in react
2. Would like to suggest that we add these to the list as well as they highlight something important
3. One is from robinwieruch and the other from Dominik Dorfmeister (TkDodo)

* [Build] React Roadmap Contributions

1. Added one topic for Composition vs Inheritance
2. Added topics for Props vs State
3. Added topic for Render Props
2023-01-06 15:52:32 +04:00
Haril Song
f76f0ea1a6 Fix typo in gRPC (#3248) 2023-01-06 15:48:48 +04:00
Kamran Ahmed
a40457edc8 Rename README.md to readme.md 2023-01-05 21:15:41 +04:00
Kamran Ahmed
076db6dd0a Fix broken image link 2023-01-05 20:24:51 +04:00
346 changed files with 4889 additions and 1154 deletions

View File

@@ -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}`);
});

View File

@@ -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 });

163
bin/roadmap-content.cjs Normal file
View File

@@ -0,0 +1,163 @@
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/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, `../public/jsons/${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');

44
bin/roadmap-links.cjs Normal file
View File

@@ -0,0 +1,44 @@
const fs = require('fs');
const path = require('path');
const roadmapId = process.argv[2];
if (!roadmapId) {
console.error('Error: roadmapId is required');
}
const fullPath = path.join(__dirname, `../src/roadmaps/${roadmapId}`);
if (!fs.existsSync(fullPath)) {
console.error(`Error: path not found: ${fullPath}!`);
process.exit(1);
}
function readFiles(folderPath) {
const stats = fs.lstatSync(folderPath);
if (stats.isFile()) {
return [folderPath];
}
const folderContent = fs.readdirSync(folderPath);
let files = [];
for (const file of folderContent) {
const filePath = path.join(folderPath, file);
files = [...files, ...readFiles(filePath)];
}
return files;
}
const files = readFiles(fullPath);
let allLinks = [];
files.forEach((file) => {
const fileContent = fs.readFileSync(file, 'utf-8');
const matches = [...fileContent.matchAll(/\[[^\]]+]\((https?:\/\/[^)]+)\)/g)];
allLinks = [...allLinks, ...matches.map((match) => match[1])];
});
allLinks.map((link) => console.log(link));

View File

@@ -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,
},
},
};

View File

@@ -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 });
}

View File

@@ -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

View File

@@ -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);
});

View File

@@ -10,8 +10,10 @@
"preview": "astro preview",
"astro": "astro",
"deploy": "NODE_DEBUG=gh-pages gh-pages -d dist -t",
"sync-content": "sh ./bin/sync-content.sh",
"compress:jsons": "node bin/compress-jsons.cjs"
"compress:jsons": "node bin/compress-jsons.cjs",
"upgrade": "ncu -u",
"roadmap-links": "node bin/roadmap-links.cjs",
"roadmap-content": "node bin/roadmap-content.cjs"
},
"dependencies": {
"@astrojs/sitemap": "^1.0.0",
@@ -20,6 +22,7 @@
"astro-compress": "^1.1.24",
"astro-critters": "^1.1.24",
"node-html-parser": "^6.1.4",
"npm-check-updates": "^16.6.2",
"rehype-external-links": "^2.0.1",
"roadmap-renderer": "^1.0.1",
"tailwindcss": "^3.2.4"

1402
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 MiB

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 MiB

After

Width:  |  Height:  |  Size: 835 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

BIN
public/pdfs/spring-boot.pdf Normal file

Binary file not shown.

View File

@@ -1,5 +1,5 @@
<p align="center">
<img src="public/brand.png" height="128">
<img src="public/images/brand.png" height="128">
<h2 align="center"><a href="https://roadmap.sh">roadmap.sh</a></h2>
<p align="center">Community driven roadmaps, articles and resources for developers<p>
<p align="center">
@@ -48,6 +48,7 @@ Here is the list of available roadmaps with more being actively worked upon.
- [Python Roadmap](https://roadmap.sh/python)
- [Go Roadmap](https://roadmap.sh/golang)
- [Java Roadmap](https://roadmap.sh/java)
- [Spring Boot Roadmap](https://roadmap.sh/spring-boot)
- [Design System Roadmap](https://roadmap.sh/design-system)
- [DBA Roadmap](https://roadmap.sh/postgresql-dba)
- [Blockchain Roadmap](https://roadmap.sh/blockchain)

View File

@@ -2,7 +2,8 @@
---
<script src='./analytics.js'></script>
<script async src='https://www.googletagmanager.com/gtag/js?id=UA-139582634-1'></script>
<script async src='https://www.googletagmanager.com/gtag/js?id=UA-139582634-1'
></script>
<script is:inline>
// @ts-nocheck
window.dataLayer = window.dataLayer || [];
@@ -12,4 +13,29 @@
gtag('js', new Date());
gtag('config', 'UA-139582634-1');
document.addEventListener('click', (e) => {
let trackEl = e.target;
if (!trackEl.getAttribute('ga-category')) {
trackEl = trackEl.closest('[ga-category]');
}
if (!trackEl) {
return;
}
const category = trackEl.getAttribute('ga-category');
const action = trackEl.getAttribute('ga-action');
const label = trackEl.getAttribute('ga-label');
if (!category) {
return;
}
window.fireEvent({
category,
action,
label,
});
});
</script>

View File

@@ -34,8 +34,21 @@ import CaptchaFields from './Captcha/CaptchaFields.astro';
type='submit'
name='submit'
class='text-white bg-gradient-to-r from-amber-700 to-blue-800 hover:from-amber-800 hover:to-blue-900 font-regular rounded-md text-md px-5 py-2.5 w-full text-center mr-2'
submit-download-form
>
Send Link
</button>
</form>
</Popup>
<script>
document
.querySelector('[submit-download-form]')
?.addEventListener('click', () => {
window.fireEvent({
category: 'Subscription',
action: 'Submitted Popup Form',
label: 'Download Roadmap Popup',
});
});
</script>

View File

@@ -0,0 +1,3 @@
<div class='text-sm sm:text-base leading-relaxed text-left p-2 sm:p-4 text-md text-gray-800 border-t border-t-gray-300 bg-gray-100 rounded-bl-md rounded-br-md'>
<slot />
</div>

View File

@@ -0,0 +1,13 @@
<div class='border-t bg-gray-100'>
<div class='container'>
<div class='flex justify-between relative -top-5'>
<h1 class='text-sm sm:text-base font-medium py-1 px-3 border bg-white rounded-md'>
Frequently Asked Questions
</h1>
</div>
<div class='flex flex-col gap-1 pb-8'>
<slot />
</div>
</div>
</div>

View File

@@ -0,0 +1,42 @@
---
import Icon from '../Icon.astro';
export interface Props {
question: string;
isActive?: boolean;
}
const { question, isActive = false } = Astro.props;
---
<div
class='faq-item bg-white border rounded-md hover:bg-gray-50 border-gray-300'
>
<button
faq-question
class='flex flex-row justify-between items-center p-2 sm:p-3 w-full'
>
<span class='text-sm sm:text-base text-left font-medium'>{question}</span>
<Icon icon='down' class='h-6 hidden sm:block text-gray-400' />
</button>
<div class:list={['answer', { hidden: !isActive }]} faq-answer>
<slot />
</div>
</div>
<script>
document.querySelectorAll('[faq-question]').forEach((el) => {
el.addEventListener('click', () => {
// Hide any other visible answers
document.querySelectorAll('[faq-answer]').forEach((element) => {
element.classList.add('hidden');
});
// Show the current answer
const answer = el.nextElementSibling;
if (answer) {
answer.classList.remove('hidden');
}
});
});
</script>

View File

@@ -84,23 +84,29 @@ import Icon from './Icon.astro';
<div class='text-gray-400 text-sm'>
<p>
<a
href='https://thenewstack.io/category/devops/'
href='https://thenewstack.io/category/devops?utm_source=roadmap.sh&utm_medium=Referral&utm_campaign=Footer'
target='_blank'
onclick="window.fireEvent({ category: 'PartnerClick', action: `TNS Referral`, label: `TNS Referral - Footer` })"
ga-category='PartnerClick'
ga-action='TNS Referral'
ga-label='TNS Referral - Footer'
class='text-gray-400 hover:text-white'>DevOps</a
>
<span class='mx-1.5'>&middot;</span>
<a
href='https://thenewstack.io/category/kubernetes/'
href='https://thenewstack.io/category/kubernetes?utm_source=roadmap.sh&utm_medium=Referral&utm_campaign=Footer'
target='_blank'
onclick="window.fireEvent({ category: 'PartnerClick', action: `TNS Referral`, label: `TNS Referral - Footer` })"
ga-category='PartnerClick'
ga-action='TNS Referral'
ga-label='TNS Referral - Footer'
class='text-gray-400 hover:text-white'>Kubernetes</a
>
<span class='mx-1.5'>&middot;</span>
<a
href='https://thenewstack.io/category/cloud-native/'
href='https://thenewstack.io/category/cloud-native?utm_source=roadmap.sh&utm_medium=Referral&utm_campaign=Footer'
target='_blank'
onclick="window.fireEvent({ category: 'PartnerClick', action: `TNS Referral`, label: `TNS Referral - Footer` })"
ga-category='PartnerClick'
ga-action='TNS Referral'
ga-label='TNS Referral - Footer'
class='text-gray-400 hover:text-white'>Cloud-Native</a
>
</p>

View File

@@ -20,7 +20,11 @@ const { author } = frontmatter;
target='_blank'
class='font-medium hover:text-gray-600 inline-flex items-center hover:underline'
>
<img src={author.imageUrl} class='w-5 h-5 inline mr-2 rounded-full' />
<img
alt={author.name}
src={author.imageUrl}
class='w-5 h-5 inline mr-2 rounded-full'
/>
{author.name}
</a>
<span class='mx-1.5'>&middot;</span>

View File

@@ -1,10 +1,10 @@
---
import DownloadPopup from "../DownloadPopup.astro";
import Loader from "../Loader.astro";
import ShareIcons from "../ShareIcons.astro";
import SubscribePopup from "../SubscribePopup.astro";
import TopicOverlay from "../TopicOverlay.astro";
import "./InteractiveRoadmap.css";
import DownloadPopup from '../DownloadPopup.astro';
import Loader from '../Loader.astro';
import ShareIcons from '../ShareIcons.astro';
import SubscribePopup from '../SubscribePopup.astro';
import TopicOverlay from '../TopicOverlay.astro';
import './InteractiveRoadmap.css';
export interface Props {
roadmapId: string;
@@ -16,31 +16,32 @@ export interface Props {
};
}
const { roadmapId, jsonUrl, dimensions = null, description } =
Astro.props;
const { roadmapId, jsonUrl, dimensions = null, description } = Astro.props;
---
<link
rel="preload"
href="/fonts/balsamiq.woff2"
as="font"
type="font/woff2"
rel='preload'
href='/fonts/balsamiq.woff2'
as='font'
type='font/woff2'
crossorigin
slot="after-header"
slot='after-header'
/>
<div class="bg-gray-50 py-4 sm:py-12">
<div class="max-w-[1000px] container relative">
<div class='bg-gray-50 py-4 sm:py-12'>
<div class='max-w-[1000px] container relative'>
<ShareIcons
description={description}
pageUrl={`https://roadmap.sh/${roadmapId}`}
/>
<DownloadPopup />
<SubscribePopup />
<TopicOverlay />
<TopicOverlay roadmapId={roadmapId} />
<div
id="roadmap-svg"
style={dimensions ? `--aspect-ratio:${dimensions.width}/${dimensions.height}` : null}
id='roadmap-svg'
style={dimensions
? `--aspect-ratio:${dimensions.width}/${dimensions.height}`
: null}
data-roadmap-id={roadmapId}
data-json-url={jsonUrl}
>
@@ -49,4 +50,4 @@ const { roadmapId, jsonUrl, dimensions = null, description } =
</div>
</div>
<script src="./roadmap.js"></script>
<script src='./roadmap.js'></script>

View File

@@ -1,6 +1,6 @@
import { wireframeJSONToSVG } from "roadmap-renderer";
import { Topic } from "./topic";
import { Sharer } from "./sharer";
import { wireframeJSONToSVG } from 'roadmap-renderer';
import { Topic } from './topic';
import { Sharer } from './sharer';
/**
* @typedef {{ roadmapId: string, jsonUrl: string }} RoadmapConfig
@@ -11,10 +11,10 @@ export class Roadmap {
* @param {RoadmapConfig} config
*/
constructor() {
this.roadmapId = "";
this.jsonUrl = "";
this.roadmapId = '';
this.jsonUrl = '';
this.containerId = "roadmap-svg";
this.containerId = 'roadmap-svg';
this.init = this.init.bind(this);
this.onDOMLoaded = this.onDOMLoaded.bind(this);
@@ -28,10 +28,16 @@ export class Roadmap {
}
prepareConfig() {
if (!this.containerEl) {
return false;
}
const dataset = this.containerEl.dataset;
this.roadmapId = dataset.roadmapId;
this.jsonUrl = dataset.jsonUrl;
return true;
}
/**
@@ -40,7 +46,7 @@ export class Roadmap {
*/
fetchRoadmapSvg(jsonUrl) {
if (!jsonUrl) {
console.error("jsonUrl not defined in frontmatter");
console.error('jsonUrl not defined in frontmatter');
return null;
}
@@ -50,13 +56,15 @@ export class Roadmap {
})
.then(function (json) {
return wireframeJSONToSVG(json, {
fontURL: "/fonts/balsamiq.woff2",
fontURL: '/fonts/balsamiq.woff2',
});
});
}
onDOMLoaded() {
this.prepareConfig();
if (!this.prepareConfig()) {
return;
}
this.fetchRoadmapSvg(this.jsonUrl)
.then((svg) => {
@@ -66,8 +74,8 @@ export class Roadmap {
}
handleRoadmapClick(e) {
const targetGroup = e.target.closest("g") || {};
const groupId = targetGroup.dataset ? targetGroup.dataset.groupId : "";
const targetGroup = e.target.closest('g') || {};
const groupId = targetGroup.dataset ? targetGroup.dataset.groupId : '';
if (!groupId) {
return;
}
@@ -75,7 +83,7 @@ export class Roadmap {
e.stopImmediatePropagation();
window.dispatchEvent(
new CustomEvent("topic.click", {
new CustomEvent('topic.click', {
detail: {
topicId: groupId,
roadmapId: this.roadmapId,
@@ -85,8 +93,8 @@ export class Roadmap {
}
init() {
window.addEventListener("DOMContentLoaded", this.onDOMLoaded);
window.addEventListener("click", this.handleRoadmapClick);
window.addEventListener('DOMContentLoaded', this.onDOMLoaded);
window.addEventListener('click', this.handleRoadmapClick);
}
}

View File

@@ -3,7 +3,7 @@ export class Sharer {
this.init = this.init.bind(this);
this.onScroll = this.onScroll.bind(this);
this.shareIconsId = "page-share-icons";
this.shareIconsId = 'page-share-icons';
}
get shareIconsEl() {
@@ -12,14 +12,18 @@ export class Sharer {
onScroll() {
if (window.scrollY < 100 || window.innerWidth < 1050) {
this.shareIconsEl.classList.add("hidden");
this.shareIconsEl.classList.add('hidden');
return null;
}
this.shareIconsEl.classList.remove("hidden");
this.shareIconsEl.classList.remove('hidden');
}
init() {
window.addEventListener("scroll", this.onScroll, { passive: true });
if (!this.shareIconsEl) {
return;
}
window.addEventListener('scroll', this.onScroll, { passive: true });
}
}
}

View File

@@ -1,204 +1,219 @@
export class Topic {
constructor() {
this.overlayId = 'topic-overlay';
this.contentId = 'topic-content';
this.loaderId = 'topic-loader';
this.topicBodyId = 'topic-body';
this.topicActionsId = 'topic-actions';
this.markTopicDoneId = 'mark-topic-done';
this.markTopicPendingId = 'mark-topic-pending';
this.closeTopicId = 'close-topic';
this.activeRoadmapId = null;
this.activeTopicId = null;
this.handleTopicClick = this.handleTopicClick.bind(this);
this.close = this.close.bind(this);
this.resetDOM = this.resetDOM.bind(this);
this.populate = this.populate.bind(this);
this.handleOverlayClick = this.handleOverlayClick.bind(this);
this.markAsDone = this.markAsDone.bind(this);
this.markAsPending = this.markAsPending.bind(this);
this.queryRoadmapElementsByTopicId = this.queryRoadmapElementsByTopicId.bind(this);
this.init = this.init.bind(this);
constructor() {
this.overlayId = 'topic-overlay';
this.contentId = 'topic-content';
this.loaderId = 'topic-loader';
this.topicBodyId = 'topic-body';
this.topicActionsId = 'topic-actions';
this.markTopicDoneId = 'mark-topic-done';
this.markTopicPendingId = 'mark-topic-pending';
this.closeTopicId = 'close-topic';
this.contributionTextId = 'contrib-meta';
this.activeRoadmapId = null;
this.activeTopicId = null;
this.handleTopicClick = this.handleTopicClick.bind(this);
this.close = this.close.bind(this);
this.resetDOM = this.resetDOM.bind(this);
this.populate = this.populate.bind(this);
this.handleOverlayClick = this.handleOverlayClick.bind(this);
this.markAsDone = this.markAsDone.bind(this);
this.markAsPending = this.markAsPending.bind(this);
this.queryRoadmapElementsByTopicId =
this.queryRoadmapElementsByTopicId.bind(this);
this.init = this.init.bind(this);
}
get loaderEl() {
return document.getElementById(this.loaderId);
}
get markTopicDoneEl() {
return document.getElementById(this.markTopicDoneId);
}
get markTopicPendingEl() {
return document.getElementById(this.markTopicPendingId);
}
get topicActionsEl() {
return document.getElementById(this.topicActionsId);
}
get contributionTextEl() {
return document.getElementById(this.contributionTextId);
}
get contentEl() {
return document.getElementById(this.contentId);
}
get overlayEl() {
return document.getElementById(this.overlayId);
}
resetDOM(hideOverlay = false) {
if (hideOverlay) {
this.overlayEl.classList.add('hidden');
} else {
this.overlayEl.classList.remove('hidden');
}
get loaderEl() {
return document.getElementById(this.loaderId);
}
get markTopicDoneEl() {
return document.getElementById(this.markTopicDoneId);
}
get markTopicPendingEl() {
return document.getElementById(this.markTopicPendingId);
}
get topicActionsEl() {
return document.getElementById(this.topicActionsId);
}
get contentEl() {
return document.getElementById(this.contentId);
}
get overlayEl() {
return document.getElementById(this.overlayId);
}
resetDOM(hideOverlay = false) {
if (hideOverlay) {
this.overlayEl.classList.add('hidden');
} else {
this.overlayEl.classList.remove('hidden');
}
this.loaderEl.classList.remove('hidden'); // Show loader
this.topicActionsEl.classList.add('hidden'); // Hide Actions
this.contentEl.replaceChildren(''); // Remove content
}
close() {
this.resetDOM(true);
this.activeRoadmapId = null;
this.activeTopicId = null;
}
/**
* @param {string | HTMLElement} html
*/
populate(html) {
this.contentEl.replaceChildren(html);
this.loaderEl.classList.add('hidden');
this.topicActionsEl.classList.remove('hidden');
const normalizedGroup = (this.activeTopicId || '').replace(/^\d+-/, '');
const isDone = localStorage.getItem(normalizedGroup) === 'done';
if (isDone) {
this.markTopicDoneEl.classList.add('hidden');
this.markTopicPendingEl.classList.remove('hidden');
} else {
this.markTopicDoneEl.classList.remove('hidden');
this.markTopicPendingEl.classList.add('hidden');
}
}
fetchTopicHtml(roadmapId, topicId) {
const topicPartial = topicId.replace(/^\d+-/, '').replaceAll(/:/g, '/');
const fullUrl = `/${roadmapId}/${topicPartial}/`;
return fetch(fullUrl)
.then((res) => {
return res.text();
})
.then((topicHtml) => {
// It's full HTML with page body, head etc.
// We only need the inner HTML of the #main-content
const node = new DOMParser().parseFromString(topicHtml, 'text/html');
return node.getElementById('main-content');
});
}
handleTopicClick(e) {
const { roadmapId, topicId } = e.detail;
if (!topicId || !roadmapId) {
console.log('Missing topic or roadmap: ', e.detail);
return;
}
this.activeRoadmapId = roadmapId;
this.activeTopicId = topicId;
if (/^ext_link/.test(topicId)) {
window.open(`https://${topicId.replace('ext_link:', '')}`);
return;
}
this.resetDOM();
this.fetchTopicHtml(roadmapId, topicId)
.then((content) => {
this.populate(content);
})
.catch((e) => {
console.error(e);
this.populate('Error loading the content!');
});
}
queryRoadmapElementsByTopicId(topicId) {
const elements = document.querySelectorAll(`[data-group-id$="-${topicId}"]`);
const matchingElements = [];
elements.forEach((element) => {
const foundGroupId = element?.dataset?.groupId || '';
const validGroupRegex = new RegExp(`^\\d+-${topicId}$`);
if (validGroupRegex.test(foundGroupId)) {
matchingElements.push(element);
}
});
return matchingElements;
}
markAsDone(topicId) {
const updatedTopicId = topicId.replace(/^\d+-/, '');
localStorage.setItem(updatedTopicId, 'done');
this.queryRoadmapElementsByTopicId(updatedTopicId).forEach((item) => {
item?.classList?.add('done');
});
}
markAsPending(topicId) {
const updatedTopicId = topicId.replace(/^\d+-/, '');
localStorage.removeItem(updatedTopicId);
this.queryRoadmapElementsByTopicId(updatedTopicId).forEach((item) => {
item?.classList?.remove('done');
});
}
handleOverlayClick(e) {
const isClickedInsideTopic = e.target.closest(`#${this.topicBodyId}`);
if (!isClickedInsideTopic) {
this.close();
return;
}
const isClickedDone = e.target.id === this.markTopicDoneId || e.target.closest(`#${this.markTopicDoneId}`);
if (isClickedDone) {
this.markAsDone(this.activeTopicId);
this.close();
}
const isClickedPending = e.target.id === this.markTopicPendingId || e.target.closest(`#${this.markTopicPendingId}`);
if (isClickedPending) {
this.markAsPending(this.activeTopicId);
this.close();
}
const isClickedClose = e.target.id === this.closeTopicId || e.target.closest(`#${this.closeTopicId}`);
if (isClickedClose) {
this.close();
}
}
init() {
window.addEventListener('topic.click', this.handleTopicClick);
window.addEventListener('click', this.handleOverlayClick);
window.addEventListener('keydown', (e) => {
if (e.key.toLowerCase() === 'escape') {
this.close();
}
});
this.loaderEl.classList.remove('hidden'); // Show loader
this.topicActionsEl.classList.add('hidden'); // Hide Actions
this.contributionTextEl.classList.add('hidden'); // Hide contribution text
this.contentEl.replaceChildren(''); // Remove content
}
close() {
this.resetDOM(true);
this.activeRoadmapId = null;
this.activeTopicId = null;
}
/**
* @param {string | HTMLElement} html
*/
populate(html) {
this.contentEl.replaceChildren(html);
this.loaderEl.classList.add('hidden');
this.topicActionsEl.classList.remove('hidden');
this.contributionTextEl.classList.remove('hidden');
const normalizedGroup = (this.activeTopicId || '').replace(/^\d+-/, '');
const isDone = localStorage.getItem(normalizedGroup) === 'done';
if (isDone) {
this.markTopicDoneEl.classList.add('hidden');
this.markTopicPendingEl.classList.remove('hidden');
} else {
this.markTopicDoneEl.classList.remove('hidden');
this.markTopicPendingEl.classList.add('hidden');
}
}
fetchTopicHtml(roadmapId, topicId) {
const topicPartial = topicId.replace(/^\d+-/, '').replaceAll(/:/g, '/');
const fullUrl = `/${roadmapId}/${topicPartial}/`;
return fetch(fullUrl)
.then((res) => {
return res.text();
})
.then((topicHtml) => {
// It's full HTML with page body, head etc.
// We only need the inner HTML of the #main-content
const node = new DOMParser().parseFromString(topicHtml, 'text/html');
return node.getElementById('main-content');
});
}
handleTopicClick(e) {
const { roadmapId, topicId } = e.detail;
if (!topicId || !roadmapId) {
console.log('Missing topic or roadmap: ', e.detail);
return;
}
this.activeRoadmapId = roadmapId;
this.activeTopicId = topicId;
if (/^ext_link/.test(topicId)) {
window.open(`https://${topicId.replace('ext_link:', '')}`);
return;
}
this.resetDOM();
this.fetchTopicHtml(roadmapId, topicId)
.then((content) => {
this.populate(content);
})
.catch((e) => {
console.error(e);
this.populate('Error loading the content!');
});
}
queryRoadmapElementsByTopicId(topicId) {
const elements = document.querySelectorAll(
`[data-group-id$="-${topicId}"]`
);
const matchingElements = [];
elements.forEach((element) => {
const foundGroupId = element?.dataset?.groupId || '';
const validGroupRegex = new RegExp(`^\\d+-${topicId}$`);
if (validGroupRegex.test(foundGroupId)) {
matchingElements.push(element);
}
});
return matchingElements;
}
markAsDone(topicId) {
const updatedTopicId = topicId.replace(/^\d+-/, '');
localStorage.setItem(updatedTopicId, 'done');
this.queryRoadmapElementsByTopicId(updatedTopicId).forEach((item) => {
item?.classList?.add('done');
});
}
markAsPending(topicId) {
const updatedTopicId = topicId.replace(/^\d+-/, '');
localStorage.removeItem(updatedTopicId);
this.queryRoadmapElementsByTopicId(updatedTopicId).forEach((item) => {
item?.classList?.remove('done');
});
}
handleOverlayClick(e) {
const isClickedInsideTopic = e.target.closest(`#${this.topicBodyId}`);
if (!isClickedInsideTopic) {
this.close();
return;
}
const isClickedDone =
e.target.id === this.markTopicDoneId ||
e.target.closest(`#${this.markTopicDoneId}`);
if (isClickedDone) {
this.markAsDone(this.activeTopicId);
this.close();
}
const isClickedPending =
e.target.id === this.markTopicPendingId ||
e.target.closest(`#${this.markTopicPendingId}`);
if (isClickedPending) {
this.markAsPending(this.activeTopicId);
this.close();
}
const isClickedClose =
e.target.id === this.closeTopicId ||
e.target.closest(`#${this.closeTopicId}`);
if (isClickedClose) {
this.close();
}
}
init() {
window.addEventListener('topic.click', this.handleTopicClick);
window.addEventListener('click', this.handleOverlayClick);
window.addEventListener('keydown', (e) => {
if (e.key.toLowerCase() === 'escape') {
this.close();
}
});
}
}

View File

@@ -1,9 +1,24 @@
---
import "../styles/prism.css";
import '../styles/prism.css';
import DownloadPopup from './DownloadPopup.astro';
import ShareIcons from './ShareIcons.astro';
import SubscribePopup from './SubscribePopup.astro';
export interface Props {
roadmapId: string;
description: string;
}
const { roadmapId, description } = Astro.props;
---
<div class="bg-gray-50 py-4 sm:py-10">
<div class="container prose prose-headings:mt-4 prose-headings:mb-2 prose-p:mb-0.5">
<div class='bg-gray-50 py-4 sm:py-10'>
<div
class='container prose prose-headings:mt-4 prose-headings:mb-2 prose-p:mb-0.5 relative prose-code:text-white'
>
<DownloadPopup />
<SubscribePopup />
<slot />
</div>
</div>

View File

@@ -32,24 +32,22 @@ import Icon from './Icon.astro';
<!-- Mobile Navigation Button -->
<button
id='show-mobile-navigation'
class='text-gray-400 hover:text-gray-50 block sm:hidden cursor-pointer'
aria-label='Menu'
onclick="document.getElementById('mobile-navigation').classList.remove('hidden');"
show-mobile-nav
>
<Icon icon='hamburger' />
</button>
<!-- Mobile Navigation Items -->
<div
id='mobile-navigation'
class='fixed top-0 bottom-0 left-0 right-0 z-40 bg-slate-900 items-center flex hidden'
mobile-nav
>
<button
id='close-mobile-navigation'
close-mobile-nav
class='text-gray-400 hover:text-gray-50 block cursor-pointer absolute top-6 right-6'
aria-label='Close Menu'
onclick="document.getElementById('mobile-navigation').classList.add('hidden');"
>
<Icon icon='close' />
</button>
@@ -80,3 +78,15 @@ import Icon from './Icon.astro';
</div>
</nav>
</div>
<script>
document.querySelector('[show-mobile-nav]')?.addEventListener('click', () => {
document.querySelector('[mobile-nav]')?.classList.remove('hidden');
});
document
.querySelector('[close-mobile-nav]')
?.addEventListener('click', () => {
document.querySelector('[mobile-nav]')?.classList.add('hidden');
});
</script>

View File

@@ -7,7 +7,7 @@ const starCount = await getFormattedStars('kamranahmedse/developer-roadmap');
<div class='py-6 sm:py-16 border-b border-t text-left sm:text-center bg-white'>
<div class='max-w-[600px] container'>
<h1 class='text-3xl sm:text-5xl font-bold'>Open Source</h1>
<h2 class='text-2xl sm:text-5xl font-bold'>Open Source</h2>
<p class='text-gray-600 text-sm sm:text-lg leading-relaxed my-2.5 sm:my-5'>
The project is OpenSource, <a
href='https://github.com/search?o=desc&q=stars%3A%3E100000&s=stars&type=Repositories'

View File

@@ -21,8 +21,7 @@ const { id, title, subtitle } = Astro.props;
<div class='relative bg-white rounded-lg shadow popup-body'>
<button
type='button'
class='absolute top-3 right-2.5 text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 ml-auto inline-flex items-center'
onclick='this.closest(".popup").classList.add("hidden")'
class='absolute top-3 right-2.5 text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 ml-auto inline-flex items-center popup-close'
>
<Icon icon='close' />
<span class='sr-only'>Close popup</span>

View File

@@ -31,8 +31,9 @@ export class Popup {
const target = e.target;
const popupBody = target.closest('.popup-body');
const closestPopup = target.closest('.popup');
const closeBtn = target.closest('.popup-close');
if (popupBody) {
if (!closeBtn && popupBody) {
return;
}

View File

@@ -16,7 +16,7 @@ const roadmapTitle =
<div
class:list={[
'mt-4 sm:mt-7 border rounded-md mb-0',
'mt-4 sm:mt-7 border-0 sm:border rounded-md mb-0',
{
'sm:-mb-[82px]': hasTNSBanner,
'sm:-mb-[65px]': !hasTNSBanner,
@@ -29,10 +29,12 @@ const roadmapTitle =
<p class='text-sm'>
Get the latest {roadmapTitle} news from our sister site{' '}
<a
href='https://thenewstack.io'
href='https://thenewstack.io?utm_source=roadmap.sh&utm_medium=Referral&utm_campaign=Alert'
target='_blank'
class='font-semibold underline'
onclick="window.fireEvent({ category: 'PartnerClick', action: `TNS Referral`, label: `TNS Referral - Roadmap` })"
ga-category='PartnerClick'
ga-action='TNS Referral'
ga-label='TNS Referral - Roadmap'
>
TheNewStack.io
</a>

View File

@@ -1,8 +1,8 @@
---
import Icon from "./Icon.astro";
import ResourcesAlert from "./ResourcesAlert.astro";
import TopicSearch from "./TopicSearch/TopicSearch.astro";
import YouTubeAlert from "./YouTubeAlert.astro";
import Icon from './Icon.astro';
import ResourcesAlert from './ResourcesAlert.astro';
import TopicSearch from './TopicSearch/TopicSearch.astro';
import YouTubeAlert from './YouTubeAlert.astro';
export interface Props {
title: string;
@@ -22,48 +22,57 @@ const {
hasTopics = false,
} = Astro.props;
const isRoadmapReady = !isUpcoming;
---
<div class="border-b">
<div class="py-5 sm:py-12 container relative">
<div class='border-b'>
<div class='py-5 sm:py-12 container relative'>
<YouTubeAlert />
<div class="mt-0 mb-3 sm:mb-6 sm:mt-4">
<h1 class="text-2xl sm:text-4xl mb-0.5 sm:mb-2 font-bold">
<div class='mt-0 mb-3 sm:mb-6 sm:mt-4'>
<h1 class='text-2xl sm:text-4xl mb-0.5 sm:mb-2 font-bold'>
{title}
</h1>
<p class="text-gray-500 text-sm sm:text-lg">{description}</p>
<p class='text-gray-500 text-sm sm:text-lg'>{description}</p>
</div>
<div class="flex justify-between">
<div class="flex gap-1 sm:gap-2">
<div class='flex justify-between'>
<div class='flex gap-1 sm:gap-2'>
{
!hasSearch && (
<>
<a href='/roadmaps' class='bg-gray-500 py-1.5 px-3 rounded-md text-white text-xs sm:text-sm font-medium hover:bg-gray-600' aria-label="Back to All Roadmaps">
&larr;<span class='hidden sm:inline'>&nbsp;All Roadmaps</span>
</a>
<a
href='/roadmaps/'
class='bg-gray-500 py-1.5 px-3 rounded-md text-white text-xs sm:text-sm font-medium hover:bg-gray-600'
aria-label='Back to All Roadmaps'
>
&larr;<span class='hidden sm:inline'>&nbsp;All Roadmaps</span>
</a>
{isRoadmapReady && (
<button
data-popup="download-popup"
class="inline-flex items-center justify-center bg-yellow-400 py-1.5 px-3 text-xs sm:text-sm font-medium rounded-md hover:bg-yellow-500"
aria-label="Download Roadmap"
data-popup='download-popup'
class='inline-flex items-center justify-center bg-yellow-400 py-1.5 px-3 text-xs sm:text-sm font-medium rounded-md hover:bg-yellow-500'
aria-label='Download Roadmap'
ga-category='Subscription'
ga-action='Clicked Popup Opener'
ga-label='Download Roadmap Popup'
>
<Icon icon="download" />
<span class="hidden sm:inline ml-2">Download</span>
<Icon icon='download' />
<span class='hidden sm:inline ml-2'>Download</span>
</button>
)}
<button
data-popup="subscribe-popup"
class="inline-flex items-center justify-center bg-yellow-400 py-1.5 px-3 text-xs sm:text-sm font-medium rounded-md hover:bg-yellow-500"
aria-label="Subscribe for Updates"
data-popup='subscribe-popup'
class='inline-flex items-center justify-center bg-yellow-400 py-1.5 px-3 text-xs sm:text-sm font-medium rounded-md hover:bg-yellow-500'
aria-label='Subscribe for Updates'
ga-category='Subscription'
ga-action='Clicked Popup Opener'
ga-label='Subscribe Roadmap Popup'
>
<Icon icon="email" />
<span class="ml-2">Subscribe</span>
<Icon icon='email' />
<span class='ml-2'>Subscribe</span>
</button>
</>
)
@@ -73,11 +82,11 @@ const isRoadmapReady = !isUpcoming;
hasSearch && (
<a
href={`/${roadmapId}/`}
class="bg-gray-500 py-1.5 px-3 rounded-md text-white text-xs sm:text-sm font-medium hover:bg-gray-600"
aria-label="Back to Visual Roadmap"
class='bg-gray-500 py-1.5 px-3 rounded-md text-white text-xs sm:text-sm font-medium hover:bg-gray-600'
aria-label='Back to Visual Roadmap'
>
&larr;
<span class="inline">&nbsp;Visual Roadmap</span>
<span class='inline'>&nbsp;Visual Roadmap</span>
</a>
)
}
@@ -87,13 +96,13 @@ const isRoadmapReady = !isUpcoming;
isRoadmapReady && (
<a
href={`https://github.com/kamranahmedse/developer-roadmap/issues/new?title=[Suggestion] ${title}`}
target="_blank"
class="inline-flex items-center justify-center bg-gray-500 text-white py-1.5 px-3 text-xs sm:text-sm font-medium rounded-md hover:bg-gray-600"
aria-label="Suggest Changes"
target='_blank'
class='inline-flex items-center justify-center bg-gray-500 text-white py-1.5 px-3 text-xs sm:text-sm font-medium rounded-md hover:bg-gray-600'
aria-label='Suggest Changes'
>
<Icon icon="comment" class="h-3 w-3" />
<span class="ml-2 hidden sm:inline">Suggest Changes</span>
<span class="ml-2 inline sm:hidden">Suggest</span>
<Icon icon='comment' class='h-3 w-3' />
<span class='ml-2 hidden sm:inline'>Suggest Changes</span>
<span class='ml-2 inline sm:hidden'>Suggest</span>
</a>
)
}

View File

@@ -1,5 +1,6 @@
---
import type { GAEventType } from '../Analytics/analytics';
import Icon from '../Icon.astro';
export type SponsorType = {
url: string;
@@ -25,10 +26,19 @@ const {
id='sponsor-ad'
target='_blank'
rel='noopener sponsored'
onclick={event ? `window.fireEvent(${JSON.stringify(event)})` : ''}
ga-category={event?.category}
ga-action={event?.action}
ga-label={event?.label}
class='fixed bottom-[15px] right-[20px] outline-transparent z-50 bg-white max-w-[330px] shadow-lg outline-0 hidden'
>
<img src={imageUrl} class='w-[100px] lg:w-[130px]' alt='Sponsor Banner' />
<button
class='absolute top-1.5 right-1.5 text-gray-300 hover:text-gray-800'
aria-label='Close'
close-sponsor
>
<Icon icon='close' class='h-4' />
</button>
<img src={imageUrl} class='h-[150px] lg:h-[169px]' alt='Sponsor Banner' />
<span class='text-sm flex flex-col justify-between'>
<span class='p-[10px]'>
<span class='font-semibold mb-0.5 block'>{title}</span>
@@ -37,3 +47,11 @@ const {
<span class='sponsor-footer'>Partner Content</span>
</span>
</a>
<script>
document.querySelector('[close-sponsor]')?.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
document.getElementById('sponsor-ad')?.classList.add('hidden');
});
</script>

View File

@@ -33,6 +33,9 @@ import CaptchaFields from './Captcha/CaptchaFields.astro';
type='submit'
name='submit'
class='text-white bg-gradient-to-r from-amber-700 to-blue-800 hover:from-amber-800 hover:to-blue-900 font-regular rounded-md text-md px-5 py-2.5 w-full text-center mr-2'
ga-category='Subscription'
ga-action='Submitted Popup Form'
ga-label='Subscribe Roadmap Popup'
>
Subscribe
</button>

View File

@@ -1,31 +1,69 @@
---
import Icon from "./Icon.astro";
import Loader from "./Loader.astro";
import Icon from './Icon.astro';
import Loader from './Loader.astro';
export interface Props {
roadmapId: string;
}
const { roadmapId } = Astro.props;
const githubLink = `https://github.com/kamranahmedse/developer-roadmap/tree/master/src/roadmaps/${roadmapId}/content`;
---
<div id='topic-overlay' class='hidden'>
<div class="fixed top-0 right-0 z-40 h-screen p-4 sm:p-6 overflow-y-auto bg-white w-full sm:max-w-[600px]" tabindex="-1" id='topic-body'>
<div id='topic-loader' class='hidden'>
<Loader />
</div>
<div id='topic-actions' class='hidden mb-2'>
<button id='mark-topic-done' class='bg-green-600 text-white p-1 px-2 text-sm rounded-md hover:bg-green-700 inline-flex items-center'>
<Icon icon="check" /> <span class='ml-2'>Mark as Done</span>
</button>
<button id='mark-topic-pending' class='hidden bg-red-600 text-white p-1 px-2 text-sm rounded-md hover:bg-red-700 inline-flex items-center'>
<Icon icon="reset" /> <span class='ml-2'>Mark as Pending</span>
</button>
<button type="button" id='close-topic' class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 absolute top-2.5 right-2.5 inline-flex items-center">
<Icon icon="close" />
</button>
</div>
<div id='topic-content' class='prose prose-h1:mt-7 prose-h1:mb-2.5 prose-p:mt-0 prose-p:mb-2 prose-li:m-0 prose-li:mb-0.5 prose-h2:mb-3 prose-h2:mt-0'></div>
<div
class='fixed top-0 right-0 z-40 h-screen p-4 sm:p-6 overflow-y-auto bg-white w-full sm:max-w-[600px]'
tabindex='-1'
id='topic-body'
>
<div id='topic-loader' class='hidden'>
<Loader />
</div>
<div class="bg-gray-900 bg-opacity-50 dark:bg-opacity-80 fixed inset-0 z-30"></div>
<div id='topic-actions' class='hidden mb-2'>
<button
id='mark-topic-done'
class='bg-green-600 text-white p-1 px-2 text-sm rounded-md hover:bg-green-700 inline-flex items-center'
>
<Icon icon='check' />
<span class='ml-2'>Mark as Done</span>
</button>
<button
id='mark-topic-pending'
class='hidden bg-red-600 text-white p-1 px-2 text-sm rounded-md hover:bg-red-700 inline-flex items-center'
>
<Icon icon='reset' />
<span class='ml-2'>Mark as Pending</span>
</button>
<button
type='button'
id='close-topic'
class='text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 absolute top-2.5 right-2.5 inline-flex items-center'
>
<Icon icon='close' />
</button>
</div>
<div
id='topic-content'
class='prose prose-h1:mt-7 prose-h1:mb-2.5 prose-p:mt-0 prose-p:mb-2 prose-li:m-0 prose-li:mb-0.5 prose-h2:mb-3 prose-h2:mt-0'
>
</div>
<p
id='contrib-meta'
class='text-gray-400 text-sm border-t pt-3 mt-10 hidden'
>
We are still working on this page. You can contribute by submitting a
brief description and a few links to learn more about this topic <a
target='_blank'
class='underline text-blue-700'
href={githubLink}>on GitHub repository.</a
>.
</p>
</div>
<div class='bg-gray-900 bg-opacity-50 dark:bg-opacity-80 fixed inset-0 z-30'>
</div>
</div>

View File

@@ -20,7 +20,11 @@ const { author } = frontmatter;
target='_blank'
class='font-medium hover:text-gray-600 inline-flex items-center hover:underline'
>
<img src={author.imageUrl} class='w-5 h-5 inline mr-2 rounded-full' />
<img
alt={author.name}
src={author.imageUrl}
class='w-5 h-5 inline mr-2 rounded-full'
/>
{author.name}
</a>
<span class='mx-1.5'>&middot;</span>

View File

@@ -1,12 +1,33 @@
---
import Icon from "./Icon.astro";
import Icon from './Icon.astro';
---
<div class='sticky top-0 border-b border-b-yellow-300 z-10 flex h-[37px]' id='sticky-youtube-banner'>
<a href='https://youtube.com/theroadmap?sub_confirmation=1' target='_blank' class='flex bg-yellow-200 text-center flex-1 items-center justify-center text-sm hover:bg-yellow-300 outline-0 '>
<Icon icon="youtube" class="mr-2" /> We now have a YouTube Channel.&nbsp;<span class='hidden sm:inline'>Subscribe for the video content.</span>
</a>
<button class='text-yellow-500 bg-yellow-200 hover:text-yellow-900 hover:bg-yellow-400 outline-0 px-2' onclick='this.parentElement.classList.add("hidden")' aria-label="Close">
<Icon icon="close" />
</button>
</div>
<div
class='sticky top-0 border-b border-b-yellow-300 z-10 flex h-[37px]'
youtube-banner
>
<a
href='https://youtube.com/theroadmap?sub_confirmation=1'
target='_blank'
class='flex bg-yellow-200 text-center flex-1 items-center justify-center text-sm hover:bg-yellow-300 outline-0'
>
<Icon icon='youtube' class='mr-2' /> We now have a YouTube Channel.&nbsp;<span
class='hidden sm:inline'>Subscribe for the video content.</span
>
</a>
<button
class='text-yellow-500 bg-yellow-200 hover:text-yellow-900 hover:bg-yellow-400 outline-0 px-2'
aria-label='Close'
close-youtube-banner
>
<Icon icon='close' />
</button>
</div>
<script>
document
.querySelector('[close-youtube-banner]')
?.addEventListener('click', () => {
document.querySelector('[youtube-banner]').remove();
});
</script>

8
src/env.d.ts vendored
View File

@@ -1 +1,9 @@
/// <reference types="astro/client" />
interface ImportMetaEnv {
GITHUB_SHA: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}

View File

@@ -22,5 +22,5 @@ tags:
Asymptotic notation is the standard way of measuring the time and space that an algorithm will consume as the input grows. In one of my last guides, I covered "Big-O notation" and a lot of you asked for a similar one for Asymptotic notation. You can find the [previous guide here](/guides/big-o-notation).
[![](/guides/asymptotic-notation.png)](/guides/asymptotic-notation.png)
[![Asymptotic Notation](/guides/asymptotic-notation.png)](/guides/asymptotic-notation.png)

View File

@@ -20,5 +20,5 @@ tags:
- "guide-sitemap"
---
[![](/guides/avoid-render-blocking-javascript-with-async-defer.png)](/guides/avoid-render-blocking-javascript-with-async-defer.png)
[![Avoid Render Blocking with Async and Defer](/guides/avoid-render-blocking-javascript-with-async-defer.png)](/guides/avoid-render-blocking-javascript-with-async-defer.png)

View File

@@ -20,5 +20,5 @@ tags:
- "guide-sitemap"
---
[![](/guides/basic-authentication.png)](/guides/basic-authentication.png)
[![Basic Authentication](/guides/basic-authentication.png)](/guides/basic-authentication.png)

View File

@@ -22,5 +22,5 @@ tags:
Big-O notation is the mathematical notation that helps analyse the algorithms to get an idea about how they might perform as the input grows. The image below explains Big-O in a simple way without using any fancy terminology.
[![](/guides/big-o-notation.png)](/guides/big-o-notation.png)
[![Big-O Notation](/guides/big-o-notation.png)](/guides/big-o-notation.png)

View File

@@ -20,5 +20,5 @@ tags:
- "guide-sitemap"
---
[![](/guides/character-encodings.png)](/guides/character-encodings.png)
[![Character Encodings](/guides/character-encodings.png)](/guides/character-encodings.png)

View File

@@ -22,5 +22,5 @@ tags:
The image below details the differences between the continuous integration and continuous delivery. Also, here is the [accompanying video on implementing that with GitHub actions](https://www.youtube.com/watch?v=nyKZTKQS_EQ).
[![](/guides/ci-cd.png)](/guides/ci-cd.png)
[![CI vs CD](/guides/ci-cd.png)](/guides/ci-cd.png)

View File

@@ -20,5 +20,5 @@ tags:
- "guide-sitemap"
---
[![](/guides/dhcp.png)](/guides/dhcp.png)
[![DHCP in One Picture](/guides/dhcp.png)](/guides/dhcp.png)

View File

@@ -24,4 +24,4 @@ DNS or Domain Name System is one of the fundamental blocks of the internet. As a
DNS at its simplest is like a phonebook on your mobile phone. Whenever you have to call one of your contacts, you can either dial their number from your memory or use their name which will then be used by your mobile phone to search their number in your phone book to call them. Every time you make a new friend, or your existing friend gets a mobile phone, you have to memorize their phone number or save it in your phonebook to be able to call them later on. DNS or Domain Name System, in a similar fashion, is a mechanism that allows you to browse websites on the internet. Just like your mobile phone does not know how to call without knowing the phone number, your browser does not know how to open a website just by the domain name; it needs to know the IP Address for the website to open. You can either type the IP Address to open, or provide the domain name and press enter which will then be used by your browser to find the IP address by going through several hoops. The picture below is the illustration of how your browser finds a website on the internet.
[![](https://i.imgur.com/z9rwm5A.png)](https://i.imgur.com/z9rwm5A.png)
[![DNS in One Picture](https://i.imgur.com/z9rwm5A.png)](https://i.imgur.com/z9rwm5A.png)

View File

@@ -31,7 +31,7 @@ Given the name "Basic Authentication", you should not confuse Basic Authenticati
Because it is a part of the HTTP specifications, all the browsers have native support for "HTTP Basic Authentication". Given below is the screenshot from the implementation in Google Chrome.
![](/guides/basic-authentication/chrome-basic-auth.png)
![Chrome Basic Authentication](/guides/basic-authentication/chrome-basic-auth.png)
## How does it Work?
@@ -52,7 +52,7 @@ The browser might use Realm to cache the credential. In the future, when there i
## Step 2
Upon receiving the response from the server, the browser will notice the `WWW-Authenticate` header and will show the authentication popup.
![](/guides/basic-authentication/chrome-basic-auth.png)
![Chrome Basic Authentication](/guides/basic-authentication/chrome-basic-auth.png)
## Step 3
After the user submits the credentials through this authentication popup, the browser will automatically encode the credentials using the `base64` encoding and send them in the `Authorization` header of the same request.

View File

@@ -39,7 +39,7 @@ Before we get into further details, let me give you an overview of the terms tha
- **Cache Validation** is the process of contacting the server to check the validity of the cached content and get it updated for when it is going to expire
- **Cache Invalidation** is the process of removing any stale content available in the cache
![](https://i.imgur.com/9MjlzvW.png)
![Web Cache](https://i.imgur.com/9MjlzvW.png)
### Caching Locations
@@ -75,21 +75,30 @@ Although you can control the reverse proxy caches (since it is implemented by yo
So, how do we control the web cache? Whenever the server emits some response, it is accompanied by some HTTP headers to guide the caches on whether and how to cache this response. The content provider is the one that has to make sure to return proper HTTP headers to force the caches on how to cache the content.
- [Expires](#expires)
- [Pragma](#pragma)
- [Cache-Control](#cache-control)
- [private](#private)
- [public](#public)
- [no-store](#no-store)
- [no-cache](#no-cache)
- [max-age: seconds](#max-age)
- [s-maxage: seconds](#s-maxage)
- [must-revalidate](#must-revalidate)
- [proxy-revalidate](#proxy-revalidate)
- [Mixing Values](#mixing-values)
- [Introduction](#introduction)
- [Caching Locations](#caching-locations)
- [Browser Cache](#browser-cache)
- [Proxy Cache](#proxy-cache)
- [Reverse Proxy Cache](#reverse-proxy-cache)
- [Caching Headers](#caching-headers)
- [Expires](#expires)
- [Pragma](#pragma)
- [Cache-Control](#cache-control)
- [private](#private)
- [public](#public)
- [no-store](#no-store)
- [no-cache](#no-cache)
- [max-age: seconds](#max-age-seconds)
- [s-maxage: seconds](#s-maxage-seconds)
- [must-revalidate](#must-revalidate)
- [proxy-revalidate](#proxy-revalidate)
- [Mixing Values](#mixing-values)
- [Validators](#validators)
- [ETag](#etag)
- [Last-Modified](#last-modified)
- [Where do I start?](#where-do-i-start)
- [Utilizing Server](#utilizing-server)
- [Caching Recommendations](#caching-recommendations)
#### Expires

View File

@@ -95,7 +95,7 @@ Three-way handshake in its simplest form is that all the `TCP` connections begin
Once the three-way handshake is completed, the data sharing between the client and server may begin. It should be noted that the client may start sending the application data as soon as it dispatches the last `ACK` packet but the server will still have to wait for the `ACK` packet to be received in order to fulfill the request.
![](http://i.imgur.com/uERG2G2.png)
![Three-way Handshake](https://i.imgur.com/uERG2G2.png)
> Please note that there is a minor issue with the image, the last `ACK` packet sent by the client to end the handshake contains only `y+1` i.e. it should have been `ACK:y+1` instead of `ACK: x+1, y+1`
@@ -131,7 +131,7 @@ After merely 3 years of `HTTP/1.0`, the next version i.e. `HTTP/1.1` was release
- New status codes
- ..and more
I am not going to dwell about all the `HTTP/1.1` features in this post as it is a topic in itself and you can already find a lot about it. The one such document that I would recommend you to read is [Key differences between `HTTP/1.0` and HTTP/1.1](http://www.ra.ethz.ch/cdstore/www8/data/2136/pdf/pd1.pdf) and here is the link to [original RFC](https://tools.ietf.org/html/rfc2616) for the overachievers.
I am not going to dwell about all the `HTTP/1.1` features in this post as it is a topic in itself and you can already find a lot about it. The one such document that I would recommend you to read is [Key differences between `HTTP/1.0` and HTTP/1.1](https://www.ra.ethz.ch/cdstore/www8/data/2136/pdf/pd1.pdf) and here is the link to [original RFC](https://tools.ietf.org/html/rfc2616) for the overachievers.
`HTTP/1.1` was introduced in 1999 and it had been a standard for many years. Although, it improved alot over its predecessor; with the web changing everyday, it started to show its age. Loading a web page these days is more resource-intensive than it ever was. A simple webpage these days has to open more than 30 connections. Well `HTTP/1.1` has persistent connections, then why so many connections? you say! The reason is, in `HTTP/1.1` it can only have one outstanding connection at any moment of time. `HTTP/1.1` tried to fix this by introducing pipelining but it didn't completely address the issue because of the **head-of-line blocking** where a slow or heavy request may block the requests behind and once a request gets stuck in a pipeline, it will have to wait for the next requests to be fulfilled. To overcome these shortcomings of `HTTP/1.1`, the developers started implementing the workarounds, for example use of spritesheets, encoded images in CSS, single humongous CSS/Javascript files, [domain sharding](https://www.maxcdn.com/one/visual-glossary/domain-sharding-2/) etc.
@@ -162,7 +162,7 @@ By now, you must be convinced that why we needed another revision of the HTTP pr
- Request Prioritization
- Security
![](http://i.imgur.com/S85j8gg.png)
![HTTP Model](https://i.imgur.com/S85j8gg.png)
#### 1. Binary Protocol
@@ -187,7 +187,7 @@ Since `HTTP/2` is now a binary protocol and as I said above that it uses frames
It was part of a separate RFC which was specifically aimed at optimizing the sent headers. The essence of it is that when we are constantly accessing the server from a same client there is alot of redundant data that we are sending in the headers over and over, and sometimes there might be cookies increasing the headers size which results in bandwidth usage and increased latency. To overcome this, `HTTP/2` introduced header compression.
![](http://i.imgur.com/3IPWXvR.png)
![Header Compression](https://i.imgur.com/3IPWXvR.png)
Unlike request and response, headers are not compressed in `gzip` or `compress` etc formats but there is a different mechanism in place for header compression which is literal values are encoded using Huffman code and a headers table is maintained by the client and server and both the client and server omit any repetitive headers (e.g. user agent etc) in the subsequent requests and reference them using the headers table maintained by both.
@@ -210,8 +210,8 @@ Without any priority information, server processes the requests asynchronously i
There was extensive discussion on whether security (through `TLS`) should be made mandatory for `HTTP/2` or not. In the end, it was decided not to make it mandatory. However, most vendors stated that they will only support `HTTP/2` when it is used over `TLS`. So, although `HTTP/2` doesn't require encryption by specs but it has kind of become mandatory by default anyway. With that out of the way, `HTTP/2` when implemented over `TLS` does impose some requirements i.e. `TLS` version `1.2` or higher must be used, there must be a certain level of minimum key sizes, ephemeral keys are required etc.
`HTTP/2` is here and it has already [surpassed SPDY in adaption](http://caniuse.com/#search=http2) which is gradually increasing. `HTTP/2` has alot to offer in terms of performance gain and it is about time we should start using it.
`HTTP/2` is here and it has already [surpassed SPDY in adaption](https://caniuse.com/#search=http2) which is gradually increasing. `HTTP/2` has alot to offer in terms of performance gain and it is about time we should start using it.
For anyone interested in further details here is the [link to specs](https:/http2.github.iohttp2-spec) and a link [demonstrating the performance benefits of `HTTP/2`](http://www.http2demo.io/).
For anyone interested in further details here is the [link to specs](https:/http2.github.iohttp2-spec) and a link [demonstrating the performance benefits of `HTTP/2`](https://www.http2demo.io/).
And that about wraps it up. Until next time! stay tuned.

View File

@@ -20,5 +20,5 @@ tags:
- "guide-sitemap"
---
[![](/guides/jwt-authentication.png)](/guides/jwt-authentication.png)
[![JWT Authentication](/guides/jwt-authentication.png)](/guides/jwt-authentication.png)

View File

@@ -20,5 +20,5 @@ tags:
- "guide-sitemap"
---
[![](/guides/oauth.png)](/guides/oauth.png)
[![OAuth - Open Authorization](/guides/oauth.png)](/guides/oauth.png)

View File

@@ -22,5 +22,5 @@ tags:
Random numbers are everywhere from computer games to lottery systems, graphics software, statistical sampling, computer simulation and cryptography. Graphic below is a quick explanation to how the random numbers are generated and why they may not be truly random.
[![](/guides/random-numbers.png)](/guides/random-numbers.png)
[![Random Numbers](/guides/random-numbers.png)](/guides/random-numbers.png)

View File

@@ -22,5 +22,5 @@ tags:
The chart below aims to give you a really basic understanding of how the capability of a DBMS is increased to handle a growing amount of load.
[![](/guides/scaling-databases.svg)](/guides/scaling-databases.svg)
[![Scaling Databases](/guides/scaling-databases.svg)](/guides/scaling-databases.svg)

View File

@@ -20,5 +20,5 @@ tags:
- "guide-sitemap"
---
[![](/guides/session-authentication.png)](/guides/session-authentication.png)
[![Session Authentication](/guides/session-authentication.png)](/guides/session-authentication.png)

View File

@@ -20,5 +20,5 @@ tags:
- "guide-sitemap"
---
[![](/guides/ssl-tls-https-ssh.png)](/guides/ssl-tls-https-ssh.png)
[![SSL vs TLS vs HTTPs vs SSH](/guides/ssl-tls-https-ssh.png)](/guides/ssl-tls-https-ssh.png)

View File

@@ -20,5 +20,5 @@ tags:
- "guide-sitemap"
---
[![](/guides/sso.png)](/guides/sso.png)
[![SSO](/guides/sso.png)](/guides/sso.png)

View File

@@ -20,5 +20,5 @@ tags:
- "guide-sitemap"
---
[![](/guides/token-authentication.png)](/guides/token-authentication.png)
[![Token based Authentication](/guides/token-authentication.png)](/guides/token-authentication.png)

View File

@@ -20,5 +20,5 @@ tags:
- "guide-sitemap"
---
[![](/guides/unfamiliar-codebase.png)](/guides/unfamiliar-codebase.png)
[![Unfamiliar Codebase](/guides/unfamiliar-codebase.png)](/guides/unfamiliar-codebase.png)

View File

@@ -20,5 +20,5 @@ tags:
- "guide-sitemap"
---
[![](/guides/web-vitals.png)](/guides/web-vitals.png)
[![Web Vitals](/guides/web-vitals.png)](/guides/web-vitals.png)

View File

@@ -81,4 +81,4 @@ Cybersecurity refers to the protective measures against criminal activity accomp
<iframe class="w-full aspect-video mb-5" width="100%" height="400" src="https://www.youtube.com/embed/AuYNXgO_f3Y" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
And that wraps it up for this article. To learn more about the Internet, [Kamran Ahmed](https://twitter.com/kamranahmedse) has a nice little guide on [DNS: How a website is found on the Internet](/guides/dns-in-one-picture). Also, go through the episodes of [howdns.works](https://howdns.works/) and read this [cartoon intro to DNS over HTTPS](https://hacks.mozilla.org/2018/05/a-cartoon-intro-to-dns-over-https/).
And that wraps it up for this article. To learn more about the Internet, [Kamran Ahmed](https://twitter.com/kamranahmedse) has a nice little guide on [DNS: How a website is found on the Internet](/guides/dns-in-one-picture/). Also, go through the episodes of [howdns.works](https://howdns.works/) and read this [cartoon intro to DNS over HTTPS](https://hacks.mozilla.org/2018/05/a-cartoon-intro-to-dns-over-https/).

View File

@@ -20,5 +20,5 @@ tags:
- "guide-sitemap"
---
[![](/guides/sli-slo-sla.jpeg)](/guides/sli-slo-sla.jpeg)
[![SLI vs SLO vs SLA](/guides/sli-slo-sla.jpeg)](/guides/sli-slo-sla.jpeg)

4
src/icons/down.svg Normal file
View File

@@ -0,0 +1,4 @@
<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg">
<path d="m16.843 10.211c.108-.141.157-.3.157-.456 0-.389-.306-.755-.749-.755h-8.501c-.445 0-.75.367-.75.755 0 .157.05.316.159.457 1.203 1.554 3.252 4.199 4.258 5.498.142.184.36.29.592.29.23 0 .449-.107.591-.291 1.002-1.299 3.044-3.945 4.243-5.498z" fill="currentColor" />
</svg>

After

Width:  |  Height:  |  Size: 433 B

View File

@@ -14,6 +14,7 @@ export interface Props {
description?: string;
keywords?: string[];
noIndex?: boolean;
permalink?: string;
sponsor?: SponsorType;
}
@@ -22,8 +23,16 @@ const {
description = siteConfig.description,
keywords = siteConfig.keywords,
noIndex = false,
permalink = '',
sponsor,
} = Astro.props;
// Remove trailing slashes to consider the page as canonical
const currentPageAbsoluteUrl = `https://roadmap.sh${permalink}`;
const commitUrl = `https://github.com/kamranahmedse/developer-roadmap/commit/${
import.meta.env.GITHUB_SHA
}`;
---
<!DOCTYPE html>
@@ -31,6 +40,7 @@ const {
<head>
<meta charset='UTF-8' />
<meta name='generator' content={Astro.generator} />
<meta name='commit' content={commitUrl} />
<title>{title}</title>
<meta name='description' content={description} />
<meta name='author' content='Kamran Ahmed' />
@@ -53,7 +63,9 @@ const {
<meta property='og:title' content={title} />
<meta property='og:description' content={description} />
<meta property='og:type' content='website' />
<meta property='og:url' content='https://roadmap.sh' />
<meta property='og:url' content={currentPageAbsoluteUrl} />
<link rel='canonical' href={currentPageAbsoluteUrl} />
<meta name='mobile-web-app-capable' content='yes' />
<meta name='apple-mobile-web-app-capable' content='yes' />

View File

@@ -2,6 +2,7 @@ const formatter = Intl.NumberFormat('en-US', {
notation: 'compact',
});
const defaultStarCount = 224000;
let starCount: number | undefined = undefined;
export async function countStars(
@@ -15,10 +16,10 @@ export async function countStars(
const repoData = await fetch(`https://api.github.com/repos/${repo}`);
const json = await repoData.json();
starCount = json.stargazers_count * 1;
starCount = json.stargazers_count * 1 || defaultStarCount;
} catch (e) {
console.log('Failed to fetch stars', e);
starCount = 224000;
starCount = defaultStarCount;
}
return starCount;
@@ -27,11 +28,7 @@ export async function countStars(
export async function getFormattedStars(
repo = 'kamranahmedse/developer-roadmap'
): Promise<string> {
if (import.meta.env.DEV) {
return '224k';
}
const stars = await countStars(repo);
const stars = import.meta.env.DEV ? defaultStarCount : await countStars(repo);
return formatter.format(stars);
}

View File

@@ -3,7 +3,7 @@ import Icon from '../components/Icon.astro';
import BaseLayout from '../layouts/BaseLayout.astro';
---
<BaseLayout title='Page not found'>
<BaseLayout title='Page not found' permalink={'/404/'}>
<div class='bg-gray-100'>
<div
class='py-10 md:py-32 container flex flex-col md:flex-row items-center justify-center gap-7 '

View File

@@ -15,16 +15,24 @@ export async function getStaticPaths() {
}
const { topicId } = Astro.params;
const { file, breadcrumbs, roadmapId, roadmap, heading } = Astro.props as TopicFileType;
const { file, breadcrumbs, roadmapId, roadmap, heading } =
Astro.props as TopicFileType;
---
<BaseLayout title={`${heading} - roadmap.sh`} description={`Free resources to learn ${heading} in ${roadmap.featuredTitle}. Everything you need to know about ${heading} and how it realtes to ${roadmap.featuredTitle}.`} noIndex={true}>
<BaseLayout
title={`${heading} - roadmap.sh`}
description={`Free resources to learn ${heading} in ${roadmap.featuredTitle}. Everything you need to know about ${heading} and how it realtes to ${roadmap.featuredTitle}.`}
noIndex={true}
permalink={`/${topicId}/`}
>
<RoadmapBanner roadmapId={roadmapId} roadmap={roadmap} />
<div class="bg-gray-50">
<div class='bg-gray-50'>
<Breadcrumbs breadcrumbs={breadcrumbs} roadmapId={roadmapId} />
<div class="container pb-16 prose prose-p:mt-0 prose-h1:mb-4 prose-h2:mb-3 prose-h2:mt-0">
<main id="main-content">
<div
class='container pb-16 prose prose-p:mt-0 prose-h1:mb-4 prose-h2:mb-3 prose-h2:mt-0'
>
<main id='main-content'>
<file.Content />
</main>
</div>

View File

@@ -1,5 +1,6 @@
---
import CaptchaScripts from '../../components/Captcha/CaptchaScripts.astro';
import FAQs from '../../components/FAQs.astro';
import InteractiveRoadmap from '../../components/InteractiveRoadmap/InteractiveRoadmap.astro';
import MarkdownRoadmap from '../../components/MarkdownRoadmap.astro';
import RoadmapHeader from '../../components/RoadmapHeader.astro';
@@ -21,10 +22,12 @@ interface Params extends Record<string, string | undefined> {
const { roadmapId } = Astro.params as Params;
const roadmapFile = await import(`../../roadmaps/${roadmapId}/${roadmapId}.md`);
const questions = await import (`../../roadmaps/${roadmapId}/faqs.astro`);
const roadmapData = roadmapFile.frontmatter as RoadmapFrontmatter;
---
<BaseLayout
permalink={`/${roadmapId}/`}
title={roadmapData?.seo?.title}
description={roadmapData.seo.description}
keywords={roadmapData.seo.keywords}
@@ -52,7 +55,10 @@ const roadmapData = roadmapFile.frontmatter as RoadmapFrontmatter;
{
!roadmapData.isUpcoming && !roadmapData.jsonUrl && (
<MarkdownRoadmap>
<MarkdownRoadmap
roadmapId={roadmapId}
description={roadmapData.description}
>
<roadmapFile.Content />
</MarkdownRoadmap>
)
@@ -60,5 +66,6 @@ const roadmapData = roadmapFile.frontmatter as RoadmapFrontmatter;
{roadmapData.isUpcoming && <UpcomingRoadmap />}
<questions.default />
<CaptchaScripts slot='after-footer' />
</BaseLayout>

View File

@@ -22,7 +22,12 @@ const roadmapFile = await import(`../../roadmaps/${roadmapId}/${roadmapId}.md`);
const roadmapData = roadmapFile.frontmatter as RoadmapFrontmatter;
---
<BaseLayout title={`${roadmapData.title} Topics`} description={roadmapData.seo.description} keywords={roadmapData.seo.keywords}>
<BaseLayout
title={`${roadmapData.title} Topics`}
description={roadmapData.seo.description}
keywords={roadmapData.seo.keywords}
permalink={`/${roadmapId}/topics/`}
>
<RoadmapHeader
description={roadmapData.description}
title={`${roadmapData.featuredTitle} Topics`}
@@ -31,8 +36,8 @@ const roadmapData = roadmapFile.frontmatter as RoadmapFrontmatter;
hasTopics={false}
/>
<div class="bg-gray-50 pt-5 pb-8 sm:pt-10 sm:pb-16">
<div class="container">
<div class='bg-gray-50 pt-5 pb-8 sm:pt-10 sm:pb-16'>
<div class='container'>
{
topics.map((topic) => {
// Breadcrumbs have three additional items e.g.

View File

@@ -2,7 +2,7 @@
import BaseLayout from '../layouts/BaseLayout.astro';
---
<BaseLayout title='About roadmap.sh'>
<BaseLayout title='About roadmap.sh' permalink={'/about/'}>
<div class='bg-white border-b pt-7 pb-7 sm:pt-12 sm:pb-10'>
<div class='container'>
<div class='flex items-center'>

View File

@@ -22,7 +22,11 @@ const { guide } = Astro.props;
const { frontmatter: guideData } = guide;
---
<BaseLayout title={guideData.seo.title} description={guideData.seo.description}>
<BaseLayout
title={guideData.seo.title}
description={guideData.seo.description}
permalink={`/guides/${guideId}/`}
>
<GuideHeader guide={guide} />
<div class='bg-gray-50 py-5 sm:py-10'>

View File

@@ -7,7 +7,11 @@ import { getAllGuides } from '../../lib/guide';
const guides = await getAllGuides();
---
<BaseLayout title='Guides - roadmap.sh' description={'Detailed guides on Software Engineering Topics'}>
<BaseLayout
title='Guides - roadmap.sh'
description={'Detailed guides on Software Engineering Topics'}
permalink={`/guides/`}
>
<SimplePageHeader
title='Guides'
description='Succinct graphical explanations to engineering topics.'

View File

@@ -13,7 +13,7 @@ const guides = await getAllGuides();
const videos = await getAllVideos();
---
<BaseLayout title='Developer Roadmaps' description={"Community driven roadmaps, articles and guides for developers to grow in their career."}>
<BaseLayout title='Developer Roadmaps - roadmap.sh' description={"Community driven roadmaps, articles and guides for developers to grow in their career."} permalink={'/'}>
<div class='bg-gradient-to-b from-slate-900 to-black'>
<div class='border-b border-b-slate-900'>
<div

View File

@@ -15,6 +15,7 @@ Here is the list of PDF links for each of the roadmaps.
* **QA Roadmap** - [Roadmap Link](https://roadmap.sh/qa) / [PDF Link](https://roadmap.sh/pdfs/qa.pdf)
* **ASP.NET Core Roadmap** - [Roadmap Link](https://roadmap.sh/aspnet-core) / [PDF Link](https://roadmap.sh/pdfs/aspnet-core.pdf)
* **Flutter Roadmap** - [Roadmap Link](https://roadmap.sh/flutter) / [PDF Link](https://roadmap.sh/pdfs/flutter.pdf)
* **Go Roadmap** - [Roadmap Link](https://roadmap.sh/golang) / [PDF Link](https://roadmap.sh/pdfs/golang.pdf)
* **Software Architect Roadmap** - [Roadmap Link](https://roadmap.sh/software-architect) / [PDF Link](https://roadmap.sh/pdfs/software-architect.pdf)
* **Software Design and Architecture Roadmap** - [Roadmap Link](https://roadmap.sh/software-design-architecture) / [PDF Link](https://roadmap.sh/pdfs/software-design-architecture.pdf)
* **JavaScript Roadmap** - [Roadmap Link](https://roadmap.sh/javascript) / [PDF Link](https://roadmap.sh/pdfs/javascript.pdf)
@@ -25,6 +26,6 @@ Here is the list of PDF links for each of the roadmaps.
* **Vue Roadmap** - [Roadmap Link](https://roadmap.sh/vue) / [PDF Link](https://roadmap.sh/pdfs/vue.pdf)
* **Design System Roadmap** - [Roadmap Link](https://roadmap.sh/design-system) / [PDF Link](https://roadmap.sh/pdfs/design-system.pdf)
* **Blockchain Roadmap** - [Roadmap Link](https://roadmap.sh/blockchain) / [PDF Link](https://roadmap.sh/pdfs/blockchain.pdf)
* **Go Roadmap** - [Roadmap Link](https://roadmap.sh/golang) / [PDF Link](https://roadmap.sh/pdfs/go.pdf)
* **Java Roadmap** - [Roadmap Link](https://roadmap.sh/java) / [PDF Link](https://roadmap.sh/pdfs/java.pdf)
* **Spring Boot Roadmap** - [Roadmap Link](https://roadmap.sh/spring-boot) / [PDF Link](https://roadmap.sh/pdfs/spring-boot.pdf)
* **Python Roadmap** - [Roadmap Link](https://roadmap.sh/python) / [PDF Link](https://roadmap.sh/pdfs/python.pdf)

View File

@@ -1,11 +1,12 @@
---
layout: ../layouts/MarkdownLayout.astro
title: Privacy Policy - roadmap.sh
noIndex: true
---
# Privacy Policy
By using or accessing the Services in any manner, you acknowledge that you accept the practices and policies outlined in this Privacy Policy, and you hereby consent that we will collect, use, and share your information in the following ways. Remember that your use of roadmap.shs Services is at all times subject to the [Terms of Use](/terms), which incorporates this Privacy Policy. Any terms we use in this Policy without defining them have the definitions given to them in the Terms of Use.
By using or accessing the Services in any manner, you acknowledge that you accept the practices and policies outlined in this Privacy Policy, and you hereby consent that we will collect, use, and share your information in the following ways. Remember that your use of roadmap.shs Services is at all times subject to the [Terms of Use](/terms/), which incorporates this Privacy Policy. Any terms we use in this Policy without defining them have the definitions given to them in the Terms of Use.
## What does this Privacy Policy cover?

View File

@@ -8,16 +8,20 @@ const roleRoadmaps = await getRoadmapsByTag('role-roadmap');
const skillRoadmaps = await getRoadmapsByTag('skill-roadmap');
---
<BaseLayout title="Developer Roadmaps" description={"Step by step guides and paths to learn different tools or technologies"}>
<BaseLayout
title='Developer Roadmaps'
description={'Step by step guides and paths to learn different tools or technologies'}
permalink={'/roadmaps/'}
>
<SimplePageHeader
title="Developer Roadmaps"
description="Step by step guides and paths to learn different tools or technologies"
title='Developer Roadmaps'
description='Step by step guides and paths to learn different tools or technologies'
showYouTubeAlert={true}
/>
<div class="bg-gray-100 pt-4 pb-14 sm:pt-8 sm:pb-16">
<div class="container">
<div class="grid grid-cols-1 sm:grid-cols-2 gap-0.5 sm:gap-3">
<div class='bg-gray-100 pt-4 pb-14 sm:pt-8 sm:pb-16'>
<div class='container'>
<div class='grid grid-cols-1 sm:grid-cols-2 gap-0.5 sm:gap-3'>
{
roleRoadmaps.map((roleRoadmap) => (
<GridRoadmapItem roadmap={roleRoadmap} />

View File

@@ -7,6 +7,8 @@ import BaseLayout from '../layouts/BaseLayout.astro';
<BaseLayout
title='Signup - roadmap.sh'
description='Register yourself to receive occasional emails about new roadmaps, updates, guides and videos'
permalink={'/signup/'}
noIndex={true}
>
<div class='container'>
<div

View File

@@ -1,6 +1,7 @@
---
layout: ../layouts/MarkdownLayout.astro
title: Terms and Conditions - roadmap.sh
noIndex: true
---
# Terms of Service
@@ -21,7 +22,7 @@ Except for changes by us as described here, no other amendment or modification o
## Do these terms cover privacy?
You can view the current roadmap.sh [Privacy Policy here](/privacy).
You can view the current roadmap.sh [Privacy Policy here](/privacy/).
The Childrens Online Privacy Protection Act (“COPPA”) requires that online service providers obtain parental consent before they knowingly collect personally identifiable information online from children who are under 13. We do not knowingly collect or solicit personally identifiable information from children under 13. If you are a child under 13, please do not attempt to register for the Services or send any personal information about yourself to us. If we learn we have collected personal information from a child under 13, we will delete that information as quickly as possible. If you believe that a child under 13 may have provided us personal information, please contact us at kamranahmed.se@gmail.com.

View File

@@ -24,6 +24,7 @@ const { video } = Astro.props;
<BaseLayout
title={video.frontmatter.title}
description={video.frontmatter.description}
permalink={`/videos/${videoId}/`}
>
<VideoHeader video={video} />

View File

@@ -10,6 +10,7 @@ const videos = await getAllVideos();
<BaseLayout
title='Illustrated Videos - roadmap.sh'
description={'Graphical video demonstrations on software engineering topics.'}
permalink={`/videos/`}
>
<SimplePageHeader
title='Videos'

View File

@@ -119,7 +119,7 @@ I would highly recommend watching [this free course](https://www.udacity.com/cou
* [Developing Android Apps with Kotlin](https://www.udacity.com/course/developing-android-apps-with-kotlin--ud9012)
* [Android Basics in Kotlin](https://developer.android.com/courses/android-basics-kotlin/course)
* [Android Developer Guides](https://developer.android.com/guide)
* [Raywenderlich](https://www.raywenderlich.com)
* [Kodeco](https://www.kodeco.com)
## Wrap Up

View File

View File

@@ -4,5 +4,5 @@ A schematic is a template-based code generator that supports complex logic. It i
Visit the following resources to learn more:
- [Angular Website](https://angular.io/guide/schematics#:~:text=A%20schematic%20is%20a%20template,collections%20and%20installed%20with%20npm.)
- [Angular Website](https://angular.io/guide/schematics)
- [Angular Blog](https://blog.angular.io/schematics-an-introduction-dc1dfbc2a2b2?gi=ad9571373944)

View File

@@ -4,4 +4,4 @@ Angular processes all data bindings once for each JavaScript event cycle, from t
Visit the following resources to learn more:
- [Angular.io Website](https://angular.io/guide/architecture-components#:~:text=Angular%20processes%20all%20data%20bindings,between%20parent%20and%20child%20components.)
- [Angular.io Website](https://angular.io/guide/architecture-components)

View File

View File

@@ -5,4 +5,4 @@ C# is a modern coding language that was developed by Microsoft that focuses on a
Visit the following resources to learn more:
- [C# official website?](https://learn.microsoft.com/en-us/dotnet/csharp//)
- [The Beginners Guide to C#](https://www.w3schools.com/CS/index.php)
- [The Beginners Guide to C#](https://www.w3schools.com/CS/index.php)

View File

@@ -1 +1,9 @@
# Dotnet
# .NET Framework
.NET (pronounced "dot net") is a software framework developed by Microsoft that can be used to create a wide range of applications, including Windows desktop and web applications, mobile apps, and gaming. The .NET Framework provides a large library of pre-built functionality, including collections, file input/output, and networking, that can be used by .NET applications. It also includes a Common Language Runtime (CLR) which manages the execution of code, providing features such as memory management, security, and exception handling.
For more information, visit the following link:
- [What is .NET?](https://dotnet.microsoft.com/en-us/learn/dotnet/what-is-dotnet)
- [Intro to .NET](https://www.codecademy.com/article/what-is-net)
- [An Overview of .NET](https://auth0.com/blog/what-is-dotnet-platform-overview/)

View File

@@ -5,3 +5,9 @@ C# (pronounced "C-sharp") is a general-purpose, object-oriented programming lang
C# is a statically-typed language, which means that the type of a variable must be specified when it is declared, and that the type of a value cannot be changed after it has been assigned. C# also supports object-oriented programming, which means that it provides features such as encapsulation, inheritance, and polymorphism.
C# is a popular language for building .NET applications, and it is used by many large companies and organizations, including Microsoft, Dell, and IBM. It is a versatile language that can be used for a wide range of purposes, and it is well-suited for building scalable and maintainable software systems.
Visit the following links for more information:
- [C Sharp Basics](https://www.codecademy.com/catalog/language/c-sharp)
- [Introduction to C#](https://learn.microsoft.com/en-us/dotnet/csharp/tour-of-csharp/tutorials/)
- [Basics Of C#](https://www.c-sharpcorner.com/UploadFile/e9fdcd/basics-of-C-Sharp/)

View File

@@ -7,4 +7,4 @@ Visit the following resources to learn more:
- [Learn Git on the command line](https://github.com/jlord/git-it-electron)
- [Version Control System Introduction](https://www.youtube.com/watch?v=zbKdDsNNOhg)
- [Git & GitHub Crash Course For Beginners](https://www.youtube.com/watch?v=SWYqp7iY_Tc)
- [Learn Git in 20 Minutes](https://youtu.be/Y9XZQO1n_7c?t=21)
- [Learn Git in 20 Minutes](https://youtu.be/Y9XZQO1n_7c?t=21)

View File

@@ -1 +1,16 @@
# Azure devops services
# Azure Devops Services
Azure DevOps Services is a collection of services provided by Microsoft that can be used to plan, build, test, and deploy .NET applications. These services can be used together or independently to support various aspects of the software development process. Some of the main services include:
- Azure Boards: Provides features for agile planning and tracking, such as backlogs, boards, and sprint planning.
- Azure Repos: Provides source control management for Git and Team Foundation Version Control (TFVC) repositories.
- Azure Artifacts: Provides package management for NuGet, npm, and Maven packages.
- Azure Test Plans: Provides support for manual and exploratory testing, as well as continuous testing and testing in production.
- Azure Pipeline: Provides a way to build, test, and deploy code automatically, with support for multiple languages and platforms, including .NET.
These services can be used to create a full-featured development environment that can be used to manage all aspects of a software development project, from planning and design to testing and deployment.
For more information, visit the following links:
- [Build, test, and deploy .NET Core apps](https://learn.microsoft.com/en-us/azure/devops/pipelines/ecosystems/dotnet-core?view=azure-devops&tabs=dotnetfive)
- [Microsoft Azure DevOps for ASP .NET Core Web apps](https://techmindfactory.com/Microsoft-Azure-DevOps-for-ASP-.NET-Core-Web-apps/)

View File

@@ -5,7 +5,7 @@ As the name indicates, a **Data Structure** is a way of organizing the data in t
Visit the following resources to learn more:
- [What are Data Structures?](https://www.geeksforgeeks.org/data-structures)
- [ Data Structures and Algorithms](https://www.javatpoint.com/data-structure-tutorial)
- [Data Structures and Algorithms](https://www.javatpoint.com/data-structure-tutorial)
- [Data Structures Illustrated](https://www.youtube.com/watch?v=9rhT3P1MDHk&list=PLkZYeFmDuaN2-KUIv-mvbjfKszIGJ4FaY)
- [C# resources](https://dev.to/adavidoaiei/fundamental-data-structures-and-algorithms-in-c-4ocf)
- [Interview Questions about Data Structures](https://www.csharpstar.com/csharp-algorithms/)

View File

@@ -1 +1,24 @@
# General development skills
# General development skills
There are several skills that are generally considered to be important for working with .NET and C#:
- Object-oriented programming: Understanding the concepts of classes, objects, inheritance, and polymorphism is essential for working with C# and the .NET Framework.
- C# language: A strong understanding of the C# language, including its syntax, keywords, and built-in classes and types, is necessary for writing efficient and maintainable code.
- .NET Framework: Familiarity with the .NET Framework, including the Common Language Runtime (CLR) and the Base Class Library (BCL), is important for understanding how C# code is executed and for utilizing the framework's many built-in features.
- Web & Software development: Knowledge of web development technologies such as HTML, CSS, JavaScript, and ASP.NET is important for creating web applications using C# and the .NET Framework and knowledge of software development methodologies such as Agile, Scrum, or Waterfall is also useful.
- Database: Familiarity with database concepts and technologies, such as SQL and ADO.NET, is important for working with data in C# applications.
- Cloud computing: Familiarity with cloud computing concepts and technologies, such as Azure, is becoming increasingly important for deploying and scaling C# applications.
- DevOps: Understanding of DevOps concepts and practices, such as continuous integration and continuous deployment, is necessary for automating and streamlining the software development process.
For more information, visit the following links:
- [A Step-by-Step Approach to Learn OOP](https://www.geeksforgeeks.org/a-step-by-step-approach-to-learn-object-oriented-programming/)
- [Asp.net - Complete Tutorial](https://www.youtube.com/watch?v=kdPtNMb8tPw)
- [Learn Cloud Computing](https://www.youtube.com/watch?v=eWwK2FKWp0g)
- [DevOps Course for Beginners](https://www.youtube.com/watch?v=hQcFE0RD0cQ)

View File

@@ -5,3 +5,5 @@ A stored procedure is a pre-compiled collection of SQL statements that can be ex
Visit the following resources to learn more:
- [Stored Procedure Tutorial](https://www.w3schools.com/sql/sql_stored_procedures.asp)
- [Stored Procedure in SQL: Benefits And How to Create It](https://www.simplilearn.com/tutorials/sql-tutorial/stored-procedure-in-sql)
- [SQL Server stored procedures for beginners](https://www.sqlshack.com/sql-server-stored-procedures-for-beginners/)

View File

@@ -5,3 +5,5 @@ Database constraints are rules that are used to limit the data that can be store
Visit the following resources to learn more:
- [SQL Constraints](https://www.w3schools.com/sql/sql_constraints.asp)
- [Constraints of SQL](https://www.educative.io/courses/database-design-fundamentals/m7JnY9Xm6Qp)
- [Constraints in DBMS](https://beginnersbook.com/2015/04/constraints-in-dbms/)

View File

@@ -1 +1,14 @@
# Triggers
# Triggers
Triggers are special type of stored procedures that are automatically executed in response to specific events that occur within a database. These events can include:
- Data modification events (INSERT, UPDATE, DELETE) on a specific table or view.
- Data definition events (CREATE, ALTER, DROP) on specific database objects such as tables or views.
- Logon events (CONNECT, DISCONNECT) that occur when a user connects to or disconnects from the database.
For more information, visit the following links:
- [Database Triggers](https://docs.oracle.com/cd/A57673_01/DOC/server/doc/SCN73/ch15.htm)
- [Database Triggers: Examples & Overview](https://study.com/academy/lesson/database-triggers-examples-overview.html)
- [What are Triggers in SQL?](https://www.edureka.co/blog/triggers-in-sql/)
- [What is a SQL Trigger?](https://www.essentialsql.com/sql-trigger/)

View File

@@ -9,3 +9,5 @@ MVC is an architectural design pattern used for developing applications, specifi
Visit the following resources to learn more:
- [MVC Official Documentation](https://learn.microsoft.com/en-us/aspnet/core/mvc/overview?WT.mc_id=dotnet-35129-website&view=aspnetcore-7.0)
- [ASP.NET MVC Architecture](https://www.tutorialsteacher.com/mvc/mvc-architecture)
- [MVC Framework - Introduction](https://www.tutorialspoint.com/mvc_framework/mvc_framework_introduction.htm)

View File

@@ -1 +1,9 @@
# Rest
# REST
REST (Representational State Transfer) is an architectural style for building web services. In the context of .NET, RESTful web services can be created using the ASP.NET Web API framework, which allows developers to create HTTP-based services that can be consumed by a wide range of clients, including web browsers and mobile devices. The Web API framework provides a set of tools and libraries for creating RESTful services, including routing, request/response handling, and support for a variety of data formats, such as JSON and XML.
For more information, visit the following resources:
- [What is REST Services?](http://www.codedigest.com/quick-start/16/what-is-rest-services-how-to-create-rest-services-in-aspnet)
- [Restful API In ASP.NET: Introduction of REST & Web API](https://www.c-sharpcorner.com/UploadFile/4b0136/restful-api-in-Asp-Net-introduction-of-rest-web-api/)
- [What are RESTful APIs](https://www.pragimtech.com/blog/blazor/what-are-restful-apis/)

Some files were not shown because too many files have changed in this diff Show More