Compare commits
163 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fa6e96731a | ||
|
|
f59dfda878 | ||
|
|
e15c62a8d8 | ||
|
|
f97c123ddb | ||
|
|
7fd287f004 | ||
|
|
f0bbdf561d | ||
|
|
a9c6bcc63f | ||
|
|
e1fc695d24 | ||
|
|
525e436b9d | ||
|
|
a98f972bef | ||
|
|
1c2fdfee37 | ||
|
|
8102086400 | ||
|
|
0366729a95 | ||
|
|
352d2af366 | ||
|
|
9b8c4039f0 | ||
|
|
262b647855 | ||
|
|
03017046f7 | ||
|
|
28d2671ddb | ||
|
|
86da1612da | ||
|
|
919465c9b6 | ||
|
|
b84ed74496 | ||
|
|
dc86c78bd1 | ||
|
|
45b38cc836 | ||
|
|
186532ef2d | ||
|
|
77858c5558 | ||
|
|
e9a98ffe3d | ||
|
|
2c06dd6f18 | ||
|
|
881d686233 | ||
|
|
a7deaafbc7 | ||
|
|
7c698f0b35 | ||
|
|
09a8c94392 | ||
|
|
35b9ded76e | ||
|
|
85c10b9d3f | ||
|
|
2675942c58 | ||
|
|
b3ad310f6f | ||
|
|
d41385b592 | ||
|
|
dfbab3919d | ||
|
|
e5c607164e | ||
|
|
49d1e91a64 | ||
|
|
19ea640705 | ||
|
|
f2d814813f | ||
|
|
f99ebf5f37 | ||
|
|
ef2c0cdd67 | ||
|
|
d658af7407 | ||
|
|
0f8f15f31c | ||
|
|
600467035a | ||
|
|
d25aee1313 | ||
|
|
0cc2a3aab7 | ||
|
|
75e6b9fb74 | ||
|
|
6e8a65fa7f | ||
|
|
01dce6b09a | ||
|
|
ceeed729c6 | ||
|
|
7f35af5bd2 | ||
|
|
c603195004 | ||
|
|
bac35e06c8 | ||
|
|
7add9cad87 | ||
|
|
16dae9b081 | ||
|
|
9f52f36c18 | ||
|
|
621d87fdb7 | ||
|
|
871e709076 | ||
|
|
1e3ae5d445 | ||
|
|
a344c1f89e | ||
|
|
ee742530d9 | ||
|
|
f102f4c873 | ||
|
|
eebeeee6b7 | ||
|
|
18842fd56f | ||
|
|
ea1e75b355 | ||
|
|
34ed51a0db | ||
|
|
4ce4bb92bb | ||
|
|
75ed86eafb | ||
|
|
a4bcfb4737 | ||
|
|
bfd3be403e | ||
|
|
bfd75229c1 | ||
|
|
91172b82d9 | ||
|
|
c9f416713a | ||
|
|
11eb4208e6 | ||
|
|
fbd387a30c | ||
|
|
018e2405f2 | ||
|
|
c631f1ef15 | ||
|
|
3ce4f97de7 | ||
|
|
77ad2c02ce | ||
|
|
ecf5a29e19 | ||
|
|
ec8632a3e9 | ||
|
|
98f91e24c1 | ||
|
|
49c5a1e1be | ||
|
|
9a00412049 | ||
|
|
03fd49bb29 | ||
|
|
8b607b9463 | ||
|
|
e3472171a5 | ||
|
|
5f1c6a4571 | ||
|
|
d05f00c0a8 | ||
|
|
0715bb7a02 | ||
|
|
00d1ddff0b | ||
|
|
0f99de91ae | ||
|
|
9cff223033 | ||
|
|
1d6021dd36 | ||
|
|
c4df5d8d04 | ||
|
|
9019f1cf27 | ||
|
|
d92b7efe00 | ||
|
|
daea45eeb2 | ||
|
|
8dcc5ebe23 | ||
|
|
d36138361d | ||
|
|
831b0d4bcd | ||
|
|
5d9fcd22be | ||
|
|
1c51ab403b | ||
|
|
c3c9e89302 | ||
|
|
e842c9f224 | ||
|
|
abeb0c5d14 | ||
|
|
18d1a685a9 | ||
|
|
9255050eee | ||
|
|
915f5fa479 | ||
|
|
24dcac6978 | ||
|
|
adef11ba8e | ||
|
|
4233caa909 | ||
|
|
8f5cad91d4 | ||
|
|
ac5121beda | ||
|
|
5460fbb484 | ||
|
|
ad71e15290 | ||
|
|
4aba0d31f6 | ||
|
|
19ad91709e | ||
|
|
74efbad6d0 | ||
|
|
0fd6a4d5e3 | ||
|
|
1c12da5495 | ||
|
|
c4c90477c5 | ||
|
|
a9aaeec047 | ||
|
|
8420f6ddb8 | ||
|
|
3a1ea75a55 | ||
|
|
b23c2cdc36 | ||
|
|
12c081b830 | ||
|
|
11492dcfbd | ||
|
|
de874ca20e | ||
|
|
92ab9c3867 | ||
|
|
12c1b7331b | ||
|
|
c98788425d | ||
|
|
a279fb90af | ||
|
|
4f868ad2ac | ||
|
|
522da1a3d7 | ||
|
|
e767f51182 | ||
|
|
3479686f44 | ||
|
|
fafab00edc | ||
|
|
fe7a3c3a85 | ||
|
|
252c9b11fe | ||
|
|
9ddc85dbe3 | ||
|
|
ee07fc3b3f | ||
|
|
18c6981958 | ||
|
|
f85b0efc33 | ||
|
|
da8925ab51 | ||
|
|
23f85d6c27 | ||
|
|
58d566983a | ||
|
|
6b59d21e2d | ||
|
|
f2b2e5a414 | ||
|
|
862e54b453 | ||
|
|
32983cb454 | ||
|
|
47c3c947ef | ||
|
|
20e7c16237 | ||
|
|
c512b7a29e | ||
|
|
5870251839 | ||
|
|
457015ae5b | ||
|
|
a19c480c19 | ||
|
|
2f397f0e99 | ||
|
|
c081599547 | ||
|
|
e3876b387e | ||
|
|
937e4061d6 |
@@ -309,7 +309,8 @@
|
||||
"profile": "https://github.com/Zeko369",
|
||||
"contributions": [
|
||||
"code",
|
||||
"doc"
|
||||
"doc",
|
||||
"test"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -625,7 +626,8 @@
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/17050715?v=4",
|
||||
"profile": "https://cloudnweb.dev/",
|
||||
"contributions": [
|
||||
"code"
|
||||
"code",
|
||||
"test"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -970,7 +972,9 @@
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/3496193?v=4",
|
||||
"profile": "https://twitter.com/dillonraphael",
|
||||
"contributions": [
|
||||
"code"
|
||||
"code",
|
||||
"test",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -2964,7 +2968,8 @@
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/12092?v=4",
|
||||
"profile": "http://zackhobson.com/",
|
||||
"contributions": [
|
||||
"code"
|
||||
"code",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -3065,7 +3070,8 @@
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/8077469?v=4",
|
||||
"profile": "https://andreas.fyi",
|
||||
"contributions": [
|
||||
"code"
|
||||
"code",
|
||||
"test"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -3397,6 +3403,448 @@
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "lovethebomb",
|
||||
"name": "Lucas Heymès",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1363056?v=4",
|
||||
"profile": "www.lucas.computer",
|
||||
"contributions": [
|
||||
"code",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "NorfeldtAbtion",
|
||||
"name": "Lasse Norfeldt",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/53769763?v=4",
|
||||
"profile": "https://github.com/NorfeldtAbtion",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "netwarex",
|
||||
"name": "Péter Nyári",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/6048614?v=4",
|
||||
"profile": "https://nyaripeter.hu/",
|
||||
"contributions": [
|
||||
"doc",
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "5minpause",
|
||||
"name": "Holger Frohloff",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/84148?v=4",
|
||||
"profile": "https://www.holgerfrohloff.de",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "basilk76",
|
||||
"name": "Basil Khan",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/45275512?v=4",
|
||||
"profile": "https://github.com/basilk76",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "danestves",
|
||||
"name": "Daniel Esteves",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/31737273?v=4",
|
||||
"profile": "https://danestves.com/",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "coryhouse",
|
||||
"name": "Cory House",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1688997?v=4",
|
||||
"profile": "http://www.bitnative.com",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "rockmanvnx6",
|
||||
"name": "Austin (Thang Pham)",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/16440123?v=4",
|
||||
"profile": "https://auspham.dev/",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "noxify",
|
||||
"name": "Marcus Reinhardt",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/521777?v=4",
|
||||
"profile": "https://jammeryhq.com",
|
||||
"contributions": [
|
||||
"doc",
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "davidchristie",
|
||||
"name": "David Christie",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/12044333?v=4",
|
||||
"profile": "https://github.com/davidchristie",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "ajanth97",
|
||||
"name": "Ajanth",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/50458502?v=4",
|
||||
"profile": "https://github.com/ajanth97",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "divpreet",
|
||||
"name": "Div",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/2805650?v=4",
|
||||
"profile": "https://github.com/divpreet",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "david-arteaga",
|
||||
"name": "David Arteaga",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/7199015?v=4",
|
||||
"profile": "https://github.com/david-arteaga",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "MukulKolpe",
|
||||
"name": "Mukul Kolpe",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/78664749?v=4",
|
||||
"profile": "https://github.com/MukulKolpe",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "skotchpine",
|
||||
"name": "tyler",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/13043909?v=4",
|
||||
"profile": "https://github.com/skotchpine",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "SofianeDjellouli",
|
||||
"name": "Sofiane Djellouli",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/38258952?v=4",
|
||||
"profile": "https://github.com/SofianeDjellouli",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "kreako",
|
||||
"name": "kreako",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/65113001?v=4",
|
||||
"profile": "https://github.com/kreako",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "sarahdayan",
|
||||
"name": "Sarah Dayan",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/5370675?v=4",
|
||||
"profile": "https://sarahdayan.dev",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "c-ciobanu",
|
||||
"name": "Cristi Ciobanu",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/33382714?v=4",
|
||||
"profile": "https://github.com/c-ciobanu",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "arpitdalal",
|
||||
"name": "Arpit Dalal",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/61059807?v=4",
|
||||
"profile": "https://arpitdalal.dev",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "robertrisch",
|
||||
"name": "robertrisch",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/73828816?v=4",
|
||||
"profile": "https://github.com/robertrisch",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "dineshgadge",
|
||||
"name": "Dinesh Gadge",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/186976?v=4",
|
||||
"profile": "https://github.com/dineshgadge",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "maltekiessling",
|
||||
"name": "Malte Kießling",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/30420110?v=4",
|
||||
"profile": "https://github.com/maltekiessling",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "ospfranco",
|
||||
"name": "Oscar Franco",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1634213?v=4",
|
||||
"profile": "ospfranco.com",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Nfinished",
|
||||
"name": "Adam Trager",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1719791?v=4",
|
||||
"profile": "adamtrager.com",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "shellord",
|
||||
"name": "saheenshoukath",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/2632896?v=4",
|
||||
"profile": "https://saheen.codes",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "husnuljahneer",
|
||||
"name": "Husnul Jahneer",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/54552763?v=4",
|
||||
"profile": "https://jahneer.me",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "ReykCS",
|
||||
"name": "Reyk",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/40463716?v=4",
|
||||
"profile": "https://github.com/ReykCS",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Lokprakash-babu",
|
||||
"name": "Lokprakash Babu",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/60031382?v=4",
|
||||
"profile": "https://github.com/Lokprakash-babu",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "eai04191",
|
||||
"name": "eai04191",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/3516343?v=4",
|
||||
"profile": "https://mizle.net",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "numanaral",
|
||||
"name": "Numan",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/25233323?v=4",
|
||||
"profile": "https://numanaral.github.io/?ref=github",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "jscyo",
|
||||
"name": "Joel Coutinho",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/6310783?v=4",
|
||||
"profile": "https://github.com/jscyo",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "davidbarker",
|
||||
"name": "David Barker",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1597139?v=4",
|
||||
"profile": "https://github.com/davidbarker",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "timfee",
|
||||
"name": "Tim Feeley",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/3246342?v=4",
|
||||
"profile": "http://www.timfeeley.com/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Caslus",
|
||||
"name": "lucas philippe",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/22855640?v=4",
|
||||
"profile": "https://github.com/Caslus",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "the-bayer",
|
||||
"name": "Blake Bayer",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/94391693?v=4",
|
||||
"profile": "https://github.com/the-bayer",
|
||||
"contributions": [
|
||||
"code",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "rmassie",
|
||||
"name": "R Massie",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/7375518?v=4",
|
||||
"profile": "https://github.com/rmassie",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "paulm17",
|
||||
"name": "Paul",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/387463?v=4",
|
||||
"profile": "https://github.com/paulm17",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "minho42",
|
||||
"name": "Min ho Kim",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/15278512?v=4",
|
||||
"profile": "https://minho42.com",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "webdeb",
|
||||
"name": "webdeb",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/14992140?v=4",
|
||||
"profile": "https://github.com/webdeb",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "iDavidB",
|
||||
"name": "David",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/32268383?v=4",
|
||||
"profile": "https://github.com/iDavidB",
|
||||
"contributions": [
|
||||
"doc",
|
||||
"code",
|
||||
"test"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "jakedee",
|
||||
"name": "Jake Dowie",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/5058625?v=4",
|
||||
"profile": "https://jdlt.co.uk",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "datner",
|
||||
"name": "Datner",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/22598347?v=4",
|
||||
"profile": "https://github.com/datner",
|
||||
"contributions": [
|
||||
"doc",
|
||||
"code",
|
||||
"test"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "remlse",
|
||||
"name": "remlse",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/54984957?v=4",
|
||||
"profile": "https://github.com/remlse",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "sergous",
|
||||
"name": "Sergei Smirnov",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/545151?v=4",
|
||||
"profile": "https://github.com/sergous",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Trancever",
|
||||
"name": "Dawid Urbaniak",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/18584155?v=4",
|
||||
"profile": "https://twitter.com/trensik",
|
||||
"contributions": [
|
||||
"doc",
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "SerekKiri",
|
||||
"name": "Kacper Potyrała",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/29735836?v=4",
|
||||
"profile": "kiri.dev",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "iojcde",
|
||||
"name": "Jeeho Ahn",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/31413538?v=4",
|
||||
"profile": "jcde.xyz",
|
||||
"contributions": [
|
||||
"doc",
|
||||
"tool"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
|
||||
4
.github/CODEOWNERS
vendored
4
.github/CODEOWNERS
vendored
@@ -1,8 +1,8 @@
|
||||
# https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners
|
||||
|
||||
* @flybayer @beerose
|
||||
* @beerose
|
||||
|
||||
# packages/cli/**/* @aem, @flybayer
|
||||
# packages/generator/**/* @aem @flybayer
|
||||
packages/generator/templates**/* @flybayer
|
||||
# packages/generator/templates**/* @flybayer
|
||||
# packages/installer/**/* @aem @flybayer
|
||||
|
||||
4
.github/workflows/compressed.yml
vendored
4
.github/workflows/compressed.yml
vendored
@@ -15,6 +15,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: "14"
|
||||
- name: Count size
|
||||
uses: preactjs/compressed-size-action@v2
|
||||
with:
|
||||
|
||||
65
.github/workflows/main.yml
vendored
65
.github/workflows/main.yml
vendored
@@ -30,9 +30,9 @@ jobs:
|
||||
path: |
|
||||
${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
**/node_modules
|
||||
key: ${{ runner.os }}-${{ runner.node_version}}-yarn-v13-${{ hashFiles('yarn.lock') }}
|
||||
key: ${{ runner.os }}-${{ runner.node_version}}-yarn-v14-${{ hashFiles('yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ runner.node_version}}-yarn-v13-
|
||||
${{ runner.os }}-${{ runner.node_version}}-yarn-v14-
|
||||
- name: Install dependencies
|
||||
run: yarn install --frozen-lockfile --silent
|
||||
env:
|
||||
@@ -74,9 +74,9 @@ jobs:
|
||||
path: |
|
||||
${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
**/node_modules
|
||||
key: ${{ runner.os }}-${{ runner.node_version}}-yarn-v13-${{ hashFiles('yarn.lock') }}
|
||||
key: ${{ runner.os }}-${{ runner.node_version}}-yarn-v14-${{ hashFiles('yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ runner.node_version}}-yarn-v13-
|
||||
${{ runner.os }}-${{ runner.node_version}}-yarn-v14-
|
||||
- run: yarn install --frozen-lockfile --check-files
|
||||
- name: Build Packages
|
||||
run: yarn build
|
||||
@@ -98,6 +98,10 @@ jobs:
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ runner.os }}-${{ github.sha }}
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: "14"
|
||||
- name: Setup kernel to increase watchers
|
||||
if: runner.os == 'Linux'
|
||||
run: echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p
|
||||
@@ -106,6 +110,33 @@ jobs:
|
||||
env:
|
||||
CI: true
|
||||
|
||||
testNextPackages:
|
||||
name: Next - Test Packages
|
||||
defaults:
|
||||
run:
|
||||
working-directory: nextjs
|
||||
needs: build-linux
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
BLITZ_TELEMETRY_DISABLED: 1
|
||||
steps:
|
||||
- uses: actions/cache@v2
|
||||
id: restore-build
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ runner.os }}-${{ github.sha }}
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: "14"
|
||||
- name: Setup kernel to increase watchers
|
||||
if: runner.os == 'Linux'
|
||||
run: echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p
|
||||
- name: Test Next Packages
|
||||
run: yarn testonly:packages
|
||||
env:
|
||||
CI: true
|
||||
|
||||
testBlitzExamples:
|
||||
timeout-minutes: 30
|
||||
name: Blitz - Test Example Apps (ubuntu-latest)
|
||||
@@ -120,6 +151,10 @@ jobs:
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ runner.os }}-${{ github.sha }}
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: "14"
|
||||
# Needed to get cypress binary
|
||||
- run: yarn cypress install
|
||||
- name: Install sass
|
||||
@@ -157,9 +192,9 @@ jobs:
|
||||
# path: |
|
||||
# ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
# **/node_modules
|
||||
# key: ${{ runner.os }}-${{ runner.node_version}}-yarn-v13-${{ hashFiles('yarn.lock') }}
|
||||
# key: ${{ runner.os }}-${{ runner.node_version}}-yarn-v14-${{ hashFiles('yarn.lock') }}
|
||||
# restore-keys: |
|
||||
# ${{ runner.os }}-${{ runner.node_version}}-yarn-v13-
|
||||
# ${{ runner.os }}-${{ runner.node_version}}-yarn-v14-
|
||||
- run: yarn install --frozen-lockfile --check-files
|
||||
- name: Build Packages
|
||||
run: yarn build
|
||||
@@ -222,6 +257,10 @@ jobs:
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ runner.os }}-${{ github.sha }}
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: "14"
|
||||
|
||||
# TODO: remove after we fix watchpack watching too much
|
||||
- name: Setup kernel to increase watchers
|
||||
@@ -256,9 +295,9 @@ jobs:
|
||||
# path: |
|
||||
# ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
# **/node_modules
|
||||
# key: ${{ runner.os }}-${{ runner.node_version}}-yarn-v13-${{ hashFiles('yarn.lock') }}
|
||||
# key: ${{ runner.os }}-${{ runner.node_version}}-yarn-v14-${{ hashFiles('yarn.lock') }}
|
||||
# restore-keys: |
|
||||
# ${{ runner.os }}-${{ runner.node_version}}-yarn-v13-
|
||||
# ${{ runner.os }}-${{ runner.node_version}}-yarn-v14-
|
||||
- run: yarn install --frozen-lockfile --check-files
|
||||
- name: Build Packages
|
||||
run: yarn build
|
||||
@@ -285,7 +324,10 @@ jobs:
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ runner.os }}-${{ github.sha }}
|
||||
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: "14"
|
||||
# TODO: remove after we fix watchpack watching too much
|
||||
- name: Setup kernel to increase watchers
|
||||
if: runner.os == 'Linux'
|
||||
@@ -318,9 +360,9 @@ jobs:
|
||||
# path: |
|
||||
# ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
# **/node_modules
|
||||
# key: ${{ runner.os }}-${{ runner.node_version}}-yarn-v13-${{ hashFiles('yarn.lock') }}
|
||||
# key: ${{ runner.os }}-${{ runner.node_version}}-yarn-v14-${{ hashFiles('yarn.lock') }}
|
||||
# restore-keys: |
|
||||
# ${{ runner.os }}-${{ runner.node_version}}-yarn-v13-
|
||||
# ${{ runner.os }}-${{ runner.node_version}}-yarn-v14-
|
||||
- run: yarn install --frozen-lockfile --check-files
|
||||
- name: Build Packages
|
||||
run: yarn build
|
||||
@@ -363,6 +405,7 @@ jobs:
|
||||
testIntegrationBlitzWin,
|
||||
testUnit,
|
||||
testBlitzPackages,
|
||||
testNextPackages,
|
||||
testBlitzExamples,
|
||||
testBlitzExamplesWin,
|
||||
]
|
||||
|
||||
@@ -4,6 +4,12 @@
|
||||
|
||||
## Notes For Core Team
|
||||
|
||||
### To Publish a new NPM Package under `@blitzjs/` namespace
|
||||
|
||||
1. cd into the package directory
|
||||
2. Run `npm publish --tag danger --access public`
|
||||
- `--access public` is required because scoped packages are set to private by default
|
||||
|
||||
### Syncing Next.js Fork
|
||||
|
||||
1. Run `yarn push-nextjs`
|
||||
|
||||
127
README.md
127
README.md
@@ -6,7 +6,7 @@
|
||||
<img alt="" src="https://img.shields.io/badge/Join%20our%20community-6700EB.svg?style=for-the-badge&labelColor=000000&logoWidth=20&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAQ9SURBVHgB7d3dVdtAEIbhcSpICUoH0IEogQqSVBBSAU4FSSpIOoAORAfQgSghHXzZ1U/YcMD4R9rZmf2ec3y448LyiNf27iLiGIAmPLrweC9Un3DhrzG6EarLNP09nlwJ1SOZ/lQr5N80/S/p2QMVCBf5N17XCfm1Y/rBHqjAG9PPHvBsz+mf9WAP+HLA9M/YA14cOP2payH7jpj+VCtk1wnTP+vj7xCy6cTpn7EHLMLp059iD1iD8eveJbVCNsSLheX1YA/YgOWnf8YeKB3Wmf7Ud6Fy4f/FHmtpxbl3YlC4MJ/Cj0bWdwPnPbARg+L0S54XQHS32WwuxClzd4CM0z9rPfeAuTtA5ulPXYQ7wZ04Y+oOoDD9KZc9YOoOoDj9s4dwFzgXR6w1wIPoOvPWA9buAHEJ173o3gWiy3AnuBUHLEbgmYwvAk1/wuM8vAgexThzbwPDkx7/DHwVXfFOxP2GmsKd4Ab6zPeAyU8CI7AHFmH2BRCBPXAyk18GzUrqAXCTiR4ssyj0VFw/oCU8+e+RZ33AWz6KMaYbIIWxB+JSLs1bsbkeMN0AqakHvoku9oA2sAfqBvbAQdw0QArsgb25aYBUQT3QgT2gB+yBuqGcHij2UCqXDZACe2Anlw2QYg/QAOyBuoE98CL3DZDCuK4/rh/Q7oGL6U+TOvcNkJoijN8X1C48+T+g75eQDrAH/qmqAVJgDwyqaoAUe4AGYA/UDZX3QLUNkEIZPRCd5+6BahsgVUgPROwBTSijB7jpVAvGHriHvmw9wAZ4BpX1ABvgmakHtPcbRuwBTWAPULgAV9D/jKDY9YRvwvgEaurD44uQHvAol7qBW7WKluVtIHiUS7GyvA0s6CiXDnxrpQfsgbqBS7GKk/2jYHCrVlGyfxTMrVo0ALdq1Q3sgSKofh0M9oA61a+D2QM0AHugbmAPqClmSRjK2apVVQ8UsySsoK1aHdgDesCtWnUDeyCrIpeFg1u3sylyWTi3btMA7IG6gT2wuuK3hoE9sKrit4YVslWLPaAN7IG6ocKt2zmY2h4O9sDiTG0PZw/QANy6XTewBxZj9ogYVHy025LMHhEz9cBn0We6B0yfERReBLfhx0/R1YQHPx/QBPbA0VwcEwf2wNFcHBPHHjiem3MC2QPHcXdSaJjA+KfgTPQ8hhfjBzHC40mhlzJ+Xq9lK4a4PCs43AVaGTed5mZq+iOXZwWHi3AnOj2wFWNcnxYe7gTxLtBKHuamP/J+Wnh8a5irB7ZC5Yk9gPX1QuXC+usHWqGyhYvUYR0a7zboUOFCNVhnk0krZAOW7wFOvzXhom2xnEbIHizTA1wEYhWW6YFGyC6c1gOcfg9wfA80Qj7g8B7g9HuCww+haIR8wf49wOn3Cvv9k8tGyC/s7gFOv3fY3QONkH+v9MBWqB7PeqDn9FcIT//kcitUn6kHOu/T/xfWzlQy3dEHhwAAAABJRU5ErkJggg==">
|
||||
</a>
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
||||
<a aria-label="All Contributors" href="#contributors-"><img alt="" src="https://img.shields.io/badge/all_contributors-360-17BB8A.svg?style=for-the-badge&labelColor=000000"></a>
|
||||
<a aria-label="All Contributors" href="#contributors-"><img alt="" src="https://img.shields.io/badge/all_contributors-408-17BB8A.svg?style=for-the-badge&labelColor=000000"></a>
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
||||
<a aria-label="License" href="https://github.com/blitz-js/blitz/blob/canary/LICENSE">
|
||||
<img alt="" src="https://img.shields.io/npm/l/blitz.svg?style=for-the-badge&labelColor=000000&color=blue">
|
||||
@@ -107,15 +107,21 @@ Your financial contributions help ensure Blitz continues to be developed and mai
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td><a aria-label="Andreas Asprou" href="https://andreas.fyi">
|
||||
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/canary/assets/andreas.jpg" width="40px"/>
|
||||
</a></td>
|
||||
<td><a aria-label="Digas" href="https://digsas.com">
|
||||
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/canary/assets/digsas.svg" width="40px"/>
|
||||
</a></td>
|
||||
<td><a aria-label="userTrack" href="https://www.usertrack.net/?ref=blitzjs">
|
||||
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/canary/assets/usertrack.png" width="40px"/>
|
||||
</a></td>
|
||||
<td>
|
||||
<a aria-label="Andreas Asprou" href="https://andreas.fyi">
|
||||
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/canary/assets/andreas.jpg" width="40px"/>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a aria-label="MeetKai" href="https://meetkai.com/?ref=blitzjs">
|
||||
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/canary/assets/meetkai.png" width="40px"/>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a aria-label="JDLT" href="https://jdlt.co.uk/?ref=blitzjs">
|
||||
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/canary/assets/jdlt.png" width="40px"/>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@@ -146,11 +152,6 @@ Your financial contributions help ensure Blitz continues to be developed and mai
|
||||
<a aria-label="Fauna" href="https://dashboard.fauna.com/accounts/register?utm_source=BlitzJS&utm_medium=sponsorship&utm_campaign=BlitzJS_Sponsorship_2020">
|
||||
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/canary/assets/Fauna_Logo_Blue.png" width="300px">
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a aria-label="GraphCMS" href="https://graphcms.com/?utm_source=BlitzJS&utm_medium=sponsorship&utm_campaign=BlitzJS_Sponsorship_2021">
|
||||
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/canary/assets/graphcms.png" width="300px">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -228,6 +229,30 @@ _Issue triage, pull request triage, community encouragement and moderation, etc_
|
||||
</sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://damilolarandolph.com/">
|
||||
<img src="https://avatars.githubusercontent.com/damilolarandolph" width="100px;" alt="Damilola Randolph avatar" /><br />
|
||||
<sub>
|
||||
<b>Damilola Randolph</b>
|
||||
</sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://www.saheen.me/">
|
||||
<img src="https://avatars.githubusercontent.com/shellord" width="100px;" alt="Saheen Shoukath avatar" /><br />
|
||||
<sub>
|
||||
<b>Saheen Shoukath</b>
|
||||
</sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://jahneer.me">
|
||||
<img src="https://avatars.githubusercontent.com/husnuljahneer" width="100px;" alt="Husnul Jahneer avatar" /><br />
|
||||
<sub>
|
||||
<b>Husnul Jahneer</b>
|
||||
</sub>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<!-- markdownlint-enable -->
|
||||
@@ -282,7 +307,7 @@ Thanks to these wonderful people ([emoji key](https://allcontributors.org/docs/e
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/sonnypgs"><img src="https://avatars3.githubusercontent.com/u/1431300?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sonny</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=sonnypgs" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/Zeko369"><img src="https://avatars3.githubusercontent.com/u/3064377?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Fran Zekan</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=Zeko369" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=Zeko369" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/Zeko369"><img src="https://avatars3.githubusercontent.com/u/3064377?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Fran Zekan</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=Zeko369" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=Zeko369" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=Zeko369" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="http://twitter.com/JanBaykara"><img src="https://avatars2.githubusercontent.com/u/237556?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jan Baykara</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=janbaykara" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://mikeattara.com"><img src="https://avatars1.githubusercontent.com/u/31483629?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mike Perry Y Attara</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=mikeattara" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://devanthe.dev"><img src="https://avatars0.githubusercontent.com/u/354652?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Devan</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=DevanB" title="Documentation">📖</a></td>
|
||||
@@ -323,7 +348,7 @@ Thanks to these wonderful people ([emoji key](https://allcontributors.org/docs/e
|
||||
<td align="center"><a href="http://anteprimorac.com.hr"><img src="https://avatars0.githubusercontent.com/u/972083?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ante Primorac</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=anteprimorac" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=anteprimorac" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="http://mykalmachon.dev"><img src="https://avatars1.githubusercontent.com/u/7844994?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mykal Machon</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=MykalMachon" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://jamiedavenport.dev"><img src="https://avatars2.githubusercontent.com/u/1329874?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jamie Davenport</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=jamiedavenport" title="Code">💻</a> <a href="#maintenance-jamiedavenport" title="Maintenance">🚧</a></td>
|
||||
<td align="center"><a href="https://cloudnweb.dev/"><img src="https://avatars0.githubusercontent.com/u/17050715?v=4?s=100" width="100px;" alt=""/><br /><sub><b>GaneshMani</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=ganeshmani" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://cloudnweb.dev/"><img src="https://avatars0.githubusercontent.com/u/17050715?v=4?s=100" width="100px;" alt=""/><br /><sub><b>GaneshMani</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=ganeshmani" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=ganeshmani" title="Tests">⚠️</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="http://ramonmorcillo.com"><img src="https://avatars3.githubusercontent.com/u/31936665?v=4?s=100" width="100px;" alt=""/><br /><sub><b>reymon359</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=reymon359" title="Code">💻</a></td>
|
||||
@@ -372,7 +397,7 @@ Thanks to these wonderful people ([emoji key](https://allcontributors.org/docs/e
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/jschepmans"><img src="https://avatars2.githubusercontent.com/u/5782977?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Johan Schepmans</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=jschepmans" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://twitter.com/dillonraphael"><img src="https://avatars0.githubusercontent.com/u/3496193?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Dillon Raphael</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=dillonraphael" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://twitter.com/dillonraphael"><img src="https://avatars0.githubusercontent.com/u/3496193?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Dillon Raphael</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=dillonraphael" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=dillonraphael" title="Tests">⚠️</a> <a href="https://github.com/blitz-js/blitz/commits?author=dillonraphael" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/clgeoio"><img src="https://avatars2.githubusercontent.com/u/37571416?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Cody G</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=clgeoio" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=clgeoio" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="https://github.com/madflow"><img src="https://avatars0.githubusercontent.com/u/183248?v=4?s=100" width="100px;" alt=""/><br /><sub><b>madflow</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=madflow" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://twitter.com/nitaking_"><img src="https://avatars2.githubusercontent.com/u/10850034?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Satoshi Nitawaki</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=nitaking" title="Code">💻</a> <a href="#maintenance-nitaking" title="Maintenance">🚧</a> <a href="#question-nitaking" title="Answering Questions">💬</a> <a href="https://github.com/blitz-js/blitz/commits?author=nitaking" title="Documentation">📖</a></td>
|
||||
@@ -646,7 +671,7 @@ Thanks to these wonderful people ([emoji key](https://allcontributors.org/docs/e
|
||||
<td align="center"><a href="https://github.com/bravo-kernel"><img src="https://avatars.githubusercontent.com/u/230500?v=4?s=100" width="100px;" alt=""/><br /><sub><b>bravo-kernel</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=bravo-kernel" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://samholmes.net"><img src="https://avatars.githubusercontent.com/u/8385528?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sam Holmes</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=sam3d" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://doncicuto.medium.com"><img src="https://avatars.githubusercontent.com/u/30386061?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Miguel Cabrerizo</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=doncicuto" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=doncicuto" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="http://zackhobson.com/"><img src="https://avatars.githubusercontent.com/u/12092?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Zack Hobson</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=zenhob" title="Code">💻</a></td>
|
||||
<td align="center"><a href="http://zackhobson.com/"><img src="https://avatars.githubusercontent.com/u/12092?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Zack Hobson</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=zenhob" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=zenhob" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://www.mokhtar.dev"><img src="https://avatars.githubusercontent.com/u/13026820?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mokhtar</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=m5r" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -661,7 +686,7 @@ Thanks to these wonderful people ([emoji key](https://allcontributors.org/docs/e
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/ratson"><img src="https://avatars.githubusercontent.com/u/2682937?v=4?s=100" width="100px;" alt=""/><br /><sub><b>(◕ᴥ◕)</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=ratson" title="Code">💻</a></td>
|
||||
<td align="center"><a href="maciejmyslinski.com"><img src="https://avatars.githubusercontent.com/u/11421186?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mat Milbury</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=maciejmyslinski" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://andreas.fyi"><img src="https://avatars.githubusercontent.com/u/8077469?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Andreas Asprou</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=andreasasprou" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://andreas.fyi"><img src="https://avatars.githubusercontent.com/u/8077469?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Andreas Asprou</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=andreasasprou" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=andreasasprou" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="https://github.com/kotx"><img src="https://avatars.githubusercontent.com/u/33439542?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Kot</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=kotx" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=kotx" title="Tests">⚠️</a> <a href="https://github.com/blitz-js/blitz/commits?author=kotx" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/isaka1022"><img src="https://avatars.githubusercontent.com/u/28589716?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Amane</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=isaka1022" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="johnleung.com"><img src="https://avatars.githubusercontent.com/u/20699?v=4?s=100" width="100px;" alt=""/><br /><sub><b>John Leung</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=fuzzthink" title="Documentation">📖</a></td>
|
||||
@@ -707,6 +732,68 @@ Thanks to these wonderful people ([emoji key](https://allcontributors.org/docs/e
|
||||
<td align="center"><a href="https://lucasvazq.github.io/"><img src="https://avatars.githubusercontent.com/u/38964964?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Lucas Vazquez</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=lucasvazq" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/chrisj-back2work"><img src="https://avatars.githubusercontent.com/u/68551954?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Chris Johnson</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=chrisj-back2work" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/thisdotrob"><img src="https://avatars.githubusercontent.com/u/12902589?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Rob Stevenson</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=thisdotrob" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="www.lucas.computer"><img src="https://avatars.githubusercontent.com/u/1363056?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Lucas Heymès</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=lovethebomb" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=lovethebomb" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/NorfeldtAbtion"><img src="https://avatars.githubusercontent.com/u/53769763?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Lasse Norfeldt</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=NorfeldtAbtion" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://nyaripeter.hu/"><img src="https://avatars.githubusercontent.com/u/6048614?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Péter Nyári</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=netwarex" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=netwarex" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://www.holgerfrohloff.de"><img src="https://avatars.githubusercontent.com/u/84148?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Holger Frohloff</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=5minpause" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/basilk76"><img src="https://avatars.githubusercontent.com/u/45275512?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Basil Khan</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=basilk76" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://danestves.com/"><img src="https://avatars.githubusercontent.com/u/31737273?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Daniel Esteves</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=danestves" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="http://www.bitnative.com"><img src="https://avatars.githubusercontent.com/u/1688997?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Cory House</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=coryhouse" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://auspham.dev/"><img src="https://avatars.githubusercontent.com/u/16440123?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Austin (Thang Pham)</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=rockmanvnx6" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://jammeryhq.com"><img src="https://avatars.githubusercontent.com/u/521777?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Marcus Reinhardt</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=noxify" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=noxify" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/davidchristie"><img src="https://avatars.githubusercontent.com/u/12044333?v=4?s=100" width="100px;" alt=""/><br /><sub><b>David Christie</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=davidchristie" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/ajanth97"><img src="https://avatars.githubusercontent.com/u/50458502?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ajanth</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=ajanth97" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/divpreet"><img src="https://avatars.githubusercontent.com/u/2805650?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Div</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=divpreet" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/david-arteaga"><img src="https://avatars.githubusercontent.com/u/7199015?v=4?s=100" width="100px;" alt=""/><br /><sub><b>David Arteaga</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=david-arteaga" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/MukulKolpe"><img src="https://avatars.githubusercontent.com/u/78664749?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mukul Kolpe</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=MukulKolpe" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/skotchpine"><img src="https://avatars.githubusercontent.com/u/13043909?v=4?s=100" width="100px;" alt=""/><br /><sub><b>tyler</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=skotchpine" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/SofianeDjellouli"><img src="https://avatars.githubusercontent.com/u/38258952?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sofiane Djellouli</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=SofianeDjellouli" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/kreako"><img src="https://avatars.githubusercontent.com/u/65113001?v=4?s=100" width="100px;" alt=""/><br /><sub><b>kreako</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=kreako" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://sarahdayan.dev"><img src="https://avatars.githubusercontent.com/u/5370675?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sarah Dayan</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=sarahdayan" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/c-ciobanu"><img src="https://avatars.githubusercontent.com/u/33382714?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Cristi Ciobanu</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=c-ciobanu" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://arpitdalal.dev"><img src="https://avatars.githubusercontent.com/u/61059807?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Arpit Dalal</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=arpitdalal" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/robertrisch"><img src="https://avatars.githubusercontent.com/u/73828816?v=4?s=100" width="100px;" alt=""/><br /><sub><b>robertrisch</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=robertrisch" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/dineshgadge"><img src="https://avatars.githubusercontent.com/u/186976?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Dinesh Gadge</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=dineshgadge" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/maltekiessling"><img src="https://avatars.githubusercontent.com/u/30420110?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Malte Kießling</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=maltekiessling" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="ospfranco.com"><img src="https://avatars.githubusercontent.com/u/1634213?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Oscar Franco</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=ospfranco" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="adamtrager.com"><img src="https://avatars.githubusercontent.com/u/1719791?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Adam Trager</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=Nfinished" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://saheen.codes"><img src="https://avatars.githubusercontent.com/u/2632896?v=4?s=100" width="100px;" alt=""/><br /><sub><b>saheenshoukath</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=shellord" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://jahneer.me"><img src="https://avatars.githubusercontent.com/u/54552763?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Husnul Jahneer</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=husnuljahneer" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/ReykCS"><img src="https://avatars.githubusercontent.com/u/40463716?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Reyk</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=ReykCS" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/Lokprakash-babu"><img src="https://avatars.githubusercontent.com/u/60031382?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Lokprakash Babu</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=Lokprakash-babu" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://mizle.net"><img src="https://avatars.githubusercontent.com/u/3516343?v=4?s=100" width="100px;" alt=""/><br /><sub><b>eai04191</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=eai04191" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://numanaral.github.io/?ref=github"><img src="https://avatars.githubusercontent.com/u/25233323?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Numan</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=numanaral" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/jscyo"><img src="https://avatars.githubusercontent.com/u/6310783?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Joel Coutinho</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=jscyo" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/davidbarker"><img src="https://avatars.githubusercontent.com/u/1597139?v=4?s=100" width="100px;" alt=""/><br /><sub><b>David Barker</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=davidbarker" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="http://www.timfeeley.com/"><img src="https://avatars.githubusercontent.com/u/3246342?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Tim Feeley</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=timfee" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/Caslus"><img src="https://avatars.githubusercontent.com/u/22855640?v=4?s=100" width="100px;" alt=""/><br /><sub><b>lucas philippe</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=Caslus" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/the-bayer"><img src="https://avatars.githubusercontent.com/u/94391693?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Blake Bayer</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=the-bayer" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=the-bayer" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/rmassie"><img src="https://avatars.githubusercontent.com/u/7375518?v=4?s=100" width="100px;" alt=""/><br /><sub><b>R Massie</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=rmassie" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/paulm17"><img src="https://avatars.githubusercontent.com/u/387463?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Paul</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=paulm17" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://minho42.com"><img src="https://avatars.githubusercontent.com/u/15278512?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Min ho Kim</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=minho42" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/webdeb"><img src="https://avatars.githubusercontent.com/u/14992140?v=4?s=100" width="100px;" alt=""/><br /><sub><b>webdeb</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=webdeb" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/iDavidB"><img src="https://avatars.githubusercontent.com/u/32268383?v=4?s=100" width="100px;" alt=""/><br /><sub><b>David</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=iDavidB" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=iDavidB" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=iDavidB" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="https://jdlt.co.uk"><img src="https://avatars.githubusercontent.com/u/5058625?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jake Dowie</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=jakedee" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/datner"><img src="https://avatars.githubusercontent.com/u/22598347?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Datner</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=datner" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=datner" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=datner" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="https://github.com/remlse"><img src="https://avatars.githubusercontent.com/u/54984957?v=4?s=100" width="100px;" alt=""/><br /><sub><b>remlse</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=remlse" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/sergous"><img src="https://avatars.githubusercontent.com/u/545151?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sergei Smirnov</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=sergous" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://twitter.com/trensik"><img src="https://avatars.githubusercontent.com/u/18584155?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Dawid Urbaniak</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=Trancever" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=Trancever" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="kiri.dev"><img src="https://avatars.githubusercontent.com/u/29735836?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Kacper Potyrała</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=SerekKiri" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="jcde.xyz"><img src="https://avatars.githubusercontent.com/u/31413538?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jeeho Ahn</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=iojcde" title="Documentation">📖</a> <a href="#tool-iojcde" title="Tools">🔧</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
@@ -6,5 +6,5 @@ TODO
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Email Brandon Bayer at b@bayer.ws to report a vulnerablity, and he will follow up with you asap.
|
||||
Email Brandon Bayer at b@bayer.ws to report a vulnerability, and he will follow up with you asap.
|
||||
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
<svg width="128" height="128" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0)">
|
||||
<path d="M79.4831 1.95572C70.2813 -0.81923 55.2811 -0.617415 46.1549 2.4098L18.6125 11.5167C9.48627 14.5313 9.38542 19.7784 18.3856 23.1588L49.9743 35.028C58.9744 38.4084 73.6217 38.194 82.5084 34.5487L110.883 22.9192C119.782 19.2739 119.53 14.0267 110.316 11.2518L79.4831 1.95572Z" fill="url(#paint0_linear)"/>
|
||||
<path d="M84.235 38.6101C75.3357 42.2554 68.0625 53.1029 68.0625 62.727V113.635C68.0625 123.259 74.9071 127.245 83.2644 122.489L109.282 107.706C117.639 102.951 124.912 91.208 125.442 81.6092L127.837 37.8281C128.366 28.2167 121.509 23.3479 112.609 26.9932L84.235 38.6101Z" fill="url(#paint1_linear)"/>
|
||||
<path d="M0.0071345 37.8409C-0.257575 28.2295 6.877 23.1211 15.8771 26.5015L47.4658 38.3707C56.466 41.7511 63.8274 52.3842 63.8274 62.0082V112.916C63.8274 122.54 56.882 126.715 48.386 122.212L17.0998 105.6C8.60392 101.085 1.44415 89.5306 1.16683 79.9192L0.0071345 37.8409Z" fill="url(#paint2_linear)"/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear" x1="63.9326" y1="0" x2="63.9326" y2="124.497" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#036EB8"/>
|
||||
<stop offset="1" stop-color="#469196"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear" x1="63.9326" y1="0" x2="63.9326" y2="124.497" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#036EB8"/>
|
||||
<stop offset="1" stop-color="#469196"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear" x1="63.9326" y1="0" x2="63.9326" y2="124.497" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#036EB8"/>
|
||||
<stop offset="1" stop-color="#469196"/>
|
||||
</linearGradient>
|
||||
<clipPath id="clip0">
|
||||
<rect width="128" height="128" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.7 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 78 KiB |
BIN
assets/jdlt.png
Normal file
BIN
assets/jdlt.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.4 KiB |
BIN
assets/meetkai.png
Normal file
BIN
assets/meetkai.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 46 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.8 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 30 KiB |
13
examples/auth/app/queries/users/currentUserQuery.ts
Normal file
13
examples/auth/app/queries/users/currentUserQuery.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import {Ctx} from "blitz"
|
||||
import db from "db"
|
||||
|
||||
export default async function currentUserQuery(_ = null, {session}: Ctx) {
|
||||
if (!session.userId) return null
|
||||
|
||||
const user = await db.user.findFirst({
|
||||
where: {id: session.userId},
|
||||
select: {id: true, name: true, email: true, role: true},
|
||||
})
|
||||
|
||||
return user
|
||||
}
|
||||
@@ -16,6 +16,9 @@ module.exports = withBundleAnalyzer({
|
||||
cli: {
|
||||
clearConsoleOnBlitzDev: false,
|
||||
},
|
||||
codegen: {
|
||||
templateDir: "./my-templates",
|
||||
},
|
||||
log: {
|
||||
// level: "trace",
|
||||
},
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
* and then export it. That way you can import here and anywhere else
|
||||
* and use it straight away.
|
||||
*/
|
||||
import previewEmail from "preview-email"
|
||||
|
||||
type ResetPasswordMailer = {
|
||||
to: string
|
||||
@@ -38,6 +37,7 @@ export function forgotPasswordMailer({to, token}: ResetPasswordMailer) {
|
||||
throw new Error("No production email implementation in mailers/forgotPasswordMailer")
|
||||
} else {
|
||||
// Preview email in the browser
|
||||
const previewEmail = (await import("preview-email")).default
|
||||
await previewEmail(msg)
|
||||
}
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"dev": "blitz dev",
|
||||
"build": "blitz build",
|
||||
"start": "blitz start",
|
||||
"console": "blitz console",
|
||||
"studio": "blitz prisma studio",
|
||||
"lint": "eslint --ignore-path .gitignore --ext .js,.ts,.tsx .",
|
||||
"analyze": "cross-env ANALYZE=true blitz build",
|
||||
@@ -29,7 +30,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@prisma/client": "2.24.1",
|
||||
"blitz": "0.42.0",
|
||||
"blitz": "0.45.4",
|
||||
"final-form": "4.20.1",
|
||||
"passport-auth0": "1.4.0",
|
||||
"passport-github2": "0.1.12",
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import {Link, BlitzPage, useMutation} from "blitz"
|
||||
import {Link, BlitzPage, useMutation, Image} from "blitz"
|
||||
import Layout from "app/layouts/Layout"
|
||||
import logout from "app/auth/mutations/logout"
|
||||
import {useCurrentUser} from "app/hooks/useCurrentUser"
|
||||
import {Suspense} from "react"
|
||||
import logo from "public/logo.png"
|
||||
|
||||
/*
|
||||
* This file is just for a pleasant getting started page for your new app.
|
||||
@@ -54,7 +55,7 @@ const Home: BlitzPage = () => {
|
||||
<div className="container">
|
||||
<main>
|
||||
<div className="logo">
|
||||
<img src="/logo.png" alt="blitz.js" />
|
||||
<Image src={logo} alt="blitz.js" />
|
||||
</div>
|
||||
<p>
|
||||
<strong>Congrats!</strong> Your app is ready, including user sign-up and log-in.
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
* This seed function is executed when you run `blitz db seed`.
|
||||
*
|
||||
* Probably you want to use a library like https://chancejs.com
|
||||
* or https://github.com/Marak/Faker.js to easily generate
|
||||
* realistic data.
|
||||
* to easily generate realistic data.
|
||||
*/
|
||||
const seed = async () => {
|
||||
// for (let i = 0; i < 5; i++) {
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@prisma/client": "2.24.1",
|
||||
"blitz": "0.42.0",
|
||||
"blitz": "0.45.4",
|
||||
"final-form": "4.20.1",
|
||||
"prisma": "2.24.1",
|
||||
"react": "0.0.0-experimental-6a589ad71",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import blitz from "blitz/custom-server"
|
||||
import {createServer} from "http"
|
||||
import {parse} from "url"
|
||||
import {log} from "@blitzjs/display"
|
||||
import {log} from "next/dist/server/lib/logging"
|
||||
|
||||
const {PORT = "3000"} = process.env
|
||||
const dev = process.env.NODE_ENV !== "production"
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
process.env.NODE_ENV = "test"
|
||||
require("dotenv-flow").config({ silent: true })
|
||||
|
||||
import { loadEnvConfig } from "@blitzjs/env"
|
||||
loadEnvConfig()
|
||||
|
||||
import "./register-ts-paths"
|
||||
import db from "db"
|
||||
|
||||
@@ -59,7 +59,7 @@ model Token {
|
||||
|
||||
// NOTE: It's highly recommended to use an enum for the token type
|
||||
// but enums only work in Postgres.
|
||||
// See: https://blitzjs.com/docs/database-overview#switch-to-postgresql
|
||||
// See: https://blitzjs.com/docs/database-overview#switch-to-postgre-sql
|
||||
// enum TokenType {
|
||||
// RESET_PASSWORD
|
||||
// }
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
* This seed function is executed when you run `blitz db seed`.
|
||||
*
|
||||
* Probably you want to use a library like https://chancejs.com
|
||||
* or https://github.com/Marak/Faker.js to easily generate
|
||||
* realistic data.
|
||||
* to easily generate realistic data.
|
||||
*/
|
||||
const seed = async () => {
|
||||
// for (let i = 0; i < 5; i++) {
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
* and then export it. That way you can import here and anywhere else
|
||||
* and use it straight away.
|
||||
*/
|
||||
import previewEmail from "preview-email"
|
||||
|
||||
type ResetPasswordMailer = {
|
||||
to: string
|
||||
@@ -38,6 +37,7 @@ export function forgotPasswordMailer({ to, token }: ResetPasswordMailer) {
|
||||
throw new Error("No production email implementation in mailers/forgotPasswordMailer")
|
||||
} else {
|
||||
// Preview email in the browser
|
||||
const previewEmail = (await import("preview-email")).default
|
||||
await previewEmail(msg)
|
||||
}
|
||||
},
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@prisma/client": "2.24.1",
|
||||
"blitz": "0.42.0",
|
||||
"blitz": "0.45.4",
|
||||
"final-form": "4.20.1",
|
||||
"react": "0.0.0-experimental-6a589ad71",
|
||||
"react-dom": "0.0.0-experimental-6a589ad71",
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
* This seed function is executed when you run `blitz db seed`.
|
||||
*
|
||||
* Probably you want to use a library like https://chancejs.com
|
||||
* or https://github.com/Marak/Faker.js to easily generate
|
||||
* realistic data.
|
||||
* to easily generate realistic data.
|
||||
*/
|
||||
const seed = async () => {
|
||||
// for (let i = 0; i < 5; i++) {
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"blitz": "0.42.0",
|
||||
"blitz": "0.45.4",
|
||||
"final-form": "4.20.1",
|
||||
"graphql": "15.5.0",
|
||||
"graphql-request": "3.4.0",
|
||||
|
||||
@@ -3,4 +3,6 @@
|
||||
// expect(element).toHaveTextContent(/react/i)
|
||||
// learn more: https://github.com/testing-library/jest-dom
|
||||
import "@testing-library/jest-dom/extend-expect"
|
||||
require("dotenv-flow").config({ silent: true })
|
||||
import { loadEnvConfig } from "@blitzjs/env"
|
||||
|
||||
loadEnvConfig()
|
||||
|
||||
@@ -4,7 +4,7 @@ This example shows how to use [next-rosetta](https://www.npmjs.com/package/next-
|
||||
|
||||
## Getting started
|
||||
|
||||
### Install dependecies
|
||||
### Install dependencies
|
||||
|
||||
```
|
||||
# with npm
|
||||
|
||||
@@ -3,7 +3,7 @@ import { LabeledTextField } from "app/core/components/LabeledTextField"
|
||||
import { Form, FORM_ERROR } from "app/core/components/Form"
|
||||
import login from "app/auth/mutations/login"
|
||||
import { Login } from "app/auth/validations"
|
||||
import { useI18n, I18nProps } from "next-rosetta"
|
||||
import { useI18n } from "next-rosetta"
|
||||
import type { MyLocale } from "app/core/i18n"
|
||||
|
||||
type LoginFormProps = {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {Document, Html, DocumentHead, Main, BlitzScript /*DocumentContext*/} from 'blitz'
|
||||
import { Document, Html, DocumentHead, Main, BlitzScript /*DocumentContext*/ } from "blitz"
|
||||
|
||||
class MyDocument extends Document {
|
||||
// Only uncomment if you need to customize this behaviour
|
||||
|
||||
@@ -59,7 +59,7 @@ model Token {
|
||||
|
||||
// NOTE: It's highly recommended to use an enum for the token type
|
||||
// but enums only work in Postgres.
|
||||
// See: https://blitzjs.com/docs/database-overview#switch-to-postgresql
|
||||
// See: https://blitzjs.com/docs/database-overview#switch-to-postgre-sql
|
||||
// enum TokenType {
|
||||
// RESET_PASSWORD
|
||||
// }
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
* This seed function is executed when you run `blitz db seed`.
|
||||
*
|
||||
* Probably you want to use a library like https://chancejs.com
|
||||
* or https://github.com/Marak/Faker.js to easily generate
|
||||
* realistic data.
|
||||
* to easily generate realistic data.
|
||||
*/
|
||||
const seed = async () => {
|
||||
// for (let i = 0; i < 5; i++) {
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
* and then export it. That way you can import here and anywhere else
|
||||
* and use it straight away.
|
||||
*/
|
||||
import previewEmail from "preview-email"
|
||||
|
||||
type ResetPasswordMailer = {
|
||||
to: string
|
||||
@@ -38,6 +37,7 @@ export function forgotPasswordMailer({ to, token }: ResetPasswordMailer) {
|
||||
throw new Error("No production email implementation in mailers/forgotPasswordMailer")
|
||||
} else {
|
||||
// Preview email in the browser
|
||||
const previewEmail = (await import("preview-email")).default
|
||||
await previewEmail(msg)
|
||||
}
|
||||
},
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@prisma/client": "2.24.1",
|
||||
"blitz": "0.42.0",
|
||||
"blitz": "0.45.4",
|
||||
"final-form": "4.20.1",
|
||||
"next-rosetta": "1.3.1",
|
||||
"react": "0.0.0-experimental-6a589ad71",
|
||||
|
||||
@@ -24,7 +24,10 @@ export * from "@testing-library/react"
|
||||
// router: { pathname: '/my-custom-pathname' },
|
||||
// });
|
||||
// --------------------------------------------------
|
||||
export function render(ui: RenderUI, { wrapper, router, dehydratedState, ...options }: RenderOptions = {}) {
|
||||
export function render(
|
||||
ui: RenderUI,
|
||||
{ wrapper, router, dehydratedState, ...options }: RenderOptions = {}
|
||||
) {
|
||||
if (!wrapper) {
|
||||
// Add a default context wrapper if one isn't supplied from the test
|
||||
wrapper = ({ children }) => (
|
||||
@@ -51,7 +54,7 @@ export function render(ui: RenderUI, { wrapper, router, dehydratedState, ...opti
|
||||
// --------------------------------------------------
|
||||
export function renderHook(
|
||||
hook: RenderHook,
|
||||
{ wrapper, router, dehydratedState,...options }: RenderHookOptions = {}
|
||||
{ wrapper, router, dehydratedState, ...options }: RenderHookOptions = {}
|
||||
) {
|
||||
if (!wrapper) {
|
||||
// Add a default context wrapper if one isn't supplied from the test
|
||||
@@ -66,7 +69,7 @@ export function renderHook(
|
||||
return defaultRenderHook(hook, { wrapper, ...options })
|
||||
}
|
||||
|
||||
export const mockRouter: BlitzRouter = {
|
||||
export const mockRouter: BlitzRouter = {
|
||||
basePath: "",
|
||||
pathname: "/",
|
||||
route: "/",
|
||||
@@ -92,8 +95,11 @@ export const mockRouter: BlitzRouter = {
|
||||
|
||||
type DefaultParams = Parameters<typeof defaultRender>
|
||||
type RenderUI = DefaultParams[0]
|
||||
type RenderOptions = DefaultParams[1] & { router?: Partial<BlitzRouter>, dehydratedState?: unknown }
|
||||
type RenderOptions = DefaultParams[1] & { router?: Partial<BlitzRouter>; dehydratedState?: unknown }
|
||||
|
||||
type DefaultHookParams = Parameters<typeof defaultRenderHook>
|
||||
type RenderHook = DefaultHookParams[0]
|
||||
type RenderHookOptions = DefaultHookParams[1] & { router?: Partial<BlitzRouter>, dehydratedState?: unknown }
|
||||
type RenderHookOptions = DefaultHookParams[1] & {
|
||||
router?: Partial<BlitzRouter>
|
||||
dehydratedState?: unknown
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"blitz": "0.42.0",
|
||||
"blitz": "0.45.4",
|
||||
"knex": "0.21.16",
|
||||
"react": "0.0.0-experimental-6a589ad71",
|
||||
"react-dom": "0.0.0-experimental-6a589ad71",
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@prisma/client": "2.24.1",
|
||||
"blitz": "0.42.0",
|
||||
"blitz": "0.45.4",
|
||||
"prisma": "2.24.1",
|
||||
"react": "0.0.0-experimental-6a589ad71",
|
||||
"react-dom": "0.0.0-experimental-6a589ad71"
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { BlitzPage } from "blitz"
|
||||
import { BlitzPage, Image } from "blitz"
|
||||
import Layout from "app/core/layouts/Layout"
|
||||
import logo from "public/logo.png"
|
||||
|
||||
const Home: BlitzPage = () => {
|
||||
return (
|
||||
<div className="container">
|
||||
<main>
|
||||
<div className="logo">
|
||||
<img src="/logo.png" alt="blitz.js" />
|
||||
<Image src={logo} alt="blitz.js" />
|
||||
</div>
|
||||
<p>
|
||||
<strong>Congrats!</strong> Your static app is ready.
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@prisma/client": "2.24.1",
|
||||
"blitz": "0.42.0",
|
||||
"blitz": "0.45.4",
|
||||
"final-form": "4.20.1",
|
||||
"prisma": "2.24.1",
|
||||
"react": "0.0.0-experimental-6a589ad71",
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@prisma/client": "2.24.1",
|
||||
"blitz": "0.42.0",
|
||||
"blitz": "0.45.4",
|
||||
"final-form": "4.20.1",
|
||||
"prisma": "2.24.1",
|
||||
"react": "0.0.0-experimental-6a589ad71",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": "0.42.1",
|
||||
"version": "0.45.4",
|
||||
"packages": ["packages/*"],
|
||||
"npmClient": "yarn",
|
||||
"useWorkspaces": true,
|
||||
|
||||
@@ -1 +1 @@
|
||||
14.18.1
|
||||
16.13.0
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
"dev": "lerna run dev --stream --parallel",
|
||||
"dev2": "while true; do yarn --check-files && yarn dev; done",
|
||||
"testonly": "jest --runInBand",
|
||||
"testonly:packages": "ultra -r --filter \"packages/*\" --concurrency 15 test",
|
||||
"testheadless": "cross-env HEADLESS=true yarn testonly",
|
||||
"testsafari": "cross-env BROWSER_NAME=safari yarn testonly",
|
||||
"testfirefox": "cross-env BROWSER_NAME=firefox yarn testonly",
|
||||
@@ -60,8 +61,8 @@
|
||||
"@types/passport": "1.0.5",
|
||||
"@types/sharp": "0.28.4",
|
||||
"@types/string-hash": "1.1.1",
|
||||
"@typescript-eslint/eslint-plugin": "4.20.0",
|
||||
"@typescript-eslint/parser": "^4.20.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.5.0",
|
||||
"@typescript-eslint/parser": "^5.5.0",
|
||||
"@vercel/fetch": "6.1.1",
|
||||
"@zeit/next-css": "1.0.2-canary.2",
|
||||
"@zeit/next-sass": "1.0.2-canary.2",
|
||||
@@ -110,7 +111,7 @@
|
||||
"lost": "8.3.1",
|
||||
"minimatch": "3.0.4",
|
||||
"moment": "^2.24.0",
|
||||
"node-fetch": "2.6.1",
|
||||
"node-fetch": "2.6.7",
|
||||
"node-notifier": "5.4.0",
|
||||
"node-sass": "5.0.0",
|
||||
"npm-run-all": "4.1.5",
|
||||
@@ -144,7 +145,8 @@
|
||||
"tailwindcss": "1.1.3",
|
||||
"taskr": "1.1.0",
|
||||
"tree-kill": "1.2.2",
|
||||
"typescript": "4.4.2",
|
||||
"typescript": "4.5.2",
|
||||
"ultra-runner": "3.10.5",
|
||||
"wait-port": "0.2.2",
|
||||
"web-streams-polyfill": "2.1.1",
|
||||
"webpack-bundle-analyzer": "4.3.0",
|
||||
|
||||
@@ -42,8 +42,8 @@
|
||||
"got": "10.7.0",
|
||||
"prompts": "2.1.0",
|
||||
"rimraf": "3.0.0",
|
||||
"tar": "6.1.11",
|
||||
"typescript": "4.3.4",
|
||||
"tar": "4.4.18",
|
||||
"typescript": "4.5.2",
|
||||
"update-check": "1.5.4",
|
||||
"validate-npm-package-name": "3.0.0"
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "eslint-config-blitz",
|
||||
"version": "0.42.1",
|
||||
"version": "0.45.4",
|
||||
"description": "Blitz.js eslint config",
|
||||
"main": "index.js",
|
||||
"license": "MIT",
|
||||
@@ -11,8 +11,8 @@
|
||||
"dependencies": {
|
||||
"@next/eslint-plugin-next": "11.1.0",
|
||||
"@rushstack/eslint-patch": "^1.0.6",
|
||||
"@typescript-eslint/eslint-plugin": "4.20.0",
|
||||
"@typescript-eslint/parser": "^4.20.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.5.0",
|
||||
"@typescript-eslint/parser": "^5.5.0",
|
||||
"eslint-import-resolver-node": "^0.3.4",
|
||||
"eslint-import-resolver-typescript": "^2.4.0",
|
||||
"eslint-plugin-import": "^2.22.1",
|
||||
|
||||
3
nextjs/packages/installer/jest.config.js
Normal file
3
nextjs/packages/installer/jest.config.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
preset: '../../../jest-unit.config.js',
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@blitzjs/installer",
|
||||
"version": "0.42.1",
|
||||
"version": "0.45.4",
|
||||
"description": "Package installation for the Blitz CLI",
|
||||
"homepage": "https://github.com/blitz-js/blitz#readme",
|
||||
"license": "MIT",
|
||||
@@ -30,21 +30,21 @@
|
||||
"dependencies": {
|
||||
"@babel/core": "7.12.10",
|
||||
"@babel/plugin-transform-typescript": "7.12.1",
|
||||
"@blitzjs/display": "0.42.1",
|
||||
"@blitzjs/generator": "0.42.1",
|
||||
"@mrleebo/prisma-ast": "^0.2.4",
|
||||
"@prisma/sdk": "2.19.0",
|
||||
"@types/jscodeshift": "0.7.2",
|
||||
"@blitzjs/generator": "0.45.4",
|
||||
"@mrleebo/prisma-ast": "0.2.6",
|
||||
"@prisma/sdk": "3.9.1",
|
||||
"@types/jscodeshift": "0.11.2",
|
||||
"ast-types": "0.14.2",
|
||||
"cross-spawn": "7.0.3",
|
||||
"diff": "5.0.0",
|
||||
"enquirer": "2.3.6",
|
||||
"fs-extra": "^9.1.0",
|
||||
"globby": "11.0.2",
|
||||
"ink": "3.0.8",
|
||||
"ink-spinner": "4.0.1",
|
||||
"ink": "3.2.0",
|
||||
"ink-spinner": "4.0.3",
|
||||
"ink-testing-library": "2.1.0",
|
||||
"jscodeshift": "0.11.0",
|
||||
"recast": "0.20.4"
|
||||
"jscodeshift": "0.13.0",
|
||||
"recast": "0.20.5"
|
||||
},
|
||||
"gitHead": "d3b9fce0bdd251c2b1890793b0aa1cd77c1c0922"
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Text } from 'ink'
|
||||
import * as React from 'react'
|
||||
import { Newline } from './newline'
|
||||
|
||||
export const EnterToContinue: React.FC<{ message?: string }> = ({
|
||||
message = 'Press ENTER to continue',
|
||||
}) => (
|
||||
<>
|
||||
<Newline />
|
||||
<Text bold>{message}</Text>
|
||||
</>
|
||||
)
|
||||
6
nextjs/packages/installer/src/components/newline.tsx
Normal file
6
nextjs/packages/installer/src/components/newline.tsx
Normal file
@@ -0,0 +1,6 @@
|
||||
import { Box } from 'ink'
|
||||
import * as React from 'react'
|
||||
|
||||
export const Newline: React.FC<{ count?: number }> = ({ count = 1 }) => {
|
||||
return <Box paddingBottom={count} />
|
||||
}
|
||||
@@ -1,13 +1,19 @@
|
||||
import {spawn} from "cross-spawn"
|
||||
import * as fs from "fs-extra"
|
||||
import {Box, Text} from "ink"
|
||||
// TODO: Once the dependency issue of ink-spinner is resolved, you should uncomment it (https://github.com/blitz-js/blitz/issues/2793)
|
||||
// import Spinner from "ink-spinner"
|
||||
import * as path from "path"
|
||||
import * as React from "react"
|
||||
import {Newline} from "../components/newline"
|
||||
import {useEnterToContinue} from "../utils/use-enter-to-continue"
|
||||
import {Executor, executorArgument, ExecutorConfig, getExecutorArgument} from "./executor"
|
||||
import { spawn } from 'cross-spawn'
|
||||
import * as fs from 'fs-extra'
|
||||
import { Box, Text } from 'ink'
|
||||
import Spinner from 'ink-spinner'
|
||||
import * as path from 'path'
|
||||
import * as React from 'react'
|
||||
import { Newline } from '../components/newline'
|
||||
import { RecipeCLIArgs } from '../types'
|
||||
import { useEnterToContinue } from '../utils/use-enter-to-continue'
|
||||
import { useUserInput } from '../utils/use-user-input'
|
||||
import {
|
||||
Executor,
|
||||
executorArgument,
|
||||
ExecutorConfig,
|
||||
getExecutorArgument,
|
||||
} from './executor'
|
||||
|
||||
interface NpmPackage {
|
||||
name: string
|
||||
@@ -21,30 +27,31 @@ export interface Config extends ExecutorConfig {
|
||||
packages: executorArgument<NpmPackage[]>
|
||||
}
|
||||
|
||||
export function isAddDependencyExecutor(executor: ExecutorConfig): executor is Config {
|
||||
export function isAddDependencyExecutor(
|
||||
executor: ExecutorConfig
|
||||
): executor is Config {
|
||||
return (executor as Config).packages !== undefined
|
||||
}
|
||||
|
||||
export const type = "add-dependency"
|
||||
export const type = 'add-dependency'
|
||||
|
||||
// TODO: Once the dependency issue of ink-spinner is resolved, you should change "loading.." to <Spinner /> (https://github.com/blitz-js/blitz/issues/2793)
|
||||
function Package({pkg, loading}: {pkg: NpmPackage; loading: boolean}) {
|
||||
function Package({ pkg, loading }: { pkg: NpmPackage; loading: boolean }) {
|
||||
return (
|
||||
<Text>
|
||||
{` `}
|
||||
{loading ? "Loading..." : "📦"}
|
||||
{loading ? <Spinner /> : '📦'}
|
||||
{` ${pkg.name}@${pkg.version}`}
|
||||
</Text>
|
||||
)
|
||||
}
|
||||
|
||||
const DependencyList = ({
|
||||
lede,
|
||||
lede = 'Hang tight! Installing dependencies...',
|
||||
depsLoading = false,
|
||||
devDepsLoading = false,
|
||||
packages,
|
||||
}: {
|
||||
lede: string
|
||||
lede?: string
|
||||
depsLoading?: boolean
|
||||
devDepsLoading?: boolean
|
||||
packages: NpmPackage[]
|
||||
@@ -60,7 +67,9 @@ const DependencyList = ({
|
||||
<Package key={pkg.name} pkg={pkg} loading={depsLoading} />
|
||||
))}
|
||||
<Newline />
|
||||
{devPackages.length ? <Text>Dev Dependencies to be installed:</Text> : null}
|
||||
{devPackages.length ? (
|
||||
<Text>Dev Dependencies to be installed:</Text>
|
||||
) : null}
|
||||
{devPackages.map((pkg) => (
|
||||
<Package key={pkg.name} pkg={pkg} loading={devDepsLoading} />
|
||||
))}
|
||||
@@ -72,10 +81,10 @@ const DependencyList = ({
|
||||
* Exported for unit testing purposes
|
||||
*/
|
||||
export function getPackageManager() {
|
||||
if (fs.existsSync(path.resolve("yarn.lock"))) {
|
||||
return "yarn"
|
||||
if (fs.existsSync(path.resolve('yarn.lock'))) {
|
||||
return 'yarn'
|
||||
}
|
||||
return "npm"
|
||||
return 'npm'
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -83,41 +92,46 @@ export function getPackageManager() {
|
||||
*/
|
||||
export async function installPackages(packages: NpmPackage[], isDev = false) {
|
||||
const packageManager = getPackageManager()
|
||||
const isNPM = packageManager === "npm"
|
||||
const pkgInstallArg = isNPM ? "install" : "add"
|
||||
const isNPM = packageManager === 'npm'
|
||||
const pkgInstallArg = isNPM ? 'install' : 'add'
|
||||
const args: string[] = [pkgInstallArg]
|
||||
|
||||
if (isDev) {
|
||||
args.push(isNPM ? "--save-dev" : "-D")
|
||||
args.push(isNPM ? '--save-dev' : '-D')
|
||||
}
|
||||
packages.forEach((pkg) => {
|
||||
pkg.version ? args.push(`${pkg.name}@${pkg.version}`) : args.push(pkg.name)
|
||||
})
|
||||
await new Promise((resolve) => {
|
||||
const cp = spawn(packageManager, args, {
|
||||
stdio: ["inherit", "pipe", "pipe"],
|
||||
stdio: ['inherit', 'pipe', 'pipe'],
|
||||
})
|
||||
cp.on("exit", resolve)
|
||||
cp.on('exit', resolve)
|
||||
})
|
||||
}
|
||||
|
||||
export const Commit: Executor["Commit"] = ({cliArgs, step, onChangeCommitted}) => {
|
||||
export const Commit: Executor['Commit'] = ({
|
||||
cliArgs,
|
||||
cliFlags,
|
||||
step,
|
||||
onChangeCommitted,
|
||||
}) => {
|
||||
const userInput = useUserInput(cliFlags)
|
||||
const [depsInstalled, setDepsInstalled] = React.useState(false)
|
||||
const [devDepsInstalled, setDevDepsInstalled] = React.useState(false)
|
||||
|
||||
const handleChangeCommitted = React.useCallback(() => {
|
||||
const packages = (step as Config).packages
|
||||
const dependencies = packages.length === 1 ? "dependency" : "dependencies"
|
||||
const dependencies = packages.length === 1 ? 'dependency' : 'dependencies'
|
||||
onChangeCommitted(`Installed ${packages.length} ${dependencies}`)
|
||||
}, [onChangeCommitted, step])
|
||||
|
||||
useEnterToContinue(handleChangeCommitted, depsInstalled && devDepsInstalled)
|
||||
|
||||
React.useEffect(() => {
|
||||
async function installDeps() {
|
||||
const packagesToInstall = getExecutorArgument((step as Config).packages, cliArgs).filter(
|
||||
(p) => !p.isDevDep,
|
||||
)
|
||||
const packagesToInstall = getExecutorArgument(
|
||||
(step as Config).packages,
|
||||
cliArgs
|
||||
).filter((p) => !p.isDevDep)
|
||||
await installPackages(packagesToInstall)
|
||||
setDepsInstalled(true)
|
||||
}
|
||||
@@ -128,9 +142,10 @@ export const Commit: Executor["Commit"] = ({cliArgs, step, onChangeCommitted}) =
|
||||
React.useEffect(() => {
|
||||
if (!depsInstalled) return
|
||||
async function installDevDeps() {
|
||||
const packagesToInstall = getExecutorArgument((step as Config).packages, cliArgs).filter(
|
||||
(p) => p.isDevDep,
|
||||
)
|
||||
const packagesToInstall = getExecutorArgument(
|
||||
(step as Config).packages,
|
||||
cliArgs
|
||||
).filter((p) => p.isDevDep)
|
||||
await installPackages(packagesToInstall, true)
|
||||
setDevDepsInstalled(true)
|
||||
}
|
||||
@@ -148,14 +163,54 @@ export const Commit: Executor["Commit"] = ({cliArgs, step, onChangeCommitted}) =
|
||||
onChangeCommitted()
|
||||
return null
|
||||
}
|
||||
|
||||
const childProps: CommitChildProps = {
|
||||
depsInstalled,
|
||||
devDepsInstalled,
|
||||
handleChangeCommitted,
|
||||
step,
|
||||
cliArgs,
|
||||
}
|
||||
|
||||
if (userInput) return <CommitWithInput {...childProps} />
|
||||
else return <CommitWithoutInput {...childProps} />
|
||||
}
|
||||
|
||||
interface CommitChildProps {
|
||||
depsInstalled: boolean
|
||||
devDepsInstalled: boolean
|
||||
handleChangeCommitted: () => void
|
||||
step: Config
|
||||
cliArgs: RecipeCLIArgs
|
||||
}
|
||||
|
||||
const CommitWithInput = ({
|
||||
depsInstalled,
|
||||
devDepsInstalled,
|
||||
handleChangeCommitted,
|
||||
step,
|
||||
cliArgs,
|
||||
}: CommitChildProps) => {
|
||||
useEnterToContinue(handleChangeCommitted, depsInstalled && devDepsInstalled)
|
||||
|
||||
return (
|
||||
<>
|
||||
<DependencyList
|
||||
lede={"Hang tight! Installing dependencies..."}
|
||||
depsLoading={!depsInstalled}
|
||||
devDepsLoading={!devDepsInstalled}
|
||||
packages={getExecutorArgument(step.packages, cliArgs)}
|
||||
/>
|
||||
</>
|
||||
<DependencyList
|
||||
depsLoading={!depsInstalled}
|
||||
devDepsLoading={!devDepsInstalled}
|
||||
packages={getExecutorArgument(step.packages, cliArgs)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
const CommitWithoutInput = ({
|
||||
depsInstalled,
|
||||
devDepsInstalled,
|
||||
step,
|
||||
cliArgs,
|
||||
}: CommitChildProps) => (
|
||||
<DependencyList
|
||||
depsLoading={!depsInstalled}
|
||||
devDepsLoading={!devDepsInstalled}
|
||||
packages={getExecutorArgument(step.packages, cliArgs)}
|
||||
/>
|
||||
)
|
||||
@@ -1,6 +1,7 @@
|
||||
import {Box, Text} from "ink"
|
||||
import * as React from "react"
|
||||
import {Newline} from "../components/newline"
|
||||
import { Box, Text } from 'ink'
|
||||
import * as React from 'react'
|
||||
import { Newline } from '../components/newline'
|
||||
import { RecipeCLIArgs, RecipeCLIFlags } from '../types'
|
||||
|
||||
export interface ExecutorConfig {
|
||||
successIcon?: string
|
||||
@@ -13,28 +14,34 @@ export interface ExecutorConfig {
|
||||
|
||||
export interface Executor {
|
||||
type: string
|
||||
Propose?: React.FC<{step: ExecutorConfig; onProposalAccepted: (data?: any) => void; cliArgs: any}>
|
||||
Propose?: React.FC<{
|
||||
step: ExecutorConfig
|
||||
onProposalAccepted: (data?: any) => void
|
||||
cliArgs: RecipeCLIArgs
|
||||
cliFlags: RecipeCLIFlags
|
||||
}>
|
||||
Commit: React.FC<{
|
||||
step: ExecutorConfig
|
||||
proposalData?: any
|
||||
onChangeCommitted: (data?: any) => void
|
||||
cliArgs: any
|
||||
cliArgs: RecipeCLIArgs
|
||||
cliFlags: RecipeCLIFlags
|
||||
}>
|
||||
}
|
||||
|
||||
type dynamicExecutorArgument<T> = (cliArgs: any) => T
|
||||
type dynamicExecutorArgument<T> = (cliArgs: RecipeCLIArgs) => T
|
||||
|
||||
function isDynamicExecutorArgument<T>(
|
||||
input: executorArgument<T>,
|
||||
input: executorArgument<T>
|
||||
): input is dynamicExecutorArgument<T> {
|
||||
return typeof (input as dynamicExecutorArgument<T>) === "function"
|
||||
return typeof (input as dynamicExecutorArgument<T>) === 'function'
|
||||
}
|
||||
|
||||
export type executorArgument<T> = T | dynamicExecutorArgument<T>
|
||||
|
||||
export function Frontmatter({executor}: {executor: ExecutorConfig}) {
|
||||
export function Frontmatter({ executor }: { executor: ExecutorConfig }) {
|
||||
const lineLength = executor.stepName.length + 6
|
||||
const verticalBorder = `+${new Array(lineLength).fill("–").join("")}+`
|
||||
const verticalBorder = `+${new Array(lineLength).fill('–').join('')}+`
|
||||
return (
|
||||
<Box flexDirection="column" paddingBottom={1}>
|
||||
<Newline />
|
||||
@@ -56,7 +63,10 @@ export function Frontmatter({executor}: {executor: ExecutorConfig}) {
|
||||
)
|
||||
}
|
||||
|
||||
export function getExecutorArgument<T>(input: executorArgument<T>, cliArgs: any): T {
|
||||
export function getExecutorArgument<T>(
|
||||
input: executorArgument<T>,
|
||||
cliArgs: RecipeCLIArgs
|
||||
): T {
|
||||
if (isDynamicExecutorArgument(input)) {
|
||||
return input(cliArgs)
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import {prompt as enquirer} from "enquirer"
|
||||
import globby from "globby"
|
||||
import { prompt as enquirer } from 'enquirer'
|
||||
import globby from 'globby'
|
||||
|
||||
enum SearchType {
|
||||
file,
|
||||
@@ -13,8 +13,8 @@ interface FilePromptOptions {
|
||||
context: any
|
||||
}
|
||||
|
||||
function getMatchingFiles(filter: string = ""): Promise<string[]> {
|
||||
return globby(filter, {expandDirectories: true})
|
||||
function getMatchingFiles(filter: string = ''): Promise<string[]> {
|
||||
return globby(filter, { expandDirectories: true })
|
||||
}
|
||||
|
||||
export async function filePrompt(options: FilePromptOptions): Promise<string> {
|
||||
@@ -24,10 +24,10 @@ export async function filePrompt(options: FilePromptOptions): Promise<string> {
|
||||
if (choices.length === 1) {
|
||||
return choices[0]
|
||||
}
|
||||
const results: {file: string} = await enquirer({
|
||||
type: "autocomplete",
|
||||
name: "file",
|
||||
message: "Select the target file",
|
||||
const results: { file: string } = await enquirer({
|
||||
type: 'autocomplete',
|
||||
name: 'file',
|
||||
message: 'Select the target file',
|
||||
// @ts-ignore
|
||||
limit: 10,
|
||||
choices,
|
||||
@@ -0,0 +1,199 @@
|
||||
import { createPatch } from 'diff'
|
||||
import * as fs from 'fs-extra'
|
||||
import { Box, Text } from 'ink'
|
||||
import Spinner from 'ink-spinner'
|
||||
import * as React from 'react'
|
||||
import { EnterToContinue } from '../components/enter-to-continue'
|
||||
import { RecipeCLIArgs } from '../types'
|
||||
import {
|
||||
processFile,
|
||||
stringProcessFile,
|
||||
StringTransformer,
|
||||
transform,
|
||||
Transformer,
|
||||
TransformStatus,
|
||||
} from '../utils/transform'
|
||||
import { useEnterToContinue } from '../utils/use-enter-to-continue'
|
||||
import { useUserInput } from '../utils/use-user-input'
|
||||
import {
|
||||
Executor,
|
||||
executorArgument,
|
||||
ExecutorConfig,
|
||||
getExecutorArgument,
|
||||
} from './executor'
|
||||
import { filePrompt } from './file-prompt'
|
||||
|
||||
export interface Config extends ExecutorConfig {
|
||||
selectTargetFiles?(cliArgs: RecipeCLIArgs): any[]
|
||||
singleFileSearch?: executorArgument<string>
|
||||
transform?: Transformer
|
||||
transformPlain?: StringTransformer
|
||||
}
|
||||
|
||||
export function isFileTransformExecutor(
|
||||
executor: ExecutorConfig
|
||||
): executor is Config {
|
||||
return (
|
||||
(executor as Config).transform !== undefined ||
|
||||
(executor as Config).transformPlain !== undefined
|
||||
)
|
||||
}
|
||||
|
||||
export const type = 'file-transform'
|
||||
export const Propose: Executor['Propose'] = ({
|
||||
cliArgs,
|
||||
cliFlags,
|
||||
onProposalAccepted,
|
||||
step,
|
||||
}) => {
|
||||
const userInput = useUserInput(cliFlags)
|
||||
const [diff, setDiff] = React.useState<string | null>(null)
|
||||
const [error, setError] = React.useState<Error | null>(null)
|
||||
const [filePath, setFilePath] = React.useState('')
|
||||
const [proposalAccepted, setProposalAccepted] = React.useState(false)
|
||||
|
||||
const acceptProposal = React.useCallback(() => {
|
||||
setProposalAccepted(true)
|
||||
onProposalAccepted(filePath)
|
||||
}, [onProposalAccepted, filePath])
|
||||
|
||||
React.useEffect(() => {
|
||||
async function generateDiff() {
|
||||
const fileToTransform: string = await filePrompt({
|
||||
context: cliArgs,
|
||||
globFilter: getExecutorArgument(
|
||||
(step as Config).singleFileSearch,
|
||||
cliArgs
|
||||
),
|
||||
getChoices: (step as Config).selectTargetFiles,
|
||||
})
|
||||
setFilePath(fileToTransform)
|
||||
const originalFile = fs.readFileSync(fileToTransform).toString('utf-8')
|
||||
const newFile = await ((step as Config).transformPlain
|
||||
? stringProcessFile(originalFile, (step as Config).transformPlain!)
|
||||
: processFile(originalFile, (step as Config).transform!))
|
||||
return createPatch(fileToTransform, originalFile, newFile)
|
||||
}
|
||||
|
||||
generateDiff().then(setDiff, setError)
|
||||
}, [cliArgs, step])
|
||||
|
||||
// Let the renderer deal with errors from file transformers, otherwise the
|
||||
// process would just hang.
|
||||
if (error) throw error
|
||||
|
||||
if (!diff) {
|
||||
return (
|
||||
<Box>
|
||||
<Text>
|
||||
<Spinner />
|
||||
Generating file diff...
|
||||
</Text>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
const childProps: ProposeChildProps = {
|
||||
diff,
|
||||
filePath,
|
||||
proposalAccepted,
|
||||
acceptProposal,
|
||||
}
|
||||
|
||||
if (userInput) return <ProposeWithInput {...childProps} />
|
||||
else return <ProposeWithoutInput {...childProps} />
|
||||
}
|
||||
|
||||
interface ProposeChildProps {
|
||||
diff: string
|
||||
filePath: string
|
||||
proposalAccepted: boolean
|
||||
acceptProposal: () => void
|
||||
}
|
||||
|
||||
const Diff = ({ diff }: { diff: string }) => (
|
||||
<>
|
||||
{diff
|
||||
.split('\n')
|
||||
.slice(2)
|
||||
.map((line, idx) => {
|
||||
let styleProps: any = {}
|
||||
if (line.startsWith('-') && !line.startsWith('---')) {
|
||||
styleProps.bold = true
|
||||
styleProps.color = 'red'
|
||||
} else if (line.startsWith('+') && !line.startsWith('+++')) {
|
||||
styleProps.bold = true
|
||||
styleProps.color = 'green'
|
||||
}
|
||||
return (
|
||||
<Text {...styleProps} key={idx}>
|
||||
{line}
|
||||
</Text>
|
||||
)
|
||||
})}
|
||||
</>
|
||||
)
|
||||
|
||||
const ProposeWithInput = ({
|
||||
diff,
|
||||
filePath,
|
||||
proposalAccepted,
|
||||
acceptProposal,
|
||||
}: ProposeChildProps) => {
|
||||
useEnterToContinue(acceptProposal, filePath !== '' && !proposalAccepted)
|
||||
|
||||
return (
|
||||
<Box flexDirection="column">
|
||||
<Diff diff={diff} />
|
||||
<EnterToContinue message="The above changes will be made. Press ENTER to continue" />
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
const ProposeWithoutInput = ({
|
||||
diff,
|
||||
filePath,
|
||||
proposalAccepted,
|
||||
acceptProposal,
|
||||
}: ProposeChildProps) => {
|
||||
React.useEffect(() => {
|
||||
if (filePath !== '' && !proposalAccepted) {
|
||||
acceptProposal()
|
||||
}
|
||||
}, [acceptProposal, filePath, proposalAccepted])
|
||||
|
||||
return (
|
||||
<Box flexDirection="column">
|
||||
<Diff diff={diff} />
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export const Commit: Executor['Commit'] = ({
|
||||
onChangeCommitted,
|
||||
proposalData: filePath,
|
||||
step,
|
||||
}) => {
|
||||
React.useEffect(() => {
|
||||
void (async function () {
|
||||
const results = await transform(
|
||||
async (original) =>
|
||||
await ((step as Config).transformPlain
|
||||
? stringProcessFile(original, (step as Config).transformPlain!)
|
||||
: processFile(original, (step as Config).transform!)),
|
||||
[filePath]
|
||||
)
|
||||
if (results.some((r) => r.status === TransformStatus.Failure)) {
|
||||
console.error(results)
|
||||
}
|
||||
onChangeCommitted(`Modified file: ${filePath}`)
|
||||
})()
|
||||
}, [filePath, onChangeCommitted, step])
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Spinner />
|
||||
<Text>Applying file changes</Text>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
158
nextjs/packages/installer/src/executors/new-file-executor.tsx
Normal file
158
nextjs/packages/installer/src/executors/new-file-executor.tsx
Normal file
@@ -0,0 +1,158 @@
|
||||
import { Generator, GeneratorOptions, SourceRootType } from '@blitzjs/generator'
|
||||
import { Box, Text } from 'ink'
|
||||
import { useEffect, useState } from 'react'
|
||||
import * as React from 'react'
|
||||
import { EnterToContinue } from '../components/enter-to-continue'
|
||||
import { useEnterToContinue } from '../utils/use-enter-to-continue'
|
||||
import { useUserInput } from '../utils/use-user-input'
|
||||
import {
|
||||
Executor,
|
||||
executorArgument,
|
||||
ExecutorConfig,
|
||||
getExecutorArgument,
|
||||
} from './executor'
|
||||
|
||||
export interface Config extends ExecutorConfig {
|
||||
targetDirectory?: executorArgument<string>
|
||||
templatePath: executorArgument<string>
|
||||
templateValues: executorArgument<{ [key: string]: string }>
|
||||
destinationPathPrompt?: executorArgument<string>
|
||||
}
|
||||
|
||||
export function isNewFileExecutor(
|
||||
executor: ExecutorConfig
|
||||
): executor is Config {
|
||||
return (executor as Config).templatePath !== undefined
|
||||
}
|
||||
|
||||
export const type = 'new-file'
|
||||
|
||||
interface TempGeneratorOptions extends GeneratorOptions {
|
||||
targetDirectory?: string
|
||||
templateRoot: string
|
||||
templateValues: any
|
||||
}
|
||||
|
||||
class TempGenerator extends Generator<TempGeneratorOptions> {
|
||||
sourceRoot: SourceRootType
|
||||
targetDirectory: string
|
||||
templateValues: any
|
||||
returnResults = true
|
||||
|
||||
constructor(options: TempGeneratorOptions) {
|
||||
super(options)
|
||||
this.sourceRoot = { type: 'absolute', path: options.templateRoot }
|
||||
this.templateValues = options.templateValues
|
||||
this.targetDirectory = options.targetDirectory || '.'
|
||||
}
|
||||
|
||||
getTemplateValues() {
|
||||
return this.templateValues
|
||||
}
|
||||
|
||||
getTargetDirectory() {
|
||||
return this.targetDirectory
|
||||
}
|
||||
}
|
||||
|
||||
export const Commit: Executor['Commit'] = ({
|
||||
cliArgs,
|
||||
cliFlags,
|
||||
onChangeCommitted,
|
||||
step,
|
||||
}) => {
|
||||
const userInput = useUserInput(cliFlags)
|
||||
const generatorArgs = React.useMemo(
|
||||
() => ({
|
||||
destinationRoot: '.',
|
||||
targetDirectory: getExecutorArgument(
|
||||
(step as Config).targetDirectory,
|
||||
cliArgs
|
||||
),
|
||||
templateRoot: getExecutorArgument((step as Config).templatePath, cliArgs),
|
||||
templateValues: getExecutorArgument(
|
||||
(step as Config).templateValues,
|
||||
cliArgs
|
||||
),
|
||||
}),
|
||||
[cliArgs, step]
|
||||
)
|
||||
const [fileCreateOutput, setFileCreateOutput] = useState('')
|
||||
const [changeCommited, setChangeCommited] = useState(false)
|
||||
const fileCreateLines = fileCreateOutput.split('\n')
|
||||
const handleChangeCommitted = React.useCallback(() => {
|
||||
setChangeCommited(true)
|
||||
onChangeCommitted(
|
||||
`Successfully created ${fileCreateLines
|
||||
.map((l) => l.split(' ').slice(1).join('').trim())
|
||||
.join(', ')}`
|
||||
)
|
||||
}, [fileCreateLines, onChangeCommitted])
|
||||
|
||||
useEffect(() => {
|
||||
async function createNewFiles() {
|
||||
if (!fileCreateOutput) {
|
||||
const generator = new TempGenerator(generatorArgs)
|
||||
const results = ((await generator.run()) as unknown) as string
|
||||
setFileCreateOutput(results)
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
createNewFiles()
|
||||
}, [fileCreateOutput, generatorArgs])
|
||||
|
||||
const childProps: CommitChildProps = {
|
||||
changeCommited,
|
||||
fileCreateOutput,
|
||||
handleChangeCommitted,
|
||||
}
|
||||
|
||||
if (userInput) return <CommitWithInput {...childProps} />
|
||||
else return <CommitWithoutInput {...childProps} />
|
||||
}
|
||||
|
||||
interface CommitChildProps {
|
||||
changeCommited: boolean
|
||||
fileCreateOutput: string
|
||||
handleChangeCommitted: () => void
|
||||
}
|
||||
|
||||
const CommitWithInput = ({
|
||||
changeCommited,
|
||||
fileCreateOutput,
|
||||
handleChangeCommitted,
|
||||
}: CommitChildProps) => {
|
||||
useEnterToContinue(
|
||||
handleChangeCommitted,
|
||||
!changeCommited && fileCreateOutput !== ''
|
||||
)
|
||||
|
||||
return (
|
||||
<Box flexDirection="column">
|
||||
{fileCreateOutput !== '' && (
|
||||
<>
|
||||
<Text>{fileCreateOutput}</Text>
|
||||
<EnterToContinue />
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
const CommitWithoutInput = ({
|
||||
changeCommited,
|
||||
fileCreateOutput,
|
||||
handleChangeCommitted,
|
||||
}: CommitChildProps) => {
|
||||
React.useEffect(() => {
|
||||
if (!changeCommited && fileCreateOutput !== '') {
|
||||
handleChangeCommitted()
|
||||
}
|
||||
}, [changeCommited, fileCreateOutput, handleChangeCommitted])
|
||||
|
||||
return (
|
||||
<Box flexDirection="column">
|
||||
{fileCreateOutput !== '' && <Text>{fileCreateOutput}</Text>}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
import { Box, Text } from 'ink'
|
||||
import * as React from 'react'
|
||||
import { EnterToContinue } from '../components/enter-to-continue'
|
||||
import { useEnterToContinue } from '../utils/use-enter-to-continue'
|
||||
import { useUserInput } from '../utils/use-user-input'
|
||||
import {
|
||||
Executor,
|
||||
executorArgument,
|
||||
ExecutorConfig,
|
||||
getExecutorArgument,
|
||||
} from './executor'
|
||||
|
||||
export interface Config extends ExecutorConfig {
|
||||
message: executorArgument<string>
|
||||
}
|
||||
|
||||
export const type = 'print-message'
|
||||
|
||||
export const Commit: Executor['Commit'] = ({
|
||||
cliArgs,
|
||||
cliFlags,
|
||||
onChangeCommitted,
|
||||
step,
|
||||
}) => {
|
||||
const userInput = useUserInput(cliFlags)
|
||||
const generatorArgs = React.useMemo(
|
||||
() => ({
|
||||
message: getExecutorArgument((step as Config).message, cliArgs),
|
||||
stepName: getExecutorArgument((step as Config).stepName, cliArgs),
|
||||
}),
|
||||
[cliArgs, step]
|
||||
)
|
||||
const [changeCommited, setChangeCommited] = React.useState(false)
|
||||
|
||||
const handleChangeCommitted = React.useCallback(() => {
|
||||
setChangeCommited(true)
|
||||
onChangeCommitted(generatorArgs.stepName)
|
||||
}, [onChangeCommitted, generatorArgs])
|
||||
|
||||
const childProps: CommitChildProps = {
|
||||
changeCommited,
|
||||
generatorArgs,
|
||||
handleChangeCommitted,
|
||||
}
|
||||
|
||||
if (userInput) return <CommitWithInput {...childProps} />
|
||||
else return <CommitWithoutInput {...childProps} />
|
||||
}
|
||||
|
||||
interface CommitChildProps {
|
||||
changeCommited: boolean
|
||||
generatorArgs: { message: string; stepName: string }
|
||||
handleChangeCommitted: () => void
|
||||
}
|
||||
|
||||
const CommitWithInput = ({
|
||||
changeCommited,
|
||||
generatorArgs,
|
||||
handleChangeCommitted,
|
||||
}: CommitChildProps) => {
|
||||
useEnterToContinue(handleChangeCommitted, !changeCommited)
|
||||
|
||||
return (
|
||||
<Box flexDirection="column">
|
||||
<Text>{generatorArgs.message}</Text>
|
||||
<EnterToContinue />
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
const CommitWithoutInput = ({
|
||||
changeCommited,
|
||||
generatorArgs,
|
||||
handleChangeCommitted,
|
||||
}: CommitChildProps) => {
|
||||
React.useEffect(() => {
|
||||
if (!changeCommited) {
|
||||
handleChangeCommitted()
|
||||
}
|
||||
}, [changeCommited, handleChangeCommitted])
|
||||
|
||||
return (
|
||||
<Box flexDirection="column">
|
||||
<Text>{generatorArgs.message}</Text>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
161
nextjs/packages/installer/src/executors/run-command-executor.tsx
Normal file
161
nextjs/packages/installer/src/executors/run-command-executor.tsx
Normal file
@@ -0,0 +1,161 @@
|
||||
import { spawn } from 'cross-spawn'
|
||||
import { Box, Text } from 'ink'
|
||||
import Spinner from 'ink-spinner'
|
||||
import * as React from 'react'
|
||||
import { Newline } from '../components/newline'
|
||||
import { RecipeCLIArgs } from '../types'
|
||||
import { useEnterToContinue } from '../utils/use-enter-to-continue'
|
||||
import { useUserInput } from '../utils/use-user-input'
|
||||
import { Executor, ExecutorConfig, getExecutorArgument } from './executor'
|
||||
|
||||
export type CliCommand = string | [string, ...string[]]
|
||||
|
||||
export interface Config extends ExecutorConfig {
|
||||
command: CliCommand
|
||||
}
|
||||
export interface CommitChildProps {
|
||||
commandInstalled: boolean
|
||||
handleChangeCommitted: () => void
|
||||
command: CliCommand
|
||||
cliArgs: RecipeCLIArgs
|
||||
step: Config
|
||||
}
|
||||
|
||||
export const type = 'run-command'
|
||||
|
||||
function Command({
|
||||
command,
|
||||
loading,
|
||||
}: {
|
||||
command: CliCommand
|
||||
loading: boolean
|
||||
}) {
|
||||
return (
|
||||
<Text>
|
||||
{` `}
|
||||
{loading ? <Spinner /> : '✅'}
|
||||
{` ${typeof command === 'string' ? command : command.join(' ')}`}
|
||||
</Text>
|
||||
)
|
||||
}
|
||||
|
||||
const CommandList = ({
|
||||
lede = 'Hang tight! Running...',
|
||||
commandLoading = false,
|
||||
step,
|
||||
command,
|
||||
}: {
|
||||
lede?: string
|
||||
commandLoading?: boolean
|
||||
step: Config
|
||||
command: CliCommand
|
||||
}) => {
|
||||
return (
|
||||
<Box flexDirection="column">
|
||||
<Text>{lede}</Text>
|
||||
<Newline />
|
||||
<Command key={step.stepId} command={command} loading={commandLoading} />
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* INFO: Exported for unit testing purposes
|
||||
*
|
||||
* This function calls the defined command with their optional arguments if defined
|
||||
*
|
||||
* @param {CliCommand} input The Command and arguments
|
||||
* @return Promise<void>
|
||||
*
|
||||
* @example await executeCommand("ls")
|
||||
* @example await executeCommand(["ls"])
|
||||
* @example await executeCommand(["ls", ...["-a", "-l"]])
|
||||
*/
|
||||
export async function executeCommand(input: CliCommand): Promise<void> {
|
||||
// from https://stackoverflow.com/a/43766456/9950655
|
||||
const argsRegex = /("[^"\\]*(?:\\[\S\s][^"\\]*)*"|'[^'\\]*(?:\\[\S\s][^'\\]*)*'|\/[^/\\]*(?:\\[\S\s][^/\\]*)*\/[gimy]*(?=\s|$)|(?:\\\s|\S)+)/g
|
||||
const command: string[] = Array.isArray(input)
|
||||
? input
|
||||
: input.match(argsRegex) || []
|
||||
|
||||
if (command.length === 0) {
|
||||
throw new Error(`The command is too short: \`${JSON.stringify(input)}\``)
|
||||
}
|
||||
|
||||
await new Promise((resolve) => {
|
||||
const cp = spawn(command[0], command.slice(1), {
|
||||
stdio: ['inherit', 'pipe', 'pipe'],
|
||||
})
|
||||
cp.on('exit', resolve)
|
||||
})
|
||||
}
|
||||
|
||||
export const Commit: Executor['Commit'] = ({
|
||||
cliArgs,
|
||||
cliFlags,
|
||||
step,
|
||||
onChangeCommitted,
|
||||
}) => {
|
||||
const userInput = useUserInput(cliFlags)
|
||||
const [commandInstalled, setCommandInstalled] = React.useState(false)
|
||||
const executorCommand = getExecutorArgument((step as Config).command, cliArgs)
|
||||
|
||||
const handleChangeCommitted = React.useCallback(() => {
|
||||
onChangeCommitted(`Executed command ${executorCommand}`)
|
||||
}, [executorCommand, onChangeCommitted])
|
||||
|
||||
React.useEffect(() => {
|
||||
async function runCommand() {
|
||||
await executeCommand(executorCommand)
|
||||
setCommandInstalled(true)
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
runCommand()
|
||||
}, [cliArgs, step, executorCommand])
|
||||
|
||||
React.useEffect(() => {
|
||||
if (commandInstalled) {
|
||||
handleChangeCommitted()
|
||||
}
|
||||
}, [commandInstalled, handleChangeCommitted])
|
||||
|
||||
const childProps: CommitChildProps = {
|
||||
commandInstalled,
|
||||
handleChangeCommitted,
|
||||
command: executorCommand,
|
||||
cliArgs,
|
||||
step: step as Config,
|
||||
}
|
||||
|
||||
if (userInput) return <CommitWithInput {...childProps} />
|
||||
else return <CommitWithoutInput {...childProps} />
|
||||
}
|
||||
|
||||
const CommitWithInput = ({
|
||||
commandInstalled,
|
||||
handleChangeCommitted,
|
||||
command,
|
||||
step,
|
||||
}: CommitChildProps) => {
|
||||
useEnterToContinue(handleChangeCommitted, commandInstalled)
|
||||
|
||||
return (
|
||||
<CommandList
|
||||
commandLoading={!commandInstalled}
|
||||
step={step}
|
||||
command={command}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
const CommitWithoutInput = ({
|
||||
commandInstalled,
|
||||
command,
|
||||
step,
|
||||
}: CommitChildProps) => (
|
||||
<CommandList
|
||||
commandLoading={!commandInstalled}
|
||||
step={step}
|
||||
command={command}
|
||||
/>
|
||||
)
|
||||
12
nextjs/packages/installer/src/index.ts
Normal file
12
nextjs/packages/installer/src/index.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
export * from './recipe-executor'
|
||||
export * from './recipe-builder'
|
||||
export * from './executors/executor'
|
||||
export { type as AddDependencyType } from './executors/add-dependency-executor'
|
||||
export { type as FileTransformType } from './executors/file-transform-executor'
|
||||
export { type as NewFileType } from './executors/new-file-executor'
|
||||
export { type as PrintMessageType } from './executors/print-message-executor'
|
||||
|
||||
export * from './utils/paths'
|
||||
export * from './transforms'
|
||||
export { customTsParser } from './utils/transform'
|
||||
export type { Program, RecipeCLIArgs, RecipeCLIFlags } from './types'
|
||||
97
nextjs/packages/installer/src/recipe-builder.ts
Normal file
97
nextjs/packages/installer/src/recipe-builder.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
import * as AddDependencyExecutor from './executors/add-dependency-executor'
|
||||
import * as TransformFileExecutor from './executors/file-transform-executor'
|
||||
import * as NewFileExecutor from './executors/new-file-executor'
|
||||
import * as PrintMessageExecutor from './executors/print-message-executor'
|
||||
import * as RunCommandExecutor from './executors/run-command-executor'
|
||||
import { ExecutorConfigUnion, RecipeExecutor } from './recipe-executor'
|
||||
import { RecipeMeta } from './types'
|
||||
|
||||
export interface IRecipeBuilder {
|
||||
setName(name: string): IRecipeBuilder
|
||||
setDescription(description: string): IRecipeBuilder
|
||||
printMessage(
|
||||
step: Omit<Omit<PrintMessageExecutor.Config, 'stepType'>, 'explanation'>
|
||||
): IRecipeBuilder
|
||||
setOwner(owner: string): IRecipeBuilder
|
||||
setRepoLink(repoLink: string): IRecipeBuilder
|
||||
addAddDependenciesStep(
|
||||
step: Omit<AddDependencyExecutor.Config, 'stepType'>
|
||||
): IRecipeBuilder
|
||||
addNewFilesStep(
|
||||
step: Omit<NewFileExecutor.Config, 'stepType'>
|
||||
): IRecipeBuilder
|
||||
addTransformFilesStep(
|
||||
step: Omit<TransformFileExecutor.Config, 'stepType'>
|
||||
): IRecipeBuilder
|
||||
addRunCommandStep(
|
||||
step: Omit<RunCommandExecutor.Config, 'stepType'>
|
||||
): IRecipeBuilder
|
||||
|
||||
build(): RecipeExecutor<any>
|
||||
}
|
||||
|
||||
export function RecipeBuilder(): IRecipeBuilder {
|
||||
const steps: ExecutorConfigUnion[] = []
|
||||
const meta: Partial<RecipeMeta> = {}
|
||||
|
||||
return {
|
||||
setName(name: string) {
|
||||
meta.name = name
|
||||
return this
|
||||
},
|
||||
setDescription(description: string) {
|
||||
meta.description = description
|
||||
return this
|
||||
},
|
||||
printMessage(step: Omit<PrintMessageExecutor.Config, 'stepType'>) {
|
||||
steps.push({
|
||||
stepType: PrintMessageExecutor.type,
|
||||
...step,
|
||||
})
|
||||
return this
|
||||
},
|
||||
setOwner(owner: string) {
|
||||
meta.owner = owner
|
||||
return this
|
||||
},
|
||||
setRepoLink(repoLink: string) {
|
||||
meta.repoLink = repoLink
|
||||
return this
|
||||
},
|
||||
addAddDependenciesStep(
|
||||
step: Omit<AddDependencyExecutor.Config, 'stepType'>
|
||||
) {
|
||||
steps.push({
|
||||
stepType: AddDependencyExecutor.type,
|
||||
...step,
|
||||
})
|
||||
return this
|
||||
},
|
||||
addNewFilesStep(step: Omit<NewFileExecutor.Config, 'stepType'>) {
|
||||
steps.push({
|
||||
stepType: NewFileExecutor.type,
|
||||
...step,
|
||||
})
|
||||
return this
|
||||
},
|
||||
addTransformFilesStep(
|
||||
step: Omit<TransformFileExecutor.Config, 'stepType'>
|
||||
) {
|
||||
steps.push({
|
||||
stepType: TransformFileExecutor.type,
|
||||
...step,
|
||||
})
|
||||
return this
|
||||
},
|
||||
addRunCommandStep(step: Omit<RunCommandExecutor.Config, 'stepType'>) {
|
||||
steps.push({
|
||||
stepType: RunCommandExecutor.type,
|
||||
...step,
|
||||
})
|
||||
return this
|
||||
},
|
||||
build() {
|
||||
return new RecipeExecutor(meta as RecipeMeta, steps)
|
||||
},
|
||||
}
|
||||
}
|
||||
52
nextjs/packages/installer/src/recipe-executor.tsx
Normal file
52
nextjs/packages/installer/src/recipe-executor.tsx
Normal file
@@ -0,0 +1,52 @@
|
||||
import { render } from 'ink'
|
||||
import { baseLogger } from 'next/dist/server/lib/logging'
|
||||
import React from 'react'
|
||||
import * as AddDependencyExecutor from './executors/add-dependency-executor'
|
||||
import * as FileTransformExecutor from './executors/file-transform-executor'
|
||||
import * as NewFileExecutor from './executors/new-file-executor'
|
||||
import * as PrintMessageExecutor from './executors/print-message-executor'
|
||||
import { RecipeRenderer } from './recipe-renderer'
|
||||
import { RecipeCLIArgs, RecipeCLIFlags, RecipeMeta } from './types'
|
||||
// const debug = require('debug')("blitz:installer")
|
||||
|
||||
type ExecutorConfig =
|
||||
| AddDependencyExecutor.Config
|
||||
| FileTransformExecutor.Config
|
||||
| NewFileExecutor.Config
|
||||
| PrintMessageExecutor.Config
|
||||
|
||||
export type { ExecutorConfig as ExecutorConfigUnion }
|
||||
|
||||
export class RecipeExecutor<Options extends RecipeMeta> {
|
||||
private readonly steps: ExecutorConfig[]
|
||||
private readonly options: Options
|
||||
|
||||
constructor(options: Options, steps: ExecutorConfig[]) {
|
||||
this.options = options
|
||||
this.steps = steps
|
||||
}
|
||||
|
||||
async run(
|
||||
cliArgs: RecipeCLIArgs = {},
|
||||
cliFlags: RecipeCLIFlags = { yesToAll: false }
|
||||
): Promise<void> {
|
||||
try {
|
||||
const { waitUntilExit } = render(
|
||||
<RecipeRenderer
|
||||
cliArgs={cliArgs}
|
||||
cliFlags={cliFlags}
|
||||
steps={this.steps}
|
||||
recipeMeta={this.options}
|
||||
/>,
|
||||
{ exitOnCtrlC: false }
|
||||
)
|
||||
await waitUntilExit()
|
||||
baseLogger({ displayDateTime: false, displayLogLevel: false }).info(
|
||||
`\n🎉 The ${this.options.name} recipe has been installed!\n`
|
||||
)
|
||||
} catch (e) {
|
||||
baseLogger({ displayDateTime: false }).error(e as any)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
308
nextjs/packages/installer/src/recipe-renderer.tsx
Normal file
308
nextjs/packages/installer/src/recipe-renderer.tsx
Normal file
@@ -0,0 +1,308 @@
|
||||
import { Box, Text, useApp, useInput } from 'ink'
|
||||
import React from 'react'
|
||||
import { EnterToContinue } from './components/enter-to-continue'
|
||||
import { Newline } from './components/newline'
|
||||
import * as AddDependencyExecutor from './executors/add-dependency-executor'
|
||||
import { Executor, ExecutorConfig, Frontmatter } from './executors/executor'
|
||||
import * as FileTransformExecutor from './executors/file-transform-executor'
|
||||
import * as NewFileExecutor from './executors/new-file-executor'
|
||||
import * as PrintMessageExecutor from './executors/print-message-executor'
|
||||
import * as RunCommandExecutor from './executors/run-command-executor'
|
||||
import { RecipeCLIArgs, RecipeCLIFlags, RecipeMeta } from './types'
|
||||
import { useEnterToContinue } from './utils/use-enter-to-continue'
|
||||
import { useUserInput } from './utils/use-user-input'
|
||||
|
||||
enum Action {
|
||||
SkipStep,
|
||||
ProposeChange,
|
||||
ApplyChange,
|
||||
CommitApproved,
|
||||
CompleteChange,
|
||||
}
|
||||
|
||||
enum Status {
|
||||
Pending,
|
||||
Proposed,
|
||||
ReadyToCommit,
|
||||
Committing,
|
||||
Committed,
|
||||
}
|
||||
|
||||
const ExecutorMap: { [key: string]: Executor } = {
|
||||
[AddDependencyExecutor.type]: AddDependencyExecutor,
|
||||
[NewFileExecutor.type]: NewFileExecutor,
|
||||
[PrintMessageExecutor.type]: PrintMessageExecutor,
|
||||
[FileTransformExecutor.type]: FileTransformExecutor,
|
||||
[RunCommandExecutor.type]: RunCommandExecutor,
|
||||
} as const
|
||||
|
||||
interface State {
|
||||
steps: {
|
||||
executor: ExecutorConfig
|
||||
status: Status
|
||||
proposalData?: any
|
||||
successMsg: string
|
||||
}[]
|
||||
current: number
|
||||
}
|
||||
|
||||
function recipeReducer(state: State, action: { type: Action; data?: any }) {
|
||||
const newState = { ...state }
|
||||
switch (action.type) {
|
||||
case Action.ProposeChange:
|
||||
newState.steps[newState.current].status = Status.Proposed
|
||||
break
|
||||
case Action.CommitApproved:
|
||||
newState.steps[newState.current].status = Status.ReadyToCommit
|
||||
newState.steps[newState.current].proposalData = action.data
|
||||
break
|
||||
case Action.ApplyChange:
|
||||
newState.steps[newState.current].status = Status.Committing
|
||||
break
|
||||
case Action.CompleteChange:
|
||||
newState.steps[newState.current].status = Status.Committed
|
||||
newState.steps[newState.current].successMsg = action.data as string
|
||||
newState.current = Math.min(
|
||||
newState.current + 1,
|
||||
newState.steps.length - 1
|
||||
)
|
||||
break
|
||||
case Action.SkipStep:
|
||||
newState.current += 1
|
||||
break
|
||||
}
|
||||
return newState
|
||||
}
|
||||
|
||||
interface RecipeProps {
|
||||
cliArgs: RecipeCLIArgs
|
||||
cliFlags: RecipeCLIFlags
|
||||
steps: ExecutorConfig[]
|
||||
recipeMeta: RecipeMeta
|
||||
}
|
||||
|
||||
const DispatchContext = React.createContext<
|
||||
React.Dispatch<{ type: Action; data?: any }>
|
||||
>(() => {})
|
||||
|
||||
function WelcomeMessage({
|
||||
recipeMeta,
|
||||
enterToContinue = true,
|
||||
}: {
|
||||
recipeMeta: RecipeMeta
|
||||
enterToContinue?: boolean
|
||||
}) {
|
||||
return (
|
||||
<Box flexDirection="column">
|
||||
<Text color="#8a3df0" bold>
|
||||
Recipe: {recipeMeta.name}
|
||||
</Text>
|
||||
<Newline />
|
||||
<Text color="gray">
|
||||
<Text italic>{recipeMeta.description}</Text>
|
||||
</Text>
|
||||
<Newline />
|
||||
<Text color="gray">
|
||||
Repo: <Text italic>{recipeMeta.repoLink}</Text>
|
||||
</Text>
|
||||
<Text color="gray">
|
||||
Author: <Text italic>{recipeMeta.owner}</Text>
|
||||
</Text>
|
||||
{enterToContinue && <EnterToContinue />}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
function StepMessages({ state }: { state: State }) {
|
||||
const messages = state.steps
|
||||
.map((step) => ({
|
||||
msg: step.successMsg,
|
||||
icon: step.executor.successIcon ?? '✅',
|
||||
}))
|
||||
.filter((s) => s.msg)
|
||||
|
||||
return (
|
||||
<>
|
||||
{messages.map(({ msg, icon }, index) => (
|
||||
<Text key={msg + index} color="green">
|
||||
{msg === '\n' ? '' : icon} {msg}
|
||||
</Text>
|
||||
))}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function StepExecutor({
|
||||
cliArgs,
|
||||
cliFlags,
|
||||
proposalData,
|
||||
step,
|
||||
status,
|
||||
}: {
|
||||
step: ExecutorConfig
|
||||
status: Status
|
||||
cliArgs: RecipeCLIArgs
|
||||
cliFlags: RecipeCLIFlags
|
||||
proposalData?: any
|
||||
}) {
|
||||
const { Propose, Commit }: Executor = ExecutorMap[step.stepType]
|
||||
const dispatch = React.useContext(DispatchContext)
|
||||
|
||||
const handleProposalAccepted = React.useCallback(
|
||||
(msg) => {
|
||||
dispatch({ type: Action.CommitApproved, data: msg })
|
||||
},
|
||||
[dispatch]
|
||||
)
|
||||
const handleChangeCommitted = React.useCallback(
|
||||
(msg) => {
|
||||
dispatch({ type: Action.CompleteChange, data: msg })
|
||||
},
|
||||
[dispatch]
|
||||
)
|
||||
|
||||
React.useEffect(() => {
|
||||
if (status === Status.Pending) {
|
||||
dispatch({ type: Action.ProposeChange })
|
||||
} else if (status === Status.ReadyToCommit) {
|
||||
dispatch({ type: Action.ApplyChange })
|
||||
}
|
||||
if (status === Status.Proposed && !Propose) {
|
||||
dispatch({ type: Action.CommitApproved })
|
||||
}
|
||||
}, [dispatch, status, Propose])
|
||||
|
||||
return (
|
||||
<Box flexDirection="column">
|
||||
{status !== Status.Committed ? <Frontmatter executor={step} /> : null}
|
||||
{[Status.Proposed].includes(status) && Propose ? (
|
||||
<Propose
|
||||
cliArgs={cliArgs}
|
||||
cliFlags={cliFlags}
|
||||
step={step}
|
||||
onProposalAccepted={handleProposalAccepted}
|
||||
/>
|
||||
) : null}
|
||||
{[Status.Committing].includes(status) ? (
|
||||
<Commit
|
||||
cliArgs={cliArgs}
|
||||
cliFlags={cliFlags}
|
||||
proposalData={proposalData}
|
||||
step={step}
|
||||
onChangeCommitted={handleChangeCommitted}
|
||||
/>
|
||||
) : null}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export function RecipeRenderer({
|
||||
cliArgs,
|
||||
cliFlags,
|
||||
steps,
|
||||
recipeMeta,
|
||||
}: RecipeProps) {
|
||||
const userInput = useUserInput(cliFlags)
|
||||
const { exit } = useApp()
|
||||
const [state, dispatch] = React.useReducer(recipeReducer, {
|
||||
current: userInput ? -1 : 0,
|
||||
steps: steps.map((e) => ({
|
||||
executor: e,
|
||||
status: Status.Pending,
|
||||
successMsg: '',
|
||||
})),
|
||||
})
|
||||
|
||||
if (steps.length === 0) {
|
||||
exit(new Error('This recipe has no steps'))
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
if (
|
||||
state.current === state.steps.length - 1 &&
|
||||
state.steps[state.current]?.status === Status.Committed
|
||||
) {
|
||||
exit()
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<DispatchContext.Provider value={dispatch}>
|
||||
{userInput ? (
|
||||
<RecipeRendererWithInput
|
||||
cliArgs={cliArgs}
|
||||
cliFlags={cliFlags}
|
||||
state={state}
|
||||
recipeMeta={recipeMeta}
|
||||
/>
|
||||
) : (
|
||||
<RecipeRendererWithoutInput
|
||||
cliArgs={cliArgs}
|
||||
cliFlags={cliFlags}
|
||||
state={state}
|
||||
recipeMeta={recipeMeta}
|
||||
/>
|
||||
)}
|
||||
</DispatchContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
function RecipeRendererWithInput({
|
||||
cliArgs,
|
||||
cliFlags,
|
||||
recipeMeta,
|
||||
state,
|
||||
}: Omit<RecipeProps, 'steps'> & { state: State }) {
|
||||
const { exit } = useApp()
|
||||
const dispatch = React.useContext(DispatchContext)
|
||||
|
||||
useInput((input, key) => {
|
||||
if (input === 'c' && key.ctrl) {
|
||||
exit(new Error('You aborted installation'))
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
useEnterToContinue(
|
||||
() => dispatch({ type: Action.SkipStep }),
|
||||
state.current === -1
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<StepMessages state={state} />
|
||||
{state.current === -1 ? (
|
||||
<WelcomeMessage recipeMeta={recipeMeta} />
|
||||
) : (
|
||||
<StepExecutor
|
||||
cliArgs={cliArgs}
|
||||
cliFlags={cliFlags}
|
||||
proposalData={state.steps[state.current]?.proposalData}
|
||||
step={state.steps[state.current]?.executor}
|
||||
status={state.steps[state.current]?.status}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function RecipeRendererWithoutInput({
|
||||
cliArgs,
|
||||
cliFlags,
|
||||
recipeMeta,
|
||||
state,
|
||||
}: Omit<RecipeProps, 'steps'> & { state: State }) {
|
||||
return (
|
||||
<>
|
||||
<WelcomeMessage recipeMeta={recipeMeta} enterToContinue={false} />
|
||||
<StepMessages state={state} />
|
||||
<StepExecutor
|
||||
cliArgs={cliArgs}
|
||||
cliFlags={cliFlags}
|
||||
proposalData={state.steps[state.current]?.proposalData}
|
||||
step={state.steps[state.current]?.executor}
|
||||
status={state.steps[state.current]?.status}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import type { ExpressionKind } from 'ast-types/gen/kinds'
|
||||
import j from 'jscodeshift'
|
||||
import { Program } from '../types'
|
||||
import { transformBlitzConfig } from '.'
|
||||
|
||||
export const addBlitzMiddleware = (
|
||||
program: Program,
|
||||
middleware: ExpressionKind
|
||||
): Program =>
|
||||
transformBlitzConfig(program, (config) => {
|
||||
// Locate the middleware property
|
||||
const middlewareProp = config.properties.find(
|
||||
(value) =>
|
||||
value.type === 'ObjectProperty' &&
|
||||
value.key.type === 'Identifier' &&
|
||||
value.key.name === 'middleware'
|
||||
) as j.ObjectProperty | undefined
|
||||
|
||||
if (middlewareProp && middlewareProp.value.type === 'ArrayExpression') {
|
||||
// We found it, pop on our middleware.
|
||||
middlewareProp.value.elements.push(middleware)
|
||||
} else {
|
||||
// No middleware prop, add our own.
|
||||
config.properties.push(
|
||||
j.property('init', j.identifier('middleware'), {
|
||||
type: 'ArrayExpression',
|
||||
elements: [middleware],
|
||||
loc: null,
|
||||
comments: null,
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
return config
|
||||
})
|
||||
@@ -1,10 +1,10 @@
|
||||
import j from "jscodeshift"
|
||||
import {Collection} from "jscodeshift/src/Collection"
|
||||
import j from 'jscodeshift'
|
||||
import { Program } from '../types'
|
||||
|
||||
export function addImport(
|
||||
program: Collection<j.Program>,
|
||||
importToAdd: j.ImportDeclaration,
|
||||
): Collection<j.Program> {
|
||||
program: Program,
|
||||
importToAdd: j.ImportDeclaration
|
||||
): Program {
|
||||
const importStatementCount = program.find(j.ImportDeclaration).length
|
||||
if (importStatementCount === 0) {
|
||||
program.find(j.Statement).at(0).insertBefore(importToAdd)
|
||||
@@ -0,0 +1,14 @@
|
||||
import j from 'jscodeshift'
|
||||
import { Program } from '../types'
|
||||
|
||||
export const findModuleExportsExpressions = (program: Program) =>
|
||||
program.find(j.AssignmentExpression).filter((path) => {
|
||||
const { left, right } = path.value
|
||||
return (
|
||||
left.type === 'MemberExpression' &&
|
||||
left.object.type === 'Identifier' &&
|
||||
left.property.type === 'Identifier' &&
|
||||
left.property.name === 'exports' &&
|
||||
right.type === 'ObjectExpression'
|
||||
)
|
||||
})
|
||||
8
nextjs/packages/installer/src/transforms/index.ts
Normal file
8
nextjs/packages/installer/src/transforms/index.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export * from './add-import'
|
||||
export * from './add-blitz-middleware'
|
||||
export * from './find-module-exports-expressions'
|
||||
export * from './prisma'
|
||||
export * from './transform-blitz-config'
|
||||
export * from './update-babel-config'
|
||||
export * from './with-utilities'
|
||||
export * from './wrap-blitz-config'
|
||||
@@ -1,5 +1,5 @@
|
||||
import {Enum} from "@mrleebo/prisma-ast"
|
||||
import {produceSchema} from "./produce-schema"
|
||||
import { Enum } from '@mrleebo/prisma-ast'
|
||||
import { produceSchema } from './produce-schema'
|
||||
|
||||
/**
|
||||
* Adds an enum to your schema.prisma data model.
|
||||
@@ -19,9 +19,14 @@ import {produceSchema} from "./produce-schema"
|
||||
})
|
||||
* ```
|
||||
*/
|
||||
export function addPrismaEnum(source: string, enumProps: Enum): Promise<string> {
|
||||
export function addPrismaEnum(
|
||||
source: string,
|
||||
enumProps: Enum
|
||||
): Promise<string> {
|
||||
return produceSchema(source, (schema) => {
|
||||
const existing = schema.list.find((x) => x.type === "enum" && x.name === enumProps.name)
|
||||
const existing = schema.list.find(
|
||||
(x) => x.type === 'enum' && x.name === enumProps.name
|
||||
)
|
||||
existing ? Object.assign(existing, enumProps) : schema.list.push(enumProps)
|
||||
})
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import {Field, Model} from "@mrleebo/prisma-ast"
|
||||
import {produceSchema} from "./produce-schema"
|
||||
import { Field, Model } from '@mrleebo/prisma-ast'
|
||||
import { produceSchema } from './produce-schema'
|
||||
|
||||
/**
|
||||
* Adds a field to a model in your schema.prisma data model.
|
||||
@@ -22,13 +22,19 @@ import {produceSchema} from "./produce-schema"
|
||||
export function addPrismaField(
|
||||
source: string,
|
||||
modelName: string,
|
||||
fieldProps: Field,
|
||||
fieldProps: Field
|
||||
): Promise<string> {
|
||||
return produceSchema(source, (schema) => {
|
||||
const model = schema.list.find((x) => x.type === "model" && x.name === modelName) as Model
|
||||
const model = schema.list.find(
|
||||
(x) => x.type === 'model' && x.name === modelName
|
||||
) as Model
|
||||
if (!model) return
|
||||
|
||||
const existing = model.properties.find((x) => x.type === "field" && x.name === fieldProps.name)
|
||||
existing ? Object.assign(existing, fieldProps) : model.properties.push(fieldProps)
|
||||
const existing = model.properties.find(
|
||||
(x) => x.type === 'field' && x.name === fieldProps.name
|
||||
)
|
||||
existing
|
||||
? Object.assign(existing, fieldProps)
|
||||
: model.properties.push(fieldProps)
|
||||
})
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import {Generator} from "@mrleebo/prisma-ast"
|
||||
import {produceSchema} from "./produce-schema"
|
||||
import { Generator } from '@mrleebo/prisma-ast'
|
||||
import { produceSchema } from './produce-schema'
|
||||
|
||||
/**
|
||||
* Adds a generator to your schema.prisma data model.
|
||||
@@ -16,11 +16,16 @@ import {produceSchema} from "./produce-schema"
|
||||
})
|
||||
* ```
|
||||
*/
|
||||
export function addPrismaGenerator(source: string, generatorProps: Generator): Promise<string> {
|
||||
export function addPrismaGenerator(
|
||||
source: string,
|
||||
generatorProps: Generator
|
||||
): Promise<string> {
|
||||
return produceSchema(source, (schema) => {
|
||||
const existing = schema.list.find(
|
||||
(x) => x.type === "generator" && x.name === generatorProps.name,
|
||||
(x) => x.type === 'generator' && x.name === generatorProps.name
|
||||
) as Generator
|
||||
existing ? Object.assign(existing, generatorProps) : schema.list.push(generatorProps)
|
||||
existing
|
||||
? Object.assign(existing, generatorProps)
|
||||
: schema.list.push(generatorProps)
|
||||
})
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import {Model, ModelAttribute} from "@mrleebo/prisma-ast"
|
||||
import {produceSchema} from "./produce-schema"
|
||||
import { Model, ModelAttribute } from '@mrleebo/prisma-ast'
|
||||
import { produceSchema } from './produce-schema'
|
||||
|
||||
/**
|
||||
* Adds a field to a model in your schema.prisma data model.
|
||||
@@ -22,16 +22,20 @@ import {produceSchema} from "./produce-schema"
|
||||
export function addPrismaModelAttribute(
|
||||
source: string,
|
||||
modelName: string,
|
||||
attributeProps: ModelAttribute,
|
||||
attributeProps: ModelAttribute
|
||||
): Promise<string> {
|
||||
return produceSchema(source, (schema) => {
|
||||
const model = schema.list.find((x) => x.type === "model" && x.name === modelName) as Model
|
||||
const model = schema.list.find(
|
||||
(x) => x.type === 'model' && x.name === modelName
|
||||
) as Model
|
||||
if (!model) return
|
||||
|
||||
const existing = model.properties.find(
|
||||
(x) => x.type === "attribute" && x.name === attributeProps.name,
|
||||
(x) => x.type === 'attribute' && x.name === attributeProps.name
|
||||
)
|
||||
|
||||
existing ? Object.assign(existing, attributeProps) : model.properties.push(attributeProps)
|
||||
existing
|
||||
? Object.assign(existing, attributeProps)
|
||||
: model.properties.push(attributeProps)
|
||||
})
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import {Model} from "@mrleebo/prisma-ast"
|
||||
import {produceSchema} from "./produce-schema"
|
||||
import { Model } from '@mrleebo/prisma-ast'
|
||||
import { produceSchema } from './produce-schema'
|
||||
|
||||
/**
|
||||
* Adds an enum to your schema.prisma data model.
|
||||
@@ -16,9 +16,16 @@ import {produceSchema} from "./produce-schema"
|
||||
})
|
||||
* ```
|
||||
*/
|
||||
export function addPrismaModel(source: string, modelProps: Model): Promise<string> {
|
||||
export function addPrismaModel(
|
||||
source: string,
|
||||
modelProps: Model
|
||||
): Promise<string> {
|
||||
return produceSchema(source, (schema) => {
|
||||
const existing = schema.list.find((x) => x.type === "model" && x.name === modelProps.name)
|
||||
existing ? Object.assign(existing, modelProps) : schema.list.push(modelProps)
|
||||
const existing = schema.list.find(
|
||||
(x) => x.type === 'model' && x.name === modelProps.name
|
||||
)
|
||||
existing
|
||||
? Object.assign(existing, modelProps)
|
||||
: schema.list.push(modelProps)
|
||||
})
|
||||
}
|
||||
7
nextjs/packages/installer/src/transforms/prisma/index.ts
Normal file
7
nextjs/packages/installer/src/transforms/prisma/index.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export * from './add-prisma-enum'
|
||||
export * from './add-prisma-field'
|
||||
export * from './add-prisma-generator'
|
||||
export * from './add-prisma-model-attribute'
|
||||
export * from './add-prisma-model'
|
||||
export * from './produce-schema'
|
||||
export * from './set-prisma-data-source'
|
||||
@@ -1,4 +1,4 @@
|
||||
import {printSchema as printer, Schema} from "@mrleebo/prisma-ast"
|
||||
import { printSchema as printer, Schema } from '@mrleebo/prisma-ast'
|
||||
|
||||
/**
|
||||
* Takes the schema.prisma document parsed from @mrleebo/prisma-ast and
|
||||
@@ -1,4 +1,4 @@
|
||||
import {getSchema, printSchema, Schema} from "@mrleebo/prisma-ast"
|
||||
import { getSchema, printSchema, Schema } from '@mrleebo/prisma-ast'
|
||||
|
||||
/**
|
||||
* A file transformer that parses a schema.prisma string, offers you a callback
|
||||
@@ -11,7 +11,7 @@ import {getSchema, printSchema, Schema} from "@mrleebo/prisma-ast"
|
||||
*/
|
||||
export async function produceSchema(
|
||||
source: string,
|
||||
producer: (schema: Schema) => void,
|
||||
producer: (schema: Schema) => void
|
||||
): Promise<string> {
|
||||
const schema = await getSchema(source)
|
||||
producer(schema)
|
||||
@@ -1,5 +1,5 @@
|
||||
import {Datasource} from "@mrleebo/prisma-ast"
|
||||
import {produceSchema} from "./produce-schema"
|
||||
import { Datasource } from '@mrleebo/prisma-ast'
|
||||
import { produceSchema } from './produce-schema'
|
||||
|
||||
/**
|
||||
* Modify the prisma datasource metadata to use the provider and url specified.
|
||||
@@ -23,9 +23,14 @@ import {produceSchema} from "./produce-schema"
|
||||
})
|
||||
* ```
|
||||
*/
|
||||
export function setPrismaDataSource(source: string, datasourceProps: Datasource): Promise<string> {
|
||||
export function setPrismaDataSource(
|
||||
source: string,
|
||||
datasourceProps: Datasource
|
||||
): Promise<string> {
|
||||
return produceSchema(source, (schema) => {
|
||||
const existing = schema.list.find((x) => x.type === "datasource")
|
||||
existing ? Object.assign(existing, datasourceProps) : schema.list.push(datasourceProps)
|
||||
const existing = schema.list.find((x) => x.type === 'datasource')
|
||||
existing
|
||||
? Object.assign(existing, datasourceProps)
|
||||
: schema.list.push(datasourceProps)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
import type { ExpressionKind } from 'ast-types/gen/kinds'
|
||||
import j from 'jscodeshift'
|
||||
import { Program } from '../types'
|
||||
|
||||
function recursiveConfigSearch(
|
||||
program: Program,
|
||||
obj: ExpressionKind
|
||||
): j.ObjectExpression | undefined {
|
||||
// Identifier being a variable name
|
||||
if (obj.type === 'Identifier') {
|
||||
const { node } = j(obj).get()
|
||||
|
||||
// Get the definition of the variable
|
||||
const identifier: j.ASTPath<j.VariableDeclarator> = program
|
||||
.find(j.VariableDeclarator, {
|
||||
id: { name: node.name },
|
||||
})
|
||||
.get()
|
||||
|
||||
// Return what is after the `=`
|
||||
return identifier.value.init
|
||||
? recursiveConfigSearch(program, identifier.value.init)
|
||||
: undefined
|
||||
} else if (obj.type === 'CallExpression') {
|
||||
// If it's an function call (like `withBundleAnalyzer`), get the first argument
|
||||
if (obj.arguments.length === 0) {
|
||||
// If it has no arguments, create an empty object: `{}`
|
||||
let config = j.objectExpression([])
|
||||
obj.arguments.push(config)
|
||||
return config
|
||||
} else {
|
||||
const arg = obj.arguments[0]
|
||||
if (arg.type === 'SpreadElement') return undefined
|
||||
else return recursiveConfigSearch(program, arg)
|
||||
}
|
||||
} else if (obj.type === 'ObjectExpression') {
|
||||
// If it's an object, return it
|
||||
return obj
|
||||
} else {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
export type TransformBlitzConfigCallback = (
|
||||
config: j.ObjectExpression
|
||||
) => j.ObjectExpression
|
||||
|
||||
export function transformBlitzConfig(
|
||||
program: Program,
|
||||
transform: TransformBlitzConfigCallback
|
||||
): Program {
|
||||
let moduleExportsExpressions = program.find(j.AssignmentExpression, {
|
||||
operator: '=',
|
||||
left: { object: { name: 'module' }, property: { name: 'exports' } },
|
||||
right: {},
|
||||
})
|
||||
|
||||
// If there isn't any `module.exports = ...`, create one
|
||||
if (moduleExportsExpressions.length === 0) {
|
||||
let config = j.objectExpression([])
|
||||
|
||||
config = transform(config)
|
||||
|
||||
let moduleExportExpression = j.expressionStatement(
|
||||
j.assignmentExpression(
|
||||
'=',
|
||||
j.memberExpression(j.identifier('module'), j.identifier('exports')),
|
||||
config
|
||||
)
|
||||
)
|
||||
|
||||
program.get().node.program.body.push(moduleExportExpression)
|
||||
} else if (moduleExportsExpressions.length === 1) {
|
||||
let moduleExportsExpression: j.ASTPath<j.AssignmentExpression> = moduleExportsExpressions.get()
|
||||
|
||||
let config: j.ObjectExpression | undefined = recursiveConfigSearch(
|
||||
program,
|
||||
moduleExportsExpression.value.right
|
||||
)
|
||||
|
||||
if (config) {
|
||||
config = transform(config)
|
||||
} else {
|
||||
console.warn(
|
||||
"The configuration couldn't be found, but there is a 'module.exports' inside `blitz.config.js`"
|
||||
)
|
||||
}
|
||||
} else {
|
||||
console.warn("There are multiple 'module.exports' inside 'blitz.config.js'")
|
||||
}
|
||||
|
||||
return program
|
||||
}
|
||||
133
nextjs/packages/installer/src/transforms/update-babel-config.ts
Normal file
133
nextjs/packages/installer/src/transforms/update-babel-config.ts
Normal file
@@ -0,0 +1,133 @@
|
||||
import type { ExpressionKind } from 'ast-types/gen/kinds'
|
||||
import j from 'jscodeshift'
|
||||
import { JsonObject, JsonValue } from '../types'
|
||||
import { Program } from '../types'
|
||||
import { findModuleExportsExpressions } from './find-module-exports-expressions'
|
||||
|
||||
type AddBabelItemDefinition = string | [name: string, options: JsonObject]
|
||||
|
||||
const jsonValueToExpression = (value: JsonValue): ExpressionKind =>
|
||||
typeof value === 'string'
|
||||
? j.stringLiteral(value)
|
||||
: typeof value === 'number'
|
||||
? j.numericLiteral(value)
|
||||
: typeof value === 'boolean'
|
||||
? j.booleanLiteral(value)
|
||||
: value === null
|
||||
? j.nullLiteral()
|
||||
: Array.isArray(value)
|
||||
? j.arrayExpression(value.map(jsonValueToExpression))
|
||||
: j.objectExpression(
|
||||
Object.entries(value)
|
||||
.filter(
|
||||
(entry): entry is [string, JsonValue] => entry[1] !== undefined
|
||||
)
|
||||
.map(([key, value]) =>
|
||||
j.objectProperty(j.stringLiteral(key), jsonValueToExpression(value))
|
||||
)
|
||||
)
|
||||
|
||||
function updateBabelConfig(
|
||||
program: Program,
|
||||
item: AddBabelItemDefinition,
|
||||
key: string
|
||||
): Program {
|
||||
findModuleExportsExpressions(program).forEach((moduleExportsExpression) => {
|
||||
j(moduleExportsExpression)
|
||||
.find(j.ObjectProperty, { key: { name: key } })
|
||||
.forEach((items) => {
|
||||
// Don't add it again if it already exists,
|
||||
// that what this code does. For simplicity,
|
||||
// all the examples will be with key = 'presets'
|
||||
|
||||
const itemName = Array.isArray(item) ? item[0] : item
|
||||
|
||||
if (
|
||||
items.node.value.type === 'Literal' ||
|
||||
items.node.value.type === 'StringLiteral'
|
||||
) {
|
||||
// {
|
||||
// presets: "this-preset"
|
||||
// }
|
||||
if (itemName !== items.node.value.value) {
|
||||
items.node.value = j.arrayExpression([
|
||||
items.node.value,
|
||||
jsonValueToExpression(item),
|
||||
])
|
||||
}
|
||||
} else if (items.node.value.type === 'ArrayExpression') {
|
||||
// {
|
||||
// presets: ["this-preset", "maybe-another", ...]
|
||||
// }
|
||||
// Here, it will return if it find the preset inside the
|
||||
// array, so the last line doesn't push a duplicated preset
|
||||
for (const [i, element] of items.node.value.elements.entries()) {
|
||||
if (!element) continue
|
||||
|
||||
if (
|
||||
element.type === 'Literal' ||
|
||||
element.type === 'StringLiteral'
|
||||
) {
|
||||
// {
|
||||
// presets: [..., "this-preset", ...]
|
||||
// }
|
||||
if (element.value === itemName) return
|
||||
} else if (element.type === 'ArrayExpression') {
|
||||
// {
|
||||
// presets: [..., ["this-preset"], ...]
|
||||
// }
|
||||
if (
|
||||
(element.elements[0]?.type === 'Literal' ||
|
||||
element.elements[0]?.type === 'StringLiteral') &&
|
||||
element.elements[0].value === itemName
|
||||
) {
|
||||
if (
|
||||
element.elements[1]?.type === 'ObjectExpression' &&
|
||||
element.elements[1].properties.length > 0
|
||||
) {
|
||||
// The preset has a config.
|
||||
// ["this-preset", {...}]
|
||||
if (Array.isArray(item)) {
|
||||
// If it has an adittional config, add the new keys
|
||||
// (don't matter if they already exists, let the user handle it later by themself)
|
||||
let obj = element.elements[1]
|
||||
|
||||
for (const key in item[1]) {
|
||||
const value = item[1][key]
|
||||
if (value === undefined) continue
|
||||
obj.properties.push(
|
||||
j.objectProperty(
|
||||
j.stringLiteral(key),
|
||||
jsonValueToExpression(value)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
items.node.value.elements[i] = obj
|
||||
}
|
||||
} else {
|
||||
// The preset has no config.
|
||||
// Its ["this-preset"]
|
||||
items.node.value.elements[i] = jsonValueToExpression(item)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
items.node.value.elements.push(jsonValueToExpression(item))
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
return program
|
||||
}
|
||||
|
||||
export const addBabelPreset = (
|
||||
program: Program,
|
||||
preset: AddBabelItemDefinition
|
||||
): Program => updateBabelConfig(program, preset, 'presets')
|
||||
export const addBabelPlugin = (
|
||||
program: Program,
|
||||
plugin: AddBabelItemDefinition
|
||||
): Program => updateBabelConfig(program, plugin, 'plugins')
|
||||
24
nextjs/packages/installer/src/transforms/with-utilities.ts
Normal file
24
nextjs/packages/installer/src/transforms/with-utilities.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import {
|
||||
CommentKind,
|
||||
TypeAnnotationKind,
|
||||
TSTypeAnnotationKind,
|
||||
} from 'ast-types/gen/kinds'
|
||||
import j from 'jscodeshift'
|
||||
|
||||
export function withComments<
|
||||
Node extends {
|
||||
comments?: CommentKind[] | null
|
||||
}
|
||||
>(node: Node, comments: CommentKind[]): Node {
|
||||
node.comments = comments
|
||||
return node
|
||||
}
|
||||
|
||||
export function withTypeAnnotation<
|
||||
Node extends {
|
||||
typeAnnotation?: TypeAnnotationKind | TSTypeAnnotationKind | null
|
||||
}
|
||||
>(node: Node, type: Parameters<typeof j.tsTypeAnnotation>[0]): Node {
|
||||
node.typeAnnotation = j.tsTypeAnnotation(type)
|
||||
return node
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import j from 'jscodeshift'
|
||||
import { Program } from '../types'
|
||||
|
||||
export function wrapBlitzConfig(
|
||||
program: Program,
|
||||
functionName: string
|
||||
): Program {
|
||||
let moduleExportsExpressions = program.find(j.AssignmentExpression, {
|
||||
operator: '=',
|
||||
left: { object: { name: 'module' }, property: { name: 'exports' } },
|
||||
right: {},
|
||||
})
|
||||
|
||||
// If there isn't any `module.exports = ...`, create one
|
||||
if (moduleExportsExpressions.length === 0) {
|
||||
let moduleExportExpression = j.expressionStatement(
|
||||
j.assignmentExpression(
|
||||
'=',
|
||||
j.memberExpression(j.identifier('module'), j.identifier('exports')),
|
||||
j.callExpression(j.identifier(functionName), [j.objectExpression([])])
|
||||
)
|
||||
)
|
||||
|
||||
program.get().node.program.body.push(moduleExportExpression)
|
||||
} else if (moduleExportsExpressions.length === 1) {
|
||||
let moduleExportsExpression: j.ASTPath<j.AssignmentExpression> = moduleExportsExpressions.get()
|
||||
|
||||
moduleExportsExpression.value.right = j.callExpression(
|
||||
j.identifier(functionName),
|
||||
[moduleExportsExpression.value.right]
|
||||
)
|
||||
} else {
|
||||
console.warn("There are multiple 'module.exports' inside 'blitz.config.js'")
|
||||
}
|
||||
|
||||
return program
|
||||
}
|
||||
41
nextjs/packages/installer/src/types.ts
Normal file
41
nextjs/packages/installer/src/types.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import type * as j from 'jscodeshift'
|
||||
|
||||
export interface RecipeMeta {
|
||||
name: string
|
||||
description: string
|
||||
owner: string
|
||||
repoLink: string
|
||||
}
|
||||
|
||||
export type RecipeCLIArgs = { [Key in string]?: string | true }
|
||||
|
||||
export interface RecipeCLIFlags {
|
||||
yesToAll: boolean
|
||||
}
|
||||
|
||||
export type Program = j.Collection<j.Program>
|
||||
|
||||
/**
|
||||
Matches a JSON object.
|
||||
This type can be useful to enforce some input to be JSON-compatible or as a super-type to be extended from. Don't use this as a direct return type as the user would have to double-cast it: `jsonObject as unknown as CustomResponse`. Instead, you could extend your CustomResponse type from it to ensure your type only uses JSON-compatible types: `interface CustomResponse extends JsonObject { … }`.
|
||||
@see https://github.com/sindresorhus/type-fest
|
||||
*/
|
||||
export type JsonObject = { [Key in string]?: JsonValue }
|
||||
|
||||
/**
|
||||
Matches a JSON array.
|
||||
@see https://github.com/sindresorhus/type-fest
|
||||
*/
|
||||
export type JsonArray = JsonValue[]
|
||||
|
||||
/**
|
||||
Matches any valid JSON primitive value.
|
||||
@see https://github.com/sindresorhus/type-fest
|
||||
*/
|
||||
export type JsonPrimitive = string | number | boolean | null
|
||||
|
||||
/**
|
||||
Matches any valid JSON value.
|
||||
@see https://github.com/sindresorhus/type-fest
|
||||
*/
|
||||
export type JsonValue = JsonPrimitive | JsonObject | JsonArray
|
||||
@@ -1,8 +1,12 @@
|
||||
import * as fs from "fs-extra"
|
||||
import * as path from "path"
|
||||
import * as fs from 'fs-extra'
|
||||
import * as path from 'path'
|
||||
|
||||
function ext(jsx = false) {
|
||||
return fs.existsSync(path.resolve("tsconfig.json")) ? (jsx ? ".tsx" : ".ts") : ".js"
|
||||
return fs.existsSync(path.resolve('tsconfig.json'))
|
||||
? jsx
|
||||
? '.tsx'
|
||||
: '.ts'
|
||||
: '.js'
|
||||
}
|
||||
|
||||
export const paths = {
|
||||
@@ -16,15 +20,15 @@ export const paths = {
|
||||
return `app/pages/index${ext(true)}`
|
||||
},
|
||||
babelConfig() {
|
||||
return "babel.config.js"
|
||||
return 'babel.config.js'
|
||||
},
|
||||
blitzConfig() {
|
||||
return `blitz.config${ext()}`
|
||||
},
|
||||
packageJson() {
|
||||
return "package.json"
|
||||
return 'package.json'
|
||||
},
|
||||
prismaSchema() {
|
||||
return "db/schema.prisma"
|
||||
return 'db/schema.prisma'
|
||||
},
|
||||
}
|
||||
@@ -1,21 +1,21 @@
|
||||
import * as fs from "fs-extra"
|
||||
import j from "jscodeshift"
|
||||
import {Collection} from "jscodeshift/src/Collection"
|
||||
import getBabelOptions, {Overrides} from "recast/parsers/_babel_options"
|
||||
import * as babel from "recast/parsers/babel"
|
||||
import * as fs from 'fs-extra'
|
||||
import j from 'jscodeshift'
|
||||
import getBabelOptions, { Overrides } from 'recast/parsers/_babel_options'
|
||||
import * as babel from 'recast/parsers/babel'
|
||||
import { Program } from '../types'
|
||||
|
||||
export const customTsParser = {
|
||||
parse(source: string, options?: Overrides) {
|
||||
const babelOptions = getBabelOptions(options)
|
||||
babelOptions.plugins.push("typescript")
|
||||
babelOptions.plugins.push("jsx")
|
||||
babelOptions.plugins.push('typescript')
|
||||
babelOptions.plugins.push('jsx')
|
||||
return babel.parser.parse(source, babelOptions)
|
||||
},
|
||||
}
|
||||
|
||||
export enum TransformStatus {
|
||||
Success = "success",
|
||||
Failure = "failure",
|
||||
Success = 'success',
|
||||
Failure = 'failure',
|
||||
}
|
||||
export interface TransformResult {
|
||||
status: TransformStatus
|
||||
@@ -24,25 +24,26 @@ export interface TransformResult {
|
||||
}
|
||||
|
||||
export type StringTransformer = (program: string) => string | Promise<string>
|
||||
export type Transformer = (
|
||||
program: Collection<j.Program>,
|
||||
) => Collection<j.Program> | Promise<Collection<j.Program>>
|
||||
export type Transformer = (program: Program) => Program | Promise<Program>
|
||||
|
||||
export function stringProcessFile(
|
||||
original: string,
|
||||
transformerFn: StringTransformer,
|
||||
transformerFn: StringTransformer
|
||||
): string | Promise<string> {
|
||||
return transformerFn(original)
|
||||
}
|
||||
|
||||
export async function processFile(original: string, transformerFn: Transformer): Promise<string> {
|
||||
const program = j(original, {parser: customTsParser})
|
||||
export async function processFile(
|
||||
original: string,
|
||||
transformerFn: Transformer
|
||||
): Promise<string> {
|
||||
const program = j(original, { parser: customTsParser })
|
||||
return (await transformerFn(program)).toSource()
|
||||
}
|
||||
|
||||
export async function transform(
|
||||
processFile: (original: string) => Promise<string>,
|
||||
targetFilePaths: string[],
|
||||
targetFilePaths: string[]
|
||||
): Promise<TransformResult[]> {
|
||||
const results: TransformResult[] = []
|
||||
for (const filePath of targetFilePaths) {
|
||||
@@ -55,7 +56,7 @@ export async function transform(
|
||||
}
|
||||
try {
|
||||
const fileBuffer = fs.readFileSync(filePath)
|
||||
const fileSource = fileBuffer.toString("utf-8")
|
||||
const fileSource = fileBuffer.toString('utf-8')
|
||||
const transformedCode = await processFile(fileSource)
|
||||
fs.writeFileSync(filePath, transformedCode)
|
||||
results.push({
|
||||
12
nextjs/packages/installer/src/utils/use-enter-to-continue.ts
Normal file
12
nextjs/packages/installer/src/utils/use-enter-to-continue.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { useInput } from 'ink'
|
||||
|
||||
export function useEnterToContinue(
|
||||
cb: Function,
|
||||
additionalCondition: boolean = true
|
||||
) {
|
||||
useInput((_input, key) => {
|
||||
if (additionalCondition && key.return) {
|
||||
cb()
|
||||
}
|
||||
})
|
||||
}
|
||||
7
nextjs/packages/installer/src/utils/use-user-input.ts
Normal file
7
nextjs/packages/installer/src/utils/use-user-input.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { useStdin } from 'ink'
|
||||
import { RecipeCLIFlags } from '../types'
|
||||
|
||||
export function useUserInput(cliFlags: RecipeCLIFlags) {
|
||||
const { isRawModeSupported } = useStdin()
|
||||
return isRawModeSupported && !cliFlags.yesToAll
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
import { spawn } from 'cross-spawn'
|
||||
import { existsSync } from 'fs-extra'
|
||||
import { mocked } from 'ts-jest/utils'
|
||||
import * as AddDependencyExecutor from '../../src/executors/add-dependency-executor'
|
||||
|
||||
jest.mock('fs-extra')
|
||||
jest.mock('cross-spawn')
|
||||
|
||||
describe('add dependency executor', () => {
|
||||
const testConfiguration = {
|
||||
stepId: 'addDependencies',
|
||||
stepName: 'Add dependencies',
|
||||
stepType: 'add-dependency',
|
||||
explanation: 'This step will add some dependencies for testing purposes',
|
||||
packages: [{ name: 'typescript', version: '4' }, { name: 'ts-node' }],
|
||||
}
|
||||
|
||||
it('should properly identify executor', () => {
|
||||
const wrongConfiguration = {
|
||||
stepId: 'wrongStep',
|
||||
stepName: 'Wrong Step',
|
||||
stepType: 'wrong-type',
|
||||
explanation: 'This step is wrong',
|
||||
}
|
||||
expect(
|
||||
AddDependencyExecutor.isAddDependencyExecutor(wrongConfiguration)
|
||||
).toBeFalsy()
|
||||
expect(
|
||||
AddDependencyExecutor.isAddDependencyExecutor(testConfiguration)
|
||||
).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should choose proper package manager according to lock file', () => {
|
||||
mocked(existsSync).mockReturnValueOnce(true)
|
||||
expect(AddDependencyExecutor.getPackageManager()).toEqual('yarn')
|
||||
expect(AddDependencyExecutor.getPackageManager()).toEqual('npm')
|
||||
})
|
||||
|
||||
it('should issue proper commands according to the specified packages', async () => {
|
||||
const mockedSpawn = mockSpawn()
|
||||
mocked(spawn).mockImplementation(mockedSpawn.spawn as any)
|
||||
|
||||
// NPM
|
||||
mocked(existsSync).mockReturnValue(false)
|
||||
await AddDependencyExecutor.installPackages(
|
||||
testConfiguration.packages,
|
||||
true
|
||||
)
|
||||
await AddDependencyExecutor.installPackages(
|
||||
testConfiguration.packages,
|
||||
false
|
||||
)
|
||||
|
||||
// Yarn
|
||||
mocked(existsSync).mockReturnValue(true)
|
||||
await AddDependencyExecutor.installPackages(
|
||||
testConfiguration.packages,
|
||||
true
|
||||
)
|
||||
await AddDependencyExecutor.installPackages(
|
||||
testConfiguration.packages,
|
||||
false
|
||||
)
|
||||
|
||||
expect(mockedSpawn.calls.length).toEqual(4)
|
||||
expect(mockedSpawn.calls[0]).toEqual(
|
||||
'npm install --save-dev typescript@4 ts-node'
|
||||
)
|
||||
expect(mockedSpawn.calls[1]).toEqual('npm install typescript@4 ts-node')
|
||||
expect(mockedSpawn.calls[2]).toEqual('yarn add -D typescript@4 ts-node')
|
||||
expect(mockedSpawn.calls[3]).toEqual('yarn add typescript@4 ts-node')
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
* Primitive mock of spawn function
|
||||
*/
|
||||
const mockSpawn = () => {
|
||||
let calls: string[] = []
|
||||
|
||||
return {
|
||||
spawn: (command: string, args: string[], _: unknown = {}) => {
|
||||
calls.push(`${command} ${args.join(' ')}`)
|
||||
|
||||
return {
|
||||
on: (_: string, resolve: () => void) => resolve(),
|
||||
}
|
||||
},
|
||||
calls,
|
||||
}
|
||||
}
|
||||
25
nextjs/packages/installer/test/executors/executor.test.tsx
Normal file
25
nextjs/packages/installer/test/executors/executor.test.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import { render } from 'ink-testing-library'
|
||||
import React from 'react'
|
||||
import stripAnsi from 'strip-ansi'
|
||||
import { Frontmatter } from '../../src/executors/executor'
|
||||
|
||||
describe('Executor', () => {
|
||||
const executorConfig = {
|
||||
stepId: 'newFile',
|
||||
stepName: 'New File',
|
||||
stepType: 'new-file',
|
||||
explanation: 'Testing text for a new file',
|
||||
}
|
||||
it('should render Frontmatter', () => {
|
||||
const { lastFrame } = render(<Frontmatter executor={executorConfig} />)
|
||||
|
||||
expect(stripAnsi(lastFrame())).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('should contain a step name and explanation', () => {
|
||||
const { frames } = render(<Frontmatter executor={executorConfig} />)
|
||||
|
||||
expect(frames[0].includes('New File')).toBeTruthy()
|
||||
expect(frames[0].includes('Testing text for a new file')).toBeTruthy()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,39 @@
|
||||
import { render } from 'ink-testing-library'
|
||||
import React from 'react'
|
||||
import stripAnsi from 'strip-ansi'
|
||||
import { Commit as PrintMessageExecutor } from '../../src/executors/print-message-executor'
|
||||
|
||||
describe('Executor', () => {
|
||||
const executorConfig = {
|
||||
stepId: 'printMessage',
|
||||
stepName: 'Print message',
|
||||
stepType: 'print-message',
|
||||
explanation: 'Testing text for a print message',
|
||||
message: 'My message',
|
||||
}
|
||||
it('should render PrintMessageExecutor', () => {
|
||||
const { lastFrame } = render(
|
||||
<PrintMessageExecutor
|
||||
cliArgs={null}
|
||||
cliFlags={{ yesToAll: false }}
|
||||
onChangeCommitted={() => {}}
|
||||
step={executorConfig}
|
||||
/>
|
||||
)
|
||||
|
||||
expect(stripAnsi(lastFrame())).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('should contain a step name and explanation', () => {
|
||||
const { frames } = render(
|
||||
<PrintMessageExecutor
|
||||
cliArgs={null}
|
||||
cliFlags={{ yesToAll: false }}
|
||||
onChangeCommitted={() => {}}
|
||||
step={executorConfig}
|
||||
/>
|
||||
)
|
||||
|
||||
expect(frames[0].includes('My message')).toBeTruthy()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,59 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`transformBlitzConfig transform empty file 1`] = `"module.exports = {};"`;
|
||||
|
||||
exports[`transformBlitzConfig transform module.exports 1`] = `
|
||||
"module.exports = {
|
||||
test: true
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`transformBlitzConfig transform the config file from examples/auth 1`] = `
|
||||
"import {sessionMiddleware, simpleRolesIsAuthorized} from \\"blitz\\"
|
||||
import db from \\"db\\"
|
||||
const withBundleAnalyzer = require(\\"@next/bundle-analyzer\\")({
|
||||
enabled: process.env.ANALYZE === \\"true\\",
|
||||
})
|
||||
|
||||
module.exports = withBundleAnalyzer({
|
||||
middleware: [
|
||||
sessionMiddleware({
|
||||
cookiePrefix: \\"blitz-auth-example\\",
|
||||
isAuthorized: simpleRolesIsAuthorized,
|
||||
// sessionExpiryMinutes: 4,
|
||||
getSession: (handle) => db.session.findFirst({where: {handle}}),
|
||||
}),
|
||||
],
|
||||
cli: {
|
||||
clearConsoleOnBlitzDev: true
|
||||
},
|
||||
codegen: {
|
||||
templateDir: \\"my-templates\\",
|
||||
},
|
||||
log: {
|
||||
// level: \\"trace\\",
|
||||
},
|
||||
experimental: {
|
||||
initServer() {
|
||||
console.log(\\"Hello world from initServer\\")
|
||||
},
|
||||
},
|
||||
/*
|
||||
webpack: (config, {buildId, dev, isServer, defaultLoaders, webpack}) => {
|
||||
// Note: we provide webpack above so you should not \`require\` it
|
||||
// Perform customizations to webpack config
|
||||
// Important: return the modified config
|
||||
return config
|
||||
},
|
||||
webpackDevMiddleware: (config) => {
|
||||
// Perform customizations to webpack dev middleware config
|
||||
// Important: return the modified config
|
||||
return config
|
||||
},
|
||||
*/
|
||||
})"
|
||||
`;
|
||||
|
||||
exports[`transformBlitzConfig transform with empty wrapper 1`] = `"module.exports = withBundleAnalyzer({})"`;
|
||||
|
||||
exports[`transformBlitzConfig transform with wrapper 1`] = `"module.exports = withBundleAnalyzer({})"`;
|
||||
@@ -0,0 +1,47 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`addBabelPlugin transform adds babel plugin array 1`] = `
|
||||
"module.exports = {
|
||||
presets: [\\"@babel/preset-typescript\\"],
|
||||
plugins: [[\\"@babel/plugin-proposal-decorators\\", {
|
||||
\\"legacy\\": true
|
||||
}]],
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`addBabelPlugin transform adds babel plugin literal 1`] = `
|
||||
"module.exports = {
|
||||
presets: [\\"@babel/preset-typescript\\"],
|
||||
plugins: [\\"@emotion\\"],
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`addBabelPlugin transform avoid duplicated 1`] = `
|
||||
"module.exports = {
|
||||
presets: [\\"@babel/preset-typescript\\"],
|
||||
plugins: [\\"@babel/plugin-proposal-decorators\\"],
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`addBabelPreset transform adds babel preset array 1`] = `
|
||||
"module.exports = {
|
||||
presets: [\\"@babel/preset-typescript\\", [\\"blitz/babel\\", {
|
||||
\\"legacy\\": true
|
||||
}]],
|
||||
plugins: [],
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`addBabelPreset transform adds babel preset literal 1`] = `
|
||||
"module.exports = {
|
||||
presets: [\\"@babel/preset-typescript\\", \\"blitz/babel\\"],
|
||||
plugins: [],
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`addBabelPreset transform avoid duplicated 1`] = `
|
||||
"module.exports = {
|
||||
presets: [[\\"blitz/babel\\", {legacy: true}]],
|
||||
plugins: [],
|
||||
}"
|
||||
`;
|
||||
36
nextjs/packages/installer/test/transforms/add-import.test.ts
Normal file
36
nextjs/packages/installer/test/transforms/add-import.test.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { addImport, customTsParser } from '@blitzjs/installer'
|
||||
import j from 'jscodeshift'
|
||||
|
||||
function executeImport(
|
||||
fileStr: string,
|
||||
importStatement: j.ImportDeclaration
|
||||
): string {
|
||||
return addImport(
|
||||
j(fileStr, { parser: customTsParser }),
|
||||
importStatement
|
||||
).toSource({ tabWidth: 60 })
|
||||
}
|
||||
|
||||
describe('addImport transform', () => {
|
||||
it('adds import at start of file with no imports present', () => {
|
||||
const file = `export const truth = () => 42`
|
||||
const importStatement = j.importDeclaration(
|
||||
[j.importDefaultSpecifier(j.identifier('React'))],
|
||||
j.literal('react')
|
||||
)
|
||||
expect(executeImport(file, importStatement)).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('adds import at the end of all imports if imports are present', () => {
|
||||
const file = `import React from 'react'
|
||||
|
||||
export default function Comp() {
|
||||
return <div>hello world!</div>
|
||||
}`
|
||||
const importStatement = j.importDeclaration(
|
||||
[],
|
||||
j.literal('app/styles/app.css')
|
||||
)
|
||||
expect(executeImport(file, importStatement)).toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user