Compare commits
1018 Commits
content/sy
...
interactiv
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9371d8b8e9 | ||
|
|
aa8e8daacb | ||
|
|
944c38736d | ||
|
|
d916319777 | ||
|
|
1c47773816 | ||
|
|
c52ea8d7f8 | ||
|
|
6c4d98d6a0 | ||
|
|
78d94cd4b0 | ||
|
|
25cf42eb47 | ||
|
|
7b11f49468 | ||
|
|
14f3a8b02d | ||
|
|
2cadbbee28 | ||
|
|
8879e694bf | ||
|
|
875b78007d | ||
|
|
a4eb636bd4 | ||
|
|
37bf6713b4 | ||
|
|
4b1fc37946 | ||
|
|
327b93ae26 | ||
|
|
a2eec9ff51 | ||
|
|
355c6f99f7 | ||
|
|
12d5500da6 | ||
|
|
f01b8ee272 | ||
|
|
e6fea586fe | ||
|
|
c95ed6400e | ||
|
|
503a5f793c | ||
|
|
76f33486c8 | ||
|
|
4b7ea2cce1 | ||
|
|
6a83ab598e | ||
|
|
960eb225e3 | ||
|
|
095f2b083f | ||
|
|
9b5b53d4d6 | ||
|
|
5e7a52d981 | ||
|
|
4706a8f37e | ||
|
|
c208219564 | ||
|
|
f8a207e8b7 | ||
|
|
8c5b9ae3ea | ||
|
|
229e515580 | ||
|
|
0b19be0a14 | ||
|
|
d4be159f35 | ||
|
|
2ec14cfb48 | ||
|
|
c984691429 | ||
|
|
01c4275315 | ||
|
|
608c8a2b00 | ||
|
|
b2bb10079a | ||
|
|
442e460b71 | ||
|
|
191f71566c | ||
|
|
49a82d073a | ||
|
|
4a15f59a17 | ||
|
|
1bbbc7d67c | ||
|
|
5388354493 | ||
|
|
ff063bd0d2 | ||
|
|
0feba5bed1 | ||
|
|
f6a721a4d0 | ||
|
|
b21e3a4aed | ||
|
|
df394605e6 | ||
|
|
aa3467430e | ||
|
|
2d591b63c4 | ||
|
|
0069be44f6 | ||
|
|
079f491c0f | ||
|
|
780fb66c8b | ||
|
|
3680e8f52c | ||
|
|
1676d156e7 | ||
|
|
de0894c83c | ||
|
|
d22fb3664f | ||
|
|
a60d8f312f | ||
|
|
b070cb84bd | ||
|
|
3676721a14 | ||
|
|
074cbddce5 | ||
|
|
b0e29ae801 | ||
|
|
a55e94e9c6 | ||
|
|
beb3bbf81a | ||
|
|
8b941feeab | ||
|
|
6e9adaa50d | ||
|
|
732b156588 | ||
|
|
de79afa645 | ||
|
|
66576bae01 | ||
|
|
127b9f58d2 | ||
|
|
9955095e93 | ||
|
|
303a97a6c7 | ||
|
|
32e8cfafec | ||
|
|
a068d14efe | ||
|
|
2a076bbb0e | ||
|
|
47f91098fd | ||
|
|
a58b484716 | ||
|
|
48580ce0ad | ||
|
|
afbd737496 | ||
|
|
b215e2d915 | ||
|
|
1385e6bc3d | ||
|
|
74f9a812fb | ||
|
|
28f388e5be | ||
|
|
413934b6d4 | ||
|
|
22f20909fe | ||
|
|
ea3cf02ee5 | ||
|
|
c354fa9121 | ||
|
|
9c0e54262f | ||
|
|
c96f05e245 | ||
|
|
46333c42a4 | ||
|
|
3bd446577b | ||
|
|
85d1f33bb0 | ||
|
|
9b68d69797 | ||
|
|
ec5c6c592b | ||
|
|
2b425eaa5e | ||
|
|
82faa52d8b | ||
|
|
7a1664328b | ||
|
|
9d5fe1ec76 | ||
|
|
ef77420e8c | ||
|
|
b4e67232ac | ||
|
|
27f39b37cf | ||
|
|
05ef7deebb | ||
|
|
c801c52dda | ||
|
|
4a4ff9c9b4 | ||
|
|
c673672cdc | ||
|
|
b55772c017 | ||
|
|
4ca06bb183 | ||
|
|
bb4c8e7227 | ||
|
|
671972a5af | ||
|
|
42e6f77a69 | ||
|
|
e3ecc5ef41 | ||
|
|
d70d35feb7 | ||
|
|
00d189d110 | ||
|
|
45435ef247 | ||
|
|
259bf45b4e | ||
|
|
18141ac580 | ||
|
|
b981dc0ecb | ||
|
|
e78a46e016 | ||
|
|
dafaa96360 | ||
|
|
a4d946964f | ||
|
|
4fa7486b22 | ||
|
|
54fbfae7cc | ||
|
|
85ff76021f | ||
|
|
5d868989fc | ||
|
|
d08eefe8c3 | ||
|
|
bfefec4e43 | ||
|
|
c80a3d546b | ||
|
|
0b78983398 | ||
|
|
ef4cb31517 | ||
|
|
2973d8c9a5 | ||
|
|
2f13b3ddbf | ||
|
|
425f656433 | ||
|
|
aaaeb56dc2 | ||
|
|
92ba7d5913 | ||
|
|
29b28e224b | ||
|
|
e356f4e1ac | ||
|
|
6be452d80b | ||
|
|
d160e1f515 | ||
|
|
6cc0c84956 | ||
|
|
e8bba697ab | ||
|
|
7c394ec3f8 | ||
|
|
f705c17523 | ||
|
|
35f844276e | ||
|
|
97cde66096 | ||
|
|
e54a44876a | ||
|
|
ef46632587 | ||
|
|
1a9a9010e1 | ||
|
|
0ef4bfa052 | ||
|
|
b41ed8726d | ||
|
|
05f39d2492 | ||
|
|
51afe7b099 | ||
|
|
5e5314707a | ||
|
|
c6aa1df112 | ||
|
|
d112e59179 | ||
|
|
5c0601db32 | ||
|
|
8f7c7d5e6b | ||
|
|
0bb2397cec | ||
|
|
a001bfa106 | ||
|
|
28df585d1a | ||
|
|
7f54175531 | ||
|
|
de28ff99ef | ||
|
|
60b6b997e7 | ||
|
|
d2b35ca191 | ||
|
|
e01fe4df5f | ||
|
|
632ecfd8ff | ||
|
|
84141cc535 | ||
|
|
95c0550e53 | ||
|
|
e00bb9219d | ||
|
|
576951014c | ||
|
|
ec2ee855d4 | ||
|
|
cc523dc168 | ||
|
|
293c287ae6 | ||
|
|
0f1ba869ea | ||
|
|
39fc32559e | ||
|
|
b416244be9 | ||
|
|
9a212dc65a | ||
|
|
2939a7e7a1 | ||
|
|
fae0bef2f8 | ||
|
|
5d8b14e9c3 | ||
|
|
3b0923ce74 | ||
|
|
ed9cc647ce | ||
|
|
295f404d7b | ||
|
|
786acc072d | ||
|
|
9c6438726a | ||
|
|
b1fc074416 | ||
|
|
355f1e610e | ||
|
|
e5d5043bca | ||
|
|
01c4870a63 | ||
|
|
04b434bdcb | ||
|
|
f3366d1fd3 | ||
|
|
8eda9bb049 | ||
|
|
b845ddad09 | ||
|
|
4759604e14 | ||
|
|
eb29d5348e | ||
|
|
3de4eaafec | ||
|
|
05eefc9e30 | ||
|
|
c9c5349926 | ||
|
|
d47bf04f9e | ||
|
|
cd676cacfd | ||
|
|
fb79348426 | ||
|
|
e468763346 | ||
|
|
e18df2b24b | ||
|
|
30bc570dc7 | ||
|
|
b18bee3828 | ||
|
|
ec6606d9d8 | ||
|
|
b60e1ee8e8 | ||
|
|
8467abf624 | ||
|
|
ef706f700b | ||
|
|
39b87fdab3 | ||
|
|
44291d828b | ||
|
|
d81b56d321 | ||
|
|
2f36199637 | ||
|
|
eff757633e | ||
|
|
bc5dcee8fb | ||
|
|
544a530fcb | ||
|
|
289d52d135 | ||
|
|
814cf7b137 | ||
|
|
87c3fb0aa8 | ||
|
|
2d17f12caf | ||
|
|
b6345f9de1 | ||
|
|
ff4485a871 | ||
|
|
1b9d74525a | ||
|
|
17120b4db0 | ||
|
|
39a17ec525 | ||
|
|
1c8efaf136 | ||
|
|
bdd9787690 | ||
|
|
47542ddfa3 | ||
|
|
9e60b1d847 | ||
|
|
540ec027d7 | ||
|
|
fbb9729d01 | ||
|
|
04f30d4e2b | ||
|
|
601be80bf3 | ||
|
|
f3565cf48a | ||
|
|
f9b8b8043a | ||
|
|
e19d202230 | ||
|
|
cba1419163 | ||
|
|
7110b35b97 | ||
|
|
370e343b91 | ||
|
|
9c4ccf27dc | ||
|
|
44f2f12dd9 | ||
|
|
90204a07ff | ||
|
|
dd1a31d577 | ||
|
|
9cecf34b1a | ||
|
|
0ba8999121 | ||
|
|
c4ef89be3c | ||
|
|
c58d956224 | ||
|
|
b141f0bcc2 | ||
|
|
9e955a61a9 | ||
|
|
b105b2eb5f | ||
|
|
17d0c3d41b | ||
|
|
a30f1ee9c9 | ||
|
|
ead5e1d399 | ||
|
|
d3f18267a1 | ||
|
|
4eec5b025f | ||
|
|
07cb445d06 | ||
|
|
b2c5d6184c | ||
|
|
2a57bb91f0 | ||
|
|
69ebb08c9e | ||
|
|
1637ef20a6 | ||
|
|
5cbc5e0fdc | ||
|
|
8595cc56b5 | ||
|
|
4199ab05a0 | ||
|
|
f3b6cd87f4 | ||
|
|
3dc6bcd8d6 | ||
|
|
c0b19fec32 | ||
|
|
ca092f69f6 | ||
|
|
616422b023 | ||
|
|
0f889af8ab | ||
|
|
462b532a94 | ||
|
|
83611cdbe1 | ||
|
|
6dbf88d0a6 | ||
|
|
e0c660dcff | ||
|
|
0ac5875237 | ||
|
|
35107862cb | ||
|
|
a2a6d7f3cf | ||
|
|
6023376452 | ||
|
|
c6def19bcd | ||
|
|
a18daa7356 | ||
|
|
9ec90f6abe | ||
|
|
f4183b7174 | ||
|
|
d82ca9b5c4 | ||
|
|
778d21558d | ||
|
|
d529ac3a12 | ||
|
|
869716212f | ||
|
|
aab8895998 | ||
|
|
5f302bf844 | ||
|
|
b3db659a9c | ||
|
|
97f2583e0c | ||
|
|
e757f429dc | ||
|
|
258beade8d | ||
|
|
864c8fb57a | ||
|
|
00f9a2f523 | ||
|
|
f32c61b690 | ||
|
|
52a21ba9ac | ||
|
|
8d7911b35d | ||
|
|
71d0218953 | ||
|
|
dff393a20b | ||
|
|
0e83a361e3 | ||
|
|
6145be9d1b | ||
|
|
8d25145db6 | ||
|
|
ff212753f6 | ||
|
|
23bb1e18be | ||
|
|
ed991df832 | ||
|
|
26bc8c0e70 | ||
|
|
0ce8e58f9f | ||
|
|
b65cf6a026 | ||
|
|
bc80ba4194 | ||
|
|
7883875126 | ||
|
|
46d53b50eb | ||
|
|
6879b9827b | ||
|
|
3b8144aafc | ||
|
|
82af3e2880 | ||
|
|
c791dbb70c | ||
|
|
24559a32ee | ||
|
|
45a66feac9 | ||
|
|
272d871d47 | ||
|
|
7cb36ca7ff | ||
|
|
d55b6927b8 | ||
|
|
c054a3f56c | ||
|
|
a3b7b45ef3 | ||
|
|
a41ee92931 | ||
|
|
6db1e89628 | ||
|
|
7c9159b5b4 | ||
|
|
c6d35aa63c | ||
|
|
3dc11ae726 | ||
|
|
85186c7fe6 | ||
|
|
432c6d5ac3 | ||
|
|
f876d2f604 | ||
|
|
ba889de406 | ||
|
|
11e8cf4630 | ||
|
|
ba211922b2 | ||
|
|
50f078a884 | ||
|
|
b8c8dfcab1 | ||
|
|
a5093ef4e0 | ||
|
|
883c28cf39 | ||
|
|
b26dbc2a62 | ||
|
|
bb0788e357 | ||
|
|
684d3d9c4a | ||
|
|
1bdc3ebd14 | ||
|
|
52fa640dcf | ||
|
|
2f0ee7f3ae | ||
|
|
178aa830f1 | ||
|
|
0ca59ab032 | ||
|
|
594ff9ab81 | ||
|
|
fd8ba60b02 | ||
|
|
ce4e5a21a0 | ||
|
|
439d622e11 | ||
|
|
ceffafd4ae | ||
|
|
1b5900f5d7 | ||
|
|
800263d195 | ||
|
|
38bf960e0d | ||
|
|
63ad44a90d | ||
|
|
f5f238d779 | ||
|
|
0b00f55238 | ||
|
|
622a6f76b0 | ||
|
|
1adecfacde | ||
|
|
63f68c4b52 | ||
|
|
6064177f6f | ||
|
|
7bbb7979c5 | ||
|
|
f7738262ef | ||
|
|
2e28eebae1 | ||
|
|
7b1d664261 | ||
|
|
729db8f40f | ||
|
|
a0095a9b96 | ||
|
|
d8b7986a6d | ||
|
|
2eab2b77ac | ||
|
|
d03f91cb01 | ||
|
|
fbb252baf3 | ||
|
|
1678bb8910 | ||
|
|
cdbfa8ae9a | ||
|
|
7b1f8c32af | ||
|
|
8899bab70d | ||
|
|
d892107e6e | ||
|
|
b8247b6d77 | ||
|
|
a912acd6d7 | ||
|
|
084a4d3569 | ||
|
|
2f23c69323 | ||
|
|
be22e8dc6b | ||
|
|
02a0b864a5 | ||
|
|
894eda60ad | ||
|
|
48d8086884 | ||
|
|
efad09de7f | ||
|
|
28e92ac515 | ||
|
|
a4d5c7c353 | ||
|
|
c2737d8a42 | ||
|
|
f7dd8e71fd | ||
|
|
791c402878 | ||
|
|
8ed9d0c106 | ||
|
|
542b0ce117 | ||
|
|
cacddd10f2 | ||
|
|
350d160eb7 | ||
|
|
f22b73bb7a | ||
|
|
874437586e | ||
|
|
40ab1f6c77 | ||
|
|
5dce3dd7ab | ||
|
|
13f018893a | ||
|
|
0400b1d6c0 | ||
|
|
a0cb4e2568 | ||
|
|
157dde2bfb | ||
|
|
07ad7bb476 | ||
|
|
f721af1251 | ||
|
|
42ed79e117 | ||
|
|
07916250f5 | ||
|
|
a7dd2df70e | ||
|
|
22e0758c6e | ||
|
|
b27a3abc10 | ||
|
|
b0e5530f24 | ||
|
|
8da5180062 | ||
|
|
d82a96b693 | ||
|
|
1277089793 | ||
|
|
623d9398f2 | ||
|
|
09f92bf601 | ||
|
|
3ae18b3d30 | ||
|
|
c498adc530 | ||
|
|
399c548570 | ||
|
|
acfca9f169 | ||
|
|
0312a89d8c | ||
|
|
5e441f647b | ||
|
|
8be9eb6101 | ||
|
|
0c799cb43b | ||
|
|
6fd69d71e0 | ||
|
|
0bb93aeef0 | ||
|
|
fb55ccc5cf | ||
|
|
8054ac8d0e | ||
|
|
6846ed12f2 | ||
|
|
684b9667a4 | ||
|
|
a35c7ffaf0 | ||
|
|
8127fbb4ee | ||
|
|
7afa06fc95 | ||
|
|
8991d2c934 | ||
|
|
39542ffa37 | ||
|
|
6e05d1c992 | ||
|
|
12fc4e671e | ||
|
|
3f7d706f04 | ||
|
|
712d65d0e6 | ||
|
|
5f44649f5e | ||
|
|
873be4ad85 | ||
|
|
8df92be9bc | ||
|
|
38a83316b1 | ||
|
|
02826f10d3 | ||
|
|
aa7413ce7a | ||
|
|
fb8f14a0db | ||
|
|
220efadfaa | ||
|
|
77a9c31c6b | ||
|
|
fb5a7fb09f | ||
|
|
6eb10b4cfd | ||
|
|
de2b672760 | ||
|
|
fe716af733 | ||
|
|
fdacc02dcf | ||
|
|
867e3f2a8c | ||
|
|
6e451ef5cf | ||
|
|
56816d15f8 | ||
|
|
15669982d9 | ||
|
|
88c6b47d01 | ||
|
|
ed84e905ed | ||
|
|
3d0153b2fe | ||
|
|
a174590424 | ||
|
|
a155832f69 | ||
|
|
75eb3a0237 | ||
|
|
a1576fd348 | ||
|
|
8a296d99c4 | ||
|
|
10f9043516 | ||
|
|
37155e7d3c | ||
|
|
ff7d24d725 | ||
|
|
7794386573 | ||
|
|
952884ec99 | ||
|
|
22fa464cca | ||
|
|
66f939f361 | ||
|
|
25b6700812 | ||
|
|
7c53be8892 | ||
|
|
be61c144e0 | ||
|
|
537ffc365e | ||
|
|
dd7306cd6d | ||
|
|
ed1c8e3709 | ||
|
|
36e2e95ab9 | ||
|
|
7309e9640c | ||
|
|
be17f594fe | ||
|
|
06329b27c1 | ||
|
|
0e05128980 | ||
|
|
9a22a457f5 | ||
|
|
985da9ae30 | ||
|
|
47e2a9b968 | ||
|
|
074adee8f0 | ||
|
|
9f68c096a9 | ||
|
|
e4654022f0 | ||
|
|
c4291394fe | ||
|
|
9b29d240e3 | ||
|
|
d09c91247e | ||
|
|
51895be81e | ||
|
|
a79b65a1b4 | ||
|
|
3fe41d2071 | ||
|
|
b25111415f | ||
|
|
0c3ea386f5 | ||
|
|
d1f6951da2 | ||
|
|
7ac1dd9b9c | ||
|
|
ba21172d20 | ||
|
|
93b538f4e1 | ||
|
|
7cac02f4b4 | ||
|
|
42e98e9cc6 | ||
|
|
0221964362 | ||
|
|
cbf0b2c496 | ||
|
|
1ff9c11361 | ||
|
|
b681aaa52e | ||
|
|
8a99ecbecd | ||
|
|
82456021ad | ||
|
|
6eba4a2afd | ||
|
|
c3704107a3 | ||
|
|
102cd8885c | ||
|
|
8259a83921 | ||
|
|
c1ecf9d8a5 | ||
|
|
381801120e | ||
|
|
fe458e0790 | ||
|
|
a42e92781a | ||
|
|
8255f69257 | ||
|
|
30e6c15ddb | ||
|
|
3eb13043ce | ||
|
|
16b2019d06 | ||
|
|
a0a5e74281 | ||
|
|
5881c27526 | ||
|
|
c16bed02ca | ||
|
|
37432582c0 | ||
|
|
0d71b2b1e6 | ||
|
|
ff3d0489cc | ||
|
|
a94af0ec14 | ||
|
|
4f1d4feff6 | ||
|
|
7bc3b4a0f3 | ||
|
|
9d9a6506cc | ||
|
|
3b47cd3542 | ||
|
|
f500d49275 | ||
|
|
098303b78b | ||
|
|
bf56db60bc | ||
|
|
cdc362625a | ||
|
|
2ec335edc8 | ||
|
|
064f97108e | ||
|
|
7d0bc2e8a2 | ||
|
|
ea4de11e30 | ||
|
|
4e7d8512cc | ||
|
|
91ef32722a | ||
|
|
87fba80759 | ||
|
|
0acd1d1fcd | ||
|
|
5228fe936f | ||
|
|
b221016269 | ||
|
|
f7d1d61528 | ||
|
|
1467865e45 | ||
|
|
17e2ffd110 | ||
|
|
d177a13aa6 | ||
|
|
1afe684699 | ||
|
|
1fbdf68573 | ||
|
|
3feea57204 | ||
|
|
00d8877a12 | ||
|
|
67c4407d30 | ||
|
|
69679addc3 | ||
|
|
272eafa013 | ||
|
|
a19b4da20d | ||
|
|
47349f00c2 | ||
|
|
dbedae78ca | ||
|
|
5733476fd9 | ||
|
|
5c17deddf5 | ||
|
|
f1b5357358 | ||
|
|
6a319fe6cc | ||
|
|
269e9eb90b | ||
|
|
844a38e739 | ||
|
|
6c8b899a35 | ||
|
|
f246b065a1 | ||
|
|
97826210fa | ||
|
|
ff5706c82f | ||
|
|
609cce403a | ||
|
|
87ad491b2c | ||
|
|
ef25a14af1 | ||
|
|
3ac1de7fb0 | ||
|
|
09e8796159 | ||
|
|
0d3c3eea11 | ||
|
|
992b6990a3 | ||
|
|
ffa18538ff | ||
|
|
65307e7cc1 | ||
|
|
46d9fd66f3 | ||
|
|
2344e6c4c3 | ||
|
|
8899b0d196 | ||
|
|
d4613330f4 | ||
|
|
283632713a | ||
|
|
d3b96d20cc | ||
|
|
374e8a04fd | ||
|
|
f1b6d13928 | ||
|
|
6a4af77ba8 | ||
|
|
a2db93873b | ||
|
|
f523b077a4 | ||
|
|
412eaa004e | ||
|
|
8e71b3c448 | ||
|
|
12030deeea | ||
|
|
5a89ecf2d8 | ||
|
|
991d344a77 | ||
|
|
cc06819a50 | ||
|
|
eb1beecfa7 | ||
|
|
135ef6a11a | ||
|
|
e902c062fc | ||
|
|
685c402f28 | ||
|
|
c88e04f809 | ||
|
|
d2167555da | ||
|
|
3f61854ccc | ||
|
|
9ca201709d | ||
|
|
0c3486df37 | ||
|
|
27d3704390 | ||
|
|
86bd47c45d | ||
|
|
b8c2056557 | ||
|
|
c190a94bc6 | ||
|
|
eafbcd5867 | ||
|
|
170b14df4c | ||
|
|
694315026e | ||
|
|
a9183dacc7 | ||
|
|
d46b543659 | ||
|
|
9a63e576c7 | ||
|
|
69ef7615fe | ||
|
|
68950ab2c9 | ||
|
|
305d0a41ac | ||
|
|
e43c21a01d | ||
|
|
b6205af02c | ||
|
|
6de2867d8a | ||
|
|
13612323d7 | ||
|
|
edd10470a7 | ||
|
|
d5b8d761d5 | ||
|
|
9f7119694b | ||
|
|
8fbde17c22 | ||
|
|
e16947bd78 | ||
|
|
33af054161 | ||
|
|
8913d5c5e4 | ||
|
|
0040d568b1 | ||
|
|
706070e42a | ||
|
|
f79fb62ff9 | ||
|
|
5c428540dc | ||
|
|
35ae0a74b3 | ||
|
|
a1606521d4 | ||
|
|
6545c8de36 | ||
|
|
0f713cbfd8 | ||
|
|
333894b75b | ||
|
|
c73a500ffd | ||
|
|
a489bc0fde | ||
|
|
09ef6bfbb0 | ||
|
|
67d2f5cb57 | ||
|
|
b28deab192 | ||
|
|
290a73c8b0 | ||
|
|
579d39e104 | ||
|
|
e7c32958c9 | ||
|
|
7c5d28b68b | ||
|
|
2f8c0c5748 | ||
|
|
5e08af99b2 | ||
|
|
2882815313 | ||
|
|
e093f98a42 | ||
|
|
d3f8e0517b | ||
|
|
efc874163b | ||
|
|
3e8abbed13 | ||
|
|
244d336d8e | ||
|
|
9d24b98f67 | ||
|
|
007bd7feb0 | ||
|
|
7619945028 | ||
|
|
3265f9729d | ||
|
|
4591ad2336 | ||
|
|
163e03f578 | ||
|
|
2215174c20 | ||
|
|
aa52e08ac4 | ||
|
|
96acb6c93e | ||
|
|
69ebd50a90 | ||
|
|
e2eaf7d19c | ||
|
|
da7ba5bf4c | ||
|
|
0d17cf145c | ||
|
|
8a7f7a4a83 | ||
|
|
7d3255576b | ||
|
|
97529cbf54 | ||
|
|
52af178a19 | ||
|
|
f0425fd964 | ||
|
|
570d6a04b1 | ||
|
|
1e677183aa | ||
|
|
14a29b4634 | ||
|
|
bc66a805e3 | ||
|
|
f6c10d7344 | ||
|
|
4e96943374 | ||
|
|
1235459d7a | ||
|
|
0d35fe0364 | ||
|
|
76d6fab581 | ||
|
|
da39147539 | ||
|
|
9e230a01a2 | ||
|
|
4c3452926a | ||
|
|
b36c5b3c26 | ||
|
|
95fe79a0f1 | ||
|
|
850a9ffc9d | ||
|
|
2b8d18d880 | ||
|
|
9b551a69a7 | ||
|
|
cdb9201b2f | ||
|
|
ab15d91614 | ||
|
|
9d2fdfa7cf | ||
|
|
bcad685e27 | ||
|
|
74ae339fe1 | ||
|
|
5811fd8832 | ||
|
|
6c710a92c1 | ||
|
|
51f068085d | ||
|
|
1795bc1495 | ||
|
|
d4ef930187 | ||
|
|
54fae335c2 | ||
|
|
0f886e9def | ||
|
|
3f299cdd8b | ||
|
|
7fccd6b399 | ||
|
|
f75512e96a | ||
|
|
32ff9a700b | ||
|
|
6976202171 | ||
|
|
cf1cca7cb3 | ||
|
|
2236c3f93c | ||
|
|
4f067504a2 | ||
|
|
42747f4f97 | ||
|
|
1d952f75f8 | ||
|
|
9e61ef5dd1 | ||
|
|
c7770cc64c | ||
|
|
af7e25dc92 | ||
|
|
cf3365e778 | ||
|
|
bd69872059 | ||
|
|
746ee3d548 | ||
|
|
73c55a0eaa | ||
|
|
299d0f3ada | ||
|
|
98097f939a | ||
|
|
f4904da3f8 | ||
|
|
465c00b4d5 | ||
|
|
69c54e5dfe | ||
|
|
6f4898c216 | ||
|
|
b8cc07c29e | ||
|
|
eae0ad3ecb | ||
|
|
56bf52e641 | ||
|
|
689f24e0f1 | ||
|
|
63d66b3f4e | ||
|
|
4930c00f78 | ||
|
|
5745fc56bf | ||
|
|
55d5ced587 | ||
|
|
018be76895 | ||
|
|
b268106684 | ||
|
|
56e2108be2 | ||
|
|
9dfbceda7c | ||
|
|
c698265f42 | ||
|
|
752d4614b8 | ||
|
|
d73e08f8f6 | ||
|
|
cf648924cf | ||
|
|
2d15290566 | ||
|
|
06dd1934f3 | ||
|
|
316ada1259 | ||
|
|
30d2f15433 | ||
|
|
4ac1319d8d | ||
|
|
4e924981c1 | ||
|
|
fdf3fd050b | ||
|
|
79afd0a6a8 | ||
|
|
03e35ee928 | ||
|
|
eaaedb8034 | ||
|
|
84e87a501e | ||
|
|
8fca669787 | ||
|
|
3c1d41119f | ||
|
|
495fd37eae | ||
|
|
4cfeb1c372 | ||
|
|
91a47faec0 | ||
|
|
8c03aedea1 | ||
|
|
9a515f85c1 | ||
|
|
0a2468aad2 | ||
|
|
fc2eb36d58 | ||
|
|
3c5ea2131d | ||
|
|
75e1f67ee8 | ||
|
|
b40894cfdc | ||
|
|
4fb2e1f46d | ||
|
|
8eccfd22e3 | ||
|
|
d84800fcaf | ||
|
|
bb3260f4b7 | ||
|
|
9a2e1fd673 | ||
|
|
3f599fab35 | ||
|
|
cdc710123f | ||
|
|
bb43c8eba6 | ||
|
|
c01d595546 | ||
|
|
77a66fd25d | ||
|
|
a93ac86766 | ||
|
|
4044dbea91 | ||
|
|
3fc9ffe8b4 | ||
|
|
880475f6de | ||
|
|
a26945288b | ||
|
|
b97ae52a1b | ||
|
|
76ddeeedb2 | ||
|
|
00b7fe6e7f | ||
|
|
c43442f127 | ||
|
|
68c62d218d | ||
|
|
47b10a1a1a | ||
|
|
1fd135d1c1 | ||
|
|
61bdc80f5a | ||
|
|
4fbefd5ae9 | ||
|
|
835476ed31 | ||
|
|
83745ae1b4 | ||
|
|
9465cfb5c2 | ||
|
|
4edd398770 | ||
|
|
21b3b7cbdf | ||
|
|
ae6763bf83 | ||
|
|
be5a61b697 | ||
|
|
8e25dca636 | ||
|
|
b91d404f17 | ||
|
|
80f2cb8cbc | ||
|
|
2dc3d4fd24 | ||
|
|
2432ff9fd4 | ||
|
|
8f1f8846c9 | ||
|
|
7dac8665a0 | ||
|
|
f0181ff08f | ||
|
|
0ad95c2dd0 | ||
|
|
d184e93519 | ||
|
|
4ef31700a5 | ||
|
|
087f4e5c25 | ||
|
|
c5ae26458a | ||
|
|
0c6de5d89b | ||
|
|
124d113162 | ||
|
|
c88b0f3b1a | ||
|
|
06d72599d9 | ||
|
|
eb9cd6cdcc | ||
|
|
c7589b8325 | ||
|
|
4c07ac509b | ||
|
|
1240b6b1bc | ||
|
|
ad05c49570 | ||
|
|
c01a854a5a | ||
|
|
7b1dde1d62 | ||
|
|
56b0275b06 | ||
|
|
7a0d784d81 | ||
|
|
2c9eb1f9ee | ||
|
|
e4ca1c9598 | ||
|
|
2b8e06d651 | ||
|
|
56088a838c | ||
|
|
542d82c2dc | ||
|
|
980322bae0 | ||
|
|
56fbe9a685 | ||
|
|
6939240d59 | ||
|
|
4caaee3da5 | ||
|
|
e829af3e62 | ||
|
|
7ba0fa9004 | ||
|
|
74433cd0d3 | ||
|
|
dec3e992b3 | ||
|
|
7a4c27460f | ||
|
|
5553b411eb | ||
|
|
98cc968ed1 | ||
|
|
3de37468a6 | ||
|
|
3364eae0a6 | ||
|
|
a06eaec5d4 | ||
|
|
10e433f538 | ||
|
|
129deed6a9 | ||
|
|
ce35a8112f | ||
|
|
35f6070133 | ||
|
|
629f1058f2 | ||
|
|
199310df93 | ||
|
|
0d45fcbf79 | ||
|
|
47cbcde5dc | ||
|
|
5b12eb9e02 | ||
|
|
6632b46d98 | ||
|
|
25e009a63f | ||
|
|
9ae7eed1e3 | ||
|
|
8db62cb19f | ||
|
|
d1a991b18c | ||
|
|
8107e008ff | ||
|
|
944858bbb1 | ||
|
|
b864c60ea3 | ||
|
|
618b55f601 | ||
|
|
b5c65b408b | ||
|
|
21f2ef80ba | ||
|
|
ebd351e133 | ||
|
|
77dab81b92 | ||
|
|
0350da2929 | ||
|
|
59c07c9000 | ||
|
|
79ab31dec7 | ||
|
|
16983cb950 | ||
|
|
e29fe52cb1 | ||
|
|
7921acb666 | ||
|
|
b53f8c982c | ||
|
|
0b72a07147 | ||
|
|
5155a0c358 | ||
|
|
bd5663ab26 | ||
|
|
af3ccd5bb5 | ||
|
|
035eaa47e8 | ||
|
|
3541d4e717 | ||
|
|
e8dcfe97f2 | ||
|
|
8f3307e53e | ||
|
|
dcc825416d | ||
|
|
f8fcb8d600 | ||
|
|
40919dec14 | ||
|
|
f3592155bf | ||
|
|
927ee73be7 | ||
|
|
4f81d5374e | ||
|
|
e95fd69886 | ||
|
|
11d9da5afb | ||
|
|
cea8abc5ef | ||
|
|
7169d3bb8f | ||
|
|
ae9c1c4992 | ||
|
|
58e560af7d | ||
|
|
09fa166f56 | ||
|
|
6ed7d9c25f | ||
|
|
e59fc5e4e9 | ||
|
|
f5da05c3ec | ||
|
|
07b200b878 | ||
|
|
ccca782f25 | ||
|
|
77d9846d9b | ||
|
|
8da175e9d8 | ||
|
|
467634889b | ||
|
|
b46b425b41 | ||
|
|
9e23439f0c | ||
|
|
c6db625e35 | ||
|
|
672245e4e4 | ||
|
|
e4ce3475c6 | ||
|
|
d15b97db73 | ||
|
|
8f040e5e8a | ||
|
|
888800d2a0 | ||
|
|
51b2c70586 | ||
|
|
5b4cc86f61 | ||
|
|
9952ee5805 | ||
|
|
dacbf09f55 | ||
|
|
a16787ab58 | ||
|
|
7d45c8e462 | ||
|
|
796bde76c9 | ||
|
|
22d5622e1e | ||
|
|
2312fdd608 | ||
|
|
bc2ecea03b | ||
|
|
84a551f906 | ||
|
|
9fab5c7134 | ||
|
|
c61f4a845d | ||
|
|
025753b279 | ||
|
|
6b9901db28 | ||
|
|
34f0e483ec | ||
|
|
0ae9bc0e3e | ||
|
|
3f17f60daf | ||
|
|
7f2acba352 | ||
|
|
907fb9915f | ||
|
|
fd2e64ec50 | ||
|
|
3fd5b9e744 | ||
|
|
edff9156ff | ||
|
|
e1c89585e9 | ||
|
|
abaa839b26 | ||
|
|
1bc7384929 | ||
|
|
6a148295f7 | ||
|
|
ea25f2d99b | ||
|
|
08303c0623 | ||
|
|
f18f9fb5b3 | ||
|
|
dfc07e0753 | ||
|
|
64a19fdc3c | ||
|
|
1b3e8712ff | ||
|
|
f242c6e358 | ||
|
|
7e2121bed9 | ||
|
|
bb80ceb7ba | ||
|
|
25dfb28368 | ||
|
|
a1c75bb9f8 | ||
|
|
efdb628120 | ||
|
|
ac23dddeb9 | ||
|
|
b208eaa1bd | ||
|
|
8ebf97277c | ||
|
|
928d79e3fb | ||
|
|
9b95218eb8 | ||
|
|
8bcdd84f0f | ||
|
|
67a72aab11 | ||
|
|
771f3a9cb7 | ||
|
|
38b6b34437 | ||
|
|
548dfd85e7 | ||
|
|
971d23c43a | ||
|
|
3aac8de849 | ||
|
|
8d605735b2 | ||
|
|
7debdb90c1 | ||
|
|
f6f5c821b3 | ||
|
|
227e08b7c4 | ||
|
|
16651606fb | ||
|
|
0ea67f695d | ||
|
|
6c4386ed7d | ||
|
|
e65ba9365b | ||
|
|
e5843568dd | ||
|
|
7968151c44 | ||
|
|
9f0753f098 | ||
|
|
98d0aa5103 | ||
|
|
c1706e2c18 | ||
|
|
84e74096b7 | ||
|
|
3d96fdf1df | ||
|
|
a157605b2b | ||
|
|
ec83830577 | ||
|
|
6babeb3f21 | ||
|
|
0b9754c9ae | ||
|
|
c2f7754b0d | ||
|
|
4df519845f | ||
|
|
910bd371dd | ||
|
|
0aa6db6007 | ||
|
|
01be603780 | ||
|
|
55a3ce4def | ||
|
|
a21264eb5e | ||
|
|
8c216782e5 | ||
|
|
ed9823245b | ||
|
|
378e53eba4 | ||
|
|
66b68bc26f | ||
|
|
0785d28bb4 | ||
|
|
f43dda522d | ||
|
|
ba98142d5b | ||
|
|
43160d3058 | ||
|
|
d40a858c6a | ||
|
|
4024005c4a | ||
|
|
91d1fc7245 | ||
|
|
328efa6ff6 | ||
|
|
cb352aba68 | ||
|
|
625ca5dcf4 | ||
|
|
25d686ae5c | ||
|
|
0ab94faa95 | ||
|
|
5299a04acd | ||
|
|
f326a58bee | ||
|
|
d8d52a6e86 | ||
|
|
ba09cc4b86 | ||
|
|
5804deb8ac | ||
|
|
63b3f0199b | ||
|
|
0b0addaee4 | ||
|
|
aab6d380aa | ||
|
|
79b5c09a06 | ||
|
|
3bd4ad5874 | ||
|
|
79887dc7d5 | ||
|
|
dc8cb8e777 | ||
|
|
a8059e73c0 | ||
|
|
ee2b3e5de0 | ||
|
|
807e5ea2c1 | ||
|
|
f7b42203a4 |
18
.eslintrc
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"extends": [
|
||||
"next",
|
||||
"next/core-web-vitals",
|
||||
"prettier"
|
||||
],
|
||||
"rules": {
|
||||
"@next/next/no-img-element": [
|
||||
"off"
|
||||
],
|
||||
"react/display-name": [
|
||||
"off"
|
||||
],
|
||||
"react/jsx-no-target-blank": [
|
||||
"off"
|
||||
]
|
||||
}
|
||||
}
|
||||
BIN
.github/images/banner.png
vendored
Normal file
|
After Width: | Height: | Size: 39 KiB |
1
.github/sponsors/doppler-logo.svg
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 3473 1069"><defs><style>.cls-1{fill:#fff;}.cls-2{fill:#111;}.cls-3{fill-rule:evenodd;fill:url(#linear-gradient);}</style><linearGradient id="linear-gradient" x1="658.73" y1="777.7" x2="341.45" y2="352.06" gradientTransform="matrix(1, 0, 0, -1, 0, 1070)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#33a9ff"/><stop offset="1" stop-color="#1673ff"/></linearGradient></defs><rect class="cls-1" width="3473" height="1069"/><path class="cls-2" d="M1054.06,633.32q4.93.45,11.23.9H1081q52.55,0,77.7-26.5,25.59-26.49,25.59-73.18,0-48.94-24.25-74.09t-76.79-25.14q-7.18,0-14.82.45-5.78,0-11,.51a3.73,3.73,0,0,0-3.33,3.74Zm202.54-98.78Q1256.6,575,1244,605t-35.92,49.84q-22.9,19.75-56.14,29.63t-74.55,9.88q-18.86,0-44-1.79A338.32,338.32,0,0,1,984,686.3V386.41a3.8,3.8,0,0,1,3.13-3.75,386.34,386.34,0,0,1,47.17-5.27q26.49-1.8,45.36-1.8,40,0,72.3,9,32.79,9,56.14,28.29T1244,462.25Q1256.61,492.33,1256.6,534.54Z"/><path class="cls-2" d="M1397.52,534.54q0,22.89,5.39,41.31a103.13,103.13,0,0,0,16.17,31.87,74.66,74.66,0,0,0,26.05,20.21q15.27,7.19,35,7.18,19.3,0,34.58-7.18a69.47,69.47,0,0,0,26-20.21A91.41,91.41,0,0,0,1557,575.85q5.83-18.42,5.84-41.31T1557,493.23q-5.39-18.86-16.17-31.88a67.73,67.73,0,0,0-26-20.65q-15.27-7.18-34.58-7.19-19.77,0-35,7.64a72.5,72.5,0,0,0-26.05,20.65q-10.33,13-16.17,31.88A145.23,145.23,0,0,0,1397.52,534.54Zm237.57,0q0,40-12.12,70.49-11.68,30.09-32.34,50.74a134.89,134.89,0,0,1-49.4,30.53,176.88,176.88,0,0,1-61.07,10.33A174.23,174.23,0,0,1,1420,686.3a140,140,0,0,1-49.4-30.53q-21.11-20.65-33.23-50.74-12.13-30.53-12.13-70.49t12.58-70q12.57-30.53,33.68-51.18a142,142,0,0,1,49.4-31A171.39,171.39,0,0,1,1480.16,372a174.08,174.08,0,0,1,60.18,10.33,136.87,136.87,0,0,1,49.4,31q21.1,20.65,33.23,51.18Q1635.09,494.58,1635.09,534.54Z"/><path class="cls-2" d="M1810.48,375.59q69.62,0,106.88,24.7,37.28,24.24,37.28,79.92,0,56.13-37.72,81.27-37.73,24.69-107.79,24.69H1791a3.83,3.83,0,0,0-3.83,3.83v96.51a3.83,3.83,0,0,1-3.83,3.83h-66.23V386.83a3.81,3.81,0,0,1,3.1-3.75,400.76,400.76,0,0,1,45.4-5.69Q1791.18,375.59,1810.48,375.59Zm4.49,59.72q-7.63,0-15.27.45-5,.3-9.06.62a3.8,3.8,0,0,0-3.51,3.8v86.28h22q36.38,0,54.79-9.88t18.42-36.82q0-13-4.94-21.55a32.47,32.47,0,0,0-13.48-13.47q-8.54-5.39-21.1-7.19A159.75,159.75,0,0,0,1815,435.31Z"/><path class="cls-2" d="M2123.07,375.59q69.61,0,106.89,24.7,37.27,24.24,37.27,79.92,0,56.13-37.72,81.27-37.73,24.69-107.78,24.69h-18.18a3.83,3.83,0,0,0-3.83,3.83v96.51a3.83,3.83,0,0,1-3.83,3.83h-66.23V386.82a3.8,3.8,0,0,1,3.1-3.74,400.76,400.76,0,0,1,45.4-5.69Q2103.77,375.59,2123.07,375.59Zm4.49,59.72q-7.64,0-15.27.45-5,.3-9.06.62a3.81,3.81,0,0,0-3.51,3.8v86.28h22q36.38,0,54.78-9.88t18.42-36.82q0-13-4.94-21.55a32.47,32.47,0,0,0-13.48-13.47q-8.52-5.39-21.1-7.19A159.75,159.75,0,0,0,2127.56,435.31Z"/><path class="cls-2" d="M2540.5,630.17a1.29,1.29,0,0,1,1.28,1.28v57.62a1.28,1.28,0,0,1-1.28,1.27H2342.25V401.41a3.83,3.83,0,0,1,2.81-3.69l67.25-18.54v251Z"/><path class="cls-2" d="M2618.88,690.34V383a3.84,3.84,0,0,1,3.83-3.83h215a1.28,1.28,0,0,1,1.23,1.64l-16.44,56.26a1.26,1.26,0,0,1-1.22.92H2692.77a3.83,3.83,0,0,0-3.83,3.83v57.24H2803.1a1.27,1.27,0,0,1,1.23,1.63l-16,54.92a1.28,1.28,0,0,1-1.22.92h-94.34a3.82,3.82,0,0,0-3.83,3.83v71.15h154.72a1.28,1.28,0,0,1,1.23,1.63l-16.34,56.27a1.27,1.27,0,0,1-1.22.92Z"/><path class="cls-2" d="M3006,375.59q70.07,0,107.34,25.15,37.28,24.69,37.27,77.22,0,32.79-15.27,53.44-14.82,20.19-43.11,31.87,9.43,11.68,19.76,26.94,10.34,14.82,20.21,31.43,10.32,16.17,19.76,34.13,8.93,16.58,16.65,32.75a1.28,1.28,0,0,1-1.16,1.82H3091.6a1.27,1.27,0,0,1-1.11-.65q-8.37-15-17.15-30.33-8.53-15.72-18-30.53-9-14.82-18-27.84a286.92,286.92,0,0,0-18-24.25h-30.76a3.83,3.83,0,0,0-3.82,3.83V686.51a3.83,3.83,0,0,1-3.83,3.83h-66.23V386.82a3.8,3.8,0,0,1,3.09-3.74,400,400,0,0,1,44.06-5.69Q2986.67,375.59,3006,375.59Zm4.05,59.72q-7.64,0-13.93.45-4,.3-7.71.61a3.82,3.82,0,0,0-3.51,3.81v80.89h19.76q39.51,0,56.58-9.88t17.07-33.67q0-22.9-17.52-32.33Q3043.71,435.31,3010,435.31Z"/><path class="cls-3" d="M307.26,310a1.79,1.79,0,0,0-1.5,2.75l89.7,140.38a14.19,14.19,0,0,0,12,6.58H528.38c39.92,0,64.87,35.28,64.72,74.79s-26.74,74.44-64.72,74.44H404.77a4.71,4.71,0,0,0-4.71,4.75V754.25a4.72,4.72,0,0,0,4.72,4.75H560.62C689.12,759,753,637.1,753.09,534.5S690,310,560.62,310ZM367,609.29H336.16C318.4,609.29,304,626,304,646.71V757.66a1.28,1.28,0,0,0,1.28,1.28h30.88c17.76,0,32.15-16.75,32.15-37.41v-111A1.27,1.27,0,0,0,367,609.29Z"/></svg>
|
||||
|
After Width: | Height: | Size: 4.4 KiB |
59
.github/sponsors/oss-logo.svg
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 25.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 646.6 105.7" style="enable-background:new 0 0 646.6 105.7;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#104366;}
|
||||
.st1{fill:#4086C6;}
|
||||
</style>
|
||||
<g>
|
||||
<path class="st0" d="M21.1,79.8c-6.6-3.5-11.7-8.4-15.5-14.6C1.9,59,0,52,0,44.3c0-7.8,1.9-14.7,5.6-20.9
|
||||
c3.7-6.2,8.9-11.1,15.5-14.6c6.6-3.5,14-5.3,22.2-5.3c8.2,0,15.6,1.8,22.1,5.3c6.6,3.5,11.7,8.4,15.5,14.6
|
||||
c3.8,6.2,5.6,13.2,5.6,20.9c0,7.8-1.9,14.7-5.6,20.9c-3.8,6.2-8.9,11.1-15.5,14.6c-6.5,3.5-13.9,5.3-22.1,5.3
|
||||
C35,85.1,27.6,83.4,21.1,79.8z M55.9,66.3c3.8-2.1,6.7-5.1,8.9-9c2.1-3.8,3.2-8.2,3.2-13.1c0-4.9-1.1-9.3-3.2-13.1
|
||||
c-2.1-3.8-5.1-6.8-8.9-9c-3.8-2.1-8-3.2-12.6-3.2c-4.7,0-8.9,1.1-12.6,3.2s-6.7,5.1-8.9,9c-2.1,3.8-3.2,8.2-3.2,13.1
|
||||
c0,4.9,1.1,9.3,3.2,13.1c2.1,3.8,5.1,6.8,8.9,9c3.8,2.1,8,3.2,12.6,3.2C47.9,69.5,52.1,68.5,55.9,66.3z"/>
|
||||
<path class="st0" d="M108.1,82.6c-5.8-1.7-10.5-3.9-14.1-6.6l6.2-13.8c3.4,2.5,7.4,4.5,12.1,6c4.7,1.5,9.3,2.3,14,2.3
|
||||
c5.2,0,9-0.8,11.5-2.3c2.5-1.5,3.7-3.6,3.7-6.2c0-1.9-0.7-3.4-2.2-4.7c-1.5-1.2-3.3-2.2-5.6-3c-2.3-0.8-5.4-1.6-9.3-2.5
|
||||
c-6-1.4-11-2.9-14.8-4.3c-3.8-1.4-7.1-3.7-9.9-6.9c-2.7-3.2-4.1-7.4-4.1-12.6c0-4.6,1.2-8.7,3.7-12.5c2.5-3.7,6.2-6.7,11.2-8.9
|
||||
c5-2.2,11.1-3.3,18.3-3.3c5,0,10,0.6,14.8,1.8c4.8,1.2,9,2.9,12.6,5.2l-5.6,13.9c-7.3-4.1-14.6-6.2-21.9-6.2
|
||||
c-5.1,0-8.9,0.8-11.3,2.5c-2.4,1.7-3.7,3.8-3.7,6.5c0,2.7,1.4,4.7,4.2,6c2.8,1.3,7.1,2.6,12.9,3.9c6,1.4,11,2.9,14.8,4.3
|
||||
c3.8,1.4,7.1,3.7,9.9,6.8c2.7,3.1,4.1,7.3,4.1,12.5c0,4.5-1.3,8.6-3.8,12.4c-2.5,3.7-6.3,6.7-11.3,8.9c-5,2.2-11.2,3.3-18.4,3.3
|
||||
C120,85.1,113.9,84.3,108.1,82.6z"/>
|
||||
<path class="st0" d="M180.1,82.6c-5.8-1.7-10.5-3.9-14.1-6.6l6.2-13.8c3.4,2.5,7.4,4.5,12.1,6c4.7,1.5,9.3,2.3,14,2.3
|
||||
c5.2,0,9-0.8,11.5-2.3c2.5-1.5,3.7-3.6,3.7-6.2c0-1.9-0.7-3.4-2.2-4.7c-1.5-1.2-3.3-2.2-5.6-3c-2.3-0.8-5.4-1.6-9.3-2.5
|
||||
c-6-1.4-10.9-2.9-14.8-4.3c-3.8-1.4-7.1-3.7-9.9-6.9c-2.7-3.2-4.1-7.4-4.1-12.6c0-4.6,1.2-8.7,3.7-12.5c2.5-3.7,6.2-6.7,11.2-8.9
|
||||
s11.1-3.3,18.3-3.3c5,0,10,0.6,14.8,1.8c4.8,1.2,9,2.9,12.6,5.2l-5.6,13.9c-7.3-4.1-14.6-6.2-21.9-6.2c-5.1,0-8.9,0.8-11.3,2.5
|
||||
c-2.4,1.7-3.7,3.8-3.7,6.5c0,2.7,1.4,4.7,4.2,6c2.8,1.3,7.1,2.6,12.9,3.9c6,1.4,10.9,2.9,14.8,4.3s7.1,3.7,9.9,6.8
|
||||
c2.7,3.1,4.1,7.3,4.1,12.5c0,4.5-1.3,8.6-3.8,12.4c-2.5,3.7-6.3,6.7-11.3,8.9c-5,2.2-11.2,3.3-18.4,3.3
|
||||
C192,85.1,186,84.3,180.1,82.6z"/>
|
||||
<path class="st1" d="M293.2,79.1c-6.2-3.5-11.1-8.2-14.7-14.3c-3.6-6.1-5.4-12.9-5.4-20.5c0-7.6,1.8-14.5,5.4-20.5
|
||||
c3.6-6.1,8.5-10.9,14.7-14.3c6.2-3.5,13.2-5.2,20.9-5.2c5.7,0,11,0.9,15.8,2.8c4.8,1.8,8.9,4.6,12.3,8.2l-3.6,3.7
|
||||
c-6.3-6.2-14.4-9.4-24.3-9.4c-6.6,0-12.6,1.5-18.1,4.5c-5.4,3-9.7,7.2-12.8,12.5s-4.6,11.2-4.6,17.8s1.5,12.5,4.6,17.8
|
||||
c3.1,5.3,7.3,9.5,12.8,12.5c5.4,3,11.4,4.5,18.1,4.5c9.8,0,17.9-3.2,24.3-9.5l3.6,3.7c-3.4,3.6-7.5,6.4-12.4,8.2
|
||||
c-4.9,1.9-10.1,2.8-15.7,2.8C306.3,84.3,299.4,82.6,293.2,79.1z"/>
|
||||
<path class="st1" d="M395.4,30c3.9,3.7,5.9,9.2,5.9,16.4v37.4h-5.4V73.3c-1.9,3.5-4.6,6.2-8.2,8.1c-3.6,1.9-7.9,2.9-13,2.9
|
||||
c-6.5,0-11.7-1.5-15.5-4.6c-3.8-3.1-5.7-7.1-5.7-12.2c0-4.9,1.8-8.9,5.2-11.9c3.5-3,9.1-4.6,16.8-4.6h20.2v-4.7
|
||||
c0-5.5-1.5-9.7-4.5-12.5c-3-2.9-7.3-4.3-13-4.3c-3.9,0-7.7,0.7-11.2,2c-3.6,1.4-6.6,3.2-9.1,5.4l-2.8-4.1c2.9-2.6,6.5-4.7,10.6-6.2
|
||||
c4.1-1.5,8.5-2.2,13-2.2C385.9,24.4,391.5,26.3,395.4,30z M387.9,76.2c3.4-2.3,6-5.5,7.7-9.8V55.3h-20.1c-5.8,0-10,1.1-12.6,3.2
|
||||
c-2.6,2.1-3.9,5-3.9,8.7c0,3.8,1.4,6.9,4.3,9.1c2.9,2.2,6.9,3.3,12.1,3.3C380.3,79.6,384.5,78.5,387.9,76.2z"/>
|
||||
<path class="st1" d="M469,28.2c4.4,2.6,7.9,6.1,10.4,10.6c2.5,4.5,3.8,9.7,3.8,15.5c0,5.8-1.3,11-3.8,15.5
|
||||
c-2.5,4.6-6,8.1-10.4,10.6c-4.4,2.5-9.4,3.8-14.9,3.8c-5.2,0-9.9-1.2-14.1-3.7c-4.2-2.4-7.5-5.9-9.8-10.2v35.3h-5.6V24.8h5.4v13.9
|
||||
c2.3-4.5,5.6-8,9.9-10.6c4.2-2.5,9-3.8,14.3-3.8C459.6,24.4,464.6,25.7,469,28.2z M465.9,76c3.6-2.1,6.5-5,8.5-8.8
|
||||
c2.1-3.8,3.1-8.1,3.1-12.9c0-4.8-1-9.1-3.1-12.9c-2.1-3.8-4.9-6.7-8.5-8.8c-3.6-2.1-7.7-3.2-12.2-3.2c-4.5,0-8.6,1.1-12.1,3.2
|
||||
c-3.6,2.1-6.4,5-8.5,8.8c-2.1,3.8-3.1,8.1-3.1,12.9c0,4.8,1,9.1,3.1,12.9c2.1,3.8,4.9,6.7,8.5,8.8c3.6,2.1,7.6,3.2,12.1,3.2
|
||||
C458.3,79.1,462.3,78.1,465.9,76z"/>
|
||||
<path class="st1" d="M500.3,9.2c-0.9-0.9-1.4-1.9-1.4-3.2c0-1.3,0.5-2.4,1.4-3.3c0.9-0.9,2-1.4,3.3-1.4c1.3,0,2.4,0.4,3.3,1.3
|
||||
c0.9,0.9,1.4,1.9,1.4,3.2c0,1.3-0.5,2.4-1.4,3.3c-0.9,0.9-2,1.4-3.3,1.4C502.3,10.5,501.2,10.1,500.3,9.2z M500.7,24.8h5.6v58.9
|
||||
h-5.6V24.8z"/>
|
||||
<path class="st1" d="M559.4,80c-1.4,1.4-3.2,2.4-5.4,3.1c-2.1,0.7-4.4,1.1-6.7,1.1c-5.1,0-9.1-1.4-11.9-4.2
|
||||
c-2.8-2.8-4.2-6.8-4.2-11.8V29.7h-10.8v-4.9h10.8V12h5.6v12.9h18.7v4.9H537v37.9c0,3.8,0.9,6.8,2.8,8.8c1.8,2,4.6,3,8.2,3
|
||||
c3.7,0,6.7-1.1,9.1-3.3L559.4,80z"/>
|
||||
<path class="st1" d="M611.8,30c3.9,3.7,5.9,9.2,5.9,16.4v37.4h-5.4V73.3c-1.9,3.5-4.6,6.2-8.2,8.1c-3.6,1.9-7.9,2.9-13,2.9
|
||||
c-6.5,0-11.7-1.5-15.5-4.6c-3.8-3.1-5.7-7.1-5.7-12.2c0-4.9,1.8-8.9,5.2-11.9c3.5-3,9.1-4.6,16.8-4.6H612v-4.7
|
||||
c0-5.5-1.5-9.7-4.5-12.5c-3-2.9-7.3-4.3-13-4.3c-3.9,0-7.7,0.7-11.2,2c-3.6,1.4-6.6,3.2-9.1,5.4l-2.8-4.1c2.9-2.6,6.5-4.7,10.6-6.2
|
||||
c4.1-1.5,8.5-2.2,13-2.2C602.3,24.4,607.9,26.3,611.8,30z M604.3,76.2c3.4-2.3,6-5.5,7.7-9.8V55.3h-20.1c-5.8,0-10,1.1-12.6,3.2
|
||||
c-2.6,2.1-3.9,5-3.9,8.7c0,3.8,1.4,6.9,4.3,9.1c2.9,2.2,6.9,3.3,12.1,3.3C596.7,79.6,600.9,78.5,604.3,76.2z"/>
|
||||
<path class="st1" d="M640.9,0h5.6v83.8h-5.6V0z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.4 KiB |
11
.github/sponsors/workos-logo-white-bg.svg
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
<svg width="1354" height="420" viewBox="0 0 1354 420" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="1354" height="420" rx="20" fill="white"/>
|
||||
<path d="M434.751 133.122H466.637L489.595 227.729C493.852 245.585 494.697 256.219 494.697 256.219H495.128C495.128 256.219 496.61 245.808 500.867 227.729L522.757 133.122H558.9L582.066 227.729C586.53 246.223 587.598 256.219 587.598 256.219H588.236C588.236 256.219 588.666 246.223 592.907 227.729L615.02 133.122H646.907L606.523 288.313H571.017L546.576 194.344C541.474 173.936 541.044 164.801 541.044 164.801H540.614C540.614 164.801 540.183 173.936 535.512 194.344L512.553 288.313H475.996L434.751 133.122Z" fill="black"/>
|
||||
<path d="M641.583 231.934C641.583 196.428 664.541 173.47 699.202 173.47C733.639 173.47 756.597 196.428 756.597 231.934C756.597 267.647 733.639 290.828 699.202 290.828C664.557 290.812 641.583 267.647 641.583 231.934ZM726.832 231.934C726.832 208.976 715.783 195.998 699.202 195.998C681.346 195.998 671.349 210.458 671.349 231.934C671.349 255.323 682.398 268.284 699.202 268.284C717.058 268.284 726.832 253.824 726.832 231.934Z" fill="black"/>
|
||||
<path d="M770.836 175.21H799.103V196.048H799.741C804.635 185.207 816.322 174.365 836.299 174.365C839.695 174.365 841.831 174.796 843.314 175.21V203.478H842.469C842.469 203.478 839.918 202.633 832.903 202.633C811.013 202.633 799.103 215.594 799.103 239.828V288.295H770.836V175.21Z" fill="black"/>
|
||||
<path d="M856.5 133.122H884.767V182.865C884.767 212.2 884.336 217.509 884.336 217.509H884.767L926.857 175.212H962.139L912.843 224.11L970.031 288.313H936.646L895.401 241.536L884.767 251.946V288.297H856.5V133.122Z" fill="black"/>
|
||||
<path d="M970.444 211.285C970.444 163.455 1000.21 131.569 1044.85 131.569C1089.49 131.569 1119.26 163.455 1119.26 211.285C1119.26 259.114 1089.49 291.001 1044.85 291.001C1000.21 291.001 970.444 259.114 970.444 211.285ZM1088.42 211.285C1088.42 178.761 1071 156.855 1044.84 156.855C1018.67 156.855 1001.26 178.761 1001.26 211.285C1001.26 243.809 1018.69 265.715 1044.84 265.715C1070.98 265.715 1088.42 243.809 1088.42 211.285Z" fill="black"/>
|
||||
<path d="M1130.08 236.656H1162.4C1162.4 254.943 1174.95 265.146 1194.08 265.146C1210.23 265.146 1221.29 257.063 1221.29 245.584C1221.29 232.622 1212.79 229.21 1185.79 223.901C1161.12 219.007 1134.98 210.716 1134.98 178.399C1134.98 151.408 1157.93 131 1193.01 131C1229.57 131 1252.11 150.132 1252.11 179.037H1219.79C1219.79 165.007 1208.95 156.286 1193.01 156.286C1176.86 156.286 1166.86 164.146 1166.86 175.625C1166.86 187.742 1173.88 192.413 1195.56 196.878C1227.65 203.685 1254.02 207.288 1254.02 243.001C1254.02 271.3 1229.36 290.432 1193.01 290.432C1156.02 290.432 1130.08 268.957 1130.08 236.656Z" fill="black"/>
|
||||
<path d="M100 210C100 214.824 101.269 219.647 103.723 223.793L148.231 300.878C152.8 308.747 159.739 315.178 168.369 318.055C185.377 323.724 202.977 316.447 211.354 301.893L222.1 283.278L179.708 210L224.47 132.408L235.216 113.792C238.431 108.208 242.747 103.638 247.824 100H243.17H178.777C166.677 100 155.508 106.431 149.5 116.923L103.723 196.208C101.269 200.354 100 205.177 100 210Z" fill="#6363F1"/>
|
||||
<path d="M353.847 210C353.847 205.177 352.578 200.353 350.124 196.207L305.024 118.107C296.647 103.638 279.047 96.3608 262.039 101.945C253.409 104.822 246.47 111.253 241.901 119.122L231.747 136.638L274.139 210L229.378 287.592L218.632 306.208C215.416 311.708 211.101 316.362 206.024 320H210.678H275.07C287.17 320 298.34 313.569 304.347 303.077L350.124 223.792C352.578 219.646 353.847 214.823 353.847 210Z" fill="#6363F1"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.4 KiB |
16
.github/workflows/deploy.yml
vendored
@@ -3,31 +3,25 @@ on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
env:
|
||||
ROADMAP_GA_SECRET: ${{ secrets.GA_SECRET }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
PAT: ${{ secrets.PAT }}
|
||||
CI: true
|
||||
NEXT_TELEMETRY_DISABLED: 1
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 18
|
||||
- run: git config --global url."https://${{ secrets.PAT }}@github.com/".insteadOf ssh://git@github.com/
|
||||
- uses: pnpm/action-setup@v2.2.2
|
||||
with:
|
||||
version: 7.13.4
|
||||
node-version: 14
|
||||
- name: Setup Environment
|
||||
run: |
|
||||
pnpm install
|
||||
npm install
|
||||
- name: Generate meta and build
|
||||
run: |
|
||||
npm run meta
|
||||
npm run build
|
||||
touch ./dist/.nojekyll
|
||||
echo 'roadmap.sh' > ./dist/CNAME
|
||||
- name: Deploy to GH Pages
|
||||
run: |
|
||||
git config user.email "kamranahmed.se@gmail.com"
|
||||
|
||||
38
.github/workflows/update-deps.yml
vendored
@@ -1,38 +0,0 @@
|
||||
name: Update dependencies
|
||||
|
||||
on:
|
||||
workflow_dispatch: # allow manual run
|
||||
schedule:
|
||||
- cron: '0 0 * * 0' # every sunday at midnight
|
||||
|
||||
jobs:
|
||||
upgrade-deps:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
- uses: pnpm/action-setup@v2.2.2
|
||||
with:
|
||||
version: 7.13.4
|
||||
- name: Upgrade dependencies
|
||||
run: |
|
||||
pnpm install
|
||||
npm run upgrade
|
||||
pnpm install --lockfile-only
|
||||
- name: Create PR
|
||||
uses: peter-evans/create-pull-request@v4
|
||||
with:
|
||||
delete-branch: false
|
||||
branch: "update-deps"
|
||||
base: "master"
|
||||
labels: |
|
||||
dependencies
|
||||
automated pr
|
||||
reviewers: kamranahmedse
|
||||
commit-message: "chore: update dependencies to latest"
|
||||
title: "Upgrade dependencies to latest"
|
||||
body: |
|
||||
Updates all dependencies to latest versions.
|
||||
Please review the changes and merge if everything looks good.
|
||||
44
.gitignore
vendored
@@ -1,26 +1,36 @@
|
||||
# build output
|
||||
dist/
|
||||
.output/
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
out
|
||||
|
||||
# dependencies
|
||||
node_modules/
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
bin/developer-roadmap
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# logs
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.idea
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
# environment variables
|
||||
.env
|
||||
.env.production
|
||||
|
||||
# macOS-specific files
|
||||
.DS_Store
|
||||
/test-results/
|
||||
/playwright-report/
|
||||
/playwright/.cache/
|
||||
tests-examples
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
5
.prettierrc
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"tabWidth": 2
|
||||
}
|
||||
4
.vscode/extensions.json
vendored
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"recommendations": ["astro-build.astro-vscode"],
|
||||
"unwantedRecommendations": []
|
||||
}
|
||||
11
.vscode/launch.json
vendored
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"command": "./node_modules/.bin/astro dev",
|
||||
"name": "Development server",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
// https://astro.build/config
|
||||
import sitemap from '@astrojs/sitemap';
|
||||
import tailwind from '@astrojs/tailwind';
|
||||
import compress from 'astro-compress';
|
||||
import { defineConfig } from 'astro/config';
|
||||
import rehypeExternalLinks from 'rehype-external-links';
|
||||
import { serializeSitemap, shouldIndexPage } from './sitemap.mjs';
|
||||
|
||||
export default defineConfig({
|
||||
site: 'https://roadmap.sh',
|
||||
markdown: {
|
||||
shikiConfig: {
|
||||
theme: 'dracula'
|
||||
},
|
||||
rehypePlugins: [
|
||||
[
|
||||
rehypeExternalLinks,
|
||||
{
|
||||
target: '_blank',
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
integrations: [
|
||||
tailwind({
|
||||
config: {
|
||||
applyBaseStyles: false,
|
||||
},
|
||||
}),
|
||||
sitemap({
|
||||
filter: shouldIndexPage,
|
||||
serialize: serializeSitemap,
|
||||
}),
|
||||
compress({
|
||||
css: false,
|
||||
js: false,
|
||||
}),
|
||||
],
|
||||
});
|
||||
@@ -1,14 +0,0 @@
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const jsonsDir = path.join(process.cwd(), 'public/jsons');
|
||||
const jsonFiles = fs.readdirSync(jsonsDir);
|
||||
|
||||
jsonFiles.forEach((jsonFileName) => {
|
||||
console.log(`Compressing ${jsonFileName}...`);
|
||||
|
||||
const jsonFilePath = path.join(jsonsDir, jsonFileName);
|
||||
const json = require(jsonFilePath);
|
||||
|
||||
fs.writeFileSync(jsonFilePath, JSON.stringify(json));
|
||||
});
|
||||
@@ -1,163 +0,0 @@
|
||||
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');
|
||||
@@ -1,44 +0,0 @@
|
||||
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));
|
||||
60
components/content-page-header.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
import { Box, Container, Flex, Heading, Image, Link, Text } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
type ContentPageHeaderProps = {
|
||||
formattedDate: string;
|
||||
title: string;
|
||||
subtitle: string;
|
||||
author?: {
|
||||
name: string;
|
||||
twitter: string;
|
||||
picture: string;
|
||||
},
|
||||
subLink?: {
|
||||
text: string;
|
||||
url: string;
|
||||
}
|
||||
};
|
||||
|
||||
export function ContentPageHeader(props: ContentPageHeaderProps) {
|
||||
const { title, subtitle, author = null, formattedDate, subLink = null } = props;
|
||||
|
||||
return (
|
||||
<Box pt={['35px', '35px', '70px']} pb={['35px', '35px', '55px']} borderBottomWidth={1} mb='30px'>
|
||||
<Container maxW='container.md' position='relative' textAlign={['left', 'left', 'center']}>
|
||||
<Flex alignItems='center' justifyContent={['flex-start', 'flex-start', 'center']}
|
||||
fontSize={['12px', '12px', '14px']}>
|
||||
|
||||
{author?.name && (
|
||||
<>
|
||||
<Link
|
||||
d={['none', 'flex', 'flex']}
|
||||
target='_blank'
|
||||
href={`https://twitter.com/${author.twitter}`}
|
||||
alignItems='center'
|
||||
fontWeight={600}
|
||||
color='gray.500'
|
||||
>
|
||||
<Image alt={''} rounded={'full'} mr='7px' w='22px' src={author.picture} />
|
||||
{author.name}
|
||||
</Link>
|
||||
<Text d={['none', 'inline', 'inline']} mx='7px' color='gray.500' as='span'>·</Text>
|
||||
</>
|
||||
)}
|
||||
|
||||
<Text color='gray.500' as='span'>{formattedDate}</Text>
|
||||
{subLink?.text && (
|
||||
<>
|
||||
<Text d={['none', 'none', 'inline']} mx='7px' color='gray.500' as='span'>·</Text>
|
||||
<Link d={['none', 'none', 'inline']} color='blue.500' fontWeight={500}
|
||||
href={subLink.url} target={'_blank'}>{subLink.text}</Link>
|
||||
</>
|
||||
)}
|
||||
</Flex>
|
||||
<Heading as='h1' color='black' fontSize={['30px', '30px', '45px']} lineHeight={['40px', '40px', '53px']}
|
||||
fontWeight={700} my={['5px', '5px', '10px']}>{title}</Heading>
|
||||
<Text fontSize={['14px', '14px', '16px']} color='gray.700'>{subtitle}</Text>
|
||||
</Container>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
37
components/custom-ad.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
export const CustomAd = () => {
|
||||
return (
|
||||
<div id='carbonads'>
|
||||
<span>
|
||||
<span className='carbon-wrap'>
|
||||
<a
|
||||
href='https://freemote.com/strategy?sl=roadmap'
|
||||
className='carbon-img'
|
||||
target='_blank'
|
||||
>
|
||||
<img
|
||||
src='/fm-img.png'
|
||||
alt='Custom Logo'
|
||||
height='100'
|
||||
width='130'
|
||||
style={{ maxWidth: '130px', border: 'none' }}
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
href='https://freemote.com/strategy?sl=roadmap'
|
||||
className='carbon-text'
|
||||
target='_blank'
|
||||
>
|
||||
He Went from ZERO TO $74,000 as a Full Time Developer in 7 Weeks
|
||||
</a>
|
||||
</span>
|
||||
<a
|
||||
href='https://github.com/sponsors/kamranahmedse'
|
||||
className='carbon-poweredby'
|
||||
target='_blank'
|
||||
>
|
||||
Sponsored by
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
46
components/dimmed-more.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import { Box, Link, Text } from '@chakra-ui/react';
|
||||
|
||||
type DimmedMoreProps = {
|
||||
text: string;
|
||||
href: string;
|
||||
};
|
||||
|
||||
export function DimmedMore(props: DimmedMoreProps) {
|
||||
const { text, href } = props;
|
||||
|
||||
return (
|
||||
<Box position='relative' textAlign='center' bottom='20px'>
|
||||
<Box
|
||||
opacity={1}
|
||||
pointerEvents='none'
|
||||
position='absolute'
|
||||
bottom={0}
|
||||
height='200px'
|
||||
width='100%'
|
||||
background='linear-gradient(180deg, rgb(255 255 255 / 40%), white)'
|
||||
/>
|
||||
|
||||
<Link
|
||||
rounded='20px'
|
||||
display='inline'
|
||||
bg='green.600'
|
||||
color='white'
|
||||
p='7px 20px'
|
||||
href={href}
|
||||
fontWeight={800}
|
||||
fontSize='11px'
|
||||
textTransform='uppercase'
|
||||
my='25px'
|
||||
position='relative'
|
||||
_hover={{
|
||||
textDecoration: 'none',
|
||||
'& .forward-arrow': {
|
||||
transform: 'translateX(3px)'
|
||||
}
|
||||
}}>
|
||||
{text}
|
||||
<Text d='inline-block' as='span' transition='200ms' ml='4px' className='forward-arrow'>→</Text>
|
||||
</Link>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
67
components/footer.tsx
Normal file
@@ -0,0 +1,67 @@
|
||||
import { Box, Container, Flex, Image, Link, Stack, Text } from '@chakra-ui/react';
|
||||
import siteConfig from '../content/site.json';
|
||||
import { CustomAd } from './custom-ad';
|
||||
|
||||
function NavigationLinks() {
|
||||
return (
|
||||
<>
|
||||
<Stack isInline d={['none', 'none', 'flex']} color='gray.400' fontWeight={600} spacing='30px'>
|
||||
<Link _hover={{ color: 'white' }} href='/roadmaps'>Roadmaps</Link>
|
||||
<Link _hover={{ color: 'white' }} href='/guides'>Guides</Link>
|
||||
<Link _hover={{ color: 'white' }} href='/watch'>Videos</Link>
|
||||
<Link _hover={{ color: 'white' }} href='/about'>About</Link>
|
||||
<Link _hover={{ color: 'white' }} href={siteConfig.url.youtube} target='_blank'>YouTube</Link>
|
||||
</Stack>
|
||||
|
||||
<Stack d={['flex', 'flex', 'none']} color='gray.400' fontWeight={600} spacing={0}>
|
||||
<Link py='7px' borderBottomWidth={1} borderBottomColor='gray.800' _hover={{ color: 'white' }}
|
||||
href='/roadmaps'>Roadmaps</Link>
|
||||
<Link py='7px' borderBottomWidth={1} borderBottomColor='gray.800' _hover={{ color: 'white' }}
|
||||
href='/guides'>Guides</Link>
|
||||
<Link py='7px' borderBottomWidth={1} borderBottomColor='gray.800' _hover={{ color: 'white' }}
|
||||
href='/watch'>Videos</Link>
|
||||
<Link py='7px' borderBottomWidth={1} borderBottomColor='gray.800' _hover={{ color: 'white' }}
|
||||
href='/about'>About</Link>
|
||||
<Link py='7px' _hover={{ color: 'white' }} target='_blank'
|
||||
href={siteConfig.url.youtube}>YouTube</Link>
|
||||
</Stack>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function Footer() {
|
||||
return (
|
||||
<Box bg='brand.hero' p={['25px 0', '25px 0', '40px 0']}>
|
||||
<Container maxW='container.md'>
|
||||
<NavigationLinks />
|
||||
|
||||
<Box mt={['40px', '40px', '50px']} mb='40px' maxW='500px'>
|
||||
<Flex spacing={0} alignItems='center' color='gray.400'>
|
||||
<Link d='flex' alignItems='center' fontWeight={600} _hover={{ textDecoration: 'none', color: 'white' }}
|
||||
href='/'>
|
||||
<Image alt='' h='25px' w='25px' src='/logo.svg' mr='6px' />
|
||||
roadmap.sh
|
||||
</Link>
|
||||
<Text as='span' mx='7px'>by</Text>
|
||||
<Link bg='blue.500' px='6px' py='2px' rounded='4px' color='white' fontWeight={600} fontSize='13px'
|
||||
_hover={{ textDecoration: 'none', bg: 'blue.600' }} href={siteConfig.url.twitter}
|
||||
target='_blank'>@kamranahmedse</Link>
|
||||
</Flex>
|
||||
|
||||
<Text my='15px' fontSize='14px' color='gray.500'>Community created roadmaps, articles, resources and
|
||||
journeys to help you choose your path and grow in your career.</Text>
|
||||
|
||||
<Text fontSize='14px' color='gray.500'>
|
||||
<Text as='span' mr='10px'>© roadmap.sh</Text>·
|
||||
<Link href='/about' _hover={{ textDecoration: 'none', color: 'white' }} color='gray.400'
|
||||
mx='10px'>FAQs</Link>·
|
||||
<Link href='/terms' _hover={{ textDecoration: 'none', color: 'white' }} color='gray.400'
|
||||
mx='10px'>Terms</Link>·
|
||||
<Link href='/privacy' _hover={{ textDecoration: 'none', color: 'white' }} color='gray.400'
|
||||
mx='10px'>Privacy</Link>
|
||||
</Text>
|
||||
</Box>
|
||||
</Container>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
117
components/global-header.tsx
Normal file
@@ -0,0 +1,117 @@
|
||||
import { useState } from 'react';
|
||||
import { HamburgerIcon } from '@chakra-ui/icons';
|
||||
import { Box, CloseButton, Container, Flex, IconButton, Image, Link, Stack, Text } from '@chakra-ui/react';
|
||||
import RoadmapLogo from '../components/icons/roadmap.svg';
|
||||
import siteConfig from '../content/site.json';
|
||||
|
||||
type MenuLinkProps = {
|
||||
text: string;
|
||||
link: string;
|
||||
};
|
||||
|
||||
function MenuLink(props: MenuLinkProps) {
|
||||
const { text, link } = props;
|
||||
|
||||
return <Link
|
||||
borderBottomWidth={0}
|
||||
borderBottomColor='gray.500'
|
||||
_hover={{ textDecoration: 'none', borderBottomColor: 'white' }}
|
||||
fontWeight={500}
|
||||
href={link}
|
||||
>
|
||||
{text}
|
||||
</Link>;
|
||||
}
|
||||
|
||||
function DesktopMenuLinks() {
|
||||
return (
|
||||
<Stack d={['none', 'flex', 'flex']} shouldWrapChildren isInline spacing='15px' alignItems='center' color='gray.50'
|
||||
fontSize='15px'>
|
||||
<MenuLink text={'Roadmaps'} link={'/roadmaps'} />
|
||||
<MenuLink text={'Guides'} link={'/guides'} />
|
||||
<MenuLink text={'Videos'} link={'/watch'} />
|
||||
|
||||
<Link ml='10px' bgGradient='linear(to-l, yellow.700, red.600)' p='7px 10px' rounded='4px'
|
||||
_hover={{ textDecoration: 'none', bgGradient: 'linear(to-l, red.800, yellow.700)' }}
|
||||
fontWeight={500} href={'/signup'}>Subscribe</Link>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
function MobileMenuLinks() {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<IconButton
|
||||
rounded='5px'
|
||||
padding={0}
|
||||
aria-label={'Menu'}
|
||||
d={['block', 'none', 'none']}
|
||||
icon={<HamburgerIcon color='white' w='25px' height='25px' />}
|
||||
color='white'
|
||||
cursor='pointer'
|
||||
h='auto'
|
||||
bg='transparent'
|
||||
_hover={{ bg: 'transparent' }}
|
||||
_active={{ bg: 'transparent' }}
|
||||
_focus={{ bg: 'transparent' }}
|
||||
onClick={() => setIsOpen(true)}
|
||||
/>
|
||||
|
||||
{isOpen && (
|
||||
<Stack color='gray.100'
|
||||
fontSize={['22px', '22px', '22px', '32px']}
|
||||
alignItems='center'
|
||||
justifyContent='center'
|
||||
pos='fixed'
|
||||
left={0}
|
||||
right={0}
|
||||
bottom={0}
|
||||
top={0}
|
||||
bg='gray.900'
|
||||
spacing='12px'
|
||||
zIndex={999}
|
||||
>
|
||||
<Link href='/roadmaps'>Roadmaps</Link>
|
||||
<Link href='/guides'>Guides</Link>
|
||||
<Link href='/watch'>Videos</Link>
|
||||
<Link href='/signup'>Subscribe</Link>
|
||||
<CloseButton onClick={() => setIsOpen(false)} pos='fixed' top='40px' right='15px' size='lg' />
|
||||
</Stack>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
type GlobalHeaderProps = {
|
||||
variant?: 'transparent' | 'solid'
|
||||
};
|
||||
|
||||
export function GlobalHeader(props: GlobalHeaderProps) {
|
||||
const { variant = 'solid' } = props;
|
||||
|
||||
return (
|
||||
<Box bg={variant === 'solid' ? 'gray.900' : 'transparent'} p='20px 0'>
|
||||
<Container maxW='container.md'>
|
||||
<Flex justifyContent='space-between' alignItems='center'>
|
||||
<Box>
|
||||
<Link w='100%'
|
||||
d='flex'
|
||||
href='/'
|
||||
alignItems='center'
|
||||
color='white'
|
||||
fontWeight={600}
|
||||
_hover={{ textDecoration: 'none' }}
|
||||
fontSize='18px'>
|
||||
<RoadmapLogo style={{ height: '30px', width: '30px', marginRight: '10px' }} />
|
||||
<Text d={['block', 'none', 'block']} as='span'>roadmap.sh</Text>
|
||||
</Link>
|
||||
</Box>
|
||||
<DesktopMenuLinks />
|
||||
<MobileMenuLinks />
|
||||
</Flex>
|
||||
</Container>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
31
components/guide/guide-grid-item.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import { Badge, Box, Heading, Link, Text } from '@chakra-ui/react';
|
||||
|
||||
type GuideGridItemProps = {
|
||||
title: string;
|
||||
href: string;
|
||||
subtitle: string;
|
||||
date: string;
|
||||
isNew?: boolean;
|
||||
colorIndex?: number;
|
||||
};
|
||||
|
||||
const bgColorList = [
|
||||
'gray.700',
|
||||
'purple.800'
|
||||
];
|
||||
|
||||
export function GuideGridItem(props: GuideGridItemProps) {
|
||||
const { title, subtitle, date, isNew = false, colorIndex = 0, href } = props;
|
||||
|
||||
return (
|
||||
<Box _hover={{ textDecoration: 'none', transform: 'scale(1.02)' }} as={Link} href={href} shadow='xl' p='20px'
|
||||
rounded='10px' bg={bgColorList[colorIndex] ?? bgColorList[0]} flex={1}>
|
||||
<Text mb='10px' fontSize='13px' color='gray.400'>
|
||||
{isNew && <Badge colorScheme={'yellow'} mr='10px'>New</Badge>}
|
||||
{date}
|
||||
</Text>
|
||||
<Heading color='white' mb={'6px'} fontSize='20px'>{title}</Heading>
|
||||
<Text color='gray.300' fontSize='14px'>{subtitle}</Text>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
165
components/helmet.tsx
Normal file
@@ -0,0 +1,165 @@
|
||||
import NextHead from 'next/head';
|
||||
import siteConfig from '../content/site.json';
|
||||
import { RoadmapType } from '../lib/roadmap';
|
||||
import { roadmapTheme } from '../styles/theme';
|
||||
|
||||
type HelmetProps = {
|
||||
title?: string;
|
||||
keywords?: string[];
|
||||
canonical?: string;
|
||||
description?: string;
|
||||
roadmap?: RoadmapType;
|
||||
};
|
||||
|
||||
function getRichSnippetJson(roadmap: RoadmapType) {
|
||||
return {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'Article',
|
||||
mainEntityOfPage: {
|
||||
'@type': 'WebPage',
|
||||
'@id': `https://roadmap.sh/${roadmap.id}`,
|
||||
},
|
||||
headline: roadmap.seo.title,
|
||||
description: roadmap.seo.description,
|
||||
image: roadmap.jsonUrl
|
||||
? `https://roadmap.sh/roadmaps/${roadmap.id}.png`
|
||||
: undefined,
|
||||
author: {
|
||||
'@type': 'Person',
|
||||
name: 'Kamran Ahmed',
|
||||
url: 'https://twitter.com/kamranahmedse',
|
||||
},
|
||||
publisher: {
|
||||
'@type': 'Organization',
|
||||
name: 'roadmap.sh',
|
||||
logo: {
|
||||
'@type': 'ImageObject',
|
||||
url: 'https://roadmap.sh/brand-square.png',
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const Helmet = (props: HelmetProps) => {
|
||||
const { roadmap, title, canonical, description, keywords } = props;
|
||||
|
||||
return (
|
||||
<NextHead>
|
||||
<meta charSet="UTF-8" />
|
||||
|
||||
<title>{title || siteConfig.title}</title>
|
||||
<meta
|
||||
name="description"
|
||||
content={description || siteConfig.description}
|
||||
/>
|
||||
|
||||
<meta name="author" content={siteConfig.author} />
|
||||
<meta
|
||||
name="keywords"
|
||||
content={keywords ? keywords.join(',') : siteConfig.keywords.join(',')}
|
||||
/>
|
||||
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, user-scalable=yes, initial-scale=1.0, maximum-scale=3.0, minimum-scale=1.0"
|
||||
/>
|
||||
{canonical && <link rel="canonical" href={canonical} />}
|
||||
<meta httpEquiv="Content-Language" content="en" />
|
||||
<meta property="og:title" content={title || siteConfig.title} />
|
||||
<meta
|
||||
property="og:description"
|
||||
content={description || siteConfig.description}
|
||||
/>
|
||||
<meta
|
||||
property="og:image"
|
||||
content={`${siteConfig.url.web}${siteConfig.logoSquare}`}
|
||||
/>
|
||||
<meta property="og:url" content={siteConfig.url.web} />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta
|
||||
property="article:publisher"
|
||||
content={`https://facebook.com/${siteConfig.facebook}`}
|
||||
/>
|
||||
<meta property="og:site_name" content={siteConfig.name} />
|
||||
<meta property="article:author" content={siteConfig.author} />
|
||||
|
||||
<meta name="twitter:card" content="summary" />
|
||||
<meta name="twitter:site" content={`@${siteConfig.twitter}`} />
|
||||
<meta name="twitter:title" content={title || siteConfig.title} />
|
||||
<meta
|
||||
name="twitter:description"
|
||||
content={description || siteConfig.description}
|
||||
/>
|
||||
<meta
|
||||
name="twitter:image"
|
||||
content={`${siteConfig.url.web}${siteConfig.logoSquare}`}
|
||||
/>
|
||||
<meta name="twitter:image:alt" content="roadmap.sh" />
|
||||
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta
|
||||
name="apple-mobile-web-app-status-bar-style"
|
||||
content="black-translucent"
|
||||
/>
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="180x180"
|
||||
href="/manifest/apple-touch-icon.png"
|
||||
/>
|
||||
<meta name="msapplication-TileColor" content="#101010" />
|
||||
<meta name="theme-color" content="#848a9a" />
|
||||
|
||||
<link rel="manifest" href="/manifest/manifest.json" />
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="32x32"
|
||||
href="/manifest/icon32.png"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="16x16"
|
||||
href="/manifest/icon16.png"
|
||||
/>
|
||||
<link
|
||||
rel="shortcut icon"
|
||||
href="/manifest/favicon.ico"
|
||||
type="image/x-icon"
|
||||
/>
|
||||
<link rel="icon" href="/manifest/favicon.ico" type="image/x-icon" />
|
||||
|
||||
{roadmap?.id && (
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: JSON.stringify(getRichSnippetJson(roadmap)),
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Global Site Tag (gtag.js) - Google Analytics */}
|
||||
{process.env.GA_SECRET && (
|
||||
<>
|
||||
<script
|
||||
async
|
||||
src={`https://www.googletagmanager.com/gtag/js?id=${process.env.GA_SECRET}`}
|
||||
/>
|
||||
<script
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
gtag('config', '${process.env.GA_SECRET}');
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</NextHead>
|
||||
);
|
||||
};
|
||||
|
||||
export default Helmet;
|
||||
3
components/icons/facebook.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="29" height="29">
|
||||
<path d="M23.2 5H5.8a.8.8 0 0 0-.8.8V23.2c0 .44.35.8.8.8h9.3v-7.13h-2.38V13.9h2.38v-2.38c0-2.45 1.55-3.66 3.74-3.66 1.05 0 1.95.08 2.2.11v2.57h-1.5c-1.2 0-1.48.57-1.48 1.4v1.96h2.97l-.6 2.97h-2.37l.05 7.12h5.1a.8.8 0 0 0 .79-.8V5.8a.8.8 0 0 0-.8-.79"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 298 B |
3
components/icons/github.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 841 B |
4
components/icons/link.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">
|
||||
<path fill-rule="evenodd"
|
||||
d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 474 B |
4
components/icons/roadmap.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="30" height="30" viewBox="0 0 283 283" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0 39C0 17.4609 17.4609 0 39 0H244C265.539 0 283 17.4609 283 39V244C283 265.539 265.539 283 244 283H39C17.4609 283 0 265.539 0 244V39Z" fill="black"></path>
|
||||
<path d="M121.215 210.72C119.348 211.28 116.361 211.84 112.255 212.4C108.335 212.96 104.228 213.24 99.9347 213.24C95.828 213.24 92.0947 212.96 88.7347 212.4C85.5614 211.84 82.8547 210.72 80.6147 209.04C78.3747 207.36 76.6014 205.12 75.2947 202.32C74.1747 199.333 73.6147 195.507 73.6147 190.84V106.84C73.6147 102.547 74.3614 98.9067 75.8547 95.92C77.5347 92.7467 79.868 89.9467 82.8547 87.52C85.8414 85.0933 89.4814 82.9467 93.7747 81.08C98.2547 79.0267 103.015 77.2533 108.055 75.76C113.095 74.2667 118.321 73.1467 123.735 72.4C129.148 71.4667 134.561 71 139.975 71C148.935 71 156.028 72.7733 161.255 76.32C166.481 79.68 169.095 85.28 169.095 93.12C169.095 95.7333 168.721 98.3467 167.975 100.96C167.228 103.387 166.295 105.627 165.175 107.68C161.255 107.68 157.241 107.867 153.135 108.24C149.028 108.613 145.015 109.173 141.095 109.92C137.175 110.667 133.441 111.507 129.895 112.44C126.535 113.187 123.641 114.12 121.215 115.24V210.72ZM166.387 188.32C166.387 180.48 168.813 173.947 173.667 168.72C178.52 163.493 185.147 160.88 193.547 160.88C201.947 160.88 208.573 163.493 213.427 168.72C218.28 173.947 220.707 180.48 220.707 188.32C220.707 196.16 218.28 202.693 213.427 207.92C208.573 213.147 201.947 215.76 193.547 215.76C185.147 215.76 178.52 213.147 173.667 207.92C168.813 202.693 166.387 196.16 166.387 188.32Z" fill="white"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
3
components/icons/twitter.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="29" height="29" fill="currentColor">
|
||||
<path d="M22.05 7.54a4.47 4.47 0 0 0-3.3-1.46 4.53 4.53 0 0 0-4.53 4.53c0 .35.04.7.08 1.05A12.9 12.9 0 0 1 5 6.89a5.1 5.1 0 0 0-.65 2.26c.03 1.6.83 2.99 2.02 3.79a4.3 4.3 0 0 1-2.02-.57v.08a4.55 4.55 0 0 0 3.63 4.44c-.4.08-.8.13-1.21.16l-.81-.08a4.54 4.54 0 0 0 4.2 3.15 9.56 9.56 0 0 1-5.66 1.94l-1.05-.08c2 1.27 4.38 2.02 6.94 2.02 8.3 0 12.86-6.9 12.84-12.85.02-.24 0-.43 0-.65a8.68 8.68 0 0 0 2.26-2.34c-.82.38-1.7.62-2.6.72a4.37 4.37 0 0 0 1.95-2.51c-.84.53-1.81.9-2.83 1.13z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 550 B |
21
components/icons/video-icon.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
export function VideoIcon(props: any) {
|
||||
return (
|
||||
<svg
|
||||
stroke='currentColor'
|
||||
fill='currentColor'
|
||||
strokeWidth='0'
|
||||
viewBox='0 0 24 24'
|
||||
height='1em'
|
||||
width='1em'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
{...props}
|
||||
>
|
||||
<g>
|
||||
<path fill='none' d='M0 0h24v24H0z' />
|
||||
<path
|
||||
d='M3 3.993C3 3.445 3.445 3 3.993 3h16.014c.548 0 .993.445.993.993v16.014a.994.994 0 0 1-.993.993H3.993A.994.994 0 0 1 3 20.007V3.993zm7.622 4.422a.4.4 0 0 0-.622.332v6.506a.4.4 0 0 0 .622.332l4.879-3.252a.4.4 0 0 0 0-.666l-4.88-3.252z'
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
3
components/icons/youtube.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill='currentColor'>
|
||||
<path d="M19.615 3.184c-3.604-.246-11.631-.245-15.23 0-3.897.266-4.356 2.62-4.385 8.816.029 6.185.484 8.549 4.385 8.816 3.6.245 11.626.246 15.23 0 3.897-.266 4.356-2.62 4.385-8.816-.029-6.185-.484-8.549-4.385-8.816zm-10.615 12.816v-8l8 3.993-8 4.007z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 369 B |
58
components/links-list-item.tsx
Normal file
@@ -0,0 +1,58 @@
|
||||
import React from 'react';
|
||||
import { Badge, Flex, Link, Text } from '@chakra-ui/react';
|
||||
|
||||
type LinksListItemProps = {
|
||||
href: string;
|
||||
title: string;
|
||||
subtitle: string;
|
||||
badgeText?: string;
|
||||
target?: string;
|
||||
icon?: React.ReactChild;
|
||||
hideSubtitleOnMobile?: boolean;
|
||||
};
|
||||
|
||||
export function LinksListItem(props: LinksListItemProps) {
|
||||
const { title, subtitle, badgeText, icon, hideSubtitleOnMobile = false, href, target } = props;
|
||||
|
||||
return (
|
||||
<Link
|
||||
target={target || '_self'}
|
||||
href={href}
|
||||
fontSize={['14px', '14px', '15px']}
|
||||
py='9px'
|
||||
d='flex'
|
||||
flexDirection={['column', 'row', 'row']}
|
||||
fontWeight={500}
|
||||
color='gray.600'
|
||||
alignItems={['flex-start', 'center']}
|
||||
justifyContent={'space-between'}
|
||||
sx={{
|
||||
'@media (hover: none)': {
|
||||
'&:hover': {
|
||||
'& .list-item-title': {
|
||||
transform: 'none'
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
_hover={{
|
||||
textDecoration: 'none',
|
||||
color: 'blue.400',
|
||||
'& .list-item-title': {
|
||||
transform: 'translateX(10px)'
|
||||
}
|
||||
}}
|
||||
isTruncated
|
||||
maxWidth='100%'
|
||||
>
|
||||
<Flex alignItems='center' className='list-item-title' transition={'200ms'}>
|
||||
{icon}
|
||||
<Text maxWidth={'345px'} isTruncated as='span'>{title}</Text>
|
||||
{badgeText &&
|
||||
<Badge pos='relative' top='1px' variant='subtle' colorScheme='purple' ml='10px'>{badgeText}</Badge>}
|
||||
</Flex>
|
||||
<Text d={[hideSubtitleOnMobile ? 'none' : 'inline', 'inline']} mt={['3px', 0]} as='span'
|
||||
fontSize={['11px', '11px', '12px']} color='gray.500'>{subtitle}</Text>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
21
components/links-list.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import React from 'react';
|
||||
import { StackDivider, VStack } from '@chakra-ui/react';
|
||||
|
||||
type LinksListProps = {
|
||||
children: React.ReactNode
|
||||
};
|
||||
|
||||
export function LinksList(props: LinksListProps) {
|
||||
const { children } = props;
|
||||
|
||||
return (
|
||||
<VStack
|
||||
rounded='5px'
|
||||
divider={<StackDivider borderColor='gray.200' />}
|
||||
spacing={0}
|
||||
align='stretch'
|
||||
>
|
||||
{children}
|
||||
</VStack>
|
||||
);
|
||||
}
|
||||
20
components/md-renderer/index.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import React from 'react';
|
||||
// @ts-ignore
|
||||
import { MDXProvider } from '@mdx-js/react';
|
||||
import { ChakraProvider } from '@chakra-ui/react';
|
||||
import MdxComponents from './mdx-components';
|
||||
import { roadmapTheme } from '../../styles/theme';
|
||||
|
||||
type MdRendererType = {
|
||||
children: React.ReactNode
|
||||
};
|
||||
|
||||
export default function MdRenderer(props: MdRendererType) {
|
||||
return (
|
||||
<ChakraProvider theme={roadmapTheme} resetCSS>
|
||||
<MDXProvider components={MdxComponents}>
|
||||
{props.children}
|
||||
</MDXProvider>
|
||||
</ChakraProvider>
|
||||
);
|
||||
};
|
||||
37
components/md-renderer/mdx-components/a.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
type EnrichedLinkProps = {
|
||||
href: string;
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
const Link = styled.a`
|
||||
font-weight: 600;
|
||||
text-decoration: underline;
|
||||
`;
|
||||
|
||||
const EnrichedLink = (props: EnrichedLinkProps) => {
|
||||
// Is external URL or is a media URL
|
||||
const isExternalUrl = /(^http(s)?:\/\/)|(\.(png|svg|jpeg|jpg)$)/.test(
|
||||
props.href
|
||||
);
|
||||
|
||||
const linkProps: Record<string, string> = {
|
||||
target: '_self',
|
||||
...(isExternalUrl
|
||||
? {
|
||||
rel: 'nofollow',
|
||||
target: '_blank',
|
||||
}
|
||||
: {}),
|
||||
};
|
||||
|
||||
return (
|
||||
<Link href={props.href} {...linkProps}>
|
||||
{props.children}
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
export default EnrichedLink;
|
||||
53
components/md-renderer/mdx-components/badge-link.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
import React from 'react';
|
||||
import { Link, Text, Badge } from '@chakra-ui/react';
|
||||
|
||||
type BadgeLinkType = {
|
||||
target: string;
|
||||
badgeText: string;
|
||||
href: string;
|
||||
colorScheme?: string;
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
export function BadgeLink(props: BadgeLinkType) {
|
||||
const {
|
||||
target = '_blank',
|
||||
colorScheme = 'purple',
|
||||
badgeText,
|
||||
href,
|
||||
children,
|
||||
} = props;
|
||||
|
||||
// Is external URL or is a media URL
|
||||
const isExternalUrl = /(^http(s)?:\/\/)|(\.(png|svg|jpeg|jpg)$)/.test(
|
||||
props.href
|
||||
);
|
||||
|
||||
const linkProps: Record<string, string> = {
|
||||
...(isExternalUrl
|
||||
? {
|
||||
rel: 'nofollow',
|
||||
}
|
||||
: {}),
|
||||
};
|
||||
|
||||
return (
|
||||
<Text mb={'0px'}>
|
||||
<Link
|
||||
fontSize="14px"
|
||||
color="blue.700"
|
||||
fontWeight={500}
|
||||
textDecoration="none"
|
||||
href={href}
|
||||
target={target}
|
||||
_hover={{ textDecoration: 'none', color: 'purple.400' }}
|
||||
{...linkProps}
|
||||
>
|
||||
<Badge fontSize="11px" mr="7px" colorScheme={colorScheme}>
|
||||
{badgeText}
|
||||
</Badge>
|
||||
{children}
|
||||
</Link>
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
27
components/md-renderer/mdx-components/blockquote.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const BlockQuote = styled.blockquote`
|
||||
padding: 16px 20px;
|
||||
position: relative;
|
||||
background: #e8e8e8;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 18px;
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
p + h4 {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
|
||||
& + p {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default BlockQuote;
|
||||
10
components/md-renderer/mdx-components/code.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import React from 'react';
|
||||
import { Code as ChakraCode } from '@chakra-ui/react';
|
||||
|
||||
type CodeType = {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export default function Code(props: CodeType) {
|
||||
return <ChakraCode bg='blue.500'>{props.children}</ChakraCode>;
|
||||
}
|
||||
81
components/md-renderer/mdx-components/heading.tsx
Normal file
@@ -0,0 +1,81 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import LinkIcon from 'components/icons/link.svg';
|
||||
|
||||
const linkify = (Component: React.FunctionComponent<any>) => {
|
||||
return function EnrichedHeading(props: { children: string }): React.ReactNode {
|
||||
const text = props.children;
|
||||
const id = text?.toLowerCase && text
|
||||
.toLowerCase()
|
||||
.replace(/[^\x00-\x7F]/g, '')
|
||||
.replace(/\s+/g, '-')
|
||||
.replace(/[?!]/g, '');
|
||||
|
||||
return (
|
||||
<Component id={id}>
|
||||
<HeaderLink href={`#${id}`}>
|
||||
<LinkIcon />
|
||||
</HeaderLink>
|
||||
{props.children}
|
||||
</Component>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
const HeaderLink = styled.a`
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -25px;
|
||||
width: 25px;
|
||||
display: none;
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
`;
|
||||
|
||||
const H1 = styled.h1`
|
||||
position: relative;
|
||||
font-size: 32px;
|
||||
line-height: 40px;
|
||||
font-weight: 700;
|
||||
margin: 20px 0 10px !important;
|
||||
|
||||
&:hover ${HeaderLink} {
|
||||
display: flex;
|
||||
}
|
||||
`;
|
||||
|
||||
const H2 = styled(H1).attrs({ as: 'h2' })`
|
||||
font-size: 30px;
|
||||
`;
|
||||
|
||||
const H3 = styled(H1).attrs({ as: 'h3' })`
|
||||
margin: 22px 0 8px;
|
||||
font-size: 28px;
|
||||
`;
|
||||
|
||||
const H4 = styled(H1).attrs({ as: 'h4' })`
|
||||
margin: 18px 0 8px;
|
||||
font-size: 24px;
|
||||
`;
|
||||
|
||||
const H5 = styled(H1).attrs({ as: 'h5' })`
|
||||
margin: 14px 0 8px;
|
||||
font-size: 18px;
|
||||
`;
|
||||
|
||||
const H6 = styled(H1).attrs({ as: 'h6' })`
|
||||
margin: 12px 0 8px;
|
||||
font-size: 18px;
|
||||
`;
|
||||
|
||||
const Headings = {
|
||||
h1: H1,
|
||||
h2: H2,
|
||||
h3: H3,
|
||||
h4: H4,
|
||||
h5: H5,
|
||||
h6: H6
|
||||
};
|
||||
|
||||
export default Headings;
|
||||
47
components/md-renderer/mdx-components/iframe.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
type IFrameProps = {
|
||||
title: string;
|
||||
src: string;
|
||||
};
|
||||
|
||||
const AspectRatioBox = styled.div`
|
||||
position: relative;
|
||||
max-width: 100%;
|
||||
margin-bottom: 18px;
|
||||
|
||||
&:before {
|
||||
height: 0;
|
||||
content: "";
|
||||
display: block;
|
||||
padding-bottom: 50%;
|
||||
}
|
||||
|
||||
& > iframe {
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
`;
|
||||
|
||||
export default function IFrame(props: IFrameProps) {
|
||||
return (
|
||||
<AspectRatioBox>
|
||||
<iframe
|
||||
frameBorder={0}
|
||||
title={props.title}
|
||||
src={props.src}
|
||||
allow={'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture'}
|
||||
allowFullScreen
|
||||
/>
|
||||
</AspectRatioBox>
|
||||
);
|
||||
}
|
||||
7
components/md-renderer/mdx-components/img.tsx
Normal file
@@ -0,0 +1,7 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const Img = styled.img`
|
||||
max-width: 100%;
|
||||
margin: 25px auto;
|
||||
display: block;
|
||||
`;
|
||||
32
components/md-renderer/mdx-components/index.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import { Code } from '@chakra-ui/react';
|
||||
import { P } from './p';
|
||||
import Headings from './heading';
|
||||
import { Pre } from './pre';
|
||||
import BlockQuote from './blockquote';
|
||||
import { Table } from './table';
|
||||
import IFrame from './iframe';
|
||||
import { Img } from './img';
|
||||
import EnrichedLink from './a';
|
||||
import { BadgeLink } from './badge-link';
|
||||
import { Li, Ul } from './ul';
|
||||
import PremiumBlock from './premium-block';
|
||||
import { ResourceGroupTitle } from './resource-group-title';
|
||||
|
||||
const MdxComponents = {
|
||||
p: P,
|
||||
...Headings,
|
||||
pre: Pre,
|
||||
blockquote: BlockQuote,
|
||||
a: EnrichedLink,
|
||||
table: Table,
|
||||
iframe: IFrame,
|
||||
img: Img,
|
||||
code: Code,
|
||||
BadgeLink: BadgeLink,
|
||||
ResourceGroupTitle: ResourceGroupTitle,
|
||||
PremiumBlock: PremiumBlock,
|
||||
ul: Ul,
|
||||
li: Li
|
||||
};
|
||||
|
||||
export default MdxComponents;
|
||||
14
components/md-renderer/mdx-components/p.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import React from 'react';
|
||||
import { Text } from '@chakra-ui/react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
type EnrichedTextType = {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export const P = styled.p`
|
||||
line-height: 27px;
|
||||
font-size: 16px;
|
||||
color: black;
|
||||
margin-bottom: 18px;
|
||||
`;
|
||||
12
components/md-renderer/mdx-components/pre.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const Pre = styled.pre`
|
||||
margin: 25px -25px 25px -25px !important;
|
||||
padding: 20px 25px !important;
|
||||
border-radius: 10px;
|
||||
line-height: 1.5 !important;
|
||||
|
||||
code {
|
||||
background: transparent;
|
||||
}
|
||||
`;
|
||||
19
components/md-renderer/mdx-components/premium-block.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import React from 'react';
|
||||
import { Box, Button, Heading, Text } from '@chakra-ui/react';
|
||||
import { LockIcon } from '@chakra-ui/icons';
|
||||
|
||||
type PremiumBlockProps = {
|
||||
title: string;
|
||||
description: string;
|
||||
};
|
||||
|
||||
export default function PremiumBlock(props: PremiumBlockProps) {
|
||||
return (
|
||||
<Box p='40px' textAlign='center' rounded='5px' mb='18px' bg='gray.50' borderWidth={1}>
|
||||
<LockIcon color='gray.300' height='45px' w='45px' mb='18px' />
|
||||
<Heading as='h3' fontSize='30px' mb='10px'>{props.title}</Heading>
|
||||
<Text mb='18px'>{props.description}</Text>
|
||||
<Button colorScheme='green'>Become a Member</Button>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import React from 'react';
|
||||
import { Heading } from '@chakra-ui/react';
|
||||
|
||||
type ResourceGroupTitleProps = {
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
export function ResourceGroupTitle(props: ResourceGroupTitleProps) {
|
||||
const { children } = props;
|
||||
|
||||
return <Heading mt='20px' color='gray.800' fontSize='14px' pb='5px' borderBottomWidth={1} textTransform='uppercase' as="h2" mb={'10px'}>{children}</Heading>;
|
||||
}
|
||||
25
components/md-renderer/mdx-components/table.js
Normal file
@@ -0,0 +1,25 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const Table = styled.table`
|
||||
border-collapse: separate;
|
||||
width: 100%;
|
||||
border-spacing: 0;
|
||||
margin: 20px 0;
|
||||
|
||||
th {
|
||||
color: #666;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
background: #FAFAFA;
|
||||
text-transform: uppercase;
|
||||
height: 40px;
|
||||
vertical-align: middle;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
td {
|
||||
font-size: 14px;
|
||||
padding: 10px;
|
||||
border-bottom: 1px solid #EAEAEA;
|
||||
}
|
||||
`;
|
||||
16
components/md-renderer/mdx-components/ul.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import React from 'react';
|
||||
import { UnorderedList } from '@chakra-ui/react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const Ul = styled.ul`
|
||||
margin-left: 40px;
|
||||
margin-bottom: 18px;
|
||||
|
||||
ul {
|
||||
margin-top: 18px;
|
||||
}
|
||||
`;
|
||||
|
||||
export const Li = styled.li`
|
||||
margin-bottom: 7px;
|
||||
`;
|
||||
46
components/opensource-banner.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import { Box, Container, Heading, Link, Text } from '@chakra-ui/react';
|
||||
|
||||
export function OpensourceBanner() {
|
||||
return (
|
||||
<Box bg='white' borderTopWidth={1} pt={['45px', '45px', '70px']} pb={['60px', '60px', '90px']} textAlign='center'>
|
||||
<Container maxW='container.md'>
|
||||
<Heading fontSize={['25px', '25px', '35px']} mb={['10px', '10px', '20px']}>Open Source</Heading>
|
||||
<Text lineHeight='26px' fontSize={['15px', '15px', '16px']} mb='20px'>The project is OpenSource,
|
||||
<Link
|
||||
_hover={{ textDecoration: 'none' }}
|
||||
href='https://github.com/search?o=desc&q=stars%3A%3E100000&s=stars&type=Repositories'
|
||||
target='_blank'
|
||||
borderBottomWidth={1}
|
||||
fontWeight={600}
|
||||
>6th most starred project on GitHub</Link> and is visited by hundreds of thousands of
|
||||
developers every month.</Text>
|
||||
<iframe
|
||||
src='https://ghbtns.com/github-btn.html?user=kamranahmedse&repo=developer-roadmap&type=star&count=true&size=large'
|
||||
frameBorder='0'
|
||||
scrolling='0'
|
||||
width='170'
|
||||
height='30'
|
||||
style={{ margin: 'auto', marginBottom: '30px' }}
|
||||
title='GitHub'
|
||||
/>
|
||||
|
||||
<Text lineHeight={['25px', '25px', '26px']} fontSize={['15px', '15px', '16px']} mb='15px'>A considerable amount of my time is spent doing unpaid
|
||||
community work on things that I hope will help humanity in some way. Your sponsorship helps me continue to
|
||||
produce more open-source and free educational material consumed by hundreds of thousands of developers every
|
||||
month.</Text>
|
||||
|
||||
<Box>
|
||||
<iframe
|
||||
src='https://ghbtns.com/github-btn.html?user=kamranahmedse&type=sponsor&size=large'
|
||||
frameBorder='0'
|
||||
scrolling='0'
|
||||
width='260'
|
||||
height='30'
|
||||
title='GitHub'
|
||||
style={{ margin: 'auto' }}
|
||||
/>
|
||||
</Box>
|
||||
</Container>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
38
components/page-header.tsx
Normal file
@@ -0,0 +1,38 @@
|
||||
import { Badge, Box, Container, Heading, Link, Text } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
import siteConfig from '../content/site.json';
|
||||
|
||||
type PageHeaderProps = {
|
||||
title: string;
|
||||
subtitle: string;
|
||||
children?: React.ReactNode;
|
||||
beforeTitle?: React.ReactNode;
|
||||
};
|
||||
|
||||
export function PageHeader(props: PageHeaderProps) {
|
||||
const { title, subtitle, children, beforeTitle = null } = props;
|
||||
|
||||
return (
|
||||
<Box pt={['25px', '20px', '45px']} pb={['20px', '15px', '30px']} borderBottomWidth={1} mb='30px'>
|
||||
<Container maxW='container.md' position='relative'>
|
||||
{beforeTitle}
|
||||
<Heading
|
||||
as='h1'
|
||||
color='black'
|
||||
fontSize={['28px', '33px', '40px']}
|
||||
fontWeight={700}
|
||||
mb={['2px', '2px', '5px']}
|
||||
>
|
||||
{title}
|
||||
</Heading>
|
||||
<Text fontSize={['13px', '14px', '15px']}>{subtitle}</Text>
|
||||
</Container>
|
||||
|
||||
{children && (
|
||||
<Container maxW='container.md'>
|
||||
{children}
|
||||
</Container>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
16
components/page-wrapper.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import React from 'react';
|
||||
import { Box } from '@chakra-ui/react';
|
||||
|
||||
type PageWrapperProps = {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export function PageWrapper(props: PageWrapperProps) {
|
||||
const { children } = props;
|
||||
|
||||
return (
|
||||
<Box bgColor='brand.bg' bgImage='url(/bg.jpg)' bgRepeat='no-repeat' bgSize='100%' w='100%' minH='100vh'>
|
||||
{ children }
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
116
components/roadmap/content-drawer.tsx
Normal file
@@ -0,0 +1,116 @@
|
||||
import { Box, Button, Flex, Text } from '@chakra-ui/react';
|
||||
import { RemoveScroll } from 'react-remove-scroll';
|
||||
import { RoadmapType } from '../../lib/roadmap';
|
||||
import RoadmapGroup from '../../pages/[roadmap]/[group]';
|
||||
import { CheckIcon, CloseIcon, RepeatIcon } from '@chakra-ui/icons';
|
||||
import { queryGroupElementsById } from '../../lib/renderer/utils';
|
||||
|
||||
type ContentDrawerProps = {
|
||||
roadmap: RoadmapType;
|
||||
groupId: string;
|
||||
onClose?: () => void;
|
||||
};
|
||||
|
||||
export function ContentDrawer(props: ContentDrawerProps) {
|
||||
const { roadmap, groupId, onClose = () => null } = props;
|
||||
if (!groupId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const isDone = localStorage.getItem(groupId) === 'done';
|
||||
|
||||
return (
|
||||
<Box zIndex={99999} pos="relative">
|
||||
<Box
|
||||
onClick={onClose}
|
||||
pos="fixed"
|
||||
top={0}
|
||||
left={0}
|
||||
right={0}
|
||||
bottom={0}
|
||||
bg="black"
|
||||
opacity={0.4}
|
||||
/>
|
||||
<RemoveScroll allowPinchZoom>
|
||||
<Box
|
||||
p="0px 30px 30px"
|
||||
position="fixed"
|
||||
w={['100%', '60%', '40%']}
|
||||
bg="white"
|
||||
top={0}
|
||||
right={0}
|
||||
bottom={0}
|
||||
borderLeftWidth={'1px'}
|
||||
overflowY="scroll"
|
||||
>
|
||||
<Flex
|
||||
mt="20px"
|
||||
justifyContent="space-between"
|
||||
alignItems="center"
|
||||
zIndex={1}
|
||||
>
|
||||
{!isDone && (
|
||||
<Button
|
||||
onClick={() => {
|
||||
localStorage.setItem(groupId, 'done');
|
||||
queryGroupElementsById(groupId).forEach((item) =>
|
||||
item?.classList?.add('done')
|
||||
);
|
||||
onClose();
|
||||
}}
|
||||
colorScheme="green"
|
||||
leftIcon={<CheckIcon />}
|
||||
size="xs"
|
||||
iconSpacing={0}
|
||||
>
|
||||
<Text
|
||||
as="span"
|
||||
d={['block', 'none', 'none', 'block']}
|
||||
ml="10px"
|
||||
>
|
||||
Mark as Done
|
||||
</Text>
|
||||
</Button>
|
||||
)}
|
||||
{isDone && (
|
||||
<Button
|
||||
onClick={() => {
|
||||
localStorage.removeItem(groupId);
|
||||
queryGroupElementsById(groupId).forEach((item) =>
|
||||
item?.classList?.remove('done')
|
||||
);
|
||||
onClose();
|
||||
}}
|
||||
colorScheme="red"
|
||||
leftIcon={<RepeatIcon />}
|
||||
size="xs"
|
||||
iconSpacing={0}
|
||||
>
|
||||
<Text
|
||||
as="span"
|
||||
d={['block', 'none', 'none', 'block']}
|
||||
ml="10px"
|
||||
>
|
||||
Mark as Pending
|
||||
</Text>
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
onClick={onClose}
|
||||
colorScheme="yellow"
|
||||
ml="5px"
|
||||
leftIcon={<CloseIcon width="8px" />}
|
||||
iconSpacing={0}
|
||||
size="xs"
|
||||
>
|
||||
<Text as="span" d={['none', 'none', 'none', 'block']} ml="10px">
|
||||
Close
|
||||
</Text>
|
||||
</Button>
|
||||
</Flex>
|
||||
<RoadmapGroup isOutlet roadmap={roadmap} group={groupId} />
|
||||
</Box>
|
||||
</RemoveScroll>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
39
components/roadmap/edit-content-page-link.tsx
Normal file
@@ -0,0 +1,39 @@
|
||||
import React from 'react';
|
||||
import { Box, Button, Divider, Link, Text } from '@chakra-ui/react';
|
||||
|
||||
type EditContentPageLinkProps = {
|
||||
href: string;
|
||||
};
|
||||
|
||||
export function EditContentPageLink(props: EditContentPageLinkProps) {
|
||||
const { href } = props;
|
||||
|
||||
return (
|
||||
<Box my='30px'>
|
||||
<Divider mb="15px" orientation="horizontal" />
|
||||
<Text
|
||||
lineHeight="23px"
|
||||
fontWeight={500}
|
||||
fontSize="14px"
|
||||
color="gray.500"
|
||||
mb="10px"
|
||||
>
|
||||
This page is a work in progress. Help us by writing a small
|
||||
introduction to the topic and suggesting a few links to read more
|
||||
about this topic.
|
||||
</Text>
|
||||
<Button
|
||||
size="sm"
|
||||
py="20px"
|
||||
as={Link}
|
||||
href={href}
|
||||
target="_blank"
|
||||
isFullWidth
|
||||
colorScheme={'gray'}
|
||||
_hover={{ textDecoration: 'none', bg: 'gray.200' }}
|
||||
>
|
||||
Edit this Page
|
||||
</Button>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
114
components/roadmap/home-roadmap-item.tsx
Normal file
@@ -0,0 +1,114 @@
|
||||
import { Box, Flex, Heading, Link, Text, Tooltip } from '@chakra-ui/react';
|
||||
import { InfoIcon } from '@chakra-ui/icons';
|
||||
|
||||
type RoadmapGridItemProps = {
|
||||
title: string;
|
||||
subtitle: string;
|
||||
isCommunity?: boolean;
|
||||
isUpcoming?: boolean;
|
||||
colorIndex?: number;
|
||||
url: string;
|
||||
};
|
||||
|
||||
const bgColorList = [
|
||||
'red.100',
|
||||
'yellow.100',
|
||||
'green.200',
|
||||
'teal.200',
|
||||
'blue.200',
|
||||
'red.200',
|
||||
'gray.200',
|
||||
'teal.200',
|
||||
'yellow.100',
|
||||
'green.200',
|
||||
'red.200',
|
||||
];
|
||||
|
||||
export function HomeRoadmapItem(props: RoadmapGridItemProps) {
|
||||
const {
|
||||
title,
|
||||
subtitle,
|
||||
isCommunity,
|
||||
colorIndex = 0,
|
||||
url,
|
||||
isUpcoming,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<Box
|
||||
as={Link}
|
||||
href={url}
|
||||
_hover={{
|
||||
textDecoration: 'none',
|
||||
bg: 'rgba(255,255,255,.10)',
|
||||
}}
|
||||
sx={{
|
||||
// On mobile devices, don't change the scale
|
||||
'@media (hover: none)': {
|
||||
'&:hover': {
|
||||
bg: 'rgba(255,255,255,.05)',
|
||||
},
|
||||
},
|
||||
}}
|
||||
flex={1}
|
||||
shadow="2xl"
|
||||
className={'home-roadmap-item'}
|
||||
bg={'rgba(255,255,255,.05)'}
|
||||
color="white"
|
||||
p="15px"
|
||||
rounded="10px"
|
||||
pos="relative"
|
||||
>
|
||||
{isCommunity && (
|
||||
<Tooltip label={'Community contribution'} hasArrow placement="top">
|
||||
<InfoIcon opacity={0.5} position="absolute" top="10px" right="10px" />
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
<Heading
|
||||
fontSize={['17px', '17px', '22px']}
|
||||
color={bgColorList[colorIndex]}
|
||||
mb="5px"
|
||||
>
|
||||
{title}
|
||||
</Heading>
|
||||
<Text color="gray.200" fontSize={['13px']}>
|
||||
{subtitle}
|
||||
</Text>
|
||||
|
||||
{isUpcoming && (
|
||||
<Flex
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
pos="absolute"
|
||||
left={0}
|
||||
right={0}
|
||||
top={0}
|
||||
bottom={0}
|
||||
rounded="10px"
|
||||
>
|
||||
<Text
|
||||
color="white"
|
||||
bg="gray.600"
|
||||
zIndex={1}
|
||||
fontWeight={600}
|
||||
p={'5px 10px'}
|
||||
rounded="10px"
|
||||
>
|
||||
Upcoming
|
||||
</Text>
|
||||
<Box
|
||||
bg={'black'}
|
||||
pos="absolute"
|
||||
top={0}
|
||||
left={0}
|
||||
right={0}
|
||||
bottom={0}
|
||||
rounded={'10px'}
|
||||
opacity={0.5}
|
||||
/>
|
||||
</Flex>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
50
components/roadmap/new-alert-banner.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
import { Badge, Link, Text } from '@chakra-ui/react';
|
||||
import siteConfig from '../../content/site.json';
|
||||
import { event } from '../../lib/gtag';
|
||||
import React from 'react';
|
||||
|
||||
export function NewAlertBanner() {
|
||||
return (
|
||||
<Text
|
||||
_hover={{
|
||||
textDecoration: 'none',
|
||||
color: 'blue.700',
|
||||
'& .new-badge': { bg: 'blue.700' },
|
||||
}}
|
||||
as={Link}
|
||||
href={siteConfig.url.youtube}
|
||||
d="block"
|
||||
target="_blank"
|
||||
color="red.700"
|
||||
fontSize="sm"
|
||||
mb="10px"
|
||||
fontWeight={500}
|
||||
onClick={() =>
|
||||
event({
|
||||
category: 'Subscription',
|
||||
action: 'Clicked the YouTube banner',
|
||||
label: 'YouTube Alert on Roadmap',
|
||||
})
|
||||
}
|
||||
>
|
||||
<Badge
|
||||
transition={'all 300ms'}
|
||||
className="new-badge"
|
||||
mr="7px"
|
||||
colorScheme="red"
|
||||
variant="solid"
|
||||
>
|
||||
New
|
||||
</Badge>
|
||||
<Text textDecoration="underline" as="span" d={['none', 'inline']}>
|
||||
Roadmap topics to be covered on our YouTube Channel
|
||||
</Text>
|
||||
<Text textDecoration="underline" as="span" d={['inline', 'none']}>
|
||||
Topic videos being made on YouTube
|
||||
</Text>
|
||||
<Text as="span" ml="5px">
|
||||
»
|
||||
</Text>
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
26
components/roadmap/roadmap-error.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import { RoadmapType } from '../../lib/roadmap';
|
||||
import { Container, Heading, Link, Text } from '@chakra-ui/react';
|
||||
import siteConfig from '../../content/site.json';
|
||||
|
||||
type RoadmapProps = {
|
||||
roadmap: RoadmapType;
|
||||
};
|
||||
|
||||
export function RoadmapError(props: RoadmapProps) {
|
||||
const { roadmap } = props;
|
||||
|
||||
return (
|
||||
<Container
|
||||
bg={'red.600'}
|
||||
maxW={'container.md'}
|
||||
position="relative"
|
||||
mt="50px"
|
||||
p='20px'
|
||||
rounded='5px'
|
||||
color='white'
|
||||
>
|
||||
<Heading mb='4px' size='md'>Oops! There's an error</Heading>
|
||||
<Text>Try refreshing or <Link target='_blank' fontWeight={700} textDecoration={'underline'} fontSize='14px' href={siteConfig.url.issue}>report a bug</Link> and use the <Link fontWeight={700} textDecoration={'underline'} href={`/roadmaps/${roadmap.id}.png`}>non-interactive version</Link></Text>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
89
components/roadmap/roadmap-grid-item.tsx
Normal file
@@ -0,0 +1,89 @@
|
||||
import { Badge, Box, Flex, Heading, Link, Text, Tooltip } from '@chakra-ui/react';
|
||||
import { InfoIcon } from '@chakra-ui/icons';
|
||||
|
||||
type RoadmapGridItemProps = {
|
||||
title: string;
|
||||
subtitle: string;
|
||||
href: string;
|
||||
isCommunity?: boolean;
|
||||
isUpcoming?: boolean;
|
||||
colorIndex?: number;
|
||||
};
|
||||
|
||||
const bgColorList = [
|
||||
'gray.900',
|
||||
'purple.900',
|
||||
'blue.900',
|
||||
'red.900',
|
||||
'green.900',
|
||||
'teal.900',
|
||||
'yellow.900',
|
||||
'cyan.900',
|
||||
'pink.900',
|
||||
|
||||
'gray.800',
|
||||
'purple.800',
|
||||
'blue.800',
|
||||
'red.800',
|
||||
'green.800',
|
||||
'teal.800',
|
||||
'yellow.800',
|
||||
'cyan.800',
|
||||
'pink.800',
|
||||
|
||||
'gray.700',
|
||||
'purple.700',
|
||||
'blue.700',
|
||||
'red.700',
|
||||
'green.700',
|
||||
'teal.700',
|
||||
'yellow.700',
|
||||
'cyan.700',
|
||||
'pink.700',
|
||||
|
||||
'gray.600',
|
||||
'purple.600',
|
||||
'blue.600',
|
||||
'red.600',
|
||||
'green.600',
|
||||
'teal.600',
|
||||
'yellow.600',
|
||||
'cyan.600',
|
||||
'pink.600'
|
||||
];
|
||||
|
||||
export function RoadmapGridItem(props: RoadmapGridItemProps) {
|
||||
const { title, subtitle, isCommunity = false, isUpcoming = false, colorIndex = 0, href = '/' } = props;
|
||||
|
||||
return (
|
||||
<Box _hover={{ textDecoration: 'none', transform: 'scale(1.02)' }} as={Link} href={href} shadow='xl' p='20px'
|
||||
rounded='10px' bg={bgColorList[colorIndex] ?? bgColorList[0]} flex={1} pos='relative'>
|
||||
|
||||
{isCommunity && (
|
||||
<Tooltip label={'Community contribution'} hasArrow placement='top'>
|
||||
<InfoIcon opacity={0.5} color='gray.100' position='absolute' top='10px' right='10px' />
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
<Heading color='white' mb={'6px'} fontSize='20px'>{title}</Heading>
|
||||
<Text color='gray.300' fontSize='14px'>{subtitle}</Text>
|
||||
|
||||
{isUpcoming && (
|
||||
<Flex
|
||||
alignItems='center'
|
||||
justifyContent='center'
|
||||
pos='absolute'
|
||||
left={0}
|
||||
right={0}
|
||||
top={0}
|
||||
bottom={0}
|
||||
rounded='10px'
|
||||
>
|
||||
<Text color='white' bg='yellow.900' zIndex={1} fontWeight={600} p={'5px 10px'}
|
||||
rounded='10px'>Upcoming</Text>
|
||||
<Box bg={'black'} pos='absolute' top={0} left={0} right={0} bottom={0} rounded={'10px'} opacity={0.5} />
|
||||
</Flex>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
20
components/roadmap/roadmap-loader.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import { Container, Spinner } from '@chakra-ui/react';
|
||||
|
||||
export function RoadmapLoader() {
|
||||
return (
|
||||
<Container
|
||||
maxW={'container.md'}
|
||||
position="relative"
|
||||
mt="60px"
|
||||
textAlign="center"
|
||||
>
|
||||
<Spinner
|
||||
thickness="7px"
|
||||
speed="0.65s"
|
||||
emptyColor="gray.200"
|
||||
color="gray.500"
|
||||
size="xl"
|
||||
/>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
113
components/roadmap/roadmap-page-header.tsx
Normal file
@@ -0,0 +1,113 @@
|
||||
import { isInteractiveRoadmap, RoadmapType } from '../../lib/roadmap';
|
||||
import { NewAlertBanner } from './new-alert-banner';
|
||||
import {
|
||||
Badge,
|
||||
Box,
|
||||
Button,
|
||||
Container,
|
||||
Flex,
|
||||
Heading,
|
||||
Link,
|
||||
Stack,
|
||||
Text,
|
||||
} from '@chakra-ui/react';
|
||||
import { AtSignIcon, DownloadIcon } from '@chakra-ui/icons';
|
||||
import React from 'react';
|
||||
|
||||
type RoadmapPageHeaderType = {
|
||||
roadmap: RoadmapType;
|
||||
};
|
||||
|
||||
export function RoadmapPageHeader(props: RoadmapPageHeaderType) {
|
||||
const { roadmap } = props;
|
||||
|
||||
return (
|
||||
<Box
|
||||
pt={['25px', '20px', '45px']}
|
||||
pb={['20px', '15px', '30px']}
|
||||
borderBottomWidth={1}
|
||||
mb="30px"
|
||||
>
|
||||
<Container maxW="container.md" position="relative">
|
||||
<NewAlertBanner />
|
||||
<Heading
|
||||
as="h1"
|
||||
color="black"
|
||||
fontSize={['28px', '33px', '40px']}
|
||||
fontWeight={700}
|
||||
mb={['2px', '2px', '5px']}
|
||||
>
|
||||
{roadmap.title}
|
||||
</Heading>
|
||||
<Text fontSize={['13px', '14px', '15px']}>{roadmap.description}</Text>
|
||||
<Flex justifyContent="space-between" alignItems={'center'} mt="20px">
|
||||
<Stack isInline>
|
||||
<Button
|
||||
d={['flex', 'flex']}
|
||||
as={Link}
|
||||
href={'/roadmaps'}
|
||||
size="xs"
|
||||
py="14px"
|
||||
px="10px"
|
||||
colorScheme="teal"
|
||||
variant="solid"
|
||||
_hover={{ textDecoration: 'none' }}
|
||||
>
|
||||
←
|
||||
<Text as="span" d={['none', 'inline']} ml="5px">
|
||||
All Roadmaps
|
||||
</Text>
|
||||
</Button>
|
||||
|
||||
{roadmap.pdfUrl && (
|
||||
<Button
|
||||
as={Link}
|
||||
href={roadmap.pdfUrl}
|
||||
target="_blank"
|
||||
size="xs"
|
||||
py="14px"
|
||||
px="10px"
|
||||
leftIcon={<DownloadIcon />}
|
||||
colorScheme="yellow"
|
||||
variant="solid"
|
||||
_hover={{ textDecoration: 'none' }}
|
||||
>
|
||||
Download
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
as={Link}
|
||||
href={'/signup'}
|
||||
size="xs"
|
||||
py="14px"
|
||||
px="10px"
|
||||
variant="solid"
|
||||
colorScheme="yellow"
|
||||
leftIcon={<AtSignIcon />}
|
||||
_hover={{ textDecoration: 'none' }}
|
||||
>
|
||||
Subscribe
|
||||
</Button>
|
||||
</Stack>
|
||||
</Flex>
|
||||
{isInteractiveRoadmap(roadmap.id) && (
|
||||
<Text
|
||||
mt="30px"
|
||||
mb={['-37px', '-32px', '-47px']}
|
||||
fontWeight={500}
|
||||
fontSize="14px"
|
||||
bg="white"
|
||||
borderWidth={1}
|
||||
p="5px 7px"
|
||||
rounded="3px"
|
||||
>
|
||||
<Badge pos="relative" top={'-1px'} mr="6px" colorScheme="yellow">
|
||||
New
|
||||
</Badge>
|
||||
Resources are here, try clicking any nodes.
|
||||
</Text>
|
||||
)}
|
||||
</Container>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
33
components/sticky-banner.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import { Flex, Link, Text } from '@chakra-ui/react';
|
||||
import YouTubeLogo from '../components/icons/youtube.svg';
|
||||
import siteConfig from '../content/site.json';
|
||||
import { event } from '../lib/gtag';
|
||||
|
||||
export function StickyBanner() {
|
||||
return (
|
||||
<Flex as={Link}
|
||||
href={siteConfig.url.youtube}
|
||||
bg={'yellow.200'}
|
||||
color='gray.900'
|
||||
alignItems='center'
|
||||
position='sticky'
|
||||
top={0}
|
||||
zIndex={999}
|
||||
justifyContent='center'
|
||||
py='8px'
|
||||
_hover={{ textDecoration: 'none', bg: 'yellow.400' }}
|
||||
target='_blank'
|
||||
onClick={() => event({
|
||||
category: 'Subscription',
|
||||
action: 'Clicked the YouTube banner',
|
||||
label: 'Sticky YouTube banner on Top'
|
||||
})}
|
||||
>
|
||||
<YouTubeLogo style={{ height: '20px', display: 'inline-block', marginRight: '7px' }} />
|
||||
<Text as='span' fontWeight={500} fontSize='14px'>
|
||||
<Text as='span'>We now have a YouTube Channel. <Text as='span' d={['none', 'inline']}>Subscribe for the video
|
||||
content.</Text></Text>
|
||||
</Text>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
71
components/watch/video-grid-item.tsx
Normal file
@@ -0,0 +1,71 @@
|
||||
import { Badge, Box, Heading, Link, Text } from '@chakra-ui/react';
|
||||
|
||||
type VideoGridItemProps = {
|
||||
href: string;
|
||||
title: string;
|
||||
subtitle: string;
|
||||
date: string;
|
||||
target?: string;
|
||||
isNew?: boolean;
|
||||
isPro?: boolean;
|
||||
colorIndex?: number;
|
||||
};
|
||||
|
||||
const bgColorList = [
|
||||
'gray.900',
|
||||
'purple.900',
|
||||
'blue.900',
|
||||
'red.900',
|
||||
'green.900',
|
||||
'teal.900',
|
||||
'yellow.900',
|
||||
'cyan.900',
|
||||
'pink.900',
|
||||
|
||||
'gray.800',
|
||||
'purple.800',
|
||||
'blue.800',
|
||||
'red.800',
|
||||
'green.800',
|
||||
'teal.800',
|
||||
'yellow.800',
|
||||
'cyan.800',
|
||||
'pink.800',
|
||||
|
||||
'gray.700',
|
||||
'purple.700',
|
||||
'blue.700',
|
||||
'red.700',
|
||||
'green.700',
|
||||
'teal.700',
|
||||
'yellow.700',
|
||||
'cyan.700',
|
||||
'pink.700',
|
||||
|
||||
'gray.600',
|
||||
'purple.600',
|
||||
'blue.600',
|
||||
'red.600',
|
||||
'green.600',
|
||||
'teal.600',
|
||||
'yellow.600',
|
||||
'cyan.600',
|
||||
'pink.600'
|
||||
];
|
||||
|
||||
export function VideoGridItem(props: VideoGridItemProps) {
|
||||
const { title, subtitle, date, isNew = false, isPro = false, colorIndex = 0, href, target } = props;
|
||||
|
||||
return (
|
||||
<Box _hover={{ textDecoration: 'none', transform: 'scale(1.02)' }} as={Link} href={ href } target={target || '_self'} shadow='xl' p='20px'
|
||||
rounded='10px' bg={bgColorList[colorIndex] ?? bgColorList[0]} flex={1}>
|
||||
<Text mb='7px' fontSize='12px' color='gray.400'>
|
||||
{isNew && <Badge colorScheme={'yellow'} mr='10px'>New</Badge>}
|
||||
{isPro && <Badge colorScheme={'blue'} mr='10px'>PRO</Badge>}
|
||||
{date}
|
||||
</Text>
|
||||
<Heading color='white' mb={'6px'} fontSize='20px' lineHeight={'28px'}>{title}</Heading>
|
||||
<Text color='gray.300' fontSize='14px'>{subtitle}</Text>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
43
content/authors.json
Normal file
@@ -0,0 +1,43 @@
|
||||
[
|
||||
{
|
||||
"username": "kamranahmedse",
|
||||
"name": "Kamran Ahmed",
|
||||
"twitter": "kamranahmedse",
|
||||
"picture": "/authors/kamranahmedse.jpeg",
|
||||
"bio": "Lead engineer at Tajawal. Lover of all things web and opensource. Created roadmap.sh to help the confused ones."
|
||||
},
|
||||
{
|
||||
"username": "jesse",
|
||||
"name": "Jesse Li",
|
||||
"twitter": "__jesse_li",
|
||||
"picture": "/authors/jesse.png",
|
||||
"bio": "Software engineer."
|
||||
},
|
||||
{
|
||||
"username": "dmytrobol",
|
||||
"name": "Dmytro Bolkachov",
|
||||
"twitter": "dmytrobol",
|
||||
"picture": "/authors/dmytrobol.png",
|
||||
"bio": "JavaScript Lad, Movie buff and coder interested in everything web related"
|
||||
},
|
||||
{
|
||||
"username": "spekulatius",
|
||||
"name": "Peter Thaleikis",
|
||||
"twitter": "spekulatius1984",
|
||||
"picture": "/authors/spekulatius.jpg",
|
||||
"bio": "Developer building side-projects for fun, lover of the web and open source"
|
||||
},
|
||||
{
|
||||
"username": "ebrahimbharmal007",
|
||||
"name": "Ebrahim Bharmal",
|
||||
"twitter": "BharmalEbrahim",
|
||||
"picture": "/authors/ebrahimbharmal007.png",
|
||||
"bio": "Love building projects using tools completely new to me. Python forever. Senior at University of Texas at Arlington (2021)"
|
||||
},
|
||||
{
|
||||
"username": "lesovsky",
|
||||
"name": "Alexey Lesovsky",
|
||||
"bio": "Linux system administrator and PostgreSQL DBA at DataEgret.",
|
||||
"picture": "/authors/lesovsky.jpeg"
|
||||
}
|
||||
]
|
||||
256
content/guides.json
Normal file
@@ -0,0 +1,256 @@
|
||||
[
|
||||
{
|
||||
"id": "avoid-render-blocking-javascript-with-async-defer",
|
||||
"title": "Async and Defer Script Loading",
|
||||
"description": "Learn how to avoid render blocking JavaScript using async and defer scripts.",
|
||||
"isPro": false,
|
||||
"authorUsername": "kamranahmedse",
|
||||
"updatedAt": "2021-09-10T19:59:14.191Z",
|
||||
"createdAt": "2021-09-10T19:59:14.191Z"
|
||||
},
|
||||
{
|
||||
"id": "what-are-web-vitals",
|
||||
"title": "What are Web Vitals?",
|
||||
"description": "Learn what are the core web vitals and how to measure them.",
|
||||
"isPro": false,
|
||||
"authorUsername": "kamranahmedse",
|
||||
"updatedAt": "2021-09-05T19:59:14.191Z",
|
||||
"createdAt": "2021-09-05T19:59:14.191Z"
|
||||
},
|
||||
{
|
||||
"id": "what-is-sli-slo-sla",
|
||||
"title": "SLIs, SLOs and SLAs",
|
||||
"description": "Learn what are different indicators for performance identification of any service.",
|
||||
"isPro": false,
|
||||
"authorUsername": "kamranahmedse",
|
||||
"updatedAt": "2021-08-31T19:59:14.191Z",
|
||||
"createdAt": "2021-08-31T19:59:14.191Z"
|
||||
},
|
||||
{
|
||||
"id": "ci-cd",
|
||||
"title": "What is CI and CD?",
|
||||
"description": "Learn the basics of CI/CD and how to implement that with GitHub Actions.",
|
||||
"isPro": false,
|
||||
"authorUsername": "kamranahmedse",
|
||||
"updatedAt": "2021-07-09T19:59:14.191Z",
|
||||
"createdAt": "2021-07-09T19:59:14.191Z"
|
||||
},
|
||||
{
|
||||
"id": "sso",
|
||||
"title": "SSO — Single Sign On",
|
||||
"description": "Learn the basics of SAML and understand how does Single Sign On work.",
|
||||
"isPro": false,
|
||||
"authorUsername": "kamranahmedse",
|
||||
"updatedAt": "2021-07-01T19:59:14.191Z",
|
||||
"createdAt": "2021-07-01T19:59:14.191Z"
|
||||
},
|
||||
{
|
||||
"id": "oauth",
|
||||
"title": "OAuth — Open Authorization",
|
||||
"description": "Learn and understand what is OAuth and how it works",
|
||||
"isPro": false,
|
||||
"authorUsername": "kamranahmedse",
|
||||
"updatedAt": "2021-06-28T19:59:14.191Z",
|
||||
"createdAt": "2021-06-28T19:59:14.191Z"
|
||||
},
|
||||
{
|
||||
"id": "jwt-authentication",
|
||||
"title": "JWT Authentication",
|
||||
"description": "Understand what is JWT authentication and how is it implemented",
|
||||
"isPro": false,
|
||||
"authorUsername": "kamranahmedse",
|
||||
"updatedAt": "2021-06-20T19:59:14.191Z",
|
||||
"createdAt": "2021-06-20T19:59:14.191Z"
|
||||
},
|
||||
{
|
||||
"id": "token-authentication",
|
||||
"title": "Token Based Authentication",
|
||||
"description": "Understand what is token based authentication and how it is implemented",
|
||||
"isPro": false,
|
||||
"authorUsername": "kamranahmedse",
|
||||
"updatedAt": "2021-06-02T20:59:14.191Z",
|
||||
"createdAt": "2021-06-02T20:59:14.191Z"
|
||||
},
|
||||
{
|
||||
"id": "session-authentication",
|
||||
"title": "Session Based Authentication",
|
||||
"description": "Understand what is session based authentication and how it is implemented",
|
||||
"isPro": false,
|
||||
"authorUsername": "kamranahmedse",
|
||||
"updatedAt": "2021-05-26T20:59:14.191Z",
|
||||
"createdAt": "2021-05-26T20:59:14.191Z"
|
||||
},
|
||||
{
|
||||
"id": "basic-authentication",
|
||||
"title": "Basic Authentication",
|
||||
"description": "Understand what is basic authentication and how it is implemented",
|
||||
"isPro": false,
|
||||
"authorUsername": "kamranahmedse",
|
||||
"updatedAt": "2021-05-19T20:59:14.191Z",
|
||||
"createdAt": "2021-05-19T20:59:14.191Z"
|
||||
},
|
||||
{
|
||||
"id": "character-encodings",
|
||||
"title": "Character Encodings",
|
||||
"description": "Covers the basics of character encodings and explains ASCII vs Unicode",
|
||||
"isPro": false,
|
||||
"authorUsername": "kamranahmedse",
|
||||
"updatedAt": "2021-05-14T20:59:14.191Z",
|
||||
"createdAt": "2021-05-14T20:59:14.191Z"
|
||||
},
|
||||
{
|
||||
"id": "unfamiliar-codebase",
|
||||
"title": "Unfamiliar Codebase",
|
||||
"description": "Tips on getting getting familiar with an unfamiliar codebase",
|
||||
"isPro": false,
|
||||
"authorUsername": "kamranahmedse",
|
||||
"updatedAt": "2021-05-04T20:59:14.191Z",
|
||||
"createdAt": "2021-05-04T20:59:14.191Z"
|
||||
},
|
||||
{
|
||||
"id": "why-build-it-and-they-will-come-wont-work-anymore",
|
||||
"title": "Build it and they will come?",
|
||||
"description": "Why “build it and they will come” alone won’t work anymore",
|
||||
"isPro": false,
|
||||
"authorUsername": "spekulatius",
|
||||
"updatedAt": "2021-05-04T12:59:14.191Z",
|
||||
"createdAt": "2021-05-04T12:59:14.191Z"
|
||||
},
|
||||
{
|
||||
"id": "dhcp-in-one-picture",
|
||||
"title": "DHCP in One Picture",
|
||||
"description": "Here is what happens when a new device joins the network.",
|
||||
"isPro": false,
|
||||
"authorUsername": "kamranahmedse",
|
||||
"updatedAt": "2021-04-28T15:48:21.191Z",
|
||||
"createdAt": "2021-04-28T15:48:21.191Z"
|
||||
},
|
||||
{
|
||||
"id": "ssl-tls-https-ssh",
|
||||
"title": "SSL vs TLS vs SSH",
|
||||
"description": "Quick tidbit on the differences between SSL, TLS, HTTPS and SSH",
|
||||
"isPro": false,
|
||||
"authorUsername": "kamranahmedse",
|
||||
"updatedAt": "2021-04-22T15:48:21.191Z",
|
||||
"createdAt": "2021-04-22T15:48:21.191Z"
|
||||
},
|
||||
{
|
||||
"id": "asymptotic-notation",
|
||||
"title": "Asymptotic Notation",
|
||||
"description": "Learn the basics of measuring the time and space complexity of algorithms",
|
||||
"isPro": false,
|
||||
"authorUsername": "kamranahmedse",
|
||||
"updatedAt": "2021-04-03T15:48:21.191Z",
|
||||
"createdAt": "2021-04-03T15:48:21.191Z"
|
||||
},
|
||||
{
|
||||
"id": "big-o-notation",
|
||||
"title": "Big-O Notation",
|
||||
"description": "Easy to understand explanation of Big-O notation without any fancy terms",
|
||||
"isPro": false,
|
||||
"authorUsername": "kamranahmedse",
|
||||
"updatedAt": "2021-03-15T15:48:21.191Z",
|
||||
"createdAt": "2021-03-15T15:48:21.191Z"
|
||||
},
|
||||
{
|
||||
"id": "random-numbers",
|
||||
"title": "Random Numbers: Are they?",
|
||||
"description": "Learn how they are generated and why they may not be truly random.",
|
||||
"isPro": false,
|
||||
"authorUsername": "kamranahmedse",
|
||||
"updatedAt": "2021-03-14T15:48:21.191Z",
|
||||
"createdAt": "2021-03-14T15:48:21.191Z"
|
||||
},
|
||||
{
|
||||
"id": "scaling-databases",
|
||||
"title": "Scaling Databases",
|
||||
"description": "Learn the ups and downs of different database scaling strategies",
|
||||
"isPro": false,
|
||||
"authorUsername": "kamranahmedse",
|
||||
"updatedAt": "2021-02-18T15:48:21.191Z",
|
||||
"createdAt": "2021-02-18T15:48:21.191Z"
|
||||
},
|
||||
{
|
||||
"id": "what-is-internet",
|
||||
"title": "How does the internet work?",
|
||||
"description": "Learn the basics of internet and everything involved with this short video series",
|
||||
"isPro": false,
|
||||
"authorUsername": "dmytrobol",
|
||||
"updatedAt": "2021-02-29T15:48:21.191Z",
|
||||
"createdAt": "2021-02-29T15:48:21.191Z"
|
||||
},
|
||||
{
|
||||
"id": "torrent-client",
|
||||
"title": "Building a BitTorrent Client",
|
||||
"description": "Learn everything you need to know about BitTorrent by writing a client in Go",
|
||||
"isPro": false,
|
||||
"authorUsername": "jesse",
|
||||
"updatedAt": "2021-01-17T15:48:21.191Z",
|
||||
"createdAt": "2021-01-17T15:48:21.191Z",
|
||||
"canonical": "https://blog.jse.li/posts/torrent/"
|
||||
},
|
||||
{
|
||||
"id": "levels-of-seniority",
|
||||
"title": "Levels of Seniority",
|
||||
"description": "How to Step Up as a Junior, Mid Level or a Senior Developer?",
|
||||
"isPro": false,
|
||||
"authorUsername": "kamranahmedse",
|
||||
"updatedAt": "2020-12-03T12:13:00.860Z",
|
||||
"createdAt": "2020-12-03T12:13:00.860Z"
|
||||
},
|
||||
{
|
||||
"id": "design-patterns-for-humans",
|
||||
"title": "Design Patterns for Humans",
|
||||
"description": "A language agnostic, ultra-simplified explanation to design patterns",
|
||||
"isPro": false,
|
||||
"authorUsername": "kamranahmedse",
|
||||
"updatedAt": "2019-10-09T12:00:00.860Z",
|
||||
"createdAt": "2019-01-23T17:00:00.860Z"
|
||||
},
|
||||
{
|
||||
"id": "journey-to-http2",
|
||||
"title": "Journey to HTTP/2",
|
||||
"description": "The evolution of HTTP. How it all started and where we stand today",
|
||||
"isPro": false,
|
||||
"authorUsername": "kamranahmedse",
|
||||
"createdAt": "2018-12-04T12:00:00.860Z",
|
||||
"updatedAt": "2018-12-04T12:00:00.860Z",
|
||||
"isDraft": true
|
||||
},
|
||||
{
|
||||
"id": "dns-in-one-picture",
|
||||
"title": "DNS in One Picture",
|
||||
"description": "Quick illustrative guide on how a website is found on the internet.",
|
||||
"isPro": false,
|
||||
"authorUsername": "kamranahmedse",
|
||||
"updatedAt": "2018-12-04T12:00:00.860Z",
|
||||
"createdAt": "2018-12-04T17:00:00.860Z"
|
||||
},
|
||||
{
|
||||
"id": "http-caching",
|
||||
"title": "HTTP Caching",
|
||||
"description": "Everything you need to know about web caching",
|
||||
"isPro": false,
|
||||
"authorUsername": "kamranahmedse",
|
||||
"createdAt": "2018-11-29T17:00:00.860Z",
|
||||
"updatedAt": "2018-11-29T17:00:00.860Z"
|
||||
},
|
||||
{
|
||||
"id": "history-of-javascript",
|
||||
"title": "Brief History of JavaScript",
|
||||
"description": "How JavaScript was introduced and evolved over the years",
|
||||
"isPro": false,
|
||||
"authorUsername": "kamranahmedse",
|
||||
"createdAt": "2017-10-28T17:00:00.860Z",
|
||||
"updatedAt": "2017-10-28T17:00:00.860Z"
|
||||
},
|
||||
{
|
||||
"id": "proxy-servers",
|
||||
"title": "Proxy Servers",
|
||||
"description": "How do proxy servers work and what are forward and reverse proxies?",
|
||||
"isPro": false,
|
||||
"authorUsername": "ebrahimbharmal007",
|
||||
"createdAt": "2017-10-24T17:00:00.860Z",
|
||||
"updatedAt": "2017-10-24T17:00:00.860Z"
|
||||
}
|
||||
]
|
||||
15
content/guides/asymptotic-notation.md
Normal file
@@ -0,0 +1,15 @@
|
||||
export const guideMeta = {
|
||||
"title": "WebStorm — Project History",
|
||||
"description": "Learn how to peek through the history of any git repository to learn how it grew.",
|
||||
"url": "/guides/project-history",
|
||||
"fileName": "project-history",
|
||||
"featured": true,
|
||||
"author": "kamranahmedse",
|
||||
"updatedAt": "2020-07-16T19:59:14.191Z",
|
||||
"createdAt": "2020-07-16T19:59:14.191Z"
|
||||
};
|
||||
|
||||
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)
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
[](/guides/avoid-render-blocking-javascript-with-async-defer.png)
|
||||
|
||||
2
content/guides/basic-authentication.md
Normal file
@@ -0,0 +1,2 @@
|
||||
[](/guides/basic-authentication.png)
|
||||
|
||||
4
content/guides/big-o-notation.md
Normal file
@@ -0,0 +1,4 @@
|
||||
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)
|
||||
|
||||
2
content/guides/character-encodings.md
Normal file
@@ -0,0 +1,2 @@
|
||||
[](/guides/character-encodings.png)
|
||||
|
||||
4
content/guides/ci-cd.md
Normal file
@@ -0,0 +1,4 @@
|
||||
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)
|
||||
|
||||
@@ -1,25 +1,3 @@
|
||||
---
|
||||
title: "Design Patterns for Humans"
|
||||
description: "A language agnostic, ultra-simplified explanation to design patterns"
|
||||
author:
|
||||
name: "Kamran Ahmed"
|
||||
url: "https://twitter.com/kamranahmedse"
|
||||
imageUrl: "/authors/kamranahmedse.jpeg"
|
||||
seo:
|
||||
title: "Design Patterns for Humans - roadmap.sh"
|
||||
description: "A language agnostic, ultra-simplified explanation to design patterns"
|
||||
isNew: false
|
||||
type: "textual"
|
||||
date: 2019-01-23
|
||||
sitemap:
|
||||
priority: 0.7
|
||||
changefreq: "weekly"
|
||||
tags:
|
||||
- "guide"
|
||||
- "textual-guide"
|
||||
- "guide-sitemap"
|
||||
---
|
||||
|
||||
Design patterns are solutions to recurring problems; **guidelines on how to tackle certain problems**. They are not classes, packages or libraries that you can plug into your application and wait for the magic to happen. These are, rather, guidelines on how to tackle certain problems in certain situations.
|
||||
|
||||
> Design patterns are solutions to recurring problems; guidelines on how to tackle certain problems
|
||||
2
content/guides/dhcp-in-one-picture.md
Normal file
@@ -0,0 +1,2 @@
|
||||
[](/guides/dhcp.png)
|
||||
|
||||
5
content/guides/dns-in-one-picture.md
Normal file
@@ -0,0 +1,5 @@
|
||||
DNS or Domain Name System is one of the fundamental blocks of the internet. As a developer, you should have at-least the basic understanding of how it works. This article is a brief introduction to what is DNS and how it works.
|
||||
|
||||
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)
|
||||
@@ -1,29 +1,7 @@
|
||||
---
|
||||
title: "Brief History of JavaScript"
|
||||
description: "How JavaScript was introduced and evolved over the years"
|
||||
author:
|
||||
name: "Kamran Ahmed"
|
||||
url: "https://twitter.com/kamranahmedse"
|
||||
imageUrl: "/authors/kamranahmedse.jpeg"
|
||||
seo:
|
||||
title: "Brief History of JavaScript - roadmap.sh"
|
||||
description: "How JavaScript was introduced and evolved over the years"
|
||||
isNew: false
|
||||
type: "textual"
|
||||
date: 2017-10-28
|
||||
sitemap:
|
||||
priority: 0.7
|
||||
changefreq: "weekly"
|
||||
tags:
|
||||
- "guide"
|
||||
- "textual-guide"
|
||||
- "guide-sitemap"
|
||||
---
|
||||
|
||||
Around 10 years ago, Jeff Atwood (the founder of stackoverflow) made a case that JavaScript is going to be the future and he coined the “Atwood Law” which states that *Any application that can be written in JavaScript will eventually be written in JavaScript*. Fast-forward to today, 10 years later, if you look at it it rings truer than ever. JavaScript is continuing to gain more and more adoption.
|
||||
|
||||
### JavaScript is announced
|
||||
JavaScript was initially created by [Brendan Eich](https://twitter.com/BrendanEich) of NetScape and was first announced in a press release by Netscape in 1995. It has a bizarre history of naming; initially it was named `Mocha` by the creator, which was later renamed to `LiveScript`. In 1996, about a year later after the release, NetScape decided to rename it to be `JavaScript` with hopes of capitalizing on the Java community (although JavaScript did not have any relationship with Java) and released Netscape 2.0 with the official support of JavaScript.
|
||||
JavaScript was initially created by [Brendan Eich](https://twitter.com/BrendanEich) of NetScape and was first announced in a press release by Netscape in 1995. It has a bizarre history of naming; initally it was named `Mocha` by the creator, which was later renamed to `LiveScript`. In 1996, about a year later after the release, NetScape decided to rename it to be `JavaScript` with hopes of capitalizing on the Java community (although JavaScript did not have any relationship with Java) and released Netscape 2.0 with the official support of JavaScript.
|
||||
|
||||
### ES1, ES2 and ES3
|
||||
In 1996, Netscape decided to submit it to [ECMA International](https://en.wikipedia.org/wiki/Ecma_International) with the hopes of getting it standardized. First edition of the standard specification was released in 1997 and the language was standardized. After the initial release, `ECMAScript` was continued to be worked upon and in no-time two more versions were released ECMAScript 2 in 1998 and ECMAScript 3 in 1999.
|
||||
251
content/guides/http-caching.md
Normal file
@@ -0,0 +1,251 @@
|
||||
As users, we easily get frustrated by the buffering videos, the images that take seconds to load, pages that got stuck because the content is being loaded. Loading the resources from some cache is much faster than fetching the same from the originating server. It reduces latency, speeds up the loading of resources, decreases the load on server, cuts down the bandwidth costs etc.
|
||||
|
||||
### Introduction
|
||||
|
||||
What is web cache? It is something that sits somewhere between the client and the server, continuously looking at the requests and their responses, looking for any responses that can be cached. So that there is less time consumed when the same request is made again.
|
||||
|
||||

|
||||
|
||||
> Note that this image is just to give you an idea. Depending upon the type of cache, the place where it is implemented could vary. More on this later.
|
||||
|
||||
Before we get into further details, let me give you an overview of the terms that will be used, further in the article
|
||||
|
||||
- **Client** could be your browser or any application requesting the server for some resource
|
||||
- **Origin Server**, the source of truth, houses all the content required by the client and is responsible for fulfilling the client requests.
|
||||
- **Stale Content** is the cached but expired content
|
||||
- **Fresh Content** is the content available in cache that hasn't expired yet
|
||||
- **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
|
||||
|
||||

|
||||
|
||||
### Caching Locations
|
||||
|
||||
Web cache can be shared or private depending upon the location where it exists. Here is the list of different caching locations
|
||||
|
||||
- [Browser Cache](#browser-cache)
|
||||
- [Proxy Cache](#proxy-cache)
|
||||
- [Reverse Proxy Cache](#reverse-proxy-cache)
|
||||
|
||||
#### Browser Cache
|
||||
|
||||
You might have noticed that when you click the back button in your browser it takes less time to load the page than the time that it took during the first load; this is the browser cache in play. Browser cache is the most common location for caching and browsers usually reserve some space for it.
|
||||
|
||||

|
||||
|
||||
A browser cache is limited to just one user and unlike other caches, it can store the "private" responses. More on it later.
|
||||
|
||||
#### Proxy Cache
|
||||
|
||||
Unlike browser cache which serves a single user, proxy caches may serve hundreds of different users accessing the same content. They are usually implemented on a broader level by ISPs or any other independent entities for example.
|
||||
|
||||

|
||||
|
||||
#### Reverse Proxy Cache
|
||||
|
||||
Reverse proxy cache or surrogate cache is implemented close to the origin servers in order to reduce the load on server. Unlike proxy caches which are implemented by ISPs etc to reduce the bandwidth usage in a network, surrogates or reverse proxy caches are implemented near to the origin servers by the server administrators to reduce the load on server.
|
||||
|
||||

|
||||
|
||||
Although you can control the reverse proxy caches (since it is implemented by you on your server) you can not avoid or control browser and proxy caches. And if your website is not configured to use these caches properly, it will still be cached using whatever the defaults are set on these caches.
|
||||
|
||||
### Caching Headers
|
||||
|
||||
So, how do we control the web cache? Whenever the server emits some response, it is accompanied with some HTTP headers to guide the caches whether and how to cache this response. 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)
|
||||
- [Validators](#validators)
|
||||
- [ETag](#etag)
|
||||
- [Last-Modified](#last-modified)
|
||||
|
||||
#### Expires
|
||||
|
||||
Before HTTP/1.1 and introduction of `Cache-Control`, there was `Expires` header which is simply a timestamp telling the caches how long should some content be considered fresh. Possible value to this header is absolute expiry date; where date has to be in GMT. Below is the sample header
|
||||
|
||||
```html
|
||||
Expires: Mon, 13 Mar 2017 12:22:00 GMT
|
||||
```
|
||||
|
||||
It should be noted that the date cannot be more than a year and if the date format is wrong, content will be considered stale. Also, the clock on cache has to be in sync with the clock on server, otherwise the desired results might not be achieved.
|
||||
|
||||
Although, `Expires` header is still valid and is supported widely by the caches, preference should be given to HTTP/1.1 successor of it i.e. `Cache-Control`.
|
||||
|
||||
#### Pragma
|
||||
|
||||
Another one from the old, pre HTTP/1.1 days, is `Pragma`. Everything that it could do is now possible using the cache-control header given below. However, one thing I would like to point out about it is, you might see `Pragma: no-cache` being used here and there in hopes of stopping the response from being cached. It might not necessarily work; as HTTP specification discusses it in the request headers and there is no mention of it in the response headers. Rather `Cache-Control` header should be used to control the caching.
|
||||
|
||||
#### Cache-Control
|
||||
|
||||
Cache-Control specifies how long and in what manner should the content be cached. This family of headers was introduced in HTTP/1.1 to overcome the limitations of the `Expires` header.
|
||||
|
||||
Value for the `Cache-Control` header is composite i.e. it can have multiple directive/values. Let's look at the possible values that this header may contain.
|
||||
|
||||
##### private
|
||||
Setting the cache to `private` means that the content will not be cached in any of the proxies and it will only be cached by the client (i.e. browser)
|
||||
|
||||
```html
|
||||
Cache-Control: private
|
||||
```
|
||||
|
||||
Having said that, don't let it fool you in to thinking that setting this header will make your data any secure; you still have to use SSL for that purpose.
|
||||
|
||||
##### public
|
||||
|
||||
If set to `public`, apart from being cached by the client, it can also be cached by the proxies; serving many other users
|
||||
|
||||
```html
|
||||
Cache-Control: public
|
||||
```
|
||||
|
||||
##### no-store
|
||||
**`no-store`** specifies that the content is not to be cached by any of the caches
|
||||
|
||||
```html
|
||||
Cache-Control: no-store
|
||||
```
|
||||
|
||||
##### no-cache
|
||||
**`no-cache`** indicates that the cache can be maintained but the cached content is to be re-validated (using `ETag` for example) from the server before being served. That is, there is still a request to server but for validation and not to download the cached content.
|
||||
|
||||
```html
|
||||
Cache-Control: max-age=3600, no-cache, public
|
||||
```
|
||||
|
||||
##### max-age: seconds
|
||||
**`max-age`** specifies the number of seconds for which the content will be cached. For example, if the `cache-control` looks like below:
|
||||
|
||||
```html
|
||||
Cache-Control: max-age=3600, public
|
||||
```
|
||||
it would mean that the content is publicly cacheable and will be considered stale after 60 minutes
|
||||
|
||||
##### s-maxage: seconds
|
||||
**`s-maxage`** here `s-` prefix stands for shared. This directive specifically targets the shared caches. Like `max-age` it also gets the number of seconds for which something is to be cached. If present, it will override `max-age` and `expires` headers for shared caching.
|
||||
|
||||
```html
|
||||
Cache-Control: s-maxage=3600, public
|
||||
```
|
||||
|
||||
##### must-revalidate
|
||||
**`must-revalidate`** it might happen sometimes that if you have network problems and the content cannot be retrieved from the server, browser may serve stale content without validation. `must-revalidate` avoids that. If this directive is present, it means that stale content cannot be served in any case and the data must be re-validated from the server before serving.
|
||||
|
||||
```html
|
||||
Cache-Control: max-age=3600, public, must-revalidate
|
||||
```
|
||||
|
||||
##### proxy-revalidate
|
||||
**`proxy-revalidate`** is similar to `must-revalidate` but it specifies the same for shared or proxy caches. In other words `proxy-revalidate` is to `must-revalidate` as `s-maxage` is to `max-age`. But why did they not call it `s-revalidate`?. I have no idea why, if you have any clue please leave a comment below.
|
||||
|
||||
##### Mixing Values
|
||||
You can combine these directives in different ways to achieve different caching behaviors, however `no-cache/no-store` and `public/private` are mutually exclusive.
|
||||
|
||||
If you specify both `no-store` and `no-cache`, `no-store` will be given precedence over `no-cache`.
|
||||
|
||||
```html
|
||||
; If specified both
|
||||
Cache-Control: no-store, no-cache
|
||||
|
||||
; Below will be considered
|
||||
Cache-Control: no-store
|
||||
```
|
||||
|
||||
For `private/public`, for any unauthenticated requests cache is considered `public` and for any authenticated ones cache is considered `private`.
|
||||
|
||||
### Validators
|
||||
|
||||
Up until now we only discussed how the content is cached and how long the cached content is to be considered fresh but we did not discuss how the client does the validation from the server. Below we discuss the headers used for this purpose.
|
||||
|
||||
#### ETag
|
||||
|
||||
Etag or "entity tag" was introduced in HTTP/1.1 specs. Etag is just a unique identifier that the server attaches with some resource. This ETag is later on used by the client to make conditional HTTP requests stating `"give me this resource if ETag is not same as the ETag that I have"` and the content is downloaded only if the etags do not match.
|
||||
|
||||
Method by which ETag is generated is not specified in the HTTP docs and usually some collision-resistant hash function is used to assign etags to each version of a resource. There could be two types of etags i.e. strong and weak
|
||||
|
||||
```html
|
||||
ETag: "j82j8232ha7sdh0q2882" - Strong Etag
|
||||
ETag: W/"j82j8232ha7sdh0q2882" - Weak Etag (prefixed with `W/`)
|
||||
```
|
||||
|
||||
A strong validating ETag means that two resources are **exactly** same and there is no difference between them at all. While a weak ETag means that two resources are although not strictly same but could be considered same. Weak etags might be useful for dynamic content, for example.
|
||||
|
||||
Now you know what etags are but how does the browser make this request? by making a request to server while sending the available Etag in `If-None-Match` header.
|
||||
|
||||
Consider the scenario, you opened a web page which loaded a logo image with caching period of 60 seconds and ETag of `abc123xyz`. After about 30 minutes you reload the page, browser will notice that the logo which was fresh for 60 seconds is now stale; it will trigger a request to server, sending the ETag of the stale logo image in `if-none-match` header
|
||||
|
||||
```html
|
||||
If-None-Match: "abc123xyz"
|
||||
```
|
||||
|
||||
Server will then compare this ETag with the ETag of the current version of resource. If both etags are matched, server will send back the response of `304 Not Modified` which will tell the client that the copy that it has is still good and it will be considered fresh for another 60 seconds. If both the etags do not match i.e. the logo has likely changed and client will be sent the new logo which it will use to replace the stale logo that it has.
|
||||
|
||||
#### Last-Modified
|
||||
|
||||
Server might include the `Last-Modified` header indicating the date and time at which some content was last modified on.
|
||||
|
||||
```html
|
||||
Last-Modified: Wed, 15 Mar 2017 12:30:26 GMT
|
||||
```
|
||||
|
||||
When the content gets stale, client will make a conditional request including the last modified date that it has inside the header called `If-Modified-Since` to server to get the updated `Last-Modified` date; if it matches the date that the client has, `Last-Modified` date for the content is updated to be considered fresh for another `n` seconds. If the received `Last-Modified` date does not match the one that the client has, content is reloaded from the server and replaced with the content that client has.
|
||||
|
||||
```html
|
||||
If-Modified-Since: Wed, 15 Mar 2017 12:30:26 GMT
|
||||
```
|
||||
|
||||
You might be questioning now, what if the cached content has both the `Last-Modified` and `ETag` assigned to it? Well, in that case both are to be used i.e. there will not be any re-downloading of the resource if and only if `ETag` matches the newly retrieved one and so does the `Last-Modified` date. If either the `ETag` does not match or the `Last-Modified` is greater than the one from the server, content has to be downloaded again.
|
||||
|
||||
### Where do I start?
|
||||
|
||||
Now that we have got *everything* covered, let us put everything in perspective and see how you can use this information.
|
||||
|
||||
#### Utilizing Server
|
||||
|
||||
Before we get into the possible caching strategies , let me add the fact that most of the servers including Apache and Nginx allow you to implement your caching policy through the server so that you don't have to juggle with headers in your code.
|
||||
|
||||
**For example**, if you are using Apache and you have your static content placed at `/static`, you can put below `.htaccess` file in the directory to make all the content in it be cached for an year using below
|
||||
|
||||
```html
|
||||
# Cache everything for an year
|
||||
Header set Cache-Control "max-age=31536000, public"
|
||||
```
|
||||
|
||||
You can further use `filesMatch` directive to add conditionals and use different caching strategy for different kinds of files e.g.
|
||||
|
||||
```html
|
||||
# Cache any images for one year
|
||||
<filesMatch ".(png|jpg|jpeg|gif)$">
|
||||
Header set Cache-Control "max-age=31536000, public"
|
||||
</filesMatch>
|
||||
|
||||
# Cache any CSS and JS files for a month
|
||||
<filesMatch ".(css|js)$">
|
||||
Header set Cache-Control "max-age=2628000, public"
|
||||
</filesMatch>
|
||||
```
|
||||
|
||||
Or if you don't want to use the `.htaccess` file you can modify Apache's configuration file `http.conf`. Same goes for Nginx, you can add the caching information in the location or server block.
|
||||
|
||||
#### Caching Recommendations
|
||||
|
||||
There is no golden rule or set standards about how your caching policy should look like, each of the application is different and you have to look and find what suits your application the best. However, just to give you a rough idea
|
||||
|
||||
- You can have aggressive caching (e.g. cache for an year) on any static content and use fingerprinted filenames (e.g. `style.ju2i90.css`) so that the cache is automatically rejected whenever the files are updated.
|
||||
Also it should be noted that you should not cross the upper limit of one year as it [might not be honored](https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9)
|
||||
- Look and decide do you even need caching for any dynamic content, if yes how long it should be. For example, in case of some RSS feed of a blog there could be the caching of a few hours but there couldn't be any caching for inventory items in an ERP.
|
||||
- Always add the validators (preferably ETags) in your response.
|
||||
- Pay attention while choosing the visibility (private or public) of the cached content. Make sure that you do not accidentally cache any user-specific or sensitive content in any public proxies. When in doubt, do not use cache at all.
|
||||
- Separate the content that changes often from the content that doesn't change that often (e.g. in javascript bundles) so that when it is updated it doesn't need to make the whole cached content stale.
|
||||
- Test and monitor the caching headers being served by your site. You can use the browser console or `curl -I http://some-url.com` for that purpose.
|
||||
|
||||
And that about wraps it up. Stay tuned for more!
|
||||
@@ -1,25 +1,3 @@
|
||||
---
|
||||
title: "Journey to HTTP/2"
|
||||
description: "The evolution of HTTP. How it all started and where we stand today"
|
||||
author:
|
||||
name: "Kamran Ahmed"
|
||||
url: "https://twitter.com/kamranahmedse"
|
||||
imageUrl: "/authors/kamranahmedse.jpeg"
|
||||
seo:
|
||||
title: "Journey to HTTP/2 - roadmap.sh"
|
||||
description: "The evolution of HTTP. How it all started and where we stand today"
|
||||
isNew: false
|
||||
type: "textual"
|
||||
date: 2018-12-04
|
||||
sitemap:
|
||||
priority: 0.7
|
||||
changefreq: "weekly"
|
||||
tags:
|
||||
- "guide"
|
||||
- "textual-guide"
|
||||
- "guide-sitemap"
|
||||
---
|
||||
|
||||
HTTP is the protocol that every web developer should know as it powers the whole web and knowing it is definitely going to help you develop better applications. In this guide, I am going to be discussing what HTTP is, how it came to be, where it is today and how did we get here.
|
||||
|
||||
### What is HTTP?
|
||||
@@ -93,9 +71,9 @@ Three-way handshake in its simplest form is that all the `TCP` connections begin
|
||||
- `SYN ACK` - Server acknowledges the request by sending an `ACK` packet back to the client which is made up of a random number, let's say `y` picked up by server and the number `x+1` where `x` is the number that was sent by the client
|
||||
- `ACK` - Client increments the number `y` received from the server and sends an `ACK` packet back with the number `y+1`
|
||||
|
||||
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.
|
||||
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 recieved in order to fulfill the request.
|
||||
|
||||

|
||||

|
||||
|
||||
> 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`
|
||||
|
||||
@@ -111,7 +89,7 @@ After merely 3 years of `HTTP/1.0`, the next version i.e. `HTTP/1.1` was release
|
||||
|
||||
- **Hostname Identification** In `HTTP/1.0` `Host` header wasn't required but `HTTP/1.1` made it required.
|
||||
|
||||
- **Persistent Connections** As discussed above, in `HTTP/1.0` there was only one request per connection and the connection was closed as soon as the request was fulfilled which resulted in acute performance hit and latency problems. `HTTP/1.1` introduced the persistent connections i.e. **connections weren't closed by default** and were kept open which allowed multiple sequential requests. To close the connections, the header `Connection: close` had to be available on the request. Clients usually send this header in the last request to safely close the connection.
|
||||
- **Persistent Connections** As discussed above, in `HTTP/1.0` there was only one request per connection and the connection was closed as soon as the request was fulfilled which resulted in accute performance hit and latency problems. `HTTP/1.1` introduced the persistent connections i.e. **connections weren't closed by default** and were kept open which allowed multiple sequential requests. To close the connections, the header `Connection: close` had to be available on the request. Clients usually send this header in the last request to safely close the connection.
|
||||
|
||||
- **Pipelining** It also introduced the support for pipelining, where the client could send multiple requests to the server without waiting for the response from server on the same connection and server had to send the response in the same sequence in which requests were received. But how does the client know that this is the point where first response download completes and the content for next response starts, you may ask! Well, to solve this, there must be `Content-Length` header present which clients can use to identify where the response ends and it can start waiting for the next response.
|
||||
|
||||
@@ -131,9 +109,9 @@ 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](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.
|
||||
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.
|
||||
|
||||
`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.
|
||||
`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 humungous CSS/Javascript files, [domain sharding](https://www.maxcdn.com/one/visual-glossary/domain-sharding-2/) etc.
|
||||
|
||||
### SPDY - 2009
|
||||
|
||||
@@ -143,7 +121,7 @@ Google went ahead and started experimenting with alternative protocols to make t
|
||||
|
||||
It was seen that if we keep increasing the bandwidth, the network performance increases in the beginning but a point comes when there is not much of a performance gain. But if you do the same with latency i.e. if we keep dropping the latency, there is a constant performance gain. This was the core idea for performance gain behind `SPDY`, decrease the latency to increase the network performance.
|
||||
|
||||
> For those who don't know the difference, latency is the delay i.e. how long it takes for data to travel between the source and destination (measured in milliseconds) and bandwidth is the amount of data transferred per second (bits per second).
|
||||
> For those who don't know the difference, latency is the delay i.e. how long it takes for data to travel between the source and destination (measured in milliseconds) and bandwidth is the amount of data transfered per second (bits per second).
|
||||
|
||||
The features of `SPDY` included, multiplexing, compression, prioritization, security etc. I am not going to get into the details of SPDY, as you will get the idea when we get into the nitty gritty of `HTTP/2` in the next section as I said `HTTP/2` is mostly inspired from SPDY.
|
||||
|
||||
@@ -162,7 +140,7 @@ By now, you must be convinced that why we needed another revision of the HTTP pr
|
||||
- Request Prioritization
|
||||
- Security
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
#### 1. Binary Protocol
|
||||
@@ -171,7 +149,7 @@ By now, you must be convinced that why we needed another revision of the HTTP pr
|
||||
|
||||
##### Frames and Streams
|
||||
|
||||
HTTP messages are now composed of one or more frames. There is a `HEADERS` frame for the meta data and `DATA` frame for the payload and there exist several other types of frames (`HEADERS`, `DATA`, `RST_STREAM`, `SETTINGS`, `PRIORITY` etc) that you can check through [the `HTTP/2` specs](https:/http2.github.iohttp2-spec/#FrameTypes).
|
||||
HTTP messages are now composed of one or more frames. There is a `HEADERS` frame for the meta data and `DATA` frame for the payload and there exist several other types of frames (`HEADERS`, `DATA`, `RST_STREAM`, `SETTINGS`, `PRIORITY` etc) that you can check through [the `HTTP/2` specs](https://http2.github.io/http2-spec/#FrameTypes).
|
||||
|
||||
Every `HTTP/2` request and response is given a unique stream ID and it is divided into frames. Frames are nothing but binary pieces of data. A collection of frames is called a Stream. Each frame has a stream id that identifies the stream to which it belongs and each frame has a common header. Also, apart from stream ID being unique, it is worth mentioning that, any request initiated by client uses odd numbers and the response from server has even numbers stream IDs.
|
||||
|
||||
@@ -187,7 +165,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.
|
||||
|
||||

|
||||

|
||||
|
||||
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.
|
||||
|
||||
@@ -198,7 +176,7 @@ While we are talking headers, let me add here that the headers are still the sam
|
||||
|
||||
Server push is another tremendous feature of `HTTP/2` where the server, knowing that the client is going to ask for a certain resource, can push it to the client without even client asking for it. For example, let's say a browser loads a web page, it parses the whole page to find out the remote content that it has to load from the server and then sends consequent requests to the server to get that content.
|
||||
|
||||
Server push allows the server to decrease the round trips by pushing the data that it knows that client is going to demand. How it is done is, server sends a special frame called `PUSH_PROMISE` notifying the client that, "Hey, I am about to send this resource to you! Do not ask me for it." The `PUSH_PROMISE` frame is associated with the stream that caused the push to happen and it contains the promised stream ID i.e. the stream on which the server will send the resource to be pushed.
|
||||
Server push allows the server to decrease the roundtrips by pushing the data that it knows that client is going to demand. How it is done is, server sends a special frame called `PUSH_PROMISE` notifying the client that, "Hey, I am about to send this resource to you! Do not ask me for it." The `PUSH_PROMISE` frame is associated with the stream that caused the push to happen and it contains the promised stream ID i.e. the stream on which the server will send the resource to be pushed.
|
||||
|
||||
#### 5. Request Prioritization
|
||||
|
||||
@@ -208,10 +186,10 @@ Without any priority information, server processes the requests asynchronously i
|
||||
|
||||
#### 6. Security
|
||||
|
||||
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.
|
||||
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 requirementsi.e. `TLS` version `1.2` or higher must be used, there must be a certain level of minimum keysizes, ephemeral keys are required etc.
|
||||
|
||||
`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.
|
||||
`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.
|
||||
|
||||
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/).
|
||||
For anyone interested in further details here is the [link to specs](https://http2.github.io/http2-spec) and a link [demonstrating the performance benefits of `HTTP/2`](http://www.http2demo.io/).
|
||||
|
||||
And that about wraps it up. Until next time! stay tuned.
|
||||
2
content/guides/jwt-authentication.md
Normal file
@@ -0,0 +1,2 @@
|
||||
[](/guides/jwt-authentication.png)
|
||||
|
||||
@@ -1,25 +1,3 @@
|
||||
---
|
||||
title: "Levels of Seniority"
|
||||
description: "How to Step Up as a Junior, Mid Level or a Senior Developer?"
|
||||
author:
|
||||
name: "Kamran Ahmed"
|
||||
url: "https://twitter.com/kamranahmedse"
|
||||
imageUrl: "/authors/kamranahmedse.jpeg"
|
||||
seo:
|
||||
title: "Levels of Seniority - roadmap.sh"
|
||||
description: "How to Step Up as a Junior, Mid Level or a Senior Developer?"
|
||||
isNew: false
|
||||
type: "textual"
|
||||
date: 2020-12-03
|
||||
sitemap:
|
||||
priority: 0.7
|
||||
changefreq: "weekly"
|
||||
tags:
|
||||
- "guide"
|
||||
- "textual-guide"
|
||||
- "guide-sitemap"
|
||||
---
|
||||
|
||||
I have been working on redoing the [roadmaps](https://roadmap.sh) – splitting the skillset based on the seniority levels to make them easier to follow and not scare the new developers away. Since the roadmaps are going to be just about the technical knowledge, I thought it would be a good idea to reiterate and have an article on what I think of different seniority roles.
|
||||
|
||||
I have seen many organizations decide the seniority of developers by giving more significance to the years of experience than they should. I have seen developers labeled "Junior" doing the work of Senior Developers and I have seen "Lead" developers who weren't even qualified to be called "Senior". The seniority of a developer cannot just be decided by their age, years of experience or technical knowledge that they have got. There are other factors in play here -- their perception of work, how they interact with their peers and how they approach problems. We discuss these three key factors in detail for each of the seniority levels below.
|
||||
2
content/guides/oauth.md
Normal file
@@ -0,0 +1,2 @@
|
||||
[](/guides/oauth.png)
|
||||
|
||||
5
content/guides/project-history.md
Normal file
@@ -0,0 +1,5 @@
|
||||
One of my favorite pastimes is going through the history of my favorite projects to learn how they grew over time or how certain features were implemented.
|
||||
|
||||
The image below describes how I do that in WebStorm.
|
||||
|
||||
[](/guides/project-history.png)
|
||||
47
content/guides/proxy-servers.md
Normal file
@@ -0,0 +1,47 @@
|
||||
Internet has connected people across the world using social media and audio/video calling features along with providing an overabundance of knowledge and tools. All this comes with an inherent danger of security and privacy breaches. In this guide we will talk about **proxies** which play a vital role in mitigating these risks. We will cover the following topics in this guide:
|
||||
|
||||
- [Proxy Server](#proxy-server)
|
||||
- [Forward Proxy Server](#forward-proxy-server)
|
||||
- [Reverse Proxy Server](#reverse-proxy-server)
|
||||
- [Summary](#summary)
|
||||
|
||||
## Proxy Server
|
||||
|
||||
***Every web request which is sent from the client to a web server goes through some type of proxy server.*** A proxy server acts as a gateway between client *(you)* and the internet and separates end-users from the websites you browse. It replaces the source IP address of the web request with the proxy server's IP address and then forwards it to the web server. The web server is unaware of the client, it only sees the proxy server.
|
||||
|
||||

|
||||
> NOTE: This is not an accurate description rather just an illustration.
|
||||
|
||||
Proxy servers serve as a single point of control making it easier to enforce security policies. It also provides caching mechanism which stores the requested web pages on the proxy server to improve performance. If the requested web-page is available in cache memory then instead of forwarding the request to the web-server it will send the cached webpage back to the client. This **saves big companies thousands of dollars** by reducing load on their servers as their website is visited by millions of users every day.
|
||||
|
||||
## Forward Proxy Server
|
||||
|
||||
A forward proxy is generally implemented on the client side and **sits in front of multiple clients** or client sources. Forward proxy servers are mainly used by companies to **manage internet usage** of their employees and **restrict content**. It is also used as a **firewall** to secure company's network by blocking any request which would pose threat to the companies's network. Proxy servers are also used to **bypass geo-restriction** and browse content which might be blocked in user's country. It enables users to **browse anonymously**, as the proxy server masks their details from the website's servers.
|
||||
|
||||

|
||||
> NOTE: This is not an accurate description rather just an illustration
|
||||
|
||||
## Reverse Proxy Server
|
||||
|
||||
Reverse proxy servers are implemented on the **server side** instead of the client side. It **sits in front of multiple webservers** and manages the incoming requests by forwarding them to the web servers. It provides anonymity for the **back-end web servers and not the client**. Reverse proxy servers are generally used to perform tasks such as **authentication, content caching, and encryption/decryption** on behalf of the web server. These tasks would **hog CPU cycles** on the web server and degrade performance of the website by introducing high amount of delay in loading the webpage. Reverse proxies are also used as **load balancers** to distribute the incoming traffic efficiently among the web servers but it is **not optimized** for this task. In essence, reverse proxy server is a gateway to a web-server or group of web-servers.
|
||||
|
||||

|
||||
> NOTE: This is not an accurate description rather just an illustration. Red lines represent server's response and black lines represent initial request from client(s).
|
||||
|
||||
## Summary
|
||||
|
||||
A proxy server acts as a gateway between client *(you)* and the internet and separates end-users from the websites you browse. ***The position of the proxy server on the network determines whether it is a forward or a reverse proxy server***. Forward proxy is implemented on the client side and **sits in front of multiple clients** or client sources and forwards requests to the web server. Reverse proxy servers are implemented on the **server side** it **sits in front of multiple webservers** and manages the incoming requests by forwarding them to the web servers.
|
||||
|
||||
If all this was too much to take in, I have a simple analogy for you.
|
||||
|
||||
At a restaurant the waiter/waitress takes your order and gives it to the kitchen head chef. The head chef then calls out the order and assigns tasks to everyone in the kitchen.
|
||||
|
||||
In this analogy:
|
||||
|
||||
* You are the client
|
||||
* Your order is the web request
|
||||
* Waiter/Waitress is your forward proxy server
|
||||
* Kitchen head chef is the reverse proxy server
|
||||
* Other chefs working in the kitchen are the web servers
|
||||
|
||||
With that said our guide comes to an end. Thank you for reading and feel free to submit any updates to the guide using the links below.
|
||||
4
content/guides/random-numbers.md
Normal file
@@ -0,0 +1,4 @@
|
||||
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)
|
||||
|
||||
4
content/guides/scaling-databases.md
Normal file
@@ -0,0 +1,4 @@
|
||||
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)
|
||||
|
||||
2
content/guides/session-authentication.md
Normal file
@@ -0,0 +1,2 @@
|
||||
[](/guides/session-authentication.png)
|
||||
|
||||
2
content/guides/ssl-tls-https-ssh.md
Normal file
@@ -0,0 +1,2 @@
|
||||
[](/guides/ssl-tls-https-ssh.png)
|
||||
|
||||
2
content/guides/sso.md
Normal file
@@ -0,0 +1,2 @@
|
||||
[](/guides/sso.png)
|
||||
|
||||
8
content/guides/threads-and-concurrency.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# Threads and Concurrency
|
||||
|
||||
A thread is an execution context in which the instructions to the CPU can be scheduled and executed independently of the parent process. Concurrency is the concept of multiple threads in a shared memory space being computed simultaneously (or intermittently executed in succession to provide that illusion). Concurrency allows multiple processes to execute at once and can apply to programming languages as well as operating systems.
|
||||
|
||||
<ResourceGroupTitle>Free Content</ResourceGroupTitle>
|
||||
<BadgeLink badgeText='Watch' href='https://www.youtube.com/watch?v=olYdb0DdGtM'>Threading Tutorial #1 - Concurrency, Threading and Parallelism</BadgeLink>
|
||||
<BadgeLink colorScheme='yellow' badgeText='Read' href='https://medium.com/@akhandmishra/operating-system-threads-and-concurrency-aec2036b90f8'>Operating System: Threads and Concurrency</BadgeLink>
|
||||
<BadgeLink colorScheme='yellow' badgeText='Read' href='https://web.mit.edu/6.005/www/fa14/classes/17-concurrency/'>Reading 17: Concurrency</BadgeLink>
|
||||
2
content/guides/token-authentication.md
Normal file
@@ -0,0 +1,2 @@
|
||||
[](/guides/token-authentication.png)
|
||||
|
||||
@@ -1,25 +1,3 @@
|
||||
---
|
||||
title: "Building a BitTorrent Client"
|
||||
description: "Learn everything you need to know about BitTorrent by writing a client in Go"
|
||||
author:
|
||||
name: "Jesse Li"
|
||||
url: "https://twitter.com/__jesse_li"
|
||||
imageUrl: "/authors/jesse.png"
|
||||
seo:
|
||||
title: "Building a BitTorrent Client - roadmap.sh"
|
||||
description: "Learn everything you need to know about BitTorrent by writing a client in Go"
|
||||
isNew: false
|
||||
type: "textual"
|
||||
date: 2021-01-17
|
||||
sitemap:
|
||||
priority: 0.7
|
||||
changefreq: "weekly"
|
||||
tags:
|
||||
- "guide"
|
||||
- "textual-guide"
|
||||
- "guide-sitemap"
|
||||
---
|
||||
|
||||
BitTorrent is a protocol for downloading and distributing files across the Internet. In contrast with the traditional client/server relationship, in which downloaders connect to a central server (for example: watching a movie on Netflix, or loading the web page you're reading now), participants in the BitTorrent network, called **peers**, download pieces of files from *each other*—this is what makes it a **peer-to-peer** protocol. In this article we will investigate how this works, and build our own client that can find peers and exchange data between them.
|
||||
|
||||

|
||||
@@ -497,7 +475,7 @@ Files, pieces, and piece hashes aren't the full story—we can go further by bre
|
||||
A peer is supposed to sever the connection if they receive a request for a block larger than 16KB. However, based on my experience, they're often perfectly happy to satisfy requests up to 128KB. I only got moderate gains in overall speed with larger block sizes, so it's probably better to stick with the spec.
|
||||
|
||||
#### Pipelining
|
||||
Network round-trips are expensive, and requesting each block one by one will absolutely thank the performance of our download. Therefore, it's important to **pipeline** our requests such that we keep up a constant pressure of some number of unfulfilled requests. This can increase the throughput of our connection by an order of magnitude.
|
||||
Network round-trips are expensive, and requesting each block one by one will absolutely tank the performance of our download. Therefore, it's important to **pipeline** our requests such that we keep up a constant pressure of some number of unfulfilled requests. This can increase the throughput of our connection by an order of magnitude.
|
||||
|
||||

|
||||
|
||||
2
content/guides/unfamiliar-codebase.md
Normal file
@@ -0,0 +1,2 @@
|
||||
[](/guides/unfamiliar-codebase.png)
|
||||
|
||||
11
content/guides/upcoming.md
Normal file
@@ -0,0 +1,11 @@
|
||||
> **Roadmap is not ready yet**. Please check back later or [subscribe to get notified](/signup).
|
||||
|
||||
While we prepare the roadmap, follow this simple advice to learn anything
|
||||
|
||||
> Just **pick a project and start working on it**, you will learn all that you need along the way.
|
||||
|
||||
**→** [All Roadmaps](/roadmaps) • [Programming guides](/guides) • [Subscribe](/signup)
|
||||
|
||||
|
||||
|
||||
|
||||
2
content/guides/what-are-web-vitals.md
Normal file
@@ -0,0 +1,2 @@
|
||||
[](/guides/web-vitals.png)
|
||||
|
||||
62
content/guides/what-is-internet.md
Normal file
@@ -0,0 +1,62 @@
|
||||
Since the explosive growth of web-based applications, every developer could stand to benefit from understanding how the Internet works. In this article, accompanied with an introductory series of short videos about the Internet from [code.org](https://code.org), you will learn the basics of the Internet and how it works. After going through this article, you will be able to answer the below questions:
|
||||
|
||||
* What is the Internet?
|
||||
* How does the information move on the internet?
|
||||
* How do the networks talk to each other and the protocols involved?
|
||||
* What's the relationship between packets, routers, and reliability?
|
||||
* HTTP and the HTML – How are you viewing this webpage in your browser?
|
||||
* How is the information transfer on the internet made secure?
|
||||
* What is cybersecurity and what are some common internet crimes?
|
||||
|
||||
## What is the Internet?
|
||||
|
||||
The Internet is a global network of computers connected to each other which communicate through a standardized set of protocols.
|
||||
|
||||
In the video below, Vint Cerf, one of the "fathers of the internet," explains the history of how the Internet works and how no one person or organization is really in charge of it.
|
||||
|
||||
<iframe width="100%" height="400" src="https://www.youtube.com/embed/Dxcc6ycZ73M" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
|
||||
|
||||
|
||||
## Wires, Cables, and Wi-Fi
|
||||
|
||||
Information on the Internet moves from computer to another in the form of bits over various mediums, including Ethernet cables, fiber optic cables, and wireless signals (i.e., radio waves).
|
||||
|
||||
In the video linked below, you will learn about the different mediums for data transfer on the Internet and the pros and cons for each.
|
||||
|
||||
<iframe width="100%" height="400" src="https://www.youtube.com/embed/ZhEf7e4kopM" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
|
||||
|
||||
## IP Addresses and DNS
|
||||
|
||||
Now that you know about the physical medium for the data transfer over the internet, it's time to learn about the protocols involved. How does the information traverse from one computer to another in this massive global network of computers?
|
||||
|
||||
In the video below, you will get a brief introduction to IP, DNS, and how these protocols make the Internet work.
|
||||
|
||||
<iframe width="100%" height="400" src="https://www.youtube.com/embed/5o8CwafCxnU" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
|
||||
|
||||
## Packets, Routing, and Reliability
|
||||
|
||||
Information transfer on the Internet from one computer to another does not need to follow a fixed path; in fact, it may change paths during the transfer. This information transfer is done in the form of packets and these packets may follow different routes depending on certain factors.
|
||||
|
||||
In this video, you will learn about how the packets of information are routed from one computer to another to reach the destination.
|
||||
|
||||
<iframe width="100%" height="400" src="https://www.youtube.com/embed/AYdF7b3nMto" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
|
||||
|
||||
## HTTP and HTML
|
||||
|
||||
HTTP is the standard protocol by which webpages are transferred over the Internet. The video below is a brief introduction to HTTP and how web browsers load websites for you.
|
||||
|
||||
<iframe width="100%" height="400" src="https://www.youtube.com/embed/kBXQZMmiA4s" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
|
||||
|
||||
## Encryption and Public Keys
|
||||
|
||||
Cryptography is what keeps our communication secure on the Internet. In this short video, you will learn the basics of cryptograpy, SSL/TLS, and how they help make the communication on the Internet secure.
|
||||
|
||||
<iframe width="100%" height="400" src="https://www.youtube.com/embed/ZghMPWGXexs" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
|
||||
|
||||
## Cybersecurity and Crime
|
||||
|
||||
In this video, you will learn about the basics of cybersecurity and common cybercrimes
|
||||
|
||||
<iframe 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/).
|
||||
2
content/guides/what-is-sli-slo-sla.md
Normal file
@@ -0,0 +1,2 @@
|
||||
[](/guides/sli-slo-sla.jpeg)
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
We all have heard the mantra *"build it and they will come"* many times. Stories of people building a startup or project and seemingly stumbling upon a goldmine aren't few, but they aren't the rule. These stories are still the exception in the mass of launched projects and startups.
|
||||
|
||||
Before the [Wright brothers](https://en.wikipedia.org/wiki/Wright_brothers) built their Kitty Hawk, people generally believed heavy objects could not fly - physics simply forbade it. The idea to regularly board airplanes as we do it these days was unthinkable. It was considered an unrealistic daydream for humans to ever claim the sky. When the first airplanes took off, people were fascinated, of course. It was a topic people continued to talk about for ages. Technology had made something impossible possible. While the wording "build it and they will come" originated from the movie [Field of Dreams](https://en.wikipedia.org/wiki/Field_of_Dreams), this and similar historic events gave birth to the idea behind it.
|
||||
|
||||
The engineers' and inventors' dreams came true: spend time doing what you love while the success follows magically. The internet and web-standards democratized access to this dream. But with it, the idea behind it faded and became less and less powerful. In 2020, there are very strong signs the popular saying isn't correct anymore.
|
||||
|
||||
|
||||
Why doesn't "build it and they will come" work anymore?
|
||||
-------------------------------------------------------
|
||||
|
||||
There are a few reasons working hard to make "build it and they will come" a thing of the past. This being said, it doesn't mean you can't succeed building a side-project anymore. You've just got to adjust the way you are building it.
|
||||
|
||||
### Building got much easier
|
||||
|
||||
As a software engineer, some websites are a blessing. Most of us couldn't work without GitHub, Stackoverflow and of course Google, ahem, DuckDuckGo. These powerful sites help us to solve problems, learn new techniques and find the right libraries to make building projects easier. If any of these sites are down, most engineers take a break and go for a coffee instead of trying to continue working. Combine this with more sophisticated web-standards and easier access to tooling, and you arrive at a world where building projects isn't just a job for highly specialist developers anymore. Powerful frameworks such as [Laravel](https://laravel.com/) and [Quasar Framework](https://quasar.dev/) are available for anyone to build projects on - for free.
|
||||
|
||||
In fact, building projects got to a point where some people simply build them as an exercise or hobby. If you spend some time browsing GitHub you will be surprised by the open source projects people built without any commercial goals. "Low code" and "No code" are the next wave of people building projects with less technological background.
|
||||
|
||||
### Too much going on: information overload
|
||||
|
||||
We are living in a world with information overload. In the online sphere, you can find a lot of useful information. But there is also a lot of noise. For each piece of information or advice you can find a number of opposing statements. This is partly due to the fact that the internet made it much easier to publish and share information. Everyone has been given a voice - for good or bad. This makes it much harder to reach potential users. Your new project probably just drowns amongst kitten videos, opinions, and news. Never has the average lifetime of published content been so low. You've got to come up with a marketing plan before setting out on the journey.
|
||||
|
||||
### Smaller Problems
|
||||
|
||||
Besides building being easier than ever before and attention being in short supply, there is another issue making the life of makers, inventors and engineers harder: today's problems are much smaller. Back when the previously mentioned Wright Brothers set out, they fascinated people with the problem they were aiming to address: flying. Unless your name is Elon, your problem is unlikely to attract many people naturally. As a solo developer or indie hacker, the chances are higher for having a much smaller problem in a niche (of a niche). With the information overload mentioned before, niches are pretty much the only way to build a side-project or startup and succeed.
|
||||
|
||||
Does sound pretty grim for inventors, developers and engineers? Well, yes and no. We've got to tweak the approach to get in front of the eye of potential users and customers.
|
||||
|
||||
|
||||
How to market your project nowadays?
|
||||
------------------------------------
|
||||
|
||||
The very first step to improving the odds of success is [idea validation](https://peterthaleikis.com/business-idea-validation/). While this sounds fairly obvious, many engineers and developers still don't validate their ideas before starting to build the MVP. The result is another stale project and wasted effort. To succeed you need to work on marketing before you start building anything. In the link mentioned before, I describe my approach to validation and collecting useful marketing information at the same time.
|
||||
|
||||
### Build your Audience first and the project after.
|
||||
|
||||
Build your audience before you build your project. Spend your time connecting with potential users, learn from their needs and talk about their problems. This will help you market your project later on. Audience first, project second. There are numerous ways to build an audience. One of the simplest and easiest is to start with a personal or [project blog](https://startupnamecheck.com/blog/how-to-start-a-small-business-blog).
|
||||
|
||||
Don't use Medium or a similar service - opt for a self-hosted blog as it allows you to build the blog freely to your needs and have decent links back to your project later on. Don't forget to add a newsletter. Newsletters are a key to reconnect in our world of short attention spans.
|
||||
|
||||
### Tool by Tool
|
||||
|
||||
Another approach is the "Tool by Tool" approach. I've first noticed this approach being used by Shopify. The team at Shopify are providing little tools such as a [logo generator](https://hatchful.shopify.com/) and release these tools free for anyone to use. This not just builds goodwill with people; it also allows Shopify to attract powerful backlinks to their projects. As developers we are in the perfect position to build such mini-tools. It boosts morale and drives attention at the same time.
|
||||
|
||||
Spend some time evaluating where your project or product will deliver value to the end-user. Look at options to split off small, independent tools. Build these and launch them before launching the whole product. This allows you to practice launching and promoting your part-projects at the same time. With each backlink to your part-projects you will enhance your ranking in Google. An example for a maker following this approach is [Kamban](https://kambanthemaker.com/) with [FlatGA](https://flatga.io/). He built FlatGA as phase one of a bigger project currently in development.
|
||||
|
||||
### Join a Maker community
|
||||
|
||||
While you are building your part-projects, don't forget to discuss the progress publicly. This helps to attract an audience around your work and makes the launches easier. You can use Twitter threads and Reddit posts to share updates. A maker community such as [makerlog](https://getmakerlog.com/) or [WIP.chat](https://wip.chat) can also extend your reach. These allow you to get instant feedback, keep yourself accountable and they will enhance your reach at the same time.
|
||||
|
||||
### Getting ready to Launch
|
||||
|
||||
Launching seems like this special moment when you release your project into the wide world. Often this moment is combined with high expectations and developers consider launching their project the key - if not only - part of their approach to marketing. While launching can help to attract some initial customers, it shouldn't be your only idea when it comes to marketing. You should also know that launching isn't a single event. You can (and should) launch again and again. Every time you launch you are increasing the chance to reach more and new customers. After the launch is before the launch.
|
||||
|
||||
### Marketing Is an On-going Fight
|
||||
|
||||
Many developers plan to launch their product on a few sites and see where it takes their project from there on. This works well, if your product goes viral by luck. A much more sustainable approach is constantly working a little on it. Marketing is most effective, if done consistently. That holds true for blogging as well as most other forms of marketing. A simple approach to keep you on the path to market your project regularly is subscribing to a free [newsletter with small marketing opportunities](https://wheretopost.email). This way, you are regularly reminded and given bite-sized tasks to complete.
|
||||
|
||||
|
||||
Closing Words
|
||||
-------------
|
||||
|
||||
I hope the article helped you to wrap your head around the idea that building side-projects alone doesn't solve any issues anymore. If you like what you've just read and want to read more, please consider subscribing to [my newsletter](https://peterthaleikis.com/newsletter). I'll send out the occasional email about interesting new articles or side-projects.
|
||||
|
||||
|
||||
About the author
|
||||
----------------
|
||||
|
||||
[Peter Thaleikis](https://peterthaleikis.com/) a software engineer and business owner. He has been developing web applications since around 2000. Before he started his own software development company [Bring Your Own Ideas Ltd.](https://bringyourownideas.com/), he has been Lead Developer for multiple organisations.
|
||||
67
content/pages/about.md
Normal file
@@ -0,0 +1,67 @@
|
||||
## What is roadmap.sh?
|
||||
|
||||
Roadmap.sh is the place containing community curated roadmaps, study plans, paths and resources for the budding
|
||||
developers. It started as a [set of charts to guide the developers](https://github.com/kamranahmedse/developer-roadmap)
|
||||
who are confused about what should they learn next but that alone wasn't enough so I expanded it into the website to get
|
||||
more contributors involved.
|
||||
|
||||
## What are the plans for roadmap.sh?
|
||||
|
||||
The website started off as
|
||||
a [simple repository containing a few charts](https://github.com/kamranahmedse/developer-roadmap) for developers and
|
||||
based on my personal opinions but it could have been much more than that so I decided to expand it to a website where
|
||||
people can contribute to study plans with their areas of expertise as well, add more roadmaps, write guides etc.
|
||||
|
||||
We haven't opened up the sign ups for now but we will be doing. My long term plans for this website are to turn it into
|
||||
a goto place for the developers to seek guidance about their careers, help others, share their journeys, incentivize the
|
||||
learnings, get feedbacks on their projects etc.
|
||||
|
||||
## How did you build roadmap.sh?
|
||||
|
||||
The basic version of the website has been built with [Next.js](https://github.com/zeit/next.js/), is opensource and can
|
||||
be found on [github](https://github.com/kamranahmedse/developer-roadmap). It was hastily done to get it out in front of the
|
||||
people and get people to start contributing, so it might be rough on the edges, but that is where we need your help.
|
||||
|
||||
## How does it make money?
|
||||
|
||||
It doesn't make any money. I have been using my personal time and budget to build it. I did not create this website with
|
||||
any intentions of monetization but as a good will, to help the people get out of the frustration that I was once in.
|
||||
|
||||
Having said that, I love teaching and my future plans are to be able to work full-time on roadmap.sh for which it has to
|
||||
make enough money to pay for my rent, groceries, bills, travel expenses, etc but even if it doesn't it's likely I'll
|
||||
continue growing the site however I can. My focus at the moment is not making money from it and just add content that
|
||||
creates value for the people.
|
||||
|
||||
> Sponsor the efforts by [paying as little as 5$ per month](https://github.com/sponsors/kamranahmedse) or with [one time payment via paypal](https://paypal.me/kamranahmedse). Alternatively, reach out to me at [kamranahmed.se@gmail.com](mailto:kamranahmed.se@gmail.com).
|
||||
|
||||
## Can I contribute?
|
||||
|
||||
You definitely can, infact you are encouraged to do that. Even your minor contributions such as typo fixes count. The
|
||||
source code of the website can be [found on Github](https://github.com/kamranahmedse/developer-roadmap). Your contributions can
|
||||
be:
|
||||
|
||||
* Adding a new roadmap
|
||||
* Updating existing roadmap
|
||||
* Suggesting changes to the existing roadmaps
|
||||
* Writing a Guide
|
||||
* Updating an existing guide
|
||||
* Fixing grammar mistakes, typos on the website or the content
|
||||
* Updating the UI of the website
|
||||
* Refactoring the codebase
|
||||
* Becoming a sponsor
|
||||
|
||||
Just make sure
|
||||
to [follow the contribution guidelines](https://github.com/kamranahmedse/developer-roadmap/tree/master/contributing) when you
|
||||
decide to contribute.
|
||||
|
||||
## Can I redistribute the content?
|
||||
|
||||
No, the license of the content on this website does not allow you to redistribute any of the content on this website
|
||||
anywhere. You can use it for personal use or share the link to the content if you have to but redistribution is not
|
||||
allowed.
|
||||
|
||||
## What is the best way to contact you?
|
||||
|
||||
Tweet or send me a message [@kamranahmedse](https://twitter.com/kamranahmedse) or email me
|
||||
at [kamranahmed.se@gmail.com](mailto:kamranahmed.se@gmail.com). I get lots of messages so apologies in advance if you don't hear back
|
||||
from me soon but I do reply to everyone.
|
||||
@@ -1,12 +1,6 @@
|
||||
---
|
||||
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.sh’s 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.sh’s 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?
|
||||
|
||||
@@ -78,3 +72,7 @@ You may be able to add, update, or delete information as explained above. When y
|
||||
## What if I have questions about this policy?
|
||||
|
||||
If you have any questions or concerns regarding our privacy policies, please send us a detailed message to kamranahmed.se@gmail.com, and we will try to resolve your concerns.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,9 +1,3 @@
|
||||
---
|
||||
layout: ../layouts/MarkdownLayout.astro
|
||||
title: Terms and Conditions - roadmap.sh
|
||||
noIndex: true
|
||||
---
|
||||
|
||||
# Terms of Service
|
||||
|
||||
PLEASE NOTE THAT YOUR USE OF AND ACCESS TO OUR SERVICES (DEFINED BELOW) ARE SUBJECT TO THE FOLLOWING TERMS; IF YOU DO NOT AGREE TO ALL OF THE FOLLOWING, YOU MAY NOT USE OR ACCESS THE SERVICES IN ANY MANNER.
|
||||
@@ -22,7 +16,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 Children’s 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.
|
||||
|
||||
@@ -127,3 +121,5 @@ Choice of Law; Arbitration. These Terms are governed by and will be construed un
|
||||
|
||||
Miscellaneous. You will be responsible for paying, withholding, filing, and reporting all taxes, duties, and other governmental assessments associated with your activity in connection with the Services, provided that roadmap.sh may, in its sole discretion, do any of the foregoing on your behalf or for itself as it sees fit. The failure of either you or us to exercise, in any way, any right herein shall not be deemed a waiver of any further rights hereunder. If any provision of these Terms is found to be unenforceable or invalid, that provision will be limited or eliminated, to the minimum extent necessary, so that these Terms shall otherwise remain in full force and effect and enforceable. You and roadmap.sh agree that these Terms are the complete and exclusive statement of the mutual understanding between you and roadmap.sh, and that it supersedes and cancels all previous written and oral agreements, communications and other understandings relating to the subject matter of these Terms. You hereby acknowledge and agree that you are not an employee, agent, partner, or joint venture of roadmap.sh, and you do not have any authority of any kind to bind roadmap.sh in any respect whatsoever. Except as expressly set forth in the section above regarding the Apple Application, you and roadmap.sh agree there are no third party beneficiaries intended under these Terms.
|
||||
|
||||
|
||||
|
||||
562
content/roadmaps.json
Normal file
@@ -0,0 +1,562 @@
|
||||
[
|
||||
{
|
||||
"seo": {
|
||||
"title": "Frontend Developer Roadmap: Learn to become a modern frontend developer",
|
||||
"description": "Learn to become a modern frontend developer using this roadmap. Community driven, articles, resources, guides, interview questions, quizzes for modern frontend development.",
|
||||
"keywords": [
|
||||
"javascript roadmap 2022",
|
||||
"frontend roadmap 2022",
|
||||
"frontend developer roadmap 2022",
|
||||
"guide to becoming a developer",
|
||||
"guide to becoming a frontend developer",
|
||||
"frontend developer",
|
||||
"frontend engineer",
|
||||
"frontend skills",
|
||||
"frontend development",
|
||||
"javascript developer",
|
||||
"frontend development skills",
|
||||
"frontend development skills test",
|
||||
"frontend roadmap",
|
||||
"frontend engineer roadmap",
|
||||
"frontend developer roadmap",
|
||||
"become a frontend developer",
|
||||
"frontend developer career path",
|
||||
"javascript developer",
|
||||
"modern javascript developer",
|
||||
"node developer",
|
||||
"skills for frontend development",
|
||||
"learn frontend development",
|
||||
"what is frontend development",
|
||||
"frontend developer quiz",
|
||||
"frontend developer interview questions"
|
||||
]
|
||||
},
|
||||
"title": "Frontend Developer",
|
||||
"description": "Step by step guide to becoming a modern frontend developer in 2022",
|
||||
"featuredTitle": "Frontend",
|
||||
"featuredDescription": "Step by step guide to becoming a frontend developer in 2022",
|
||||
"type": "role",
|
||||
"author": {
|
||||
"name": "Kamran Ahmed",
|
||||
"url": "https://twitter.com/kamranahmedse"
|
||||
},
|
||||
"featured": true,
|
||||
"imageUrl": "/roadmaps/frontend.png",
|
||||
"jsonUrl": "/project/frontend.json",
|
||||
"resourcesPath": "/roadmaps/100-frontend/resources.md",
|
||||
"pdfUrl": "/pdfs/frontend.pdf",
|
||||
"contentPathsFilePath": "/roadmaps/100-frontend/content-paths.json",
|
||||
"id": "frontend",
|
||||
"metaPath": "/roadmaps/100-frontend/meta.json",
|
||||
"isUpcoming": false
|
||||
},
|
||||
{
|
||||
"seo": {
|
||||
"title": "Backend Developer Roadmap: Learn to become a modern backend developer",
|
||||
"description": "Learn to become a modern backend developer using this roadmap. Community driven, articles, resources, guides, interview questions, quizzes for modern backend development.",
|
||||
"keywords": [
|
||||
"backend roadmap 2022",
|
||||
"backend developer roadmap 2022",
|
||||
"guide to becoming a developer",
|
||||
"guide to becoming a backend developer",
|
||||
"backend roadmap",
|
||||
"backend developer",
|
||||
"backend engineer",
|
||||
"backend skills",
|
||||
"backend development",
|
||||
"javascript developer",
|
||||
"backend development skills",
|
||||
"backend development skills test",
|
||||
"backend engineer roadmap",
|
||||
"backend developer roadmap",
|
||||
"become a backend developer",
|
||||
"backend developer career path",
|
||||
"javascript developer",
|
||||
"modern javascript developer",
|
||||
"node developer",
|
||||
"skills for backend development",
|
||||
"learn backend development",
|
||||
"what is backend development",
|
||||
"backend developer quiz",
|
||||
"backend developer interview questions"
|
||||
]
|
||||
},
|
||||
"title": "Backend Developer",
|
||||
"description": "Step by step guide to becoming a modern backend developer in 2022",
|
||||
"type": "role",
|
||||
"featuredTitle": "Backend",
|
||||
"featuredDescription": "Step by step guide to becoming a backend developer in 2022",
|
||||
"featured": true,
|
||||
"imageUrl": "/roadmaps/backend.png",
|
||||
"jsonUrl": "/project/backend.json",
|
||||
"resourcesPath": "/roadmaps/101-backend/resources.md",
|
||||
"author": {
|
||||
"name": "Kamran Ahmed",
|
||||
"url": "https://twitter.com/kamranahmedse"
|
||||
},
|
||||
"pdfUrl": "/pdfs/backend.pdf",
|
||||
"contentPathsFilePath": "/roadmaps/101-backend/content-paths.json",
|
||||
"id": "backend",
|
||||
"metaPath": "/roadmaps/101-backend/meta.json",
|
||||
"isUpcoming": false
|
||||
},
|
||||
{
|
||||
"seo": {
|
||||
"title": "DevOps Roadmap: Learn to become a DevOps Engineer or SRE",
|
||||
"description": "Community driven, articles, resources, guides, interview questions, quizzes for DevOps. Learn to become a modern DevOps engineer by following the steps, skills, resources and guides listed in this roadmap.",
|
||||
"keywords": [
|
||||
"devops roadmap 2022",
|
||||
"sre roadmap 2022",
|
||||
"operations roadmap 2022",
|
||||
"guide to becoming a devops enginer",
|
||||
"devops roadmap",
|
||||
"sre roadmap",
|
||||
"site reliability engineer roadmap",
|
||||
"operations roles",
|
||||
"become devops",
|
||||
"devops skills",
|
||||
"modern devops skills",
|
||||
"devops skills test",
|
||||
"skills for devops",
|
||||
"learn devops",
|
||||
"what is devops",
|
||||
"what is sre",
|
||||
"devops quiz",
|
||||
"devops interview questions"
|
||||
]
|
||||
},
|
||||
"title": "DevOps Roadmap",
|
||||
"description": "Step by step guide for DevOps, SRE or any other Operations Role in 2022",
|
||||
"featuredTitle": "DevOps",
|
||||
"type": "role",
|
||||
"featuredDescription": "Step by step guide for DevOps or operations role in 2022",
|
||||
"featured": true,
|
||||
"imageUrl": "/roadmaps/devops.png",
|
||||
"jsonUrl": "/project/devops.json",
|
||||
"resourcesPath": "/roadmaps/102-devops/resources.md",
|
||||
"versions": [
|
||||
"latest",
|
||||
"2018",
|
||||
"2017"
|
||||
],
|
||||
"author": {
|
||||
"name": "Kamran Ahmed",
|
||||
"url": "https://twitter.com/kamranahmedse"
|
||||
},
|
||||
"pdfUrl": "/pdfs/devops.pdf",
|
||||
"contentPathsFilePath": "/roadmaps/102-devops/content-paths.json",
|
||||
"id": "devops",
|
||||
"metaPath": "/roadmaps/102-devops/meta.json",
|
||||
"isUpcoming": false
|
||||
},
|
||||
{
|
||||
"seo": {
|
||||
"title": "React Developer Roadmap: Learn to become a React developer",
|
||||
"description": "Community driven, articles, resources, guides, interview questions, quizzes for react development. Learn to become a modern React developer by following the steps, skills, resources and guides listed in this roadmap.",
|
||||
"keywords": [
|
||||
"react roadmap 2022",
|
||||
"react developer roadmap 2022",
|
||||
"guide to becoming a react developer",
|
||||
"react developer roadmap",
|
||||
"react roadmap",
|
||||
"become react developer",
|
||||
"react developer skills",
|
||||
"react skills test",
|
||||
"skills for react development",
|
||||
"learn react development",
|
||||
"what is react",
|
||||
"react quiz",
|
||||
"react interview questions"
|
||||
]
|
||||
},
|
||||
"title": "React Developer",
|
||||
"description": "Everything that is there to learn about React and the ecosystem in 2022.",
|
||||
"featuredTitle": "React",
|
||||
"type": "tool",
|
||||
"featuredDescription": "Step by step guide to become a React Developer in 2022",
|
||||
"isTextHeavy": false,
|
||||
"isCommunity": false,
|
||||
"featured": true,
|
||||
"resourcesPath": "/roadmaps/103-react/resources.md",
|
||||
"jsonUrl": "/project/react.json",
|
||||
"versions": [
|
||||
"latest",
|
||||
"2018",
|
||||
"2017"
|
||||
],
|
||||
"author": {
|
||||
"name": "Kamran Ahmed",
|
||||
"url": "https://twitter.com/kamranahmedse"
|
||||
},
|
||||
"pdfUrl": "/pdfs/react.pdf",
|
||||
"contentPathsFilePath": "/roadmaps/103-react/content-paths.json",
|
||||
"id": "react",
|
||||
"metaPath": "/roadmaps/103-react/meta.json",
|
||||
"isUpcoming": false
|
||||
},
|
||||
{
|
||||
"seo": {
|
||||
"title": "Angular Developer Roadmap: Learn to become a Angular developer",
|
||||
"description": "Community driven, articles, resources, guides, interview questions, quizzes for angular development. Learn to become a modern Angular developer by following the steps, skills, resources and guides listed in this roadmap.",
|
||||
"keywords": [
|
||||
"guide to becoming a angular developer",
|
||||
"angular developer roadmap",
|
||||
"angular roadmap",
|
||||
"become angular developer",
|
||||
"angular developer skills",
|
||||
"angular skills test",
|
||||
"skills for angular development",
|
||||
"learn angular development",
|
||||
"what is angular",
|
||||
"angular quiz",
|
||||
"angular interview questions"
|
||||
]
|
||||
},
|
||||
"title": "Angular Developer",
|
||||
"description": "Everything that is there to learn about Angular and the ecosystem in 2022.",
|
||||
"featuredTitle": "Angular",
|
||||
"type": "tool",
|
||||
"featuredDescription": "Step by step guide to become a Angular Developer in 2022",
|
||||
"isTextHeavy": false,
|
||||
"isCommunity": false,
|
||||
"featured": true,
|
||||
"jsonUrl": "/project/angular.json",
|
||||
"landingPath": "/roadmaps/104-angular/landscape.md",
|
||||
"resourcesPath": "/roadmaps/104-angular/resources.md",
|
||||
"versions": [
|
||||
"latest",
|
||||
"2018",
|
||||
"2017"
|
||||
],
|
||||
"author": {
|
||||
"name": "Kamran Ahmed",
|
||||
"url": "https://twitter.com/kamranahmedse"
|
||||
},
|
||||
"pdfUrl": "/pdfs/angular.pdf",
|
||||
"id": "angular",
|
||||
"metaPath": "/roadmaps/104-angular/meta.json",
|
||||
"isUpcoming": false
|
||||
},
|
||||
{
|
||||
"seo": {
|
||||
"title": "Vue Developer Roadmap: Learn to become a Vue developer",
|
||||
"description": "Community driven, articles, resources, guides, interview questions, quizzes for vue development. Learn to become a modern Vue developer by following the steps, skills, resources and guides listed in this roadmap.",
|
||||
"keywords": [
|
||||
"guide to becoming a vue developer",
|
||||
"guide to becoming a vue.js developer",
|
||||
"vue developer roadmap",
|
||||
"vue.js developer roadmap",
|
||||
"vue roadmap",
|
||||
"vue.js roadmap",
|
||||
"become vue developer",
|
||||
"become vue.js developer",
|
||||
"vue developer skills",
|
||||
"vue.js developer skills",
|
||||
"vue skills test",
|
||||
"vue.js skills test",
|
||||
"skills for vue development",
|
||||
"skills for vue.js development",
|
||||
"learn vue development",
|
||||
"learn vue.js development",
|
||||
"what is vue",
|
||||
"what is vue.js",
|
||||
"vue quiz",
|
||||
"vue.js quiz",
|
||||
"vue interview questions",
|
||||
"vue.js interview questions"
|
||||
]
|
||||
},
|
||||
"title": "Vue Developer",
|
||||
"description": "Everything that is there to learn about Vue and the ecosystem in 2022.",
|
||||
"featuredTitle": "Vue",
|
||||
"type": "tool",
|
||||
"featuredDescription": "Step by step guide to become a Vue Developer in 2022",
|
||||
"isTextHeavy": false,
|
||||
"isCommunity": false,
|
||||
"featured": true,
|
||||
"jsonUrl": "/project/vue.json",
|
||||
"resourcesPath": "/roadmaps/105-vue/resources.md",
|
||||
"versions": [
|
||||
"latest",
|
||||
"2018",
|
||||
"2017"
|
||||
],
|
||||
"author": {
|
||||
"name": "Kamran Ahmed",
|
||||
"url": "https://twitter.com/kamranahmedse"
|
||||
},
|
||||
"pdfUrl": "/pdfs/vue.pdf",
|
||||
"contentPathsFilePath": "/roadmaps/105-vue/content-paths.json",
|
||||
"id": "vue",
|
||||
"metaPath": "/roadmaps/105-vue/meta.json",
|
||||
"isUpcoming": false
|
||||
},
|
||||
{
|
||||
"seo": {
|
||||
"title": "Android Developer Roadmap: Learn to become an Android developer",
|
||||
"description": "Community driven, articles, resources, guides, interview questions, quizzes for android development. Learn to become a modern Android developer by following the steps, skills, resources and guides listed in this roadmap.",
|
||||
"keywords": [
|
||||
"guide to becoming an android developer",
|
||||
"android developer roadmap",
|
||||
"android roadmap",
|
||||
"become android developer",
|
||||
"android developer skills",
|
||||
"android skills test",
|
||||
"skills for android development",
|
||||
"learn android development",
|
||||
"what is android",
|
||||
"android quiz",
|
||||
"android interview questions"
|
||||
]
|
||||
},
|
||||
"title": "Android Developer",
|
||||
"description": "Step by step guide to becoming an Android developer in 2022",
|
||||
"featuredTitle": "Android",
|
||||
"type": "role",
|
||||
"featuredDescription": "Step by step guide to becoming an Android Developer in 2022",
|
||||
"isTextHeavy": true,
|
||||
"isCommunity": false,
|
||||
"featured": true,
|
||||
"jsonUrl": "/project/android.json",
|
||||
"landingPath": "/roadmaps/106-android/landscape.md",
|
||||
"resourcesPath": "/roadmaps/106-android/resources.md",
|
||||
"versions": [
|
||||
"latest",
|
||||
"2018",
|
||||
"2017"
|
||||
],
|
||||
"author": {
|
||||
"name": "Kamran Ahmed",
|
||||
"url": "https://twitter.com/kamranahmedse"
|
||||
},
|
||||
"id": "android",
|
||||
"metaPath": "/roadmaps/106-android/meta.json",
|
||||
"isUpcoming": false
|
||||
},
|
||||
{
|
||||
"seo": {
|
||||
"title": "Learn to become a modern Python developer",
|
||||
"description": "Community driven, articles, resources, guides, interview questions, quizzes for python development. Learn to become a modern Python developer by following the steps, skills, resources and guides listed in this roadmap.",
|
||||
"keywords": [
|
||||
"guide to becoming an python developer",
|
||||
"python developer roadmap",
|
||||
"python roadmap",
|
||||
"become python developer",
|
||||
"python developer skills",
|
||||
"python skills test",
|
||||
"skills for python development",
|
||||
"learn python development",
|
||||
"what is python",
|
||||
"python quiz",
|
||||
"python interview questions"
|
||||
]
|
||||
},
|
||||
"title": "Python Developer",
|
||||
"description": "Step by step guide to becoming a Python developer in 2022",
|
||||
"featuredTitle": "Python",
|
||||
"type": "tool",
|
||||
"featuredDescription": "Step by step guide to becoming a Python Developer in 2022",
|
||||
"isTextHeavy": true,
|
||||
"isCommunity": false,
|
||||
"featured": true,
|
||||
"jsonUrl": "/project/python.json",
|
||||
"resourcesPath": "/roadmaps/107-python/resources.md",
|
||||
"landingPath": "/roadmaps/107-python/landscape.md",
|
||||
"pdfUrl": "/pdfs/python.pdf",
|
||||
"versions": [
|
||||
"latest"
|
||||
],
|
||||
"author": {
|
||||
"name": "Kamran Ahmed",
|
||||
"url": "https://twitter.com/kamranahmedse"
|
||||
},
|
||||
"id": "python",
|
||||
"metaPath": "/roadmaps/107-python/meta.json",
|
||||
"isUpcoming": false
|
||||
},
|
||||
{
|
||||
"seo": {
|
||||
"title": "Learn to become a Go developer",
|
||||
"description": "Community driven, articles, resources, guides, interview questions, quizzes for Go development. Learn to become a modern React developer by following the steps, skills, resources and guides listed in this roadmap.",
|
||||
"keywords": [
|
||||
"guide to becoming a golang developer",
|
||||
"guide to becoming a go developer",
|
||||
"golang developer",
|
||||
"go developer",
|
||||
"guide to golang",
|
||||
"guide to go",
|
||||
"golang roadmap",
|
||||
"go roadmap",
|
||||
"golang skills",
|
||||
"go skills",
|
||||
"golang skills test",
|
||||
"go skills test",
|
||||
"skills for golang",
|
||||
"skills for go",
|
||||
"cloud development",
|
||||
"what is golang",
|
||||
"what is go",
|
||||
"golang quiz",
|
||||
"go quiz",
|
||||
"golang interview questions",
|
||||
"go interview questions"
|
||||
]
|
||||
},
|
||||
"title": "Go Developer",
|
||||
"description": "Step by step guide to becoming a Go developer in 2022",
|
||||
"featuredTitle": "Go",
|
||||
"type": "tool",
|
||||
"featuredDescription": "Step by step guide to becoming a Go developer in 2022",
|
||||
"isTextHeavy": false,
|
||||
"isCommunity": false,
|
||||
"isUpcoming": false,
|
||||
"featured": true,
|
||||
"jsonUrl": "/project/golang.json",
|
||||
"landingPath": "/roadmaps/108-golang/landscape.md",
|
||||
"resourcesPath": "/roadmaps/108-golang/resources.md",
|
||||
"pdfUrl": "/pdfs/go.pdf",
|
||||
"versions": [
|
||||
"latest",
|
||||
"2018",
|
||||
"2017"
|
||||
],
|
||||
"author": {
|
||||
"name": "Kamran Ahmed",
|
||||
"url": "https://twitter.com/kamranahmedse"
|
||||
},
|
||||
"id": "golang",
|
||||
"metaPath": "/roadmaps/108-golang/meta.json"
|
||||
},
|
||||
{
|
||||
"seo": {
|
||||
"title": "Learn to become a modern Java developer",
|
||||
"description": "Community driven, articles, resources, guides, interview questions, quizzes for java development. Learn to become a modern Java developer by following the steps, skills, resources and guides listed in this roadmap.",
|
||||
"keywords": [
|
||||
"guide to becoming a developer",
|
||||
"guide to becoming a java developer",
|
||||
"java developer",
|
||||
"java engineer",
|
||||
"java skills",
|
||||
"guide to java",
|
||||
"java roadmap",
|
||||
"java skills",
|
||||
"java skills test",
|
||||
"skills for java",
|
||||
"cloud development",
|
||||
"what is java",
|
||||
"java quiz",
|
||||
"java interview questions",
|
||||
"java engineer roadmap",
|
||||
"java developer roadmap",
|
||||
"become a java developer",
|
||||
"java developer career path",
|
||||
"java developer",
|
||||
"modern java developer"
|
||||
]
|
||||
},
|
||||
"title": "Java Developer",
|
||||
"description": "Step by step guide to becoming a Java developer in 2022",
|
||||
"featuredTitle": "Java",
|
||||
"type": "tool",
|
||||
"featuredDescription": "Step by step guide to becoming a Java Developer in 2022",
|
||||
"isTextHeavy": false,
|
||||
"isCommunity": false,
|
||||
"isUpcoming": false,
|
||||
"featured": true,
|
||||
"jsonUrl": "/project/java.json",
|
||||
"landingPath": "/roadmaps/109-java/landscape.md",
|
||||
"resourcesPath": "/roadmaps/109-java/resources.md",
|
||||
"versions": [
|
||||
"latest",
|
||||
"2018",
|
||||
"2017"
|
||||
],
|
||||
"author": {
|
||||
"name": "Kamran Ahmed",
|
||||
"url": "https://twitter.com/kamranahmedse"
|
||||
},
|
||||
"pdfUrl": "/pdfs/java.pdf",
|
||||
"id": "java",
|
||||
"metaPath": "/roadmaps/109-java/meta.json"
|
||||
},
|
||||
{
|
||||
"seo": {
|
||||
"title": "DBA Roadmap: Learn to become a database administrator with PostgreSQL",
|
||||
"description": "Community driven, articles, resources, guides, interview questions, quizzes for DevOps. Learn to become a modern DevOps engineer by following the steps, skills, resources and guides listed in this roadmap.",
|
||||
"keywords": [
|
||||
"guide to becoming a database administrator",
|
||||
"guide to becoming a DBA",
|
||||
"dba roadmap",
|
||||
"db administrator roadmap",
|
||||
"database administrator roadmap",
|
||||
"postgresql roadmap",
|
||||
"dba skills",
|
||||
"db administrator skills",
|
||||
"become dba",
|
||||
"postgresql skills",
|
||||
"modern dba skills",
|
||||
"dba skills test",
|
||||
"skills for dba",
|
||||
"skills for database administrator",
|
||||
"learn dba",
|
||||
"what is dba",
|
||||
"database administrator quiz",
|
||||
"dba interview questions"
|
||||
]
|
||||
},
|
||||
"title": "PostgreSQL DBA",
|
||||
"description": "Step by step guide to becoming a modern PostgreSQL DB Administrator in 2022",
|
||||
"featuredTitle": "DBA",
|
||||
"type": "role",
|
||||
"featuredDescription": "Step by step guide to become a PostgreSQL DBA in 2022",
|
||||
"landingPath": "/roadmaps/110-postgresql-dba/landscape.md",
|
||||
"resourcesPath": "/roadmaps/110-postgresql-dba/resources.md",
|
||||
"author": {
|
||||
"name": "Alexey Lesovsky",
|
||||
"url": "https://github.com/lesovsky"
|
||||
},
|
||||
"isCommunity": false,
|
||||
"isTextHeavy": true,
|
||||
"featured": true,
|
||||
"detailed": false,
|
||||
"versions": [],
|
||||
"id": "postgresql-dba",
|
||||
"metaPath": "/roadmaps/110-postgresql-dba/meta.json",
|
||||
"isUpcoming": false
|
||||
},
|
||||
{
|
||||
"seo": {
|
||||
"title": "QA Roadmap: Learn to become a modern QA engineer",
|
||||
"description": "Community driven, articles, resources, guides, interview questions, quizzes for modern QA development. Learn to become a modern QA engineer by following the steps, skills, resources and guides listed in this roadmap.",
|
||||
"keywords": [
|
||||
"guide to becoming a QA engineer",
|
||||
"QA engineer",
|
||||
"QA skills",
|
||||
"QA development skills",
|
||||
"QA development skills test",
|
||||
"QA engineer roadmap",
|
||||
"become a QA engineer",
|
||||
"QA engineer career path",
|
||||
"skills for QA development",
|
||||
"what is QA engineer",
|
||||
"QA engineer quiz",
|
||||
"QA engineer interview questions"
|
||||
]
|
||||
},
|
||||
"title": "QA Engineer",
|
||||
"description": "Steps to follow in order to become a modern QA Engineer in 2022",
|
||||
"featuredTitle": "QA",
|
||||
"type": "role",
|
||||
"featuredDescription": "Step by step guide to becoming a modern QA Engineer in 2022",
|
||||
"isUpcoming": true,
|
||||
"featured": true,
|
||||
"landingPath": "/roadmaps/112-qa/landscape.md",
|
||||
"resourcesPath": "/roadmaps/112-qa/resources.md",
|
||||
"author": {
|
||||
"name": "Anas Fitiani",
|
||||
"url": "https://github.com/anas-qa"
|
||||
},
|
||||
"id": "qa",
|
||||
"metaPath": "/roadmaps/112-qa/meta.json"
|
||||
}
|
||||
]
|
||||
136
content/roadmaps/100-frontend/content-paths.json
Normal file
@@ -0,0 +1,136 @@
|
||||
{
|
||||
"home": "/roadmaps/100-frontend/content/readme.md",
|
||||
"internet": "/roadmaps/100-frontend/content/100-internet/readme.md",
|
||||
"internet:how-does-the-internet-work": "/roadmaps/100-frontend/content/100-internet/100-how-does-the-internet-work.md",
|
||||
"internet:what-is-http": "/roadmaps/100-frontend/content/100-internet/101-what-is-http.md",
|
||||
"internet:browsers-and-how-they-work": "/roadmaps/100-frontend/content/100-internet/102-browsers-and-how-they-work.md",
|
||||
"internet:dns-and-how-it-works": "/roadmaps/100-frontend/content/100-internet/103-dns-and-how-it-works.md",
|
||||
"internet:what-is-domain-name": "/roadmaps/100-frontend/content/100-internet/104-what-is-domain-name.md",
|
||||
"internet:what-is-hosting": "/roadmaps/100-frontend/content/100-internet/105-what-is-hosting.md",
|
||||
"html": "/roadmaps/100-frontend/content/101-html/readme.md",
|
||||
"html:learn-the-basics": "/roadmaps/100-frontend/content/101-html/100-learn-the-basics.md",
|
||||
"html:writing-semantic-html": "/roadmaps/100-frontend/content/101-html/101-writing-semantic-html.md",
|
||||
"html:forms-and-validations": "/roadmaps/100-frontend/content/101-html/102-forms-and-validations.md",
|
||||
"html:conventions-and-best-practices": "/roadmaps/100-frontend/content/101-html/103-conventions-and-best-practices.md",
|
||||
"html:accessibility": "/roadmaps/100-frontend/content/101-html/104-accessibility.md",
|
||||
"html:seo-basics": "/roadmaps/100-frontend/content/101-html/105-seo-basics.md",
|
||||
"css": "/roadmaps/100-frontend/content/102-css/readme.md",
|
||||
"css:learn-the-basics": "/roadmaps/100-frontend/content/102-css/100-learn-the-basics.md",
|
||||
"css:making-layouts": "/roadmaps/100-frontend/content/102-css/101-making-layouts.md",
|
||||
"css:responsive-design-and-media-queries": "/roadmaps/100-frontend/content/102-css/102-responsive-design-and-media-queries.md",
|
||||
"javascript": "/roadmaps/100-frontend/content/103-javascript/readme.md",
|
||||
"javascript:syntax-and-basic-constructs": "/roadmaps/100-frontend/content/103-javascript/100-syntax-and-basic-constructs.md",
|
||||
"javascript:learn-dom-manipulation": "/roadmaps/100-frontend/content/103-javascript/101-learn-dom-manipulation.md",
|
||||
"javascript:learn-fetch-api-ajax-xhr": "/roadmaps/100-frontend/content/103-javascript/102-learn-fetch-api-ajax-xhr.md",
|
||||
"javascript:es6-and-modular-javascript": "/roadmaps/100-frontend/content/103-javascript/103-es6-and-modular-javascript.md",
|
||||
"javascript:concepts": "/roadmaps/100-frontend/content/103-javascript/104-concepts.md",
|
||||
"version-control-systems": "/roadmaps/100-frontend/content/104-version-control-systems/readme.md",
|
||||
"version-control-systems:basic-usage-of-git": "/roadmaps/100-frontend/content/104-version-control-systems/100-basic-usage-of-git.md",
|
||||
"repo-hosting-services": "/roadmaps/100-frontend/content/105-repo-hosting-services/readme.md",
|
||||
"repo-hosting-services:github": "/roadmaps/100-frontend/content/105-repo-hosting-services/100-github.md",
|
||||
"repo-hosting-services:gitlab": "/roadmaps/100-frontend/content/105-repo-hosting-services/101-gitlab.md",
|
||||
"repo-hosting-services:bitbucket": "/roadmaps/100-frontend/content/105-repo-hosting-services/102-bitbucket.md",
|
||||
"web-security-knowledge": "/roadmaps/100-frontend/content/106-web-security-knowledge/readme.md",
|
||||
"web-security-knowledge:cors": "/roadmaps/100-frontend/content/106-web-security-knowledge/102-cors.md",
|
||||
"web-security-knowledge:https": "/roadmaps/100-frontend/content/106-web-security-knowledge/100-https.md",
|
||||
"web-security-knowledge:content-security-policy": "/roadmaps/100-frontend/content/106-web-security-knowledge/101-content-security-policy.md",
|
||||
"web-security-knowledge:owasp-security-risks": "/roadmaps/100-frontend/content/106-web-security-knowledge/103-owasp-security-risks.md",
|
||||
"package-managers": "/roadmaps/100-frontend/content/107-package-managers/readme.md",
|
||||
"package-managers:npm": "/roadmaps/100-frontend/content/107-package-managers/100-npm.md",
|
||||
"package-managers:yarn": "/roadmaps/100-frontend/content/107-package-managers/101-yarn.md",
|
||||
"css-architecture": "/roadmaps/100-frontend/content/108-css-architecture/readme.md",
|
||||
"css-architecture:bem": "/roadmaps/100-frontend/content/108-css-architecture/100-bem.md",
|
||||
"css-architecture:oocss": "/roadmaps/100-frontend/content/108-css-architecture/101-oocss.md",
|
||||
"css-architecture:smacss": "/roadmaps/100-frontend/content/108-css-architecture/102-smacss.md",
|
||||
"css-preprocessors": "/roadmaps/100-frontend/content/109-css-preprocessors/readme.md",
|
||||
"css-preprocessors:sass": "/roadmaps/100-frontend/content/109-css-preprocessors/100-sass.md",
|
||||
"css-preprocessors:postcss": "/roadmaps/100-frontend/content/109-css-preprocessors/101-postcss.md",
|
||||
"css-preprocessors:less": "/roadmaps/100-frontend/content/109-css-preprocessors/102-less.md",
|
||||
"build-tools": "/roadmaps/100-frontend/content/110-build-tools/readme.md",
|
||||
"build-tools:task-runners": "/roadmaps/100-frontend/content/110-build-tools/100-task-runners/readme.md",
|
||||
"build-tools:task-runners:npm-scripts": "/roadmaps/100-frontend/content/110-build-tools/100-task-runners/100-npm-scripts.md",
|
||||
"build-tools:linters-formatters": "/roadmaps/100-frontend/content/110-build-tools/102-linters-formatters/readme.md",
|
||||
"build-tools:linters-formatters:prettier": "/roadmaps/100-frontend/content/110-build-tools/102-linters-formatters/100-prettier.md",
|
||||
"build-tools:linters-formatters:eslint": "/roadmaps/100-frontend/content/110-build-tools/102-linters-formatters/101-eslint.md",
|
||||
"build-tools:linters-formatters:standardjs": "/roadmaps/100-frontend/content/110-build-tools/102-linters-formatters/102-standardjs.md",
|
||||
"build-tools:module-bundlers": "/roadmaps/100-frontend/content/110-build-tools/101-module-bundlers/readme.md",
|
||||
"build-tools:module-bundlers:webpack": "/roadmaps/100-frontend/content/110-build-tools/101-module-bundlers/100-webpack.md",
|
||||
"build-tools:module-bundlers:esbuild": "/roadmaps/100-frontend/content/110-build-tools/101-module-bundlers/101-esbuild.md",
|
||||
"build-tools:module-bundlers:rollup": "/roadmaps/100-frontend/content/110-build-tools/101-module-bundlers/102-rollup.md",
|
||||
"build-tools:module-bundlers:parcel": "/roadmaps/100-frontend/content/110-build-tools/101-module-bundlers/103-parcel.md",
|
||||
"build-tools:module-bundlers:vite": "/roadmaps/100-frontend/content/110-build-tools/101-module-bundlers/104-vite.md",
|
||||
"pick-a-framework": "/roadmaps/100-frontend/content/111-pick-a-framework/readme.md",
|
||||
"pick-a-framework:react-js": "/roadmaps/100-frontend/content/111-pick-a-framework/100-react-js/readme.md",
|
||||
"pick-a-framework:react-js:recoil": "/roadmaps/100-frontend/content/111-pick-a-framework/100-react-js/102-recoil.md",
|
||||
"pick-a-framework:react-js:redux": "/roadmaps/100-frontend/content/111-pick-a-framework/100-react-js/100-redux.md",
|
||||
"pick-a-framework:react-js:mobx": "/roadmaps/100-frontend/content/111-pick-a-framework/100-react-js/101-mobx.md",
|
||||
"pick-a-framework:angular": "/roadmaps/100-frontend/content/111-pick-a-framework/101-angular/readme.md",
|
||||
"pick-a-framework:angular:rxjs": "/roadmaps/100-frontend/content/111-pick-a-framework/101-angular/100-rxjs.md",
|
||||
"pick-a-framework:angular:ngrx": "/roadmaps/100-frontend/content/111-pick-a-framework/101-angular/101-ngrx.md",
|
||||
"pick-a-framework:vue-js": "/roadmaps/100-frontend/content/111-pick-a-framework/102-vue-js/readme.md",
|
||||
"pick-a-framework:vue-js:pinia": "/roadmaps/100-frontend/content/111-pick-a-framework/102-vue-js/100-pinia.md",
|
||||
"modern-css": "/roadmaps/100-frontend/content/112-modern-css/readme.md",
|
||||
"modern-css:styled-components": "/roadmaps/100-frontend/content/112-modern-css/100-styled-components.md",
|
||||
"modern-css:css-modules": "/roadmaps/100-frontend/content/112-modern-css/101-css-modules.md",
|
||||
"modern-css:styled-jsx": "/roadmaps/100-frontend/content/112-modern-css/102-styled-jsx.md",
|
||||
"modern-css:emotion": "/roadmaps/100-frontend/content/112-modern-css/103-emotion.md",
|
||||
"web-components": "/roadmaps/100-frontend/content/113-web-components/readme.md",
|
||||
"web-components:html-templates": "/roadmaps/100-frontend/content/113-web-components/100-html-templates.md",
|
||||
"web-components:custom-elements": "/roadmaps/100-frontend/content/113-web-components/101-custom-elements.md",
|
||||
"web-components:shadow-dom": "/roadmaps/100-frontend/content/113-web-components/102-shadow-dom.md",
|
||||
"css-frameworks": "/roadmaps/100-frontend/content/114-css-frameworks/readme.md",
|
||||
"css-frameworks:js-first": "/roadmaps/100-frontend/content/114-css-frameworks/114-js-first/readme.md",
|
||||
"css-frameworks:js-first:chakra-ui": "/roadmaps/100-frontend/content/114-css-frameworks/114-js-first/101-chakra-ui.md",
|
||||
"css-frameworks:js-first:material-ui": "/roadmaps/100-frontend/content/114-css-frameworks/114-js-first/102-material-ui.md",
|
||||
"css-frameworks:js-first:radix-ui": "/roadmaps/100-frontend/content/114-css-frameworks/114-js-first/103-radix-ui.md",
|
||||
"css-frameworks:js-first:tailwind-css": "/roadmaps/100-frontend/content/114-css-frameworks/114-js-first/100-tailwind-css.md",
|
||||
"css-frameworks:css-first": "/roadmaps/100-frontend/content/114-css-frameworks/114-css-first/readme.md",
|
||||
"css-frameworks:css-first:bootstrap": "/roadmaps/100-frontend/content/114-css-frameworks/114-css-first/100-bootstrap.md",
|
||||
"css-frameworks:css-first:bulma": "/roadmaps/100-frontend/content/114-css-frameworks/114-css-first/101-bulma.md",
|
||||
"testing-your-apps": "/roadmaps/100-frontend/content/115-testing-your-apps/readme.md",
|
||||
"testing-your-apps:jest": "/roadmaps/100-frontend/content/115-testing-your-apps/100-jest.md",
|
||||
"testing-your-apps:react-testing-library": "/roadmaps/100-frontend/content/115-testing-your-apps/101-react-testing-library.md",
|
||||
"testing-your-apps:cypress": "/roadmaps/100-frontend/content/115-testing-your-apps/102-cypress.md",
|
||||
"testing-your-apps:enzyme": "/roadmaps/100-frontend/content/115-testing-your-apps/103-enzyme.md",
|
||||
"testing-your-apps:other-options": "/roadmaps/100-frontend/content/115-testing-your-apps/104-other-options.md",
|
||||
"testing-your-apps:mocha": "/roadmaps/100-frontend/content/115-testing-your-apps/105-mocha.md",
|
||||
"testing-your-apps:chai": "/roadmaps/100-frontend/content/115-testing-your-apps/106-chai.md",
|
||||
"testing-your-apps:ava": "/roadmaps/100-frontend/content/115-testing-your-apps/107-ava.md",
|
||||
"testing-your-apps:jasmine": "/roadmaps/100-frontend/content/115-testing-your-apps/108-jasmine.md",
|
||||
"type-checkers": "/roadmaps/100-frontend/content/116-type-checkers/readme.md",
|
||||
"type-checkers:typescript": "/roadmaps/100-frontend/content/116-type-checkers/100-typescript.md",
|
||||
"type-checkers:flow": "/roadmaps/100-frontend/content/116-type-checkers/101-flow.md",
|
||||
"progressive-web-apps": "/roadmaps/100-frontend/content/117-progressive-web-apps/readme.md",
|
||||
"progressive-web-apps:performance": "/roadmaps/100-frontend/content/117-progressive-web-apps/101-performance.md",
|
||||
"progressive-web-apps:apis": "/roadmaps/100-frontend/content/117-progressive-web-apps/100-apis.md",
|
||||
"server-side-rendering": "/roadmaps/100-frontend/content/118-server-side-rendering/readme.md",
|
||||
"server-side-rendering:react-js": "/roadmaps/100-frontend/content/118-server-side-rendering/100-react-js/readme.md",
|
||||
"server-side-rendering:react-js:next-js": "/roadmaps/100-frontend/content/118-server-side-rendering/100-react-js/100-next-js.md",
|
||||
"server-side-rendering:react-js:after-js": "/roadmaps/100-frontend/content/118-server-side-rendering/100-react-js/101-after-js.md",
|
||||
"server-side-rendering:angular": "/roadmaps/100-frontend/content/118-server-side-rendering/101-angular/readme.md",
|
||||
"server-side-rendering:angular:universal": "/roadmaps/100-frontend/content/118-server-side-rendering/101-angular/100-universal.md",
|
||||
"server-side-rendering:vue-js": "/roadmaps/100-frontend/content/118-server-side-rendering/102-vue-js/readme.md",
|
||||
"server-side-rendering:vue-js:nuxt-js": "/roadmaps/100-frontend/content/118-server-side-rendering/102-vue-js/100-nuxt-js.md",
|
||||
"graphql": "/roadmaps/100-frontend/content/119-graphql/readme.md",
|
||||
"graphql:apollo": "/roadmaps/100-frontend/content/119-graphql/100-apollo.md",
|
||||
"graphql:relay-modern": "/roadmaps/100-frontend/content/119-graphql/101-relay-modern.md",
|
||||
"static-site-generators": "/roadmaps/100-frontend/content/120-static-site-generators/readme.md",
|
||||
"static-site-generators:next-js": "/roadmaps/100-frontend/content/120-static-site-generators/100-next-js.md",
|
||||
"static-site-generators:gatsbyjs": "/roadmaps/100-frontend/content/120-static-site-generators/101-gatsbyjs.md",
|
||||
"static-site-generators:nuxt-js": "/roadmaps/100-frontend/content/120-static-site-generators/102-nuxt-js.md",
|
||||
"static-site-generators:vuepress": "/roadmaps/100-frontend/content/120-static-site-generators/103-vuepress.md",
|
||||
"static-site-generators:jekyll": "/roadmaps/100-frontend/content/120-static-site-generators/104-jekyll.md",
|
||||
"static-site-generators:hugo": "/roadmaps/100-frontend/content/120-static-site-generators/105-hugo.md",
|
||||
"static-site-generators:gridsome": "/roadmaps/100-frontend/content/120-static-site-generators/106-gridsome.md",
|
||||
"static-site-generators:eleventy": "/roadmaps/100-frontend/content/120-static-site-generators/107-eleventy.md",
|
||||
"mobile-applications": "/roadmaps/100-frontend/content/121-mobile-applications/readme.md",
|
||||
"mobile-applications:react-native": "/roadmaps/100-frontend/content/121-mobile-applications/100-react-native.md",
|
||||
"mobile-applications:nativescript": "/roadmaps/100-frontend/content/121-mobile-applications/101-nativescript.md",
|
||||
"mobile-applications:flutter": "/roadmaps/100-frontend/content/121-mobile-applications/102-flutter.md",
|
||||
"mobile-applications:ionic": "/roadmaps/100-frontend/content/121-mobile-applications/103-ionic.md",
|
||||
"desktop-applications": "/roadmaps/100-frontend/content/122-desktop-applications/readme.md",
|
||||
"desktop-applications:electron": "/roadmaps/100-frontend/content/122-desktop-applications/100-electron.md",
|
||||
"desktop-applications:tauri": "/roadmaps/100-frontend/content/122-desktop-applications/101-tauri.md",
|
||||
"desktop-applications:proton-native": "/roadmaps/100-frontend/content/122-desktop-applications/102-proton-native.md",
|
||||
"web-assembly": "/roadmaps/100-frontend/content/123-web-assembly.md"
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
# Internet
|
||||
|
||||
The Internet is a global network of computers connected to each other which communicate through a standardized set of protocols.
|
||||
|
||||
<ResourceGroupTitle>Free Content</ResourceGroupTitle>
|
||||
<BadgeLink colorScheme='yellow' badgeText='Read' href='https://www.vox.com/2014/6/16/18076282/the-internet'>The Internet Explained</BadgeLink>
|
||||
<BadgeLink colorScheme='yellow' badgeText='Read' href='http://web.stanford.edu/class/msande91si/www-spr04/readings/week1/InternetWhitepaper.htm'>How Does the Internet Work?</BadgeLink>
|
||||
<BadgeLink colorScheme='yellow' badgeText='Read' href='https://developer.mozilla.org/en-US/docs/Learn/Common_questions/How_does_the_Internet_work'>How Does the Internet Work? MDN Docs</BadgeLink>
|
||||
<BadgeLink badgeText='Watch' href='/guides/what-is-internet'>Introduction to Internet</BadgeLink>
|
||||
<BadgeLink badgeText='Watch' href='https://www.youtube.com/watch?v=TNQsmPf24go'>How does the Internet work?</BadgeLink>
|
||||
<BadgeLink badgeText='Watch' href='https://www.youtube.com/watch?v=7_LPdttKXPc'>How the Internet Works in 5 Minutes</BadgeLink>
|
||||
@@ -0,0 +1,11 @@
|
||||
# What is HTTP?
|
||||
|
||||
HTTP is the `TCP/IP` based application layer communication protocol which standardizes how the client and server communicate with each other. It defines how the content is requested and transmitted across the internet.
|
||||
|
||||
<ResourceGroupTitle>Free Content</ResourceGroupTitle>
|
||||
<BadgeLink colorScheme='yellow' badgeText='Read' href='https://www.cloudflare.com/en-gb/learning/ddos/glossary/hypertext-transfer-protocol-http/'>What is HTTP?</BadgeLink>
|
||||
<BadgeLink colorScheme='yellow' badgeText='Read' href='https://howhttps.works'>How HTTPS Works ...in a comic!</BadgeLink>
|
||||
<BadgeLink colorScheme='yellow' badgeText='Read' href='https://developer.mozilla.org/en-US/docs/Web/HTTP/Overview'>An overview of HTTP</BadgeLink>
|
||||
<BadgeLink colorScheme='yellow' badgeText='Read' href='https://kamranahmed.info/blog/2016/08/13/http-in-depth'>Journey to HTTP/2</BadgeLink>
|
||||
<BadgeLink colorScheme='yellow' badgeText='Read' href='https://www.smashingmagazine.com/2021/08/http3-core-concepts-part1/'>HTTP/3 From A To Z: Core Concepts</BadgeLink>
|
||||
<BadgeLink badgeText='Watch' href='https://www.youtube.com/watch?v=iYM2zFP3Zn0'>HTTP Crash Course & Exploration</BadgeLink>
|
||||
@@ -0,0 +1,9 @@
|
||||
# Browsers
|
||||
|
||||
A web browser is a software application that enables a user to access and display web pages or other online content through its graphical user interface.
|
||||
|
||||
<ResourceGroupTitle>Free Content</ResourceGroupTitle>
|
||||
<BadgeLink colorScheme='yellow' badgeText='Read' href='https://www.html5rocks.com/en/tutorials/internals/howbrowserswork/'>How Browsers Work</BadgeLink>
|
||||
<BadgeLink colorScheme='yellow' badgeText='Read' href='https://www.browserstack.com/guide/browser-rendering-engine'>Role of Rendering Engine in Browsers</BadgeLink>
|
||||
<BadgeLink colorScheme='yellow' badgeText='Read' href='https://developer.mozilla.org/en-US/docs/Web/Performance/How_browsers_work'>Populating the Page: How Browsers Work</BadgeLink>
|
||||
<BadgeLink badgeText='Watch' href='https://www.youtube.com/watch?v=WjDrMKZWCt0'>How Do Web Browsers Work?</BadgeLink>
|
||||