Compare commits
465 Commits
blitz@2.0.
...
v0.9.2-can
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d76fc7494c | ||
|
|
33e28877e9 | ||
|
|
114ae77bec | ||
|
|
4cf74b19b5 | ||
|
|
96b12eabe3 | ||
|
|
d51b92bbec | ||
|
|
5e4227181b | ||
|
|
7761f63534 | ||
|
|
f1881993dd | ||
|
|
7cfc586667 | ||
|
|
19e4620c23 | ||
|
|
3a407b7c08 | ||
|
|
6eb07b688f | ||
|
|
c147048e6d | ||
|
|
750cba1b19 | ||
|
|
ffaba32b12 | ||
|
|
dedad9e55a | ||
|
|
ea91de84c0 | ||
|
|
944ec4576b | ||
|
|
96b8f69464 | ||
|
|
17a43b8001 | ||
|
|
cbce0c7ac1 | ||
|
|
0117198864 | ||
|
|
150d320e7f | ||
|
|
290702e7be | ||
|
|
75a7feabf9 | ||
|
|
603e5cbaf2 | ||
|
|
3c52246478 | ||
|
|
3acf590e1c | ||
|
|
060cfac2f8 | ||
|
|
3e5d2f4c6f | ||
|
|
623c4b37fc | ||
|
|
6e85653f82 | ||
|
|
8e5e2b3cbd | ||
|
|
094df2a7bb | ||
|
|
47155fbec3 | ||
|
|
96ef3fe6b7 | ||
|
|
6cd9d9496c | ||
|
|
223ab891db | ||
|
|
25c5e05ea3 | ||
|
|
aad5ab772f | ||
|
|
4651ae021a | ||
|
|
8abcc8e78c | ||
|
|
44532424a9 | ||
|
|
bea6fdc0ec | ||
|
|
b6b110fd10 | ||
|
|
3339b131e1 | ||
|
|
58d1bcf2f9 | ||
|
|
c5008e263a | ||
|
|
ef451feb6a | ||
|
|
2bf589d4f8 | ||
|
|
fd8a29f9cd | ||
|
|
f0517e072d | ||
|
|
6cd3abe6ea | ||
|
|
2d8e33151f | ||
|
|
307f373b42 | ||
|
|
45cbd02370 | ||
|
|
cddba4ef7a | ||
|
|
4988dd7a00 | ||
|
|
ea61978d08 | ||
|
|
a11c1d4191 | ||
|
|
4ac78002ff | ||
|
|
10bb5873cf | ||
|
|
ac30c9f39b | ||
|
|
a5d3f9a493 | ||
|
|
2461caa685 | ||
|
|
1d16593dce | ||
|
|
3790e2cf17 | ||
|
|
92c0f85c82 | ||
|
|
2f2496fa00 | ||
|
|
e12d17b6c1 | ||
|
|
0153bd1444 | ||
|
|
230c363409 | ||
|
|
14a09e1e03 | ||
|
|
229272f772 | ||
|
|
f450692e65 | ||
|
|
d4654c8837 | ||
|
|
85fcebadd5 | ||
|
|
a7fd429e20 | ||
|
|
e688267f3f | ||
|
|
c8043d93d4 | ||
|
|
2c9e8e1318 | ||
|
|
386a7fdbe5 | ||
|
|
1b02b5fb39 | ||
|
|
ff097bd67b | ||
|
|
5a6f73b140 | ||
|
|
aa64c5e467 | ||
|
|
74487a37d5 | ||
|
|
50a2bd1db7 | ||
|
|
d1c2d44f94 | ||
|
|
26ebf8ce48 | ||
|
|
2063a1878f | ||
|
|
6911a2c60e | ||
|
|
a44dea27ac | ||
|
|
21f29d23ec | ||
|
|
83fbd5827b | ||
|
|
3b595320aa | ||
|
|
e10f1253db | ||
|
|
b7331375df | ||
|
|
b249ce04a6 | ||
|
|
aa5eb90952 | ||
|
|
6819da290b | ||
|
|
fd1cd78a05 | ||
|
|
2b79c3f3bd | ||
|
|
e9ae33700c | ||
|
|
bec4a65d36 | ||
|
|
b7a8667003 | ||
|
|
e6ddb8603f | ||
|
|
d72f2839d7 | ||
|
|
7ec6244884 | ||
|
|
3892b23dfd | ||
|
|
a6c018b93d | ||
|
|
32d05e2447 | ||
|
|
b08de105ef | ||
|
|
b0120d718d | ||
|
|
f8c0f122b9 | ||
|
|
bfd4642955 | ||
|
|
2fb9abce86 | ||
|
|
a406c82607 | ||
|
|
ae15456576 | ||
|
|
99b4cd628c | ||
|
|
2552a99e6d | ||
|
|
a2b94603a0 | ||
|
|
e846560b3b | ||
|
|
fb298bb0c0 | ||
|
|
b417a6da83 | ||
|
|
c3f0609b0d | ||
|
|
6e1b8d63e4 | ||
|
|
1774d816a8 | ||
|
|
c16f8e65f3 | ||
|
|
54b716ef23 | ||
|
|
1e0cd973e0 | ||
|
|
d1d3c97303 | ||
|
|
3f428e3cb3 | ||
|
|
44c48839e0 | ||
|
|
79d9619c3d | ||
|
|
7b0a483c7b | ||
|
|
a33005e7c5 | ||
|
|
cec8b6130d | ||
|
|
9340d8dec9 | ||
|
|
d29e62965f | ||
|
|
9531593463 | ||
|
|
6fabfc41ba | ||
|
|
dd97061ac1 | ||
|
|
86ace11282 | ||
|
|
75816d11fd | ||
|
|
612c09f9b3 | ||
|
|
303ce52757 | ||
|
|
9e663e1c37 | ||
|
|
268f8bb4c0 | ||
|
|
01c016ab7b | ||
|
|
746eb7508c | ||
|
|
aeed847926 | ||
|
|
3bcc1de895 | ||
|
|
6f3ef059af | ||
|
|
523ce93402 | ||
|
|
e8f68a5c19 | ||
|
|
d5aa2b77e8 | ||
|
|
a36d943e05 | ||
|
|
7459f691ae | ||
|
|
08e351dcc1 | ||
|
|
ae3c530d8f | ||
|
|
79f1d23604 | ||
|
|
07a97ba992 | ||
|
|
4eb17f088a | ||
|
|
07312c1162 | ||
|
|
f519f159cf | ||
|
|
badd5adaf0 | ||
|
|
15f30d394c | ||
|
|
c2b85a747e | ||
|
|
cb028a6e3e | ||
|
|
192f79e064 | ||
|
|
a8d152ba87 | ||
|
|
875f99a20c | ||
|
|
c73bdc7487 | ||
|
|
040deeed32 | ||
|
|
9c670ef77d | ||
|
|
7db98c3636 | ||
|
|
08195b04e6 | ||
|
|
cdf8cc4a9f | ||
|
|
0c3b8dd17a | ||
|
|
fd624213b3 | ||
|
|
9d91117b97 | ||
|
|
8cd670296e | ||
|
|
14df845e16 | ||
|
|
7b6b024388 | ||
|
|
d736ea11f3 | ||
|
|
9a67da5993 | ||
|
|
ba981694ad | ||
|
|
1eebdb54c6 | ||
|
|
fe1f4b0785 | ||
|
|
63921f3e23 | ||
|
|
69b02ffb54 | ||
|
|
86eec51d24 | ||
|
|
d59c0a4fd3 | ||
|
|
852e32a484 | ||
|
|
052300f09d | ||
|
|
d2f94ff0cb | ||
|
|
262a228cd9 | ||
|
|
9bf36b0421 | ||
|
|
73d2251168 | ||
|
|
2d52ceec7f | ||
|
|
c8527d93f9 | ||
|
|
5a69c49976 | ||
|
|
49044611eb | ||
|
|
2997fcc17d | ||
|
|
6f1900c5e6 | ||
|
|
a342d62cb5 | ||
|
|
5c89d7feb9 | ||
|
|
92cf7b599f | ||
|
|
47d16202c3 | ||
|
|
5fb24faa1a | ||
|
|
815b351f4a | ||
|
|
b7528b2058 | ||
|
|
6af0eb7ea5 | ||
|
|
dcc9c4ddc9 | ||
|
|
e7982a1132 | ||
|
|
64eccf8bb4 | ||
|
|
caad5b44f1 | ||
|
|
4b4d65cb7e | ||
|
|
55fdaa0530 | ||
|
|
fb3893a8d4 | ||
|
|
ca5d6ee0aa | ||
|
|
f17820f81d | ||
|
|
ab6f60571a | ||
|
|
5f3d5dd7a3 | ||
|
|
8b8143d2c2 | ||
|
|
8b6a51f8f1 | ||
|
|
0c23a7873c | ||
|
|
0fa28d20c4 | ||
|
|
97132996b8 | ||
|
|
96ac63d3a4 | ||
|
|
132601f0c2 | ||
|
|
c47d15a09f | ||
|
|
0892137370 | ||
|
|
797060c068 | ||
|
|
1c7134c272 | ||
|
|
2638ec73b3 | ||
|
|
3c4db0453e | ||
|
|
d824459190 | ||
|
|
5da2eae6e1 | ||
|
|
73f6be0814 | ||
|
|
2dfa86556d | ||
|
|
7097150c2d | ||
|
|
84a8ac7512 | ||
|
|
ef48721625 | ||
|
|
a54b48190c | ||
|
|
ff5cab07fc | ||
|
|
02ccc7db0e | ||
|
|
9de79ba621 | ||
|
|
dfac941a80 | ||
|
|
f3f17a2452 | ||
|
|
aa6d0e0dc0 | ||
|
|
4b0b1b8c66 | ||
|
|
7e72dd3ccd | ||
|
|
0aa16e25ae | ||
|
|
9e5d58ddbd | ||
|
|
edd673f54c | ||
|
|
6cccbe3487 | ||
|
|
92069cfef8 | ||
|
|
b16424592f | ||
|
|
92c4a4bfc8 | ||
|
|
ffd87a80af | ||
|
|
9c54cfaef1 | ||
|
|
fd2e9d2988 | ||
|
|
3cc4476a98 | ||
|
|
1a45b2d394 | ||
|
|
bac7f21295 | ||
|
|
9755466f9b | ||
|
|
3a85e83b56 | ||
|
|
4e49239f53 | ||
|
|
f0a56ff2fe | ||
|
|
2a71e3441d | ||
|
|
63cacbb73c | ||
|
|
219d12aaa2 | ||
|
|
b1321c77e1 | ||
|
|
f7685b4abb | ||
|
|
cf215b4a12 | ||
|
|
32f0a76b18 | ||
|
|
e9e8841f07 | ||
|
|
d6b29a2f71 | ||
|
|
af338d99e4 | ||
|
|
bd63e4cab0 | ||
|
|
e387936cb0 | ||
|
|
0650252c53 | ||
|
|
76cfb8c076 | ||
|
|
6273ffd255 | ||
|
|
6e447f1f44 | ||
|
|
4c4721250a | ||
|
|
6852cfc208 | ||
|
|
da488ccf33 | ||
|
|
36d0ae8587 | ||
|
|
8bcaf62641 | ||
|
|
820d191a63 | ||
|
|
45842d48ad | ||
|
|
67ed16429d | ||
|
|
45cb96f9f1 | ||
|
|
9063758292 | ||
|
|
2a892ba4d0 | ||
|
|
73b3920a53 | ||
|
|
fae90065df | ||
|
|
fc762c15fc | ||
|
|
cc10b71f9d | ||
|
|
998d227832 | ||
|
|
35c150a46a | ||
|
|
920716a0d8 | ||
|
|
39675d1da8 | ||
|
|
dd20c0ac6c | ||
|
|
2ea1e7b522 | ||
|
|
44c3ea22e2 | ||
|
|
f4760776ee | ||
|
|
65bb7e8e84 | ||
|
|
8547585cc7 | ||
|
|
16b810e0c3 | ||
|
|
8d38c6c225 | ||
|
|
c00e78cbc7 | ||
|
|
11325ba136 | ||
|
|
477094d042 | ||
|
|
2c91340539 | ||
|
|
4c42176ec9 | ||
|
|
6564a268f6 | ||
|
|
9c66a2938e | ||
|
|
27e314821d | ||
|
|
bf4b90943b | ||
|
|
3b5f7bc6c8 | ||
|
|
0659b37d77 | ||
|
|
67950db948 | ||
|
|
45a09911a4 | ||
|
|
e89127358f | ||
|
|
cf94dc866d | ||
|
|
bd3841b43b | ||
|
|
50dd2dcc4e | ||
|
|
6443184f0f | ||
|
|
9f8a7afd17 | ||
|
|
5d3afb0230 | ||
|
|
9dfc5c53ed | ||
|
|
ba540bb546 | ||
|
|
b70296a7a2 | ||
|
|
36c0acdb92 | ||
|
|
549115af97 | ||
|
|
a4d6fe139e | ||
|
|
9010566895 | ||
|
|
167fc251fd | ||
|
|
4bcc2efbec | ||
|
|
4c1eed6248 | ||
|
|
36b4f101f9 | ||
|
|
9209c934ee | ||
|
|
ecd1118ce5 | ||
|
|
29820ac964 | ||
|
|
93dcd6d884 | ||
|
|
6f66e5bd72 | ||
|
|
df76a4b2ab | ||
|
|
b9391305b0 | ||
|
|
9e1ce7dda0 | ||
|
|
d956da624f | ||
|
|
4b29564f12 | ||
|
|
227c1204c2 | ||
|
|
1ca88afcef | ||
|
|
bb4272bbe6 | ||
|
|
b749884ddc | ||
|
|
0b18df1390 | ||
|
|
256387cbbd | ||
|
|
f80071913d | ||
|
|
c05afe8bf6 | ||
|
|
95713e627b | ||
|
|
4184a4fe5d | ||
|
|
5933b6189e | ||
|
|
2d545688cb | ||
|
|
c3dee2271e | ||
|
|
93865f4431 | ||
|
|
be1c57b345 | ||
|
|
5d22f9b2cc | ||
|
|
1ea4398216 | ||
|
|
bb93ed8843 | ||
|
|
e2eed221e0 | ||
|
|
6719104cb3 | ||
|
|
d103345b77 | ||
|
|
ae99dc4a55 | ||
|
|
4658f616f1 | ||
|
|
48861cbf25 | ||
|
|
c82de5cfd0 | ||
|
|
64185b884d | ||
|
|
9bdc4350f9 | ||
|
|
81735c4dec | ||
|
|
53eab985fd | ||
|
|
e16d66a4c5 | ||
|
|
bc3aa30929 | ||
|
|
9d8edb6ead | ||
|
|
25ff55291a | ||
|
|
2a9ac48a72 | ||
|
|
925098534f | ||
|
|
277c704be8 | ||
|
|
046b2ed300 | ||
|
|
ef579daf1c | ||
|
|
e44785bcff | ||
|
|
72697f25f9 | ||
|
|
fa6067eee7 | ||
|
|
6879be005e | ||
|
|
52e93a608b | ||
|
|
bce5a4bd37 | ||
|
|
1dad620368 | ||
|
|
f09b968e27 | ||
|
|
7f81d6291d | ||
|
|
f48a776e99 | ||
|
|
3e63287fa2 | ||
|
|
828c8d2f23 | ||
|
|
d8c2f696b1 | ||
|
|
4f21628365 | ||
|
|
0521b595fe | ||
|
|
f0159a05ae | ||
|
|
4a1e7e361f | ||
|
|
b1f620a579 | ||
|
|
dfbcb5bf67 | ||
|
|
0677a16e75 | ||
|
|
968b507570 | ||
|
|
e773b26f5f | ||
|
|
54e4759791 | ||
|
|
df0e5d3539 | ||
|
|
32944666f4 | ||
|
|
b5e0d7afed | ||
|
|
4bb86dc8b8 | ||
|
|
93a5fb057e | ||
|
|
3c044fd4d0 | ||
|
|
b430c87b65 | ||
|
|
903644b628 | ||
|
|
d26be24cb3 | ||
|
|
949e7eb83f | ||
|
|
97bb455cc4 | ||
|
|
cdb5ff2133 | ||
|
|
b1aee93e2d | ||
|
|
667566e341 | ||
|
|
c51443bf5d | ||
|
|
ab4a3d2748 | ||
|
|
6d6a689557 | ||
|
|
16c2031d2a | ||
|
|
590b20f12e | ||
|
|
c82c0b3689 | ||
|
|
18d38d79e7 | ||
|
|
4113124ec4 | ||
|
|
8f6d0e03ac | ||
|
|
c48fd8925b | ||
|
|
66af983955 | ||
|
|
55b735086c | ||
|
|
4e64784749 | ||
|
|
3ee2ef0b42 | ||
|
|
eaa6fc8802 | ||
|
|
ef6bf61c5b | ||
|
|
5f5b589a7f | ||
|
|
82ae27841c | ||
|
|
8623d5a817 | ||
|
|
250c49c7bd | ||
|
|
7739c3e951 | ||
|
|
8a7f7931f4 | ||
|
|
6579e85a96 | ||
|
|
1195f5225e | ||
|
|
9b206a9831 | ||
|
|
dae1db73e9 | ||
|
|
d63f59d2fa | ||
|
|
bbf9cb0d2b | ||
|
|
a44b1d93c1 | ||
|
|
3dab930a75 | ||
|
|
939fad20f6 | ||
|
|
2e06ef8637 | ||
|
|
12ab14bc57 | ||
|
|
45000493e0 |
521
.all-contributorsrc
Normal file
521
.all-contributorsrc
Normal file
@@ -0,0 +1,521 @@
|
||||
{
|
||||
"projectName": "blitz",
|
||||
"projectOwner": "blitz-js",
|
||||
"repoType": "github",
|
||||
"repoHost": "https://github.com",
|
||||
"files": [
|
||||
"README.md"
|
||||
],
|
||||
"badgeTemplate": "<a aria-label=\"All Contributors\" href=\"#contributors-\"><img alt=\"\" src=\"https://img.shields.io/badge/all_contributors-<%= contributors.length %>-17BB8A.svg?style=for-the-badge&labelColor=000000\"></a>",
|
||||
"imageSize": 100,
|
||||
"commit": true,
|
||||
"commitConvention": "none",
|
||||
"contributors": [
|
||||
{
|
||||
"login": "flybayer",
|
||||
"name": "Brandon Bayer",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/8813276?v=4",
|
||||
"profile": "https://twitter.com/flybayer",
|
||||
"contributions": [
|
||||
"code",
|
||||
"content",
|
||||
"ideas",
|
||||
"review"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "ryardley",
|
||||
"name": "Rudi Yardley",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/1256409?v=4",
|
||||
"profile": "https://medium.com/@ryardley",
|
||||
"contributions": [
|
||||
"code",
|
||||
"ideas",
|
||||
"review",
|
||||
"test"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "merelinguist",
|
||||
"name": "Dylan Brookes",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/24858006?v=4",
|
||||
"profile": "https://merelinguist.me",
|
||||
"contributions": [
|
||||
"code",
|
||||
"ideas",
|
||||
"review",
|
||||
"test",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "aem",
|
||||
"name": "Adam Markon",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/1909883?v=4",
|
||||
"profile": "https://github.com/aem",
|
||||
"contributions": [
|
||||
"code",
|
||||
"ideas",
|
||||
"review",
|
||||
"test",
|
||||
"maintenance"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "coreybrown89",
|
||||
"name": "Corey Brown",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/12791148?v=4",
|
||||
"profile": "https://corey-brown.com",
|
||||
"contributions": [
|
||||
"code",
|
||||
"review",
|
||||
"maintenance"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "LoriKarikari",
|
||||
"name": "Lori Karikari",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/7902980?v=4",
|
||||
"profile": "https://github.com/LoriKarikari",
|
||||
"contributions": [
|
||||
"code",
|
||||
"review",
|
||||
"maintenance"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "eliasjohansson",
|
||||
"name": "Elias Johansson",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/22719177?v=4",
|
||||
"profile": "https://twitter.com/GeggsElias",
|
||||
"contributions": [
|
||||
"code",
|
||||
"review",
|
||||
"maintenance"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "medelman17",
|
||||
"name": "Michael Edelman ",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/14793389?v=4",
|
||||
"profile": "https://fabulas.io",
|
||||
"contributions": [
|
||||
"infra",
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "toddgeist",
|
||||
"name": "Todd Geist",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/316792?v=4",
|
||||
"profile": "http://www.geistinteractive.com",
|
||||
"contributions": [
|
||||
"financial"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "robdrosenberg",
|
||||
"name": "Robert Rosenberg",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/20813991?v=4",
|
||||
"profile": "http://robdrosenberg.com",
|
||||
"contributions": [
|
||||
"code",
|
||||
"maintenance",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "quirk0o",
|
||||
"name": "Beata Obrok",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/5123725?v=4",
|
||||
"profile": "https://github.com/quirk0o",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "tsawan",
|
||||
"name": "Tahir Awan",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/3263082?v=4",
|
||||
"profile": "https://github.com/tsawan",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "camilo86",
|
||||
"name": "Camilo Gonzalez",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/2454632?v=4",
|
||||
"profile": "https://raluce.com",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "dkempner",
|
||||
"name": "Daniel Kempner",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/2532112?v=4",
|
||||
"profile": "http://da.nielkempner.com",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "gielcobben",
|
||||
"name": "Giel",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/2663212?v=4",
|
||||
"profile": "http://gielcobben.com",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "MrLeebo",
|
||||
"name": "Jeremy Liberman",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/2754163?v=4",
|
||||
"profile": "http://jeremyliberman.com/",
|
||||
"contributions": [
|
||||
"code",
|
||||
"maintenance",
|
||||
"test"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "jimthedev",
|
||||
"name": "Jim Cummins",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/108938?v=4",
|
||||
"profile": "https://jimthedev.com",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Kristina Matuška",
|
||||
"avatar_url": "https://media-exp1.licdn.com/dms/image/C5603AQHVPAjV21gw9g/profile-displayphoto-shrink_200_200/0?e=1591228800&v=beta&t=0MlbmiYhNvGv1xjLD_fOhOFjVDZ7ltNwfGNeJ4DHedQ",
|
||||
"profile": "http://kristinamatuska.com/",
|
||||
"contributions": [
|
||||
"design"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "jasonblalock",
|
||||
"name": "Jason Blalock",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/5899929?v=4",
|
||||
"profile": "https://github.com/jasonblalock",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "aej11a",
|
||||
"name": "aej11a",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/10066422?v=4",
|
||||
"profile": "https://github.com/aej11a",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "marcoseoane",
|
||||
"name": "marcoseoane",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/28088807?v=4",
|
||||
"profile": "https://github.com/marcoseoane",
|
||||
"contributions": [
|
||||
"ideas"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "rishabhpoddar",
|
||||
"name": "Rishabh Poddar",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/2976287?v=4",
|
||||
"profile": "https://github.com/rishabhpoddar",
|
||||
"contributions": [
|
||||
"ideas"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "lorenzorapetti",
|
||||
"name": "Lorenzo Rapetti",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/2632174?v=4",
|
||||
"profile": "https://github.com/lorenzorapetti",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "wKovacs64",
|
||||
"name": "Justin Hall",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/1288694?v=4",
|
||||
"profile": "https://github.com/wKovacs64",
|
||||
"contributions": [
|
||||
"code",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "sijad",
|
||||
"name": "Sajjad Hashemian",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/7693001?v=4",
|
||||
"profile": "https://github.com/sijad",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "ETLopes",
|
||||
"name": "Eduardo Lopes",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/34959471?v=4",
|
||||
"profile": "https://github.com/ETLopes",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "mattleff",
|
||||
"name": "Matthew Leffler",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/120155?v=4",
|
||||
"profile": "https://github.com/mattleff",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "hew",
|
||||
"name": "Matt",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/3103241?v=4",
|
||||
"profile": "https://hew.tools",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "sonnypgs",
|
||||
"name": "Sonny",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/1431300?v=4",
|
||||
"profile": "https://github.com/sonnypgs",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Zeko369",
|
||||
"name": "Fran Zekan",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/3064377?v=4",
|
||||
"profile": "https://github.com/Zeko369",
|
||||
"contributions": [
|
||||
"code",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "janbaykara",
|
||||
"name": "Jan Baykara",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/237556?v=4",
|
||||
"profile": "http://twitter.com/JanBaykara",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "mikeattara",
|
||||
"name": "Mike Perry Y Attara",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/31483629?v=4",
|
||||
"profile": "https://mikeattara.com",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "DevanB",
|
||||
"name": "Devan",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/354652?v=4",
|
||||
"profile": "https://devanthe.dev",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "jclancy93",
|
||||
"name": "Jack Clancy",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/7850202?v=4",
|
||||
"profile": "https://github.com/jclancy93",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "ntgussoni",
|
||||
"name": "Nicolas Torres",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/10161067?v=4",
|
||||
"profile": "https://github.com/ntgussoni",
|
||||
"contributions": [
|
||||
"test",
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "skn0tt",
|
||||
"name": "Simon Knott",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/14912729?v=4",
|
||||
"profile": "http://simonknott.de",
|
||||
"contributions": [
|
||||
"code",
|
||||
"test",
|
||||
"maintenance"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "kandros",
|
||||
"name": "Jaga Santagostino",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/4562878?v=4",
|
||||
"profile": "http://jagascript.com",
|
||||
"contributions": [
|
||||
"code",
|
||||
"doc",
|
||||
"maintenance"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "jportela",
|
||||
"name": "João Portela",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/1010018?v=4",
|
||||
"profile": "http://www.joaoportela.com",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "dajinchu",
|
||||
"name": "Da-Jin Chu",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/7122182?v=4",
|
||||
"profile": "http://dajin.dev",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Shinyaigeek",
|
||||
"name": "Shinobu Hayashi",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/42742053?v=4",
|
||||
"profile": "https://shinyaigeek.dev/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "karankiri",
|
||||
"name": "Karan Kiri",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/19989161?v=4",
|
||||
"profile": "http://karankiri.com",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "fullmetalengineer",
|
||||
"name": "Alan Long",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/5294903?v=4",
|
||||
"profile": "https://github.com/fullmetalengineer",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "developerfred",
|
||||
"name": "codingsh",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/57037080?v=4",
|
||||
"profile": "http://codingsh.xyz",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "peaonunes",
|
||||
"name": "Rafael Nunes",
|
||||
"avatar_url": "https://avatars0.githubusercontent.com/u/3356720?v=4",
|
||||
"profile": "http://twitter.com/peaonunes",
|
||||
"contributions": [
|
||||
"review",
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "0ww",
|
||||
"name": "Simon Debbarma",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/31207418?v=4",
|
||||
"profile": "https://simonpeterdebbarma.com",
|
||||
"contributions": [
|
||||
"design",
|
||||
"maintenance",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "0xflotus",
|
||||
"name": "0xflotus",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/26602940?v=4",
|
||||
"profile": "https://github.com/0xflotus",
|
||||
"contributions": [
|
||||
"code",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "tmns",
|
||||
"name": "tmns",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/35785003?v=4",
|
||||
"profile": "https://dev.to/tmns",
|
||||
"contributions": [
|
||||
"code",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "harris1717",
|
||||
"name": "Jru Harris",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/8636691?v=4",
|
||||
"profile": "http://jruharris.com",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "ivandevp",
|
||||
"name": "Ivan Medina",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/9284690?v=4",
|
||||
"profile": "https://twitter.com/ivandevp",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "dwightwatson",
|
||||
"name": "Dwight Watson",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/1100408?v=4",
|
||||
"profile": "https://www.dwightwatson.com",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "is2ei",
|
||||
"name": "Horie Issei #hashtagtest",
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/3948353?v=4",
|
||||
"profile": "http://is2ei.com/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "lednhatkhanh",
|
||||
"name": "Nhat Khanh",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/9303093?v=4",
|
||||
"profile": "https://twitter.com/lednhatkhanh",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
"skipCi": true
|
||||
}
|
||||
14
.eslintrc.js
Normal file
14
.eslintrc.js
Normal file
@@ -0,0 +1,14 @@
|
||||
module.exports = {
|
||||
extends: ['react-app', 'prettier/@typescript-eslint', 'plugin:prettier/recommended'],
|
||||
rules: {
|
||||
'import/first': 0,
|
||||
'import/no-default-export': ['error'],
|
||||
'unicorn/filename-case': [
|
||||
'error',
|
||||
{
|
||||
case: 'kebabCase',
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: ['unicorn'],
|
||||
}
|
||||
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
||||
* text=auto eol=lf
|
||||
9
.github/CODEOWNERS
vendored
Normal file
9
.github/CODEOWNERS
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
# https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners
|
||||
|
||||
* @flybayer
|
||||
*.md @merelinguist
|
||||
|
||||
packages/server/**/* @ryardley
|
||||
packages/cli/**/* @aem
|
||||
packages/generator/**/* @aem
|
||||
packages/installer/**/* @aem
|
||||
4
.github/FUNDING.yml
vendored
Normal file
4
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
github: blitz-js
|
||||
custom: ['https://paypal.me/thebayers']
|
||||
open_collective: blitzjs
|
||||
patreon: flybayer
|
||||
23
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
23
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Something is not working right. Or error messages are unclear.
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
### What is the problem?
|
||||
|
||||
### Steps to Reproduce
|
||||
|
||||
1.
|
||||
|
||||
### Versions
|
||||
|
||||
```
|
||||
output of `blitz --version --verbose`
|
||||
```
|
||||
|
||||
### Other
|
||||
|
||||
Please include applicable logs and screenshots that show your problem.
|
||||
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Question or Discussion
|
||||
url: https://github.com/blitz-js/blitz/discussions/new
|
||||
about: Ask questions and discuss with other community members
|
||||
19
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
19
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
---
|
||||
name: Feature/change request
|
||||
about: Something new or better!
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
### What do you want and why?
|
||||
|
||||
The more information the better!
|
||||
|
||||
### Possible implementation(s)
|
||||
|
||||
How might we do this?
|
||||
|
||||
### Additional context
|
||||
|
||||
Add any other context or screenshots about the feature request here.
|
||||
14
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
14
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
Closes: ??
|
||||
|
||||
### What are the changes and their implications?
|
||||
|
||||
### Checklist
|
||||
|
||||
- [ ] Tests added for changes
|
||||
- [ ] User facing changes documented
|
||||
|
||||
### Breaking change: yes/no
|
||||
|
||||
### Other information
|
||||
|
||||
<!-- IMPORTANT: Make sure to check the "Allow edits from maintainers" box below this window -->
|
||||
18
.github/checkInstallTime.js
vendored
Executable file
18
.github/checkInstallTime.js
vendored
Executable file
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const fs = require('fs')
|
||||
const yarnOut = fs.readFileSync(0, {encoding: 'utf8'})
|
||||
|
||||
const [installTimeString] = /(?<=^Done in )\d+\.\d+(?=s\.$)/m.exec(yarnOut)
|
||||
const installTime = Number(installTimeString)
|
||||
|
||||
console.log(`Install time: ${installTime}s`)
|
||||
|
||||
if (installTime < 30) {
|
||||
console.log("We're below 30 secs. That's awesome!")
|
||||
} else if (installTime < 50) {
|
||||
console.log("We're below 50 secs. That's fine!")
|
||||
} else {
|
||||
console.log("We're above 50 secs. That's not great!")
|
||||
process.exit(1)
|
||||
}
|
||||
59
.github/workflows/main.yml
vendored
Normal file
59
.github/workflows/main.yml
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
name: Continuous Integration
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- canary
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- canary
|
||||
|
||||
jobs:
|
||||
check_install_time:
|
||||
name: Check install time
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '12.16.1'
|
||||
- name: Test Install time
|
||||
run: |
|
||||
cd ../ && mkdir test && cd test
|
||||
yarn add file:../blitz/packages/blitz --non-interactive | tee out.txt
|
||||
cat out.txt | ../blitz/.github/checkInstallTime.js
|
||||
|
||||
build_and_test:
|
||||
name: Build & Test
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '12.16.1'
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Cache Node.js modules
|
||||
id: yarn-cache
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
- name: Install dependencies
|
||||
run: yarn install --frozen-lockfile --silent
|
||||
env:
|
||||
CI: true
|
||||
- name: Test Blitz Packages
|
||||
run: yarn test
|
||||
env:
|
||||
CI: true
|
||||
24
.gitignore
vendored
Normal file
24
.gitignore
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
.log
|
||||
.DS_Store
|
||||
.jest-*
|
||||
lib
|
||||
node_modules
|
||||
reports
|
||||
*.log
|
||||
.nyc_output
|
||||
**/coverage
|
||||
.yarn
|
||||
.yarnrc
|
||||
tsconfig.tsbuildinfo
|
||||
.blitz
|
||||
.next
|
||||
dist
|
||||
.now
|
||||
# local env files
|
||||
**/.envrc
|
||||
**/.env
|
||||
**/.env.local
|
||||
**/.env.development.local
|
||||
**/.env.test.local
|
||||
**/.env.production.local
|
||||
.blitz-*
|
||||
16
.npmignore
Normal file
16
.npmignore
Normal file
@@ -0,0 +1,16 @@
|
||||
.DS_Store
|
||||
.prettierrc
|
||||
.nyc_output
|
||||
.travis.yml
|
||||
coverage
|
||||
coverage.lcov
|
||||
bench
|
||||
docs
|
||||
src
|
||||
examples
|
||||
babel.config.js
|
||||
test
|
||||
CONTRIBUTING.md
|
||||
CODE_OF_CONDUCT.md
|
||||
*.ts
|
||||
!*.d.ts
|
||||
20
.prettierignore
Normal file
20
.prettierignore
Normal file
@@ -0,0 +1,20 @@
|
||||
**/migrations/**
|
||||
.blitz
|
||||
.next
|
||||
.log
|
||||
.DS_Store
|
||||
.jest-*
|
||||
lib
|
||||
node_modules
|
||||
reports
|
||||
*.log
|
||||
**/.env*
|
||||
.nyc_output
|
||||
**/coverage
|
||||
.yarn
|
||||
.yarnrc
|
||||
tsconfig.tsbuildinfo
|
||||
dist
|
||||
bin
|
||||
packages/generator/templates/**
|
||||
.github/ISSUE_TEMPLATE/bug_report.md
|
||||
9
BACKERS.md
Normal file
9
BACKERS.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# Bronze Sponsors of Blitz.js
|
||||
|
||||
A huge thank you to our bronze sponsors!
|
||||
|
||||
<br>
|
||||
|
||||
<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>
|
||||
60
CODE_OF_CONDUCT.md
Normal file
60
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,60 @@
|
||||
# The Blitz Community Code of Conduct
|
||||
|
||||
The Blitz core members take this CoC very serious. All members, contributors and volunteers in this community are required to act according to the following Code of Conduct to keep Blitz a positive, growing project and community and help us provide and ensure a safe environment for everyone.
|
||||
|
||||
The Blitz community
|
||||
|
||||
## When Something Happens
|
||||
|
||||
If you see a Code of Conduct violation, follow these steps:
|
||||
|
||||
1. Let the person know that what they did is not appropriate and ask them to stop and/or edit their message(s).
|
||||
2. That person should immediately stop the behavior and correct the issue.
|
||||
3. If this doesn’t happen, or if you’re uncomfortable speaking up, contact Brandon Bayer ([Twitter](https://twitter.com/flybayer) | [Email](mailto:b@bayer.ws)).
|
||||
|
||||
When reporting, please include any relevant details, links, screenshots, context, or other information that may be used to better understand and resolve the situation.
|
||||
|
||||
The core members will prioritize the well-being and comfort of the recipients of the violation over the comfort of the violator.
|
||||
|
||||
## What We Believe and How We Act
|
||||
|
||||
- We are committed to providing a friendly, safe and welcoming environment for everyone, regardless of age, body size, culture, ethnicity, gender expression, gender identity, level of experience, nationality, personal ability or disability, physical appearance, physical or mental difference, race, religion, set of skills, sexual orientation, socio-economic status, and subculture. We welcome people regardless of these or other attributes.
|
||||
- We are better together. We are more alike than different.
|
||||
- Our community is based on mutual respect, tolerance, and encouragement.
|
||||
- We believe that a diverse community where people treat each other with respect is stronger, more vibrant and has more potential contributors and more sources for ideas. We aim for more diversity.
|
||||
- We are kind, welcoming and courteous to everyone.
|
||||
- We’re respectful of others, their positions, their skills, their commitments and their efforts.
|
||||
- We’re attentive in our communications, whether in person or online, and we’re tactful when approaching differing views.
|
||||
- We are aware that language shapes reality. Thus, we use inclusive, gender-neutral language in the documents we provide and when we talk to people. When referring to a group of people, we aim to use gender-neutral terms like “team”, “folks”, “everyone”. (For details, we recommend [this post](https://modelviewculture.com/pieces/gendered-language-feature-or-bug-in-software-documentation)).
|
||||
- We respect that people have differences of opinion and criticize constructively.
|
||||
- We value people over code.
|
||||
|
||||
## Don'ts
|
||||
|
||||
- Don’t discriminate against anyone.
|
||||
- Sexism and racism of any kind (including sexist and racist “jokes”), demeaning or insulting behaviour and harassment are seen as direct violations to this Code of Conduct. Harassment includes offensive verbal comments related to age, body size, culture, ethnicity, gender expression, gender identity, level of experience, nationality, personal ability or disability, physical appearance, physical or mental difference, race, religion, set of skills, sexual orientation, socio-economic status, and subculture. Harassment also includes sexual images in public spaces, deliberate intimidation, stalking, following, harassing photography or recording, inappropriate physical contact, and unwelcome sexual attention.
|
||||
- On Slack and other online or offline communications channels, don't use overtly sexual nicknames or other nicknames that might detract from a friendly, safe and welcoming environment for all.
|
||||
- Don’t be mean or rude.
|
||||
- Respect that some individuals and cultures consider the casual use of profanity offensive and off-putting.
|
||||
- Unwelcome / non-consensual sexual advances over Slack or any other channels related with this community are not okay.
|
||||
- Derailing, tone arguments and otherwise playing on people’s desires to be nice are not welcome, especially in discussions about violations to this Code of Conduct.
|
||||
- Please avoid unstructured critique.
|
||||
- Likewise any spamming, trolling, flaming, baiting or other attention-stealing behavior is not welcome.
|
||||
- Sponsors of Blitz are also subject to this Code of Conduct. In particular, sponsors are required to not use sexualized images, activities, or other material which is not according to this Code of Conduct.
|
||||
|
||||
## Consequences for Violations to this Code of Conduct
|
||||
|
||||
If a participant engages in any behavior violating this Code of Conduct, the core members of this community will take any action they deem appropriate, starting with a gentle warning and then escalating as needed to expulsion from the community, exclusion from any interaction and loss of all rights in this community.
|
||||
|
||||
## Decisions About Consequences of Violations
|
||||
|
||||
Decisions about consequences of violations of this Code of Conduct are made by this community’s core members and may not be discussed with the person responsible for the violation.
|
||||
|
||||
## For Questions or Feedback
|
||||
|
||||
If you have any questions or feedback on this Code of Conduct, we’re happy to hear from you.
|
||||
|
||||
## Thanks for the Inspiration To
|
||||
|
||||
- [Hood.ie](http://hood.ie/code-of-conduct/)
|
||||
- [WeAllJS](https://wealljs.org/code-of-conduct)
|
||||
154
CONTRIBUTING.md
Normal file
154
CONTRIBUTING.md
Normal file
@@ -0,0 +1,154 @@
|
||||

|
||||
|
||||
<br>
|
||||
|
||||
We're so excited you're interested in helping with Blitz! We happy to help you get started, even if you don't have any previous open-source experience :)
|
||||
|
||||
<br>
|
||||
|
||||
### Blitz is a Community Project
|
||||
|
||||
Blitz is built by and for the community. There's no large company sponsoring development. So all community contributions are very appreciated!
|
||||
|
||||
<br>
|
||||
|
||||
### Our Codebase is a Garden
|
||||
|
||||
The Blitz codebase is like a community garden. There's a lot of beautiful plants and vegetables, but it won't take long until you find some weeds! When you find weeds, please remove them :) Minor refactoring is always encouraged. If you'd like to do some major refactoring, it's best to first either open an issue or check with us in Slack. Most likely we'll agree with you.
|
||||
|
||||
<br>
|
||||
|
||||
### First Things First
|
||||
|
||||
1. New to open source? take a look at [How to Contribute to an Open Source Project on GitHub](https://egghead.io/courses/how-to-contribute-to-an-open-source-project-on-github)
|
||||
2. Familiarize yourself with the [Blitz Code of Conduct](https://github.com/blitz-js/blitz/blob/canary/CODE_OF_CONDUCT.md)
|
||||
3. Join the [Blitz Slack Community](https://slack.blitzjs.com)
|
||||
|
||||
<br>
|
||||
|
||||
### Weekly Contributors video call
|
||||
|
||||
Every week Blitz contributors meet in a video call to discuss progress and ideas.
|
||||
|
||||
The contributor video call is mainly for those contributing or want to start contributing. However anyone is welcome to join and listen!
|
||||
|
||||
#### The timezone
|
||||
|
||||
We are literally all over the globe, so the weekly contributors call alternates timezones every other week to accomodate different regions
|
||||
|
||||
- Even weeks of the year: **Tuesday at 1am UTC** (Note: this is Monday evening in the USA)
|
||||
- Odd weeks of the year: **Wednesday at 9am UTC**
|
||||
- Use this link to see [the current week of the year](https://whatweekisit.com)
|
||||
|
||||
#### How to join
|
||||
|
||||
Zoom link will be posted in slack `#-announcements` channel before the call
|
||||
|
||||
#### Recording
|
||||
|
||||
Recordings of previous calls can be found [here](https://www.youtube.com/playlist?list=PLvm6NqxNNnBLFxZux5OHraTAcIBJz2FvR)
|
||||
|
||||
<br>
|
||||
|
||||
### What to Work On?
|
||||
|
||||
Issues with the label [`status/ready-to-work-on`](https://github.com/blitz-js/blitz/labels/status%2Fready-to-work-on) are the best place to start. If you find one that looks interesting and no one else is already working on it, comment in the issue that you are going to work on it. Please ask as many questions as you need, either directly in the issue or in Slack. We're happy to help!
|
||||
|
||||
The Blitzjs.com website and documentation repo also has issues with [`ready to work on | help wanted`](https://github.com/blitz-js/blitzjs.com/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22ready+to+work+on+%7C+help+wanted%22).
|
||||
|
||||
#### Things that are ALWAYS welcome
|
||||
|
||||
- Adding tests
|
||||
- Improved documentation
|
||||
- Improved error messages
|
||||
- Improved logging (i.e. more clear, more beautiful)
|
||||
- Performance or security improvements
|
||||
- Educational content like blogs, videos, courses
|
||||
|
||||
If there's some other way you'd like to contribute, just ask us about it in slack!
|
||||
|
||||
After you contribute in any way, please add yourself as a contributor via the [@all-contributors bot](https://allcontributors.org/docs/en/bot/usage)!
|
||||
|
||||
<br>
|
||||
|
||||
## Development Setup
|
||||
|
||||
**1.** Fork this repo
|
||||
|
||||
**2.** Clone your fork repo
|
||||
|
||||
```sh
|
||||
# If you didn't fork the repo use blitz-js as the USERNAME
|
||||
git clone git@github.com:USERNAME/blitz.git
|
||||
cd blitz
|
||||
```
|
||||
|
||||
**3.** Install dependencies
|
||||
|
||||
```
|
||||
yarn
|
||||
```
|
||||
|
||||
**4.** Start the package server. This must be running for any package development or example development
|
||||
|
||||
```
|
||||
yarn dev
|
||||
```
|
||||
|
||||
**5.** Run tests
|
||||
|
||||
```
|
||||
yarn test
|
||||
```
|
||||
|
||||
#### Sync your fork
|
||||
|
||||
```sh
|
||||
./scripts/fetchRemote.sh
|
||||
git merge upstream/canary
|
||||
```
|
||||
|
||||
#### Link the Blitz CLI (Optional)
|
||||
|
||||
The following will link the development CLI as a local binary so you can use it anywhere for testing.
|
||||
|
||||
```
|
||||
yarn link-cli
|
||||
// `yarn unlink-cli` will unlink
|
||||
```
|
||||
|
||||
#### Develop a Blitz `package`
|
||||
|
||||
**1.** Change to a package directory
|
||||
|
||||
```
|
||||
cd packages/core
|
||||
```
|
||||
|
||||
**2.** Start the test runner
|
||||
|
||||
```
|
||||
yarn test:watch
|
||||
```
|
||||
|
||||
#### Develop a Blitz `example`
|
||||
|
||||
**1.** Change to an example directory
|
||||
|
||||
```
|
||||
cd examples/store
|
||||
```
|
||||
|
||||
**2.** Follow instructions in the example's README
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If you run into issues that should be documented here, please submit a PR! ❤️
|
||||
|
||||
**#### Windows Subsystem for Linux**
|
||||
|
||||
`node-pty` error when running `yarn`. Fix by installing `node-pty dependencies`
|
||||
|
||||
```
|
||||
sudo apt install -y make python build-essential
|
||||
```
|
||||
11
GOVERNANCE.md
Normal file
11
GOVERNANCE.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Blitz.js Governance
|
||||
|
||||
_From Brandon Bayer (@flybayer), the creator:_
|
||||
|
||||
Currently at this very early stage it's basically a [BDFL situation](https://opensource.guide/leadership-and-governance/#what-are-some-of-the-common-governance-structures-for-open-source-projects), with me having the final say in decisions.
|
||||
|
||||
However we will move away from BDFL to something that looks more like Ember.js. It's extremely important to me (Brandon) that Blitz.js is a long-term, sustainable, and community-run project.
|
||||
|
||||
I would love some mentorship from people with experience in large open-source projects on making this transition.
|
||||
|
||||
Also, it's possible I will create one or more business around Blitz, perhaps similar to how Taylor Otwell has around Laravel, but Blitz itself will always remain a separate community-run project.
|
||||
104
MAINTAINERS.md
Normal file
104
MAINTAINERS.md
Normal file
@@ -0,0 +1,104 @@
|
||||
# ❤️ Blitz Maintainers
|
||||
|
||||
Aside from the core team, there are two levels of maintainers, described below.
|
||||
|
||||
## Becoming a Maintainer
|
||||
|
||||
We always need more level 1 maintainers! The main requirement is that you can show empathy when communicating online. We'll train you as needed on the other specifics. **This is a great role if you have limited time, because you can spend just as much time as you have without any ongoing responsibilities (unlike level 2)**
|
||||
|
||||
Level 2 maintainers have a much higher responsibility, so usually you will spend time as a level 1 maintainer before moving to level 2.
|
||||
|
||||
Please DM a core team member (Brandon Bayer, Rudi Yardley, or Dylan Brookes) in Slack if you're interested in becoming an official maintainer!
|
||||
|
||||
## Level 1 Maintainers
|
||||
|
||||
Level 1 maintainers are critical for a healthy Blitz community and project. They take a lot of burden off the core team and level 2 maintainers so they can focus on higher level things with longer term impact.
|
||||
|
||||
The primary responsibilities of level 1 maintainers are:
|
||||
|
||||
- Being a friendly, welcoming voice for the Blitz community
|
||||
- Issue triage
|
||||
- Pull request triage
|
||||
- Monitor and answer the `#-help` slack channel
|
||||
- Community encouragement
|
||||
- Community moderation
|
||||
- Tracking and ensuring progress of key issues
|
||||
|
||||
## Level 2 Maintainers
|
||||
|
||||
Level 2 maintainers are the backbone of the project. They are watchdogs over the code, ensuring code quality, correctness, and security. They also facilitate a rapid pace of progress.
|
||||
|
||||
The primary responsibilities of level 2 maintainers are:
|
||||
|
||||
- Code ownership over specific parts of the project
|
||||
- Maintaining and improving the architecture of what they own
|
||||
- Final pull request reviews
|
||||
- Merging pull requests
|
||||
- Tracking and ensuring progress of open pull requests
|
||||
|
||||
## ⚠️ Fundamentals
|
||||
|
||||
Maintainers are the face of the project and the front-line touch point for the community. Therefore maintainers have the very important responsibility of making people feel welcome, valued, understood, and appreciated.
|
||||
|
||||
**Please take time to read and understand everything outlined in this [guide on building welcoming communities](https://opensource.guide/building-community)**
|
||||
|
||||
Some especially important points:
|
||||
|
||||
- **Gratitude:** immediately express gratitude when someone opens an issue or PR. This takes effort/time and we appreciate it
|
||||
- **Responsiveness:** during issue/PR triage, even if we can’t do a full review right away, leave a comment thanking them and saying we’ll review it soon
|
||||
- **Understanding:** it's critical to ensure you understand exactly what someone is saying before you respond. Ask plenty of questions if needed. It's very bad if someone has to reply to your response and say "actually I was asking about X"
|
||||
- In fact, at least one question is almost always required before you can respond appropriately — whether in Github or in Slack
|
||||
|
||||
## Slack
|
||||
|
||||
- All `#-*` channels are for Blitz users
|
||||
- All `#dev-*` channels are for Blitz internal development
|
||||
|
||||
If someone that's not a maintainer post in the wrong area, that's fine. Don't tell them they posted in the wrong place. But as a maintainer, you should for sure post in the right channel :)
|
||||
|
||||
## Issue Triage
|
||||
|
||||
#### If a bug report:
|
||||
|
||||
- Does it have enough information? Versions? Logs? Some way to reproduce?
|
||||
- Has this already been fixed in a previous release?
|
||||
- Is there already an existing issue for this?
|
||||
|
||||
### If a feature/change request:
|
||||
|
||||
- Is it clear what the request is and what the benefit will be?
|
||||
- Is this an obvious win for Blitz? Then accept it
|
||||
- If not obvious, then pull in a core team member or level 2 maintainer for more review
|
||||
|
||||
### Actions
|
||||
|
||||
1. Add tags:
|
||||
- Add a `kind/*` tag
|
||||
- Add a `scope/*` tag
|
||||
- Add a `status/*` tag
|
||||
- Add a good first/second issue tag if appropriate
|
||||
|
||||
## Pull Request Triage
|
||||
|
||||
- Are the changes covered by tests?
|
||||
- Do the changes look ok? Make sure there's no obvious issues
|
||||
|
||||
### Actions
|
||||
|
||||
1. Kindly request any changes if needed
|
||||
2. Else add a Github approval so that level 2 maintainers know it's already had an initial review
|
||||
|
||||
## Final PR Review & Merging (Level 2 maintainers)
|
||||
|
||||
As a level 2 maintainer, it is your responsibility to make sure broken code and regressions never reach the canary branch.
|
||||
|
||||
1. Ensure the PR'ed code fully works as intended and that there are no regressions in related code
|
||||
1. If not fully covered by automated tests, you need to pull down the code locally and manually verify everything (the Github CLI helps with this!)
|
||||
2. During squash & merge:
|
||||
1. Change the commit title to be public friendly - this exact text will go in the release notes
|
||||
2. Add the commit type in the description, in parenthesis like `(patch)`. Commit types:
|
||||
- `major` - major breaking change
|
||||
- `minor` - minor feature addition
|
||||
- `patch` - patches, bug fixes, perf improvements, etc
|
||||
- `example` - change to an example app
|
||||
- `meta` - internal meta change related to the Blitz repo/project
|
||||
74
MANIFESTO.md
Normal file
74
MANIFESTO.md
Normal file
@@ -0,0 +1,74 @@
|
||||
# The Blitz.js Manifesto
|
||||
|
||||
## Background
|
||||
|
||||
Technology follows a repeating cycle of bundling and unbundling. Created in 2005, Ruby on Rails became a major bundling force. It made web application development easier and more accessible than ever before. This benefited everyone, from those learning programming to seniors building production systems.
|
||||
|
||||
A major unbundling happened in 2013 with the release of React because it is hyper focused on the rendering layer. As React grew in popularity, so did the choices for all the other parts, leaving developers with hundreds of decisions to make when starting a new app. While this has contributed to JavaScript Fatigue, it's been a powerful driving force for rapid frontend innovation.
|
||||
|
||||
Now, in 2020, is the perfect time for another major bundling. Developers are yearning for an easier, simpler way to build web applications. Beginners want a guiding hand for building a robust app. And seniors want a framework that removes mundane tasks and provides a highly scalable architecture.
|
||||
|
||||
Hence the creation of Blitz.
|
||||
|
||||
## What is Blitz For?
|
||||
|
||||
Blitz is for building tiny to large fullstack database-backed applications that have one or more graphical user interfaces like web or mobile apps.
|
||||
|
||||
## Foundational Principles
|
||||
|
||||
1. Fullstack & Monolithic
|
||||
2. API Not Required
|
||||
3. Convention over Configuration
|
||||
4. Loose Opinions
|
||||
5. Easy to Start, Easy to Scale
|
||||
6. Stability
|
||||
7. Community over Code
|
||||
|
||||
### 1. Fullstack & Monolithic
|
||||
|
||||
A fullstack, monolithic application is simpler than an application where frontend and backend are developed and deployed separately. Monolithic doesn't mean it will be slow or hard to scale to large teams. Monolithic doesn't mean there isn't separation of concerns. Monolithic means you can reason about your app as a single entity.
|
||||
|
||||
### 2. API Not Required
|
||||
|
||||
Until now, choosing React for your view layer required you to have a REST or GraphQL API even if it wasn't used by third-parties. This additional complexity is a significant drawback not shared by traditional server rendered apps like Ruby on Rails.
|
||||
|
||||
Blitz is a framework for the 99% of us at companies with <100 employees. The vast majority of these apps don't actually need an API, at least not until years down the road when you have the time and resources to build one.
|
||||
|
||||
### 3. Convention over Configuration
|
||||
|
||||
Starting a new fullstack React app is currently too hard. You have to spend days on things like configuring eslint, prettier, husky, jest, cypress, typescript, deciding on a file structure, setting up a database, adding authentication and authorization, setting up a router, defining routing conventions, and setting up your styling library.
|
||||
|
||||
Blitz makes as many decisions and does as much work for you as possible. This makes it lightning fast to start real development. It also greatly benefits the community. Common project structure and architectural patterns make it easy to move from Blitz app to Blitz app and immediately feel at home.
|
||||
|
||||
Convention over configuration doesn't mean no configuration. It means configuration is optional. Blitz will provide all the escape hatches you need for bespoke customization.
|
||||
|
||||
### 4. Loose Opinions
|
||||
|
||||
Blitz is opinionated. The out-of-the-box experience guides you on a path perfect for most applications. However, Blitz isn't arrogant. It understands there are very good reasons for deviating from convention, and it allows you to do so. For example, Blitz has a conventional file structure, but, with few exceptions, doesn't _enforce_ it.
|
||||
|
||||
And when there's not community consensus, `blitz new` prompts you to choose.
|
||||
|
||||
### 5. Easy to Start, Easy to Scale
|
||||
|
||||
A framework that's only easy for one end of an application lifecycle is not a good framework. Both starting and scaling must be easy.
|
||||
|
||||
Easy to start includes being easy for beginners and being easy to migrate existing Next.js apps to Blitz.
|
||||
|
||||
Scaling is important in all forms: lines of code, number of people working in the codebase, and code execution.
|
||||
|
||||
### 6. Stability
|
||||
|
||||
In the fast-paced world of Javascript, a stable, predictable release cycle is a breath of fresh air. A stable release cycle ensures minimal breaking changes, and it ensures you know exactly what and when a breaking change will occur. It also minimizes bugs in stable releases by ensuring features are in beta for a minimum amount of time. [Ember is the model citizen](https://emberjs.com/releases/) in this regard.
|
||||
|
||||
The exact details of the Blitz release cycle are to be determined, but we'll follow a pattern similar to Ember which strictly follows SemVer with stable releases every 6 weeks and LTS releases every 6 months.
|
||||
|
||||
Blitz will follow a public RFC (request for comments) process so all users and companies can participate in proposing and evaluating new features.
|
||||
|
||||
If a Blitz API needs to be removed, it will be deprecated in a minor release. Major releases will simply remove APIs already deprecated in a previous release.
|
||||
|
||||
### 7. Community over Code
|
||||
|
||||
The Blitz community is the most important aspect of the framework, by far.
|
||||
We have a comprehensive [Code of Conduct](https://github.com/blitz-js/blitz/blob/canary/CODE_OF_CONDUCT.md). LGBTQ+, women, and minorities are especially welcome.
|
||||
|
||||
We are all in this together, from the youngest to the oldest. We are all more similar than we are different. We can and should solve problems together. We should learn from other communities, not compete against them.
|
||||
255
README.md
255
README.md
@@ -1,2 +1,253 @@
|
||||
# blitz
|
||||
Framework for building monolithic, full-stack, serverless React apps with zero data-fetching and zero client-side state management
|
||||
[](https://blitzjs.com)
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
<p align="center">
|
||||
<a aria-label="Join our Slack Community" href="https://slack.blitzjs.com">
|
||||
<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-52-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">
|
||||
</a>
|
||||
<a aria-label="NPM version" href="https://www.npmjs.com/package/blitz">
|
||||
<img alt="" src="https://img.shields.io/npm/v/blitz.svg?style=for-the-badge&labelColor=000000&color=E65528">
|
||||
</a>
|
||||
</p>
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
<br>
|
||||
|
||||
<h3 align="center">Blitz is a Rails-like framework for monolithic, full-stack React apps — built on Next.js</h3>
|
||||
|
||||
<br>
|
||||
|
||||
Blitz brings back the **simplicity and conventions** of server-rendered frameworks like Ruby on Rails while preserving everything we love about React and client-side rendering!
|
||||
|
||||
Blitz is the framework for the 99% of us at companies with <100 employees. This means **we don't force you to use advanced technologies like GraphQL**. We let you add advanced technologies on your terms and at your pace.
|
||||
|
||||
Blitz **maximizes your productivity** both when starting an app and when scaling it to lots of code and users.
|
||||
|
||||
<br>
|
||||
|
||||
### :tada: Alpha Release Now Available :tada:
|
||||
|
||||
1. `npm i -g blitz`
|
||||
2. `blitz new myapp`
|
||||
3. [Read the Alpha User Guide](https://github.com/blitz-js/blitz/blob/canary/USER_GUIDE.md)
|
||||
|
||||
or
|
||||
|
||||
3. Start with the [Blitz Beginner Tutorial](https://github.com/blitz-js/blitz/blob/canary/TUTORIAL.md)
|
||||
|
||||
<br>
|
||||
|
||||
**Features:**<br>
|
||||
⚡️ Built on Next.js<br>
|
||||
⚡️ Don't have to build an API for client-side rendering<br>
|
||||
⚡️ Client-side rendering, Server-side rendering, and fully static pages all in the same app<br>
|
||||
⚡️ Full Typescript support with static, end-to-end typing (no code generation step needed like with GraphQL)<br>
|
||||
⚡️ React Concurrent Mode enabled<br>
|
||||
⚡️ Database/ORM agnostic, but Prisma 2 is default<br>
|
||||
⚡️ CLI with code scaffolding, Rails-style console REPL, etc<br>
|
||||
⚡️ GraphQL Ready<br>
|
||||
⚡️ Deploy serverless or serverful<br>
|
||||
|
||||
**Other key features coming:**<br>
|
||||
⚡️ Highly secure authentication <br>
|
||||
⚡️ Authorization you can use on both server and client<br>
|
||||
⚡️ Model validation you can use on both server and client<br>
|
||||
⚡️ Plugins for easily adding libraries like Tailwind, CSS-in-JS, etc.<br>
|
||||
⚡️ React native support<br>
|
||||
⚡️ GUI so you don't have to use the CLI<br>
|
||||
|
||||
<br>
|
||||
|
||||
### What is Blitz Designed For?
|
||||
|
||||
Blitz is designed for tiny to large database-backed applications that have one or more graphical user interfaces.
|
||||
|
||||
While we currently only support web, we are pursuing the dream of a single monolithic application that runs on web and mobile with maximum code sharing and minimal boilerplate.
|
||||
|
||||
<br>
|
||||
|
||||
### What are the Foundational Principles?
|
||||
|
||||
1. Fullstack & Monolithic
|
||||
2. API Not Required
|
||||
3. Convention over Configuration
|
||||
4. Loose Opinions
|
||||
5. Easy to Start, Easy to Scale
|
||||
6. Stability
|
||||
7. Community over Code
|
||||
|
||||
[The Blitz Manifesto](https://github.com/blitz-js/blitz/blob/canary/MANIFESTO.md) explains these principles in detail.
|
||||
|
||||
<br>
|
||||
|
||||
## Welcome to the Blitz Community 👋
|
||||
|
||||
The Blitz community is warm, safe, diverse, inclusive, and fun! LGBTQ+, women, and minorities are especially welcome. Please read our [Code of Conduct](https://github.com/blitz-js/blitz/blob/canary/CODE_OF_CONDUCT.md).
|
||||
|
||||
[Join our Slack Community](https://slack.blitzjs.com) where we help each other build Blitz apps. It's also where we collaborate on building Blitz itself.
|
||||
|
||||
There's still a lot of work to do, so you are especially invited to join us in building Blitz! A good place to start is [The Contributing Guide](CONTRIBUTING.md).
|
||||
|
||||
<br>
|
||||
|
||||
## Sponsors and Donations
|
||||
|
||||
- Contribute via [GitHub Sponsors](https://github.com/sponsors/blitz-js)
|
||||
- Contribute via [PayPal](https://paypal.me/thebayers)
|
||||
- Contribute via [Open Collective](https://opencollective.com/blitzjs)
|
||||
- Contribute via [Patreon](https://patreon.com/flybayer)
|
||||
|
||||
_Sponsor Blitz and display your logo and hiring status here. This is a great way to get in front of early adopters!_
|
||||
|
||||
[View our Bronze Sponsors](https://github.com/blitz-js/blitz/blob/canary/BACKERS.md)
|
||||
|
||||
<br>
|
||||
|
||||
## Core Team ✨
|
||||
|
||||
<!-- markdownlint-disable -->
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center"><a href="https://twitter.com/flybayer"><img src="https://avatars3.githubusercontent.com/u/8813276?v=4" width="100px;" alt=""/><br /><sub><b>Brandon Bayer</b></sub></a><br />Creator</td>
|
||||
<td align="center"><a href="https://medium.com/@ryardley"><img src="https://avatars0.githubusercontent.com/u/1256409?v=4" width="100px;" alt=""/><br /><sub><b>Rudi Yardley</b></sub></a><br />Node.js Wizard</td>
|
||||
<td align="center"><a href="https://merelinguist.now.sh"><img src="https://avatars3.githubusercontent.com/u/24858006?v=4" width="100px;" alt=""/><br /><sub><b>Dylan Brookes</b></sub></a><br />Friendly Generalist</td>
|
||||
</tr>
|
||||
</table>
|
||||
<!-- markdownlint-enable -->
|
||||
|
||||
|
||||
<br>
|
||||
|
||||
|
||||
## Maintainers (Level 2) ✨
|
||||
|
||||
_Code ownership, pull request approvals and merging, etc_ (see [MAINTAINERS.md](./MAINTAINERS.md))
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
<!-- markdownlint-disable -->
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/aem"><img src="https://avatars0.githubusercontent.com/u/1909883?v=4" width="100px;" alt=""/><br /><sub><b>Adam Markon</b></sub></a><br />CLI</td>
|
||||
<td align="center"><a href="http://robdrosenberg.com"><img src="https://avatars0.githubusercontent.com/u/20813991?v=4" width="100px;" alt=""/><br /><sub><b>Robert Rosenberg</b></sub></a><br />Website/Docs</td>
|
||||
</tr>
|
||||
</table>
|
||||
<!-- markdownlint-enable -->
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
<br>
|
||||
|
||||
## Maintainers (Level 1) ✨
|
||||
|
||||
_Issue triage, pull request triage, community encouragement and moderation, etc_ (see [MAINTAINERS.md](./MAINTAINERS.md))
|
||||
|
||||
We need more woman & nonbinary level 1 maintainers. See [MAINTAINERS.md](./MAINTAINERS.md) for what this entails
|
||||
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
<!-- markdownlint-disable -->
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/LoriKarikari"><img src="https://avatars1.githubusercontent.com/u/7902980?v=4" width="100px;" alt=""/><br /><sub><b>Lori Karikari</b></sub></a></td>
|
||||
<td align="center"><a href="https://corey-brown.com"><img src="https://avatars1.githubusercontent.com/u/12791148?v=4" width="100px;" alt=""/><br /><sub><b>Corey Brown</b></sub></a></td>
|
||||
<td align="center"><a href="http://simonknott.de"><img src="https://avatars1.githubusercontent.com/u/14912729?v=4" width="100px;" alt=""/><br /><sub><b>Simon Knott</b></sub></a></td>
|
||||
<td align="center"><a href="https://twitter.com/GeggsElias"><img src="https://avatars3.githubusercontent.com/u/22719177?v=4" width="100px;" alt=""/><br /><sub><b>Elias Johansson</b></sub></a></td>
|
||||
<td align="center"><a href="http://jeremyliberman.com/"><img src="https://avatars3.githubusercontent.com/u/2754163?v=4" width="100px;" alt=""/><br /><sub><b>Jeremy Liberman</b></td>
|
||||
<td align="center"><a href="http://jagascript.com"><img src="https://avatars0.githubusercontent.com/u/4562878?v=4" width="100px;" alt=""/><br /><sub><b>Jaga Santagostino</b></sub></a></td>
|
||||
<td align="center"><a href="https://simonpeterdebbarma.com"><img src="https://avatars3.githubusercontent.com/u/31207418?v=4" width="100px;" alt=""/><br /><sub><b>Simon Debbarma</b></sub></a></td>
|
||||
</tr>
|
||||
</table>
|
||||
<!-- markdownlint-enable -->
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
|
||||
<br>
|
||||
|
||||
## Contributors ✨
|
||||
|
||||
Thanks to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
|
||||
<!-- prettier-ignore-start -->
|
||||
<!-- markdownlint-disable -->
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center"><a href="https://twitter.com/flybayer"><img src="https://avatars3.githubusercontent.com/u/8813276?v=4" width="100px;" alt=""/><br /><sub><b>Brandon Bayer</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=flybayer" title="Code">💻</a> <a href="#content-flybayer" title="Content">🖋</a> <a href="#ideas-flybayer" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/blitz-js/blitz/pulls?q=is%3Apr+reviewed-by%3Aflybayer" title="Reviewed Pull Requests">👀</a></td>
|
||||
<td align="center"><a href="https://medium.com/@ryardley"><img src="https://avatars0.githubusercontent.com/u/1256409?v=4" width="100px;" alt=""/><br /><sub><b>Rudi Yardley</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=ryardley" title="Code">💻</a> <a href="#ideas-ryardley" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/blitz-js/blitz/pulls?q=is%3Apr+reviewed-by%3Aryardley" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/blitz-js/blitz/commits?author=ryardley" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="https://merelinguist.me"><img src="https://avatars3.githubusercontent.com/u/24858006?v=4" width="100px;" alt=""/><br /><sub><b>Dylan Brookes</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=merelinguist" title="Code">💻</a> <a href="#ideas-merelinguist" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/blitz-js/blitz/pulls?q=is%3Apr+reviewed-by%3Amerelinguist" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/blitz-js/blitz/commits?author=merelinguist" title="Tests">⚠️</a> <a href="https://github.com/blitz-js/blitz/commits?author=merelinguist" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/aem"><img src="https://avatars0.githubusercontent.com/u/1909883?v=4" width="100px;" alt=""/><br /><sub><b>Adam Markon</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=aem" title="Code">💻</a> <a href="#ideas-aem" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/blitz-js/blitz/pulls?q=is%3Apr+reviewed-by%3Aaem" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/blitz-js/blitz/commits?author=aem" title="Tests">⚠️</a> <a href="#maintenance-aem" title="Maintenance">🚧</a></td>
|
||||
<td align="center"><a href="https://corey-brown.com"><img src="https://avatars1.githubusercontent.com/u/12791148?v=4" width="100px;" alt=""/><br /><sub><b>Corey Brown</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=coreybrown89" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/pulls?q=is%3Apr+reviewed-by%3Acoreybrown89" title="Reviewed Pull Requests">👀</a> <a href="#maintenance-coreybrown89" title="Maintenance">🚧</a></td>
|
||||
<td align="center"><a href="https://github.com/LoriKarikari"><img src="https://avatars1.githubusercontent.com/u/7902980?v=4" width="100px;" alt=""/><br /><sub><b>Lori Karikari</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=LoriKarikari" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/pulls?q=is%3Apr+reviewed-by%3ALoriKarikari" title="Reviewed Pull Requests">👀</a> <a href="#maintenance-LoriKarikari" title="Maintenance">🚧</a></td>
|
||||
<td align="center"><a href="https://twitter.com/GeggsElias"><img src="https://avatars3.githubusercontent.com/u/22719177?v=4" width="100px;" alt=""/><br /><sub><b>Elias Johansson</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=eliasjohansson" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/pulls?q=is%3Apr+reviewed-by%3Aeliasjohansson" title="Reviewed Pull Requests">👀</a> <a href="#maintenance-eliasjohansson" title="Maintenance">🚧</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://fabulas.io"><img src="https://avatars1.githubusercontent.com/u/14793389?v=4" width="100px;" alt=""/><br /><sub><b>Michael Edelman </b></sub></a><br /><a href="#infra-medelman17" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="https://github.com/blitz-js/blitz/commits?author=medelman17" title="Code">💻</a></td>
|
||||
<td align="center"><a href="http://www.geistinteractive.com"><img src="https://avatars2.githubusercontent.com/u/316792?v=4" width="100px;" alt=""/><br /><sub><b>Todd Geist</b></sub></a><br /><a href="#financial-toddgeist" title="Financial">💵</a></td>
|
||||
<td align="center"><a href="http://robdrosenberg.com"><img src="https://avatars0.githubusercontent.com/u/20813991?v=4" width="100px;" alt=""/><br /><sub><b>Robert Rosenberg</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=robdrosenberg" title="Code">💻</a> <a href="#maintenance-robdrosenberg" title="Maintenance">🚧</a> <a href="https://github.com/blitz-js/blitz/commits?author=robdrosenberg" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/quirk0o"><img src="https://avatars3.githubusercontent.com/u/5123725?v=4" width="100px;" alt=""/><br /><sub><b>Beata Obrok</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=quirk0o" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/tsawan"><img src="https://avatars3.githubusercontent.com/u/3263082?v=4" width="100px;" alt=""/><br /><sub><b>Tahir Awan</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=tsawan" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://raluce.com"><img src="https://avatars1.githubusercontent.com/u/2454632?v=4" width="100px;" alt=""/><br /><sub><b>Camilo Gonzalez</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=camilo86" title="Code">💻</a></td>
|
||||
<td align="center"><a href="http://da.nielkempner.com"><img src="https://avatars3.githubusercontent.com/u/2532112?v=4" width="100px;" alt=""/><br /><sub><b>Daniel Kempner</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=dkempner" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="http://gielcobben.com"><img src="https://avatars0.githubusercontent.com/u/2663212?v=4" width="100px;" alt=""/><br /><sub><b>Giel</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=gielcobben" title="Code">💻</a></td>
|
||||
<td align="center"><a href="http://jeremyliberman.com/"><img src="https://avatars3.githubusercontent.com/u/2754163?v=4" width="100px;" alt=""/><br /><sub><b>Jeremy Liberman</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=MrLeebo" title="Code">💻</a> <a href="#maintenance-MrLeebo" title="Maintenance">🚧</a> <a href="https://github.com/blitz-js/blitz/commits?author=MrLeebo" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="https://jimthedev.com"><img src="https://avatars0.githubusercontent.com/u/108938?v=4" width="100px;" alt=""/><br /><sub><b>Jim Cummins</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=jimthedev" title="Code">💻</a></td>
|
||||
<td align="center"><a href="http://kristinamatuska.com/"><img src="https://media-exp1.licdn.com/dms/image/C5603AQHVPAjV21gw9g/profile-displayphoto-shrink_200_200/0?e=1591228800&v=beta&t=0MlbmiYhNvGv1xjLD_fOhOFjVDZ7ltNwfGNeJ4DHedQ" width="100px;" alt=""/><br /><sub><b>Kristina Matuška</b></sub></a><br /><a href="#design" title="Design">🎨</a></td>
|
||||
<td align="center"><a href="https://github.com/jasonblalock"><img src="https://avatars0.githubusercontent.com/u/5899929?v=4" width="100px;" alt=""/><br /><sub><b>Jason Blalock</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=jasonblalock" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/aej11a"><img src="https://avatars2.githubusercontent.com/u/10066422?v=4" width="100px;" alt=""/><br /><sub><b>aej11a</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=aej11a" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/marcoseoane"><img src="https://avatars0.githubusercontent.com/u/28088807?v=4" width="100px;" alt=""/><br /><sub><b>marcoseoane</b></sub></a><br /><a href="#ideas-marcoseoane" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/rishabhpoddar"><img src="https://avatars2.githubusercontent.com/u/2976287?v=4" width="100px;" alt=""/><br /><sub><b>Rishabh Poddar</b></sub></a><br /><a href="#ideas-rishabhpoddar" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||
<td align="center"><a href="https://github.com/lorenzorapetti"><img src="https://avatars1.githubusercontent.com/u/2632174?v=4" width="100px;" alt=""/><br /><sub><b>Lorenzo Rapetti</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=lorenzorapetti" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/wKovacs64"><img src="https://avatars1.githubusercontent.com/u/1288694?v=4" width="100px;" alt=""/><br /><sub><b>Justin Hall</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=wKovacs64" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=wKovacs64" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/sijad"><img src="https://avatars3.githubusercontent.com/u/7693001?v=4" width="100px;" alt=""/><br /><sub><b>Sajjad Hashemian</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=sijad" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/ETLopes"><img src="https://avatars3.githubusercontent.com/u/34959471?v=4" width="100px;" alt=""/><br /><sub><b>Eduardo Lopes</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=ETLopes" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/mattleff"><img src="https://avatars0.githubusercontent.com/u/120155?v=4" width="100px;" alt=""/><br /><sub><b>Matthew Leffler</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=mattleff" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://hew.tools"><img src="https://avatars0.githubusercontent.com/u/3103241?v=4" width="100px;" alt=""/><br /><sub><b>Matt</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=hew" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/sonnypgs"><img src="https://avatars3.githubusercontent.com/u/1431300?v=4" 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" 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="http://twitter.com/JanBaykara"><img src="https://avatars2.githubusercontent.com/u/237556?v=4" 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" 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" 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>
|
||||
<td align="center"><a href="https://github.com/jclancy93"><img src="https://avatars2.githubusercontent.com/u/7850202?v=4" width="100px;" alt=""/><br /><sub><b>Jack Clancy</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=jclancy93" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/ntgussoni"><img src="https://avatars0.githubusercontent.com/u/10161067?v=4" width="100px;" alt=""/><br /><sub><b>Nicolas Torres</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=ntgussoni" title="Tests">⚠️</a> <a href="https://github.com/blitz-js/blitz/commits?author=ntgussoni" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="http://simonknott.de"><img src="https://avatars1.githubusercontent.com/u/14912729?v=4" width="100px;" alt=""/><br /><sub><b>Simon Knott</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=skn0tt" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=skn0tt" title="Tests">⚠️</a> <a href="#maintenance-skn0tt" title="Maintenance">🚧</a></td>
|
||||
<td align="center"><a href="http://jagascript.com"><img src="https://avatars0.githubusercontent.com/u/4562878?v=4" width="100px;" alt=""/><br /><sub><b>Jaga Santagostino</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=kandros" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=kandros" title="Documentation">📖</a> <a href="#maintenance-kandros" title="Maintenance">🚧</a></td>
|
||||
<td align="center"><a href="http://www.joaoportela.com"><img src="https://avatars0.githubusercontent.com/u/1010018?v=4" width="100px;" alt=""/><br /><sub><b>João Portela</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=jportela" title="Code">💻</a></td>
|
||||
<td align="center"><a href="http://dajin.dev"><img src="https://avatars0.githubusercontent.com/u/7122182?v=4" width="100px;" alt=""/><br /><sub><b>Da-Jin Chu</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=dajinchu" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://shinyaigeek.dev/"><img src="https://avatars1.githubusercontent.com/u/42742053?v=4" width="100px;" alt=""/><br /><sub><b>Shinobu Hayashi</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=Shinyaigeek" title="Code">💻</a></td>
|
||||
<td align="center"><a href="http://karankiri.com"><img src="https://avatars2.githubusercontent.com/u/19989161?v=4" width="100px;" alt=""/><br /><sub><b>Karan Kiri</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=karankiri" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/fullmetalengineer"><img src="https://avatars2.githubusercontent.com/u/5294903?v=4" width="100px;" alt=""/><br /><sub><b>Alan Long</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=fullmetalengineer" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="http://codingsh.xyz"><img src="https://avatars2.githubusercontent.com/u/57037080?v=4" width="100px;" alt=""/><br /><sub><b>codingsh</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=developerfred" title="Code">💻</a></td>
|
||||
<td align="center"><a href="http://twitter.com/peaonunes"><img src="https://avatars0.githubusercontent.com/u/3356720?v=4" width="100px;" alt=""/><br /><sub><b>Rafael Nunes</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/pulls?q=is%3Apr+reviewed-by%3Apeaonunes" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/blitz-js/blitz/commits?author=peaonunes" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://simonpeterdebbarma.com"><img src="https://avatars3.githubusercontent.com/u/31207418?v=4" width="100px;" alt=""/><br /><sub><b>Simon Debbarma</b></sub></a><br /><a href="#design-0ww" title="Design">🎨</a> <a href="#maintenance-0ww" title="Maintenance">🚧</a> <a href="https://github.com/blitz-js/blitz/commits?author=0ww" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/0xflotus"><img src="https://avatars3.githubusercontent.com/u/26602940?v=4" width="100px;" alt=""/><br /><sub><b>0xflotus</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=0xflotus" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=0xflotus" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://dev.to/tmns"><img src="https://avatars3.githubusercontent.com/u/35785003?v=4" width="100px;" alt=""/><br /><sub><b>tmns</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=tmns" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=tmns" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="http://jruharris.com"><img src="https://avatars1.githubusercontent.com/u/8636691?v=4" width="100px;" alt=""/><br /><sub><b>Jru Harris</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=harris1717" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://twitter.com/ivandevp"><img src="https://avatars3.githubusercontent.com/u/9284690?v=4" width="100px;" alt=""/><br /><sub><b>Ivan Medina</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=ivandevp" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://www.dwightwatson.com"><img src="https://avatars3.githubusercontent.com/u/1100408?v=4" width="100px;" alt=""/><br /><sub><b>Dwight Watson</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=dwightwatson" title="Code">💻</a></td>
|
||||
<td align="center"><a href="http://is2ei.com/"><img src="https://avatars3.githubusercontent.com/u/3948353?v=4" width="100px;" alt=""/><br /><sub><b>Horie Issei #hashtagtest</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=is2ei" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://twitter.com/lednhatkhanh"><img src="https://avatars2.githubusercontent.com/u/9303093?v=4" width="100px;" alt=""/><br /><sub><b>Nhat Khanh</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=lednhatkhanh" title="Code">💻</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<!-- markdownlint-enable -->
|
||||
<!-- prettier-ignore-end -->
|
||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||
|
||||
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
|
||||
|
||||
589
TUTORIAL.md
Normal file
589
TUTORIAL.md
Normal file
@@ -0,0 +1,589 @@
|
||||
# Blitz Tutorial
|
||||
|
||||
← [Back to Alpha Guide](https://github.com/blitz-js/blitz/blob/canary/USER_GUIDE.md)
|
||||
|
||||
Thanks for trying out Blitz! In this tutorial, we’ll walk you through the creation of a basic voting application.
|
||||
|
||||
We’ll assume that you have [Blitz installed](https://github.com/blitz-js/blitz/blob/canary/USER_GUIDE.md#blitz-app-development) already. You can tell if Blitz is installed, and which version you have by running the following command in your terminal:
|
||||
|
||||
```sh
|
||||
$ blitz -v
|
||||
```
|
||||
|
||||
If Blitz is installed, you should see the version of your installation. If it isn’t, you’ll get an error saying something like “command not found: blitz”.
|
||||
|
||||
## Creating a project
|
||||
|
||||
If this is your first time using Blitz, you’ll have to begin with some initial setup. We provide a command which takes care of all this for you, generating the configuration and code you need to get started.
|
||||
|
||||
From the command line, `cd` into the directory where you’d like to store your code, and then run the following command:
|
||||
|
||||
```sh
|
||||
blitz new mysite
|
||||
```
|
||||
|
||||
This should create a `mysite` directory in your current directory.
|
||||
|
||||
Let’s look at what `blitz new` created:
|
||||
|
||||
```
|
||||
mysite
|
||||
├── app
|
||||
│ ├── components
|
||||
│ │ └── ErrorBoundary.tsx
|
||||
│ ├── layouts
|
||||
│ └── pages
|
||||
│ ├── _app.tsx
|
||||
│ ├── _document.tsx
|
||||
│ └── index.tsx
|
||||
├── db
|
||||
│ ├── migrations
|
||||
│ ├── index.ts
|
||||
│ └── schema.prisma
|
||||
├── integrations
|
||||
├── jobs
|
||||
├── node_modules
|
||||
├── public
|
||||
│ ├── favicon.ico
|
||||
│ └── logo.png
|
||||
├── utils
|
||||
├── .babelrc.js
|
||||
├── .env
|
||||
├── .eslintrc.js
|
||||
├── .gitignore
|
||||
├── .npmrc
|
||||
├── .prettierignore
|
||||
├── README.md
|
||||
├── blitz.config.js
|
||||
├── package.json
|
||||
├── tsconfig.json
|
||||
└── yarn.lock
|
||||
```
|
||||
|
||||
These files are:
|
||||
|
||||
- The `app/` directory is a container for most of your project. This is where you’ll put any pages or API routes.
|
||||
|
||||
- `db`/ is where your database configuration goes. If you’re writing models or checking migrations, this is where to go.
|
||||
|
||||
- `node_modules/` is where your “dependencies” are stored. This directory is updated by your package manager, so you don’t have to worry too much about it.
|
||||
|
||||
- `public/` is a directory where you will put any static assets. If you have images, files, or videos which you want to use in your app, this is where to put them.
|
||||
|
||||
- `utils/` is a good place to put any shared utility files which you might use across different sections of your app.
|
||||
|
||||
- `.babelrc.js`, `.env`, etc. ("dotfiles") are configuration files for various bits of JavaScript tooling.
|
||||
|
||||
- `blitz.config.js` is for advanced custom configuration of Blitz. It extends [`next.config.js`](https://nextjs.org/docs/api-reference/next.config.js/introduction).
|
||||
|
||||
- `package.json` contains information about your dependencies and devDependencies. If you’re using a tool like `npm` or `yarn`, you won’t have to worry about this much.
|
||||
|
||||
- `tsconfig.json` is our recommended setup for TypeScript.
|
||||
|
||||
## The development server
|
||||
|
||||
Let’s check that your Blitz project works. Make sure you are in the `mysite` directory, if you haven’t already, and run the following command:
|
||||
|
||||
```sh
|
||||
$ blitz start
|
||||
```
|
||||
|
||||
You’ll see the following output on the command line:
|
||||
|
||||
```sh
|
||||
✔ Prepped for launch
|
||||
[ wait ] starting the development server ...
|
||||
[ info ] waiting on http://localhost:3000 ...
|
||||
[ info ] bundled successfully, waiting for typecheck results...
|
||||
[ wait ] compiling ...
|
||||
[ info ] bundled successfully, waiting for typecheck results...
|
||||
[ ready ] compiled successfully - ready on http://localhost:3000
|
||||
```
|
||||
|
||||
Now that the server’s running, visit http://localhost:3000/ with your Web browser. You’ll see a welcome page, with the Blitz logo. It worked!
|
||||
|
||||
## Write your first page
|
||||
|
||||
Now that your development environment—a “project”—is set up, you’re ready to start building out the app. First, we’ll create your first page.
|
||||
|
||||
Open the file `app/pages/index.tsx` and put the following code in it:
|
||||
|
||||
```tsx
|
||||
export default () => (
|
||||
<div>
|
||||
<h1>Hello, world!</h1>
|
||||
</div>
|
||||
)
|
||||
```
|
||||
|
||||
This is the simplest page possible in Blitz. To look at it, go back to your browser and go to http://localhost:3000. You should see your text appear! Try editing the `index.tsx` file, and make it your own! When you’re ready, move on to the next section.
|
||||
|
||||
## Database setup
|
||||
|
||||
Now, we’ll setup the database and create your first model.
|
||||
|
||||
Open up `db/schema.prisma`. It’s a configuration file which our default database engine Prisma uses.
|
||||
|
||||
By default, the apps is created with SQLite. If you’re new to databases, or you’re just interested in trying Blitz, this is the easiest choice. Note that when starting your first project, you may want to use a more scalable database like PostgreSQL, to avoid the pains of switching your database down the road.
|
||||
|
||||
## Creating models
|
||||
|
||||
Now we’ll define your models — essentially your database layout — with additional metadata.
|
||||
|
||||
In `schema.prisma`, we’ll create two models: `Question`, and `Choice`. A `Question` has a question and a publication date. A `Choice` has two fields: the text of the choice and a vote count. Each has an id, and each `Choice` is associated with a `Question`.
|
||||
|
||||
Edit the `schema.prisma` file so it looks like this:
|
||||
|
||||
```
|
||||
// (datasource and generator)
|
||||
|
||||
...
|
||||
|
||||
model Question {
|
||||
id Int @default(autoincrement()) @id
|
||||
text String
|
||||
publishedAt DateTime
|
||||
choices Choice[]
|
||||
}
|
||||
|
||||
model Choice {
|
||||
id Int @default(autoincrement()) @id
|
||||
text String
|
||||
votes Int @default(0)
|
||||
question Question @relation(fields: [questionId], references: [id])
|
||||
questionId Int
|
||||
}
|
||||
```
|
||||
|
||||
Now, we need to migrate our database. This is a way of telling it that you have edited your schema in some way. Run the below command. When it asks you to enter a migration name you can enter anything you want, perhaps `
|
||||
"init db":
|
||||
|
||||
```sh
|
||||
$ blitz db migrate
|
||||
```
|
||||
|
||||
## Playing with the API
|
||||
|
||||
Now, let’s hop into the interactive Blitz shell and play around with the free API Blitz gives you. To invoke the Blitz console, use this command:
|
||||
|
||||
```sh
|
||||
$ blitz console
|
||||
```
|
||||
|
||||
Once you’re in the console, explore the Database API:
|
||||
|
||||
[//]: # 'Let’s move this to await when it’s available for all */'
|
||||
|
||||
```sh
|
||||
# No questions are in the system yet.
|
||||
⚡ > db.question.findMany().then(console.log)
|
||||
[]
|
||||
|
||||
# Create a new Question.
|
||||
⚡ > let q
|
||||
undefined
|
||||
|
||||
⚡ > db.question.create({data: {text: 'What’s new?', publishedAt: new Date()}}).then(res => q = res)
|
||||
Promise { <pending> }
|
||||
|
||||
# See the entire object
|
||||
⚡ > q
|
||||
{ id: 1, text: "What’s new?", publishedAt: 2020-04-24T22:08:17.307Z }
|
||||
|
||||
# Or access individual values on the object.
|
||||
⚡ > q.text
|
||||
"What’s new?"
|
||||
|
||||
⚡ > q.publishedAt
|
||||
2020-04-24T22:08:17.307Z
|
||||
|
||||
# Change values by using the update function
|
||||
⚡ > db.question.update({where: {id: 1}, data: {text: 'What’s up?'}}).then(res => q = res)
|
||||
Promise { <pending> }
|
||||
|
||||
# See the result
|
||||
⚡ > q
|
||||
{ id: 1, text: 'What’s up?', publishedAt: 2020-04-24T22:08:17.307Z }
|
||||
|
||||
# db.question.findMany() displays all the questions in the database.
|
||||
⚡ > db.question.findMany().then(console.log)
|
||||
[
|
||||
{ id: 1, text: 'What’s up?', publishedAt: 2020-04-24T22:08:17.307Z }
|
||||
]
|
||||
```
|
||||
|
||||
## Writing more pages
|
||||
|
||||
Let’s create some more pages. Blitz provides a handy utility for scaffolding out pages, called `generate`. Let’s run it now with our `Question` model:
|
||||
|
||||
```sh
|
||||
$ blitz generate all question
|
||||
```
|
||||
|
||||
Great! Before running the app again, we need to customise some of these pages which have just been generated. Open your text editor and look at `app/questions/pages/questions/index.tsx`. Notice that a `QuestionsList` component has been generated for you:
|
||||
|
||||
```jsx
|
||||
export const QuestionsList = () => {
|
||||
const [questions] = useQuery(getQuestions)
|
||||
|
||||
return (
|
||||
<ul>
|
||||
{questions.map((question) => (
|
||||
<li key={question.id}>
|
||||
<Link href="/questions/[id]" as={`/questions/${question.id}`}>
|
||||
<a>{question.name}</a>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
This won’t work though! Remember that the `Question` model we created above doesn’t have any `name` field. To fix this, replace `question.name` with `question.text`:
|
||||
|
||||
```jsx
|
||||
export const QuestionsList = () => {
|
||||
const [questions] = useQuery(getQuestions)
|
||||
|
||||
return (
|
||||
<ul>
|
||||
{questions.map((question) => (
|
||||
<li key={question.id}>
|
||||
<Link href="/questions/[id]" as={`/questions/${question.id}`}>
|
||||
<a>{question.text}</a>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
Next, let’s apply a similar fix to `app/questions/pages/questions/new.tsx`. In the form submission, replace
|
||||
|
||||
```jsx
|
||||
const question = await createQuestion({data: {name: 'MyName'}})
|
||||
```
|
||||
|
||||
with
|
||||
|
||||
```jsx
|
||||
const question = await createQuestion({
|
||||
data: {text: 'Do you love Blitz?', publishedAt: new Date()},
|
||||
})
|
||||
```
|
||||
|
||||
Finally, we just need to fix the edit page. Open `app/questions/pages/questions/[id]/edit.tsx` and replace
|
||||
|
||||
```jsx
|
||||
const updated = await updateQuestion({
|
||||
where: {id: question.id},
|
||||
data: {name: 'MyNewName'},
|
||||
})
|
||||
```
|
||||
|
||||
with
|
||||
|
||||
```jsx
|
||||
const updated = await updateQuestion({
|
||||
where: {id: question.id},
|
||||
data: {text: 'Do you really love Blitz?'},
|
||||
})
|
||||
```
|
||||
|
||||
Great! Now make sure your app is running. If it isn’t, just run `blitz start` in your terminal, and visit http://localhost:3000/questions. Play around with your new app a bit! Try creating questions, editing, and deleting them.
|
||||
|
||||
## Writing a minimal form
|
||||
|
||||
You’re doing great so far! The next thing we’ll do is give our form some real inputs. At the moment it’s giving every `Question` the same name! Have a look at `app/questions/pages/questions/new.tsx` in your editor.
|
||||
|
||||
Delete the div that says: `<div>Put your form fields here. But for now, just click submit</div>`, and replace it with some inputs:
|
||||
|
||||
```jsx
|
||||
<input placeholder="Name" />
|
||||
|
||||
<input placeholder="Choice 1" />
|
||||
<input placeholder="Choice 1" />
|
||||
<input placeholder="Choice 1" />
|
||||
```
|
||||
|
||||
Finally, we’re going to make sure all that data is submitted. In the end, your page should look something like this:
|
||||
|
||||
```jsx
|
||||
import {Head, Link, useRouter} from 'blitz'
|
||||
import createQuestion from 'app/questions/mutations/createQuestion'
|
||||
|
||||
const NewQuestionPage = () => {
|
||||
const router = useRouter()
|
||||
|
||||
return (
|
||||
<div className="container">
|
||||
<Head>
|
||||
<title>New Question</title>
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
</Head>
|
||||
|
||||
<main>
|
||||
<h1>Create New Question </h1>
|
||||
|
||||
<form
|
||||
onSubmit={async (event) => {
|
||||
event.preventDefault()
|
||||
|
||||
try {
|
||||
const question = await createQuestion({
|
||||
data: {
|
||||
text: event.target[0].value,
|
||||
publishedAt: new Date(),
|
||||
choices: {
|
||||
create: [
|
||||
{text: event.target[1].value},
|
||||
{text: event.target[2].value},
|
||||
{text: event.target[3].value},
|
||||
],
|
||||
},
|
||||
},
|
||||
})
|
||||
alert('Success!' + JSON.stringify(question))
|
||||
router.push('/questions/[id]', `/questions/${question.id}`)
|
||||
} catch (error) {
|
||||
alert('Error creating question ' + JSON.stringify(error, null, 2))
|
||||
}
|
||||
}}>
|
||||
<input placeholder="Name" />
|
||||
|
||||
<input placeholder="Choice 1" />
|
||||
<input placeholder="Choice 1" />
|
||||
<input placeholder="Choice 1" />
|
||||
|
||||
<button>Submit</button>
|
||||
</form>
|
||||
|
||||
<p>
|
||||
<Link href="/questions">
|
||||
<a>Questions</a>
|
||||
</Link>
|
||||
</p>
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default NewQuestionPage
|
||||
```
|
||||
|
||||
## Listing choices
|
||||
|
||||
Time for a breather. Go back to `http://localhost:3000/questions` in your browser and look at all the questions you‘ve created. How about we list these questions’ choices here too? First, we need to customise the question queries. In Prisma, you need to manually let the client know that you want to query for nested relations. Change your `getQuestion.ts` and `getQuestions.ts` files to look like this:
|
||||
|
||||
```js
|
||||
// app/questions/queries/getQuestion.ts
|
||||
|
||||
import db, {FindOneQuestionArgs} from 'db'
|
||||
|
||||
export default async function getQuestion(args: FindOneQuestionArgs) {
|
||||
const question = await db.question.findOne({...args, include: {choices: true}})
|
||||
|
||||
return question
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
// app/questions/queries/getQuestions.ts
|
||||
|
||||
import db, {FindManyQuestionArgs} from 'db'
|
||||
|
||||
export default async function getQuestions(args: FindManyQuestionArgs) {
|
||||
const questions = await db.question.findMany({...args, include: {choices: true}})
|
||||
|
||||
return questions
|
||||
}
|
||||
```
|
||||
|
||||
Now hop back to our main questions page in your editor, and we can list the choices of each question easily. Just add this code beneath the `Link` in our `QuestionsList`:
|
||||
|
||||
```jsx
|
||||
<ul>
|
||||
{question.choices.map((choice) => (
|
||||
<li key={choice.id}>
|
||||
{choice.text} - {choice.votes} votes
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
```
|
||||
|
||||
Magic! Let’s do one more thing–let people vote on these questions!
|
||||
|
||||
Open `app/questions/pages/questions/[id].tsx` in your editor. First, we’re going to improve this page somewhat.
|
||||
|
||||
1. Replace `<h1>Question {question.id}</h1>` with `<h1>{question.text}</h1>`.
|
||||
|
||||
2. Delete the `pre` element, and copy in our choices list which we wrote before:
|
||||
|
||||
```jsx
|
||||
<ul>
|
||||
{question.choices.map((choice) => (
|
||||
<li key={choice.id}>
|
||||
{choice.text} - {choice.votes} votes
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
```
|
||||
|
||||
If you go back to your browser, your page should now look something like this!
|
||||
|
||||
<img width="567" alt="Screenshot 2020-04-27 at 16 06 55" src="https://user-images.githubusercontent.com/24858006/80387990-3c3d8b80-88a1-11ea-956a-5be85f1e8f12.png">
|
||||
|
||||
Now that you’ve improved the question page, it’s time for the vote button.
|
||||
|
||||
First, we need a new mutation. Create a file at `app/questions/mutations/updateChoice.ts`, and paste in the following code:
|
||||
|
||||
```js
|
||||
import db, {ChoiceUpdateArgs} from 'db'
|
||||
|
||||
export default async function updateChoice(args: ChoiceUpdateArgs) {
|
||||
// Don't allow updating ID
|
||||
delete args.data.id
|
||||
|
||||
const choice = await db.choice.update(args)
|
||||
|
||||
return choice
|
||||
}
|
||||
```
|
||||
|
||||
Back in `app/questions/pages/questions/[id].tsx`, we can now add a vote button.
|
||||
|
||||
In our `li`, add a button like so:
|
||||
|
||||
```jsx
|
||||
<li key={choice.id}>
|
||||
{choice.text} - {choice.votes} votes
|
||||
<button>Vote</button>
|
||||
</li>
|
||||
```
|
||||
|
||||
Then, import our `updateChoice` mutation, and create a `handleVote` function in our page:
|
||||
|
||||
```jsx
|
||||
import updateChoice from "app/questions/mutations/updateChoice"
|
||||
|
||||
...
|
||||
|
||||
const handleVote = async (id, votes) => {
|
||||
try {
|
||||
const updated = await updateChoice({
|
||||
where: { id },
|
||||
data: { votes: votes + 1 },
|
||||
})
|
||||
alert("Success!" + JSON.stringify(updated))
|
||||
} catch (error) {
|
||||
alert("Error creating question " + JSON.stringify(error, null, 2))
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
Finally, we’ll just tell our `button` to call that function!
|
||||
|
||||
```jsx
|
||||
<button onClick={() => handleVote(choice.id, choice.votes)}>Vote</button>
|
||||
```
|
||||
|
||||
Just to be sure, this is what all that should look like:
|
||||
|
||||
```jsx
|
||||
import { Suspense } from "react"
|
||||
import { Head, Link, useRouter, useQuery } from "blitz"
|
||||
import getQuestion from "app/questions/queries/getQuestion"
|
||||
import deleteQuestion from "app/questions/mutations/deleteQuestion"
|
||||
import updateChoice from "app/questions/mutations/updateChoice"
|
||||
|
||||
export const Question = () => {
|
||||
const router = useRouter()
|
||||
const id = parseInt(router?.query.id as string)
|
||||
const [question] = useQuery(getQuestion, { where: { id } })
|
||||
|
||||
const handleVote = async (id, votes) => {
|
||||
try {
|
||||
const updated = await updateChoice({
|
||||
where: { id },
|
||||
data: { votes: votes + 1 },
|
||||
})
|
||||
alert("Success!" + JSON.stringify(updated))
|
||||
} catch (error) {
|
||||
alert("Error creating question " + JSON.stringify(error, null, 2))
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>{question.text}</h1>
|
||||
<ul>
|
||||
{question.choices.map((choice) => (
|
||||
<li key={choice.id}>
|
||||
{choice.text} - {choice.votes} votes
|
||||
<button onClick={() => handleVote(choice.id, choice.votes)}>Vote</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<Link href="/questions/[id]/edit" as={`/questions/${question.id}/edit`}>
|
||||
<a>Edit</a>
|
||||
</Link>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={async () => {
|
||||
if (confirm("This will be deleted")) {
|
||||
await deleteQuestion({ where: { id: question.id } })
|
||||
router.push("/questions")
|
||||
}
|
||||
}}
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const ShowQuestionPage = () => {
|
||||
return (
|
||||
<div className="container">
|
||||
<Head>
|
||||
<title>Question</title>
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
</Head>
|
||||
|
||||
<main>
|
||||
<p>
|
||||
<Link href="/questions">
|
||||
<a>Questions</a>
|
||||
</Link>
|
||||
</p>
|
||||
|
||||
<Suspense fallback={<div>Loading...</div>}>
|
||||
<Question />
|
||||
</Suspense>
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ShowQuestionPage
|
||||
```
|
||||
|
||||
## Conclusion
|
||||
|
||||
🥳 Congrats! You just created your very own Blitz app! Have fun playing around with it, or sharing it with your friends. Now that you’ve finished this tutorial, why not try making your voting app even better? You could try:
|
||||
|
||||
- Adding styling
|
||||
- Showing some more statistics about votes
|
||||
- Deploying live so you can send it around (we recommend [Vercel](https://vercel.com/))
|
||||
|
||||
If you want to share your project with the world wide Blitz community there is no better place to do that than on Slack.
|
||||
|
||||
Just visit https://slack.blitzjs.com. Then, post the link to the **#show-and-tell** channel to share it with everyone!
|
||||
382
USER_GUIDE.md
Normal file
382
USER_GUIDE.md
Normal file
@@ -0,0 +1,382 @@
|
||||

|
||||
|
||||
<br>
|
||||
|
||||
Before getting started, you should know **this is alpha software**. Blitz is incomplete. There are rough spots and bugs. APIs may change. But you can build an app and deploy it to production. We're excited to see what you build!
|
||||
|
||||
If you have any issues at all, please [open an issue](https://github.com/blitz-js/blitz/issues/new/choose) or join the [Blitz slack](https://slack.blitzjs.com) and talk to us in the **#help** channel. If you get stuck and frustrated, please don't blame yourself. This user guide, and Blitz in general, is not yet fine-tuned for those with less experience. But eventually, it will be because this is very important to us.
|
||||
|
||||
If you’re looking for a slower, more guided start to Blitz, read the **[Blitz Beginner Tutorial](https://github.com/blitz-js/blitz/blob/canary/TUTORIAL.md)**.
|
||||
|
||||
<br>
|
||||
|
||||
## Introduction
|
||||
|
||||
Blitz is a Rails-like framework for building monolithic, full-stack React apps. The idea is that Blitz makes you extremely productive by doing as much set up and grunt work for you.
|
||||
|
||||
**When building a Blitz app, you don’t have to think about “building an API” or “fetching data from your API”**. You only think about writing functions that get and change data. And to use those functions in your component, you simply import and call them like a regular function.
|
||||
|
||||
Blitz is built on Next.js, so if you are familiar with that, you will feel right at home.
|
||||
|
||||
<br>
|
||||
|
||||
## Blitz App Development
|
||||
|
||||
### Set Up Your Computer
|
||||
|
||||
- [ ] You need Node.js 12 or newer
|
||||
|
||||
<br>
|
||||
|
||||
### Create Your Blitz App
|
||||
|
||||
1. `npm install -g blitz` or `yarn global add blitz`
|
||||
2. Run `blitz new myAppName` to create a new blitz app in the `myAppName` directory
|
||||
3. `cd myAppName`
|
||||
4. `blitz start`
|
||||
5. View your baby app at [http://localhost:3000](http://localhost:3000)
|
||||
|
||||
<br>
|
||||
|
||||
### Set Up Your Database
|
||||
|
||||
By default, Blitz uses Prisma 2 which is a strongly typed database client. **You probably want to read [the Prisma 2 documentation](https://www.prisma.io/docs/understand-prisma/introduction).** _Note, Prisma 2 is not required for Blitz. The only Prisma-Blitz integration is the `blitz db` cli command. You can use anything you want, such as Mongo, TypeORM, etc._
|
||||
|
||||
1. Open `db/schema.prisma` and add the following:
|
||||
|
||||
```prisma
|
||||
model Project {
|
||||
id Int @default(autoincrement()) @id
|
||||
name String
|
||||
tasks Task[]
|
||||
}
|
||||
|
||||
model Task {
|
||||
id Int @default(autoincrement()) @id
|
||||
name String
|
||||
project Project @relation(fields: [projectId], references: [id])
|
||||
projectId Int
|
||||
}
|
||||
```
|
||||
|
||||
2. Run `blitz db migrate`
|
||||
- If this fails, you need to change the `DATABASE_URL` value in `.env` to whatever is required by your Postgres installation.
|
||||
|
||||
<br>
|
||||
|
||||
### Scaffold out all the files your basic CRUD actions
|
||||
|
||||
_CRUD = create, read, update, delete_
|
||||
|
||||
1. Run `blitz generate all project` to generate fully working queries, mutations, and pages
|
||||
2. Open [http://localhost:3000/projects](http://localhost:3000/projects) to see the default project list page
|
||||
3. Explore the generated pages and view, create, update, and delete projects.
|
||||
|
||||
<br>
|
||||
|
||||
### Pages
|
||||
|
||||
Blitz.js pages are exactly the same as Next.js pages. If you need, read [the Next.js Page documentation](https://nextjs.org/docs/basic-features/pages)
|
||||
|
||||
- Unlike Next.js, you can have many `pages/` folders nested inside `app/`. This way pages can be organized neatly, especially for larger projects. Like this:
|
||||
- `app/pages/about.tsx`
|
||||
- `app/projects/pages/projects/index.tsx`
|
||||
- `app/tasks/pages/projects/[projectId]/tasks/[id].tsx`
|
||||
- All React components inside a `pages/` folder are accessible at a URL corresponding to its path inside `pages/`. So `pages/about.tsx` will be at `localhost:3000/about`.
|
||||
|
||||
The Next.js router APIs are all exported from the `blitz` package: `useRouter()`, `withRouter()`, and `Router`. If you need, read [the Next.js Router documentation](https://nextjs.org/docs/api-reference/next/router).
|
||||
|
||||
<br>
|
||||
|
||||
### Writing Queries & Mutations
|
||||
|
||||
Blitz queries and mutations are plain, asynchronous Javascript functions that always run on the server.
|
||||
|
||||
We automatically alias the root of your project, so `import db from 'db'` is importing `<project_root>/db/index.ts`
|
||||
|
||||
**Example Query:**
|
||||
|
||||
```ts
|
||||
// app/products/queries/getProduct.tsx
|
||||
import db, {FindOneProductArgs} from 'db'
|
||||
|
||||
export default async function getProduct(args: FindOneProductArgs) {
|
||||
// Can do any pre-processing or event triggers here
|
||||
const product = await db.product.findOne(args)
|
||||
// Can do any post-processing or event triggers here
|
||||
|
||||
return product
|
||||
}
|
||||
```
|
||||
|
||||
**Example Mutation:**
|
||||
|
||||
```ts
|
||||
// app/products/mutations/createProduct.tsx
|
||||
import db, {ProductCreateArgs} from 'db'
|
||||
|
||||
export default async function createProduct(args: ProductCreateArgs) {
|
||||
// Can do any pre-processing or event triggers here
|
||||
const product = await db.product.create(args)
|
||||
// Can do any post-processing or event triggers here
|
||||
|
||||
return product
|
||||
}
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
### Using Queries
|
||||
|
||||
#### In a React Component
|
||||
|
||||
Blitz provides a `useQuery` hook, which is built on [`react-query`](https://github.com/tannerlinsley/react-query). The first argument is a query function. The second argument is the input to the query function. The third argument is any valid react-query configuration item.
|
||||
|
||||
At build time, the direct function import is swapped out for a function that executes a network call to run the query serverside.
|
||||
|
||||
**React Concurrent Mode is enabled by default for Blitz apps.** So the `<Suspense>` component is used for loading states and `<ErrorBoundary>` is used to display errors. If you need, you can read the [React Concurrent Mode Docs](https://reactjs.org/docs/concurrent-mode-intro.html).
|
||||
|
||||
```tsx
|
||||
import {Suspense} from 'react'
|
||||
import {useRouter, useQuery} from 'blitz'
|
||||
import getProduct from 'app/products/queries/getProduct'
|
||||
import ErrorBoundary from 'app/components/ErrorBoundary'
|
||||
|
||||
function Product() {
|
||||
const router = useRouter()
|
||||
const id = parseInt(router.query.id as string)
|
||||
const [product] = useQuery(getProduct, {where: {id}})
|
||||
|
||||
return <div>{product.name}</div>
|
||||
}
|
||||
|
||||
function WrappedProduct() {
|
||||
return (
|
||||
<div>
|
||||
<ErrorBoundary fallback={(error) => <div>Error: {JSON.stringify(error)}</div>}>
|
||||
<Suspense fallback={<div>Loading...</div>}>
|
||||
<Product />
|
||||
</Suspense>
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default WrappedProduct
|
||||
```
|
||||
|
||||
#### On the Server
|
||||
|
||||
In `getStaticProps`, a query function can be called directly without `useQuery`
|
||||
|
||||
```tsx
|
||||
import getProduct from '/app/products/queries/getProduct'
|
||||
|
||||
export const getStaticProps = async (context) => {
|
||||
const product = await getProduct({where: {id: context.params?.id}})
|
||||
return {props: {product}}
|
||||
}
|
||||
|
||||
function ProductPage({product}) {
|
||||
return <div>{product.name}</div>
|
||||
}
|
||||
|
||||
export default ProductPage
|
||||
```
|
||||
|
||||
In `getServerSideProps`, pass a query function to `ssrQuery` which will ensure appropriate middleware is run before/after your query function.
|
||||
|
||||
```tsx
|
||||
import {ssrQuery} from 'blitz'
|
||||
import getProduct from '/app/products/queries/getProduct'
|
||||
|
||||
export const getServerSideProps = async ({params, req, res}) => {
|
||||
const product = await ssrQuery(getProduct, {where: {id: params.id}}, {req, res}))
|
||||
return {props: {product}}
|
||||
}
|
||||
|
||||
function ProductPage ({product}) {
|
||||
return <div>{product.name}</div>
|
||||
}
|
||||
|
||||
export default ProductPage
|
||||
```
|
||||
|
||||
For more details, read the comprehensive [Query & Mutation Usage Issue](https://github.com/blitz-js/blitz/issues/89)
|
||||
|
||||
<br>
|
||||
|
||||
### Using Mutations
|
||||
|
||||
Mutations are called directly, like a regular asynchronous function.
|
||||
|
||||
At build time, the direct function import is swapped out for a function that executes a network call to run the mutation server-side.
|
||||
|
||||
```tsx
|
||||
import {useQuery} from 'blitz'
|
||||
import getProduct from '/app/products/queries/getProduct'
|
||||
import updateProduct from '/app/products/mutations/updateProduct'
|
||||
|
||||
function (props) {
|
||||
const [product] = useQuery(getProduct, {where: {id: props.id}})
|
||||
|
||||
return (
|
||||
<Formik
|
||||
initialValues={product}
|
||||
onSubmit={async values => {
|
||||
try {
|
||||
const product = await updateProduct(values)
|
||||
} catch (error) {
|
||||
alert('Error saving product')
|
||||
}
|
||||
}}>
|
||||
{/* ... */}
|
||||
</Formik>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
For more details, read the comprehensive [Query & Mutation Usage Issue](https://github.com/blitz-js/blitz/issues/89)
|
||||
|
||||
<br>
|
||||
|
||||
### Custom API Routes
|
||||
|
||||
Blitz.js custom API routes are exactly the same as Next.js custom API routes. If you need, read [the Next.js API route documentation](https://nextjs.org/docs/api-routes/introduction)
|
||||
|
||||
- Unlike Next.js, your `api/` folder must be a sibling of `pages/` instead of being nested inside.
|
||||
- All React components inside an `api/` folder are accessible at a URL corresponding to it's path inside `api/`. So `app/projects/api/webhook.tsx` will be at `localhost:3000/api/webhook`.
|
||||
|
||||
<br>
|
||||
|
||||
### Customize the Webpack Config
|
||||
|
||||
Blitz uses the `blitz.config.js` config file at the root of your project. This is exactly the same as `next.config.js`. Read [the Next.js docs on customizing webpack](https://nextjs.org/docs/api-reference/next.config.js/custom-webpack-config).
|
||||
|
||||
<br>
|
||||
|
||||
### Deploy to Production
|
||||
|
||||
1. You need a production Postgres database. It's easy to set this up on [Digital Ocean](https://www.digitalocean.com/products/managed-databases-postgresql/?refcode=466ad3d3063d).
|
||||
2. For deploying serverless, you also need a connection pool. This is also relatively easy to set up on Digital Ocean.
|
||||
1. [Read the Digital Ocean docs on setting up your connection pool](https://www.digitalocean.com/docs/databases/postgresql/how-to/manage-connection-pools/#creating-a-connection-pool?refcode=466ad3d3063d)
|
||||
2. Ensure you set your "Pool Mode" to be "Session" instead of "Transaction" (because of a bug in Prisma)
|
||||
3. You need your entire database connection string. If you need, [read the Prisma docs on this](https://www.prisma.io/docs/reference/database-connectors/postgresql#connection-details).
|
||||
1. If deploying to serverless with a connection pool, make sure you get the connection string to your connection pool, not directly to the DB.
|
||||
4. You need to change the defined datasource in `db/schema.prisma` from SQLite to Postgres
|
||||
5. Change your build script in package.json to be `blitz db migrate && blitz build` so that the production DB will be migrated on each deploy
|
||||
|
||||
#### Serverless
|
||||
|
||||
Assuming you already have a Vercel account and the `now` cli installed, you can do the following:
|
||||
|
||||
1. Add your DB url as a secret environment variable by running `now secrets add @database-url "DATABASE_CONNECTION_STRING"`
|
||||
2. Add a `now.json` at your project root with
|
||||
|
||||
```json
|
||||
{
|
||||
"env": {
|
||||
"DATABASE_URL": "@database-url"
|
||||
},
|
||||
"build": {
|
||||
"env": {
|
||||
"DATABASE_URL": "@database-url"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. Run `now`
|
||||
|
||||
Once working and deployed to production, your app should be very stable because it’s running Next.js which is already battle-tested.
|
||||
|
||||
#### Traditional, Long-Running Server
|
||||
|
||||
You can deploy a Blitz app like a regular Node or Express project.
|
||||
|
||||
`blitz start --production` will start your app in production mode. Make sure you provide the `DATABASE_URL` environment variable for your production database.
|
||||
|
||||
<br>
|
||||
|
||||
## Blitz CLI Commands
|
||||
|
||||
#### `blitz new NAME`
|
||||
|
||||
Generate a new blitz project at `<current_folder>./NAME`
|
||||
|
||||
#### `blitz start`
|
||||
|
||||
Start your app in development mode
|
||||
|
||||
#### `blitz start --production`
|
||||
|
||||
Start your app in production mode
|
||||
|
||||
#### `blitz db migrate`
|
||||
|
||||
Run any needed migrations via Prisma 2 and generate Prisma Client
|
||||
|
||||
#### `blitz db introspect`
|
||||
|
||||
Will introspect the database defined in `db/schema.prisma` and automatically generate a complete `schema.prisma` file for you. Lastly, it'll generate Prisma Client.
|
||||
|
||||
#### `blitz db studio`
|
||||
|
||||
Open the Prisma Studio UI at [http://localhost:5555](http://localhost:5555) so you can easily see and change data in your database.
|
||||
|
||||
#### `blitz generate -h`
|
||||
|
||||
Generate different types of files for a model. Your model input can be singular or plural, but the generated files will be the same in both cases.
|
||||
|
||||
#### `blitz console`
|
||||
|
||||
Start a Node.js REPL that's preloaded with your `db` object and all your queries and mutations. This is awesome for quickly trying your code without running the app!
|
||||
|
||||
<br>
|
||||
|
||||
## More Information
|
||||
|
||||
- Read the [Architecture RFC](https://github.com/blitz-js/blitz/pull/73) for more details on the architecture, our decision making, and how queries/mutations work under the hood
|
||||
- Read the [File Structure & Routing RFC](https://github.com/blitz-js/blitz/pull/74) for more details about the file structure and routing conventions.
|
||||
- View an example Blitz app at [`examples/store`](https://github.com/blitz-js/blitz/tree/canary/examples/store)
|
||||
|
||||
<br>
|
||||
|
||||
## What's Next for Blitz.js?
|
||||
|
||||
Here's the list of big things that are currently missing from Blitz but are a top priority for us:
|
||||
|
||||
- A real Blitzjs.com website and documentation
|
||||
- Translated documentation. If you're interested in helping, [comment in this issue](https://github.com/blitz-js/blitzjs.com/issues/20).
|
||||
- Authentication
|
||||
- Authorization (use auth rules both on server and client)
|
||||
- Model validation (use model validation both on server and client)
|
||||
- React-Native support
|
||||
- GUI for folks who prefer that over CLIs
|
||||
- ... and tons more 🙂
|
||||
|
||||
<br>
|
||||
|
||||
## FAQ
|
||||
|
||||
- **Does Blitz support vanilla Javascript?** Yes, but `blitz new` generates all Typescript files right now. We are actively working on this. It mostly works, but we have a few major bugs to fix before it's ready for prime time.
|
||||
- **Will you support other ESLint configs for the `blitz new` app?** Yes, there's [an issue for this](https://github.com/blitz-js/blitz/issues/161)
|
||||
|
||||
<br>
|
||||
|
||||
## You are invited to help — let’s build the future of web dev together! 🤝
|
||||
|
||||
Blitz is just getting started, and it's going to take an entire community to bring it to fruition!
|
||||
|
||||
How you can help:
|
||||
|
||||
1. Tell others about Blitz
|
||||
2. Report bugs by opening an issue here on GitHub
|
||||
3. Send us feedback in the [Blitz slack](https://slack.blitzjs.com).
|
||||
4. Contribute code. We have a lot of issues that are ready to work on! Start by reading [The Contributing Guide](https://github.com/blitz-js/blitz/blob/canary/CONTRIBUTING.md). Let us know if you need help.
|
||||
5. Any way you want! We totally appreciate any type of contribution, such as documentation, videos, blog posts, etc. If you have a crazy idea, feel free to run it past us in Slack! :)
|
||||
6. [Sponsorships & donations](https://github.com/blitz-js/blitz#sponsors-and-donations)
|
||||
|
||||
<br>
|
||||
|
||||
That's all for now. We hope to see you in the [Blitz slack community](https://slack.blitzjs.com)!
|
||||
3
__mocks__/fs.js
Normal file
3
__mocks__/fs.js
Normal file
@@ -0,0 +1,3 @@
|
||||
const {fs} = require('memfs')
|
||||
|
||||
module.exports = fs
|
||||
BIN
assets/Fauna_Logo_Blue.png
Normal file
BIN
assets/Fauna_Logo_Blue.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
4
examples/store/.babelrc.js
Normal file
4
examples/store/.babelrc.js
Normal file
@@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
presets: ['next/babel'],
|
||||
plugins: [],
|
||||
}
|
||||
1
examples/store/.env.example
Normal file
1
examples/store/.env.example
Normal file
@@ -0,0 +1 @@
|
||||
DATABASE_URL=postgresql://USERNAME@localhost:5432/blitz-example-store
|
||||
13
examples/store/.eslintrc.js
Normal file
13
examples/store/.eslintrc.js
Normal file
@@ -0,0 +1,13 @@
|
||||
module.exports = {
|
||||
extends: ['react-app', 'plugin:jsx-a11y/recommended'],
|
||||
plugins: ['jsx-a11y', 'cypress'],
|
||||
rules: {
|
||||
'import/no-anonymous-default-export': 'error',
|
||||
'import/no-webpack-loader-syntax': 'off',
|
||||
'react/react-in-jsx-scope': 'off', // React is always in scope with Blitz
|
||||
'jsx-a11y/anchor-is-valid': 'off', //Doesn't play well with Blitz/Next <Link> usage
|
||||
},
|
||||
env: {
|
||||
'cypress/globals': true,
|
||||
},
|
||||
}
|
||||
39
examples/store/.gitignore
vendored
Normal file
39
examples/store/.gitignore
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
cypress/videos
|
||||
cypress/screenshots
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
/.blitz/
|
||||
*.sqlite
|
||||
.generated-prisma-client
|
||||
.blitz-console-history
|
||||
|
||||
# production
|
||||
/build
|
||||
.now
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# local env files
|
||||
.envrc
|
||||
.env
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
5
examples/store/.prettierignore
Normal file
5
examples/store/.prettierignore
Normal file
@@ -0,0 +1,5 @@
|
||||
.gitkeep
|
||||
.env
|
||||
*.ico
|
||||
*.lock
|
||||
|
||||
15
examples/store/README.md
Normal file
15
examples/store/README.md
Normal file
@@ -0,0 +1,15 @@
|
||||
## Getting Started
|
||||
|
||||
1. DB migrate
|
||||
|
||||
```
|
||||
yarn blitz db migrate
|
||||
```
|
||||
|
||||
2. Start the dev server
|
||||
|
||||
```
|
||||
yarn blitz start
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
18
examples/store/app/admin/pages/admin/index.tsx
Normal file
18
examples/store/app/admin/pages/admin/index.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import {Link} from 'blitz'
|
||||
|
||||
function StoreAdminPage() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Store Admin</h1>
|
||||
<div>
|
||||
<p>
|
||||
<Link href="/admin/products">
|
||||
<a>Manage Products</a>
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default StoreAdminPage
|
||||
32
examples/store/app/admin/pages/admin/products/[id].tsx
Normal file
32
examples/store/app/admin/pages/admin/products/[id].tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import {Suspense} from 'react'
|
||||
import {Link, useRouter, useQuery} from 'blitz'
|
||||
import getProduct from 'app/products/queries/getProduct'
|
||||
import ProductForm from 'app/products/components/ProductForm'
|
||||
|
||||
function Product() {
|
||||
const router = useRouter()
|
||||
const id = parseInt(router?.query.id as string)
|
||||
const [product] = useQuery(getProduct, {where: {id}})
|
||||
|
||||
return <ProductForm product={product} onSuccess={() => router.push('/admin/products')} />
|
||||
}
|
||||
|
||||
function EditProductPage() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Edit Product</h1>
|
||||
<p>
|
||||
<Link href="/admin/products">
|
||||
<a>Manage Products</a>
|
||||
</Link>
|
||||
</p>
|
||||
<div>
|
||||
<Suspense fallback={<div>Loading...</div>}>
|
||||
<Product />
|
||||
</Suspense>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default EditProductPage
|
||||
42
examples/store/app/admin/pages/admin/products/index.tsx
Normal file
42
examples/store/app/admin/pages/admin/products/index.tsx
Normal file
@@ -0,0 +1,42 @@
|
||||
import {Suspense} from 'react'
|
||||
import {useQuery, Link} from 'blitz'
|
||||
import getProducts from 'app/products/queries/getProducts'
|
||||
|
||||
function ProductsList() {
|
||||
const [products] = useQuery(getProducts, {orderBy: {id: 'desc'}})
|
||||
|
||||
return (
|
||||
<ul>
|
||||
{products.map((product) => (
|
||||
<li key={product.id}>
|
||||
<Link href="/admin/products/[id]" as={`/admin/products/${product.id}`}>
|
||||
<a>{product.name}</a>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
|
||||
function AdminProducts() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Products</h1>
|
||||
|
||||
<p>
|
||||
<Link href="/admin/products/new">
|
||||
<a>Create Product</a>
|
||||
</Link>
|
||||
<Link href="/admin">
|
||||
<a style={{marginLeft: 16}}>Admin</a>
|
||||
</Link>
|
||||
</p>
|
||||
|
||||
<Suspense fallback={<div>Loading...</div>}>
|
||||
<ProductsList />
|
||||
</Suspense>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default AdminProducts
|
||||
21
examples/store/app/admin/pages/admin/products/new.tsx
Normal file
21
examples/store/app/admin/pages/admin/products/new.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import {Link, useRouter} from 'blitz'
|
||||
import ProductForm from 'app/products/components/ProductForm'
|
||||
|
||||
function AdminNewProductPage() {
|
||||
const router = useRouter()
|
||||
return (
|
||||
<div>
|
||||
<h1>Create a New Product</h1>
|
||||
<p>
|
||||
<Link href="/admin/products">
|
||||
<a>Manage Products</a>
|
||||
</Link>
|
||||
</p>
|
||||
<div>
|
||||
<ProductForm onSuccess={() => router.push('/admin/products')} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default AdminNewProductPage
|
||||
0
examples/store/app/components/.keep
Normal file
0
examples/store/app/components/.keep
Normal file
19
examples/store/app/components/ErrorBoundary.tsx
Normal file
19
examples/store/app/components/ErrorBoundary.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import React from 'react'
|
||||
|
||||
export default class ErrorBoundary extends React.Component<{fallback: (error: any) => React.ReactNode}> {
|
||||
state = {hasError: false, error: null}
|
||||
|
||||
static getDerivedStateFromError(error: any) {
|
||||
return {
|
||||
hasError: true,
|
||||
error,
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.hasError) {
|
||||
return this.props.fallback(this.state.error)
|
||||
}
|
||||
return this.props.children
|
||||
}
|
||||
}
|
||||
0
examples/store/app/layouts/.keep
Normal file
0
examples/store/app/layouts/.keep
Normal file
15
examples/store/app/pages/_app.tsx
Normal file
15
examples/store/app/pages/_app.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import ErrorBoundary from 'app/components/ErrorBoundary'
|
||||
|
||||
export default function MyApp({Component, pageProps}) {
|
||||
return (
|
||||
<ErrorBoundary
|
||||
fallback={(error) => (
|
||||
<div>
|
||||
<h1>Unhandled Error</h1>
|
||||
<pre>{JSON.stringify(error, null, 2)}</pre>
|
||||
</div>
|
||||
)}>
|
||||
<Component {...pageProps} />
|
||||
</ErrorBoundary>
|
||||
)
|
||||
}
|
||||
22
examples/store/app/pages/_document.tsx
Normal file
22
examples/store/app/pages/_document.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import {Document, Html, DocumentHead, Main, NextScript, DocumentContext} from '@blitzjs/core'
|
||||
|
||||
class MyDocument extends Document {
|
||||
static async getInitialProps(ctx: DocumentContext) {
|
||||
const initialProps = await Document.getInitialProps(ctx)
|
||||
return {...initialProps}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Html lang="en">
|
||||
<DocumentHead />
|
||||
<body>
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
</Html>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default MyDocument
|
||||
189
examples/store/app/pages/index.tsx
Normal file
189
examples/store/app/pages/index.tsx
Normal file
@@ -0,0 +1,189 @@
|
||||
import {Head, Link} from 'blitz'
|
||||
|
||||
const Home = () => (
|
||||
<div className="container">
|
||||
<Head>
|
||||
<title>Blitz Example Store</title>
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
</Head>
|
||||
|
||||
<main>
|
||||
<h1 className="title" style={{marginBottom: 24}}>
|
||||
Blitz Store Example
|
||||
</h1>
|
||||
<ul>
|
||||
<li>
|
||||
<Link href="/products">
|
||||
<a>Static Product Listings</a>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/products/ssr">
|
||||
<a>SSR Product Listings</a>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/products/paginated">
|
||||
<a>Paginated Product Listings (client-rendered)</a>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/admin/products">
|
||||
<a>Admin Section (client-rendered)</a>
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<a
|
||||
href="https://blitzjs.com?utm_source=blitz-new&utm_medium=app-template&utm_campaign=blitz-new"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer">
|
||||
Powered by Blitz.js
|
||||
</a>
|
||||
</footer>
|
||||
|
||||
<style jsx>{`
|
||||
.container {
|
||||
min-height: 100vh;
|
||||
padding: 0 0.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 5rem 0;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
footer {
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
border-top: 1px solid #eaeaea;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
footer img {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
footer a {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #0070f3;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
li + li {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.title a:hover,
|
||||
.title a:focus,
|
||||
.title a:active {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 0;
|
||||
line-height: 1.15;
|
||||
font-size: 3rem;
|
||||
}
|
||||
|
||||
.title,
|
||||
.description {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.description {
|
||||
line-height: 1.5;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
code {
|
||||
background: #fafafa;
|
||||
border-radius: 5px;
|
||||
padding: 0.75rem;
|
||||
font-size: 1.1rem;
|
||||
font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
|
||||
Bitstream Vera Sans Mono, Courier New, monospace;
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
|
||||
max-width: 800px;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
.card {
|
||||
margin: 1rem;
|
||||
flex-basis: 45%;
|
||||
padding: 1.5rem;
|
||||
text-align: left;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
border: 1px solid #eaeaea;
|
||||
border-radius: 10px;
|
||||
transition: color 0.15s ease, border-color 0.15s ease;
|
||||
}
|
||||
|
||||
.card:hover,
|
||||
.card:focus,
|
||||
.card:active {
|
||||
color: #0070f3;
|
||||
border-color: #0070f3;
|
||||
}
|
||||
|
||||
.card h3 {
|
||||
margin: 0 0 1rem 0;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.card p {
|
||||
margin: 0;
|
||||
font-size: 1.25rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.grid {
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
`}</style>
|
||||
|
||||
<style jsx global>{`
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans,
|
||||
Droid Sans, Helvetica Neue, sans-serif;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
)
|
||||
|
||||
export default Home
|
||||
76
examples/store/app/products/components/ProductForm.tsx
Normal file
76
examples/store/app/products/components/ProductForm.tsx
Normal file
@@ -0,0 +1,76 @@
|
||||
import {Form, Field} from 'react-final-form'
|
||||
import {Product, ProductCreateInput, ProductUpdateInput} from 'db'
|
||||
import createProduct from '../mutations/createProduct'
|
||||
import updateProduct from '../mutations/updateProduct'
|
||||
|
||||
type ProductInput = ProductCreateInput | ProductUpdateInput
|
||||
|
||||
function isNew(product: ProductInput): product is ProductCreateInput {
|
||||
return (product as ProductUpdateInput).id === undefined
|
||||
}
|
||||
|
||||
type ProductFormProps = {
|
||||
product?: ProductUpdateInput
|
||||
style?: React.CSSProperties
|
||||
onSuccess: (product: Product) => any
|
||||
}
|
||||
|
||||
function ProductForm({product, style, onSuccess, ...props}: ProductFormProps) {
|
||||
return (
|
||||
<Form
|
||||
initialValues={product || {name: null, handle: null, description: null, price: null}}
|
||||
onSubmit={async (data: ProductInput) => {
|
||||
if (isNew(data)) {
|
||||
try {
|
||||
const product = await createProduct({data})
|
||||
onSuccess(product)
|
||||
} catch (error) {
|
||||
alert('Error creating product ' + JSON.stringify(error, null, 2))
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
const product = await updateProduct({where: {id: data.id}, data})
|
||||
onSuccess(product)
|
||||
} catch (error) {
|
||||
alert('Error updating product ' + JSON.stringify(error, null, 2))
|
||||
}
|
||||
}
|
||||
}}
|
||||
render={({handleSubmit}) => (
|
||||
<form onSubmit={handleSubmit} style={{maxWidth: 400, ...style}} {...props}>
|
||||
<div style={{marginBottom: 16}}>
|
||||
<label style={{display: 'flex', flexDirection: 'column'}}>
|
||||
Product Name
|
||||
<Field name="name" component="input" />
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div style={{marginBottom: 16}}>
|
||||
<label style={{display: 'flex', flexDirection: 'column'}}>
|
||||
Handle
|
||||
<Field name="handle" component="input" />
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div style={{marginBottom: 16}}>
|
||||
<label style={{display: 'flex', flexDirection: 'column'}}>
|
||||
Description
|
||||
<Field name="description" component="textarea" />
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div style={{marginBottom: 16}}>
|
||||
<label style={{display: 'flex', flexDirection: 'column'}}>
|
||||
Price
|
||||
<Field name="price" component="input" parse={(value) => (value ? parseInt(value) : null)} />
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<button>{product ? 'Update' : 'Create'} Product</button>
|
||||
</form>
|
||||
)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProductForm
|
||||
7
examples/store/app/products/mutations/createProduct.ts
Normal file
7
examples/store/app/products/mutations/createProduct.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import db, {ProductCreateArgs} from 'db'
|
||||
|
||||
export default async function createProduct(args: ProductCreateArgs) {
|
||||
const product = await db.product.create(args)
|
||||
|
||||
return product
|
||||
}
|
||||
7
examples/store/app/products/mutations/deleteProduct.ts
Normal file
7
examples/store/app/products/mutations/deleteProduct.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import db, {ProductDeleteArgs} from 'db'
|
||||
|
||||
export default async function deleteProduct(args: ProductDeleteArgs) {
|
||||
const product = await db.product.delete(args)
|
||||
|
||||
return product
|
||||
}
|
||||
10
examples/store/app/products/mutations/updateProduct.ts
Normal file
10
examples/store/app/products/mutations/updateProduct.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import db, {ProductUpdateArgs} from 'db'
|
||||
|
||||
export default async function updateProduct(args: ProductUpdateArgs) {
|
||||
// Don't allow updating ID
|
||||
delete args.data.id
|
||||
|
||||
const product = await db.product.update(args)
|
||||
|
||||
return product
|
||||
}
|
||||
44
examples/store/app/products/pages/products/[handle].tsx
Normal file
44
examples/store/app/products/pages/products/[handle].tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
import {Link, BlitzPage, GetStaticProps, GetStaticPaths} from 'blitz'
|
||||
import getProduct from 'app/products/queries/getProduct'
|
||||
import getProducts from 'app/products/queries/getProducts'
|
||||
import {Product} from 'db'
|
||||
|
||||
type StaticProps = {
|
||||
product: Product
|
||||
}
|
||||
|
||||
export const getStaticProps: GetStaticProps<StaticProps> = async (ctx) => {
|
||||
const product = await getProduct({where: {handle: ctx.params.handle as string}})
|
||||
|
||||
return {
|
||||
props: {product},
|
||||
// Unstable beacuse revalidate is still under RFC: https://nextjs.link/issg
|
||||
unstable_revalidate: 1,
|
||||
}
|
||||
}
|
||||
export const getStaticPaths: GetStaticPaths = async () => {
|
||||
const paths = (await getProducts()).map(({handle}) => ({params: {handle}}))
|
||||
return {
|
||||
paths,
|
||||
fallback: true,
|
||||
}
|
||||
}
|
||||
|
||||
const Page: BlitzPage<StaticProps> = function ({product}) {
|
||||
if (!product) {
|
||||
return <div>Building Page...</div>
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>{product.name}</h1>
|
||||
<p>{product.description}</p>
|
||||
<p>Price: ${product.price}</p>
|
||||
|
||||
<Link href="/products">
|
||||
<a>All Products</a>
|
||||
</Link>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default Page
|
||||
35
examples/store/app/products/pages/products/index.tsx
Normal file
35
examples/store/app/products/pages/products/index.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import {Link, BlitzPage, GetStaticProps} from 'blitz'
|
||||
import getProducts from '../../queries/getProducts'
|
||||
import {Product} from 'db'
|
||||
|
||||
type StaticProps = {
|
||||
products: Product[]
|
||||
}
|
||||
|
||||
export const getStaticProps: GetStaticProps<StaticProps> = async () => {
|
||||
const products = await getProducts()
|
||||
|
||||
return {
|
||||
props: {products},
|
||||
// Unstable beacuse revalidate is still under RFC: https://nextjs.link/issg
|
||||
unstable_revalidate: 1,
|
||||
}
|
||||
}
|
||||
|
||||
const Page: BlitzPage<StaticProps> = function ({products}) {
|
||||
return (
|
||||
<div>
|
||||
<h1>Products</h1>
|
||||
<div id="products">
|
||||
{products.map((product) => (
|
||||
<p key={product.id}>
|
||||
<Link href="/products/[handle]" as={`/products/${product.handle}`}>
|
||||
<a>{product.name}</a>
|
||||
</Link>
|
||||
</p>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default Page
|
||||
47
examples/store/app/products/pages/products/paginated.tsx
Normal file
47
examples/store/app/products/pages/products/paginated.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
import {Suspense, useState} from 'react'
|
||||
import {Link, BlitzPage, useQuery} from 'blitz'
|
||||
import getProducts from 'app/products/queries/getProducts'
|
||||
|
||||
const ITEMS_PER_PAGE = 3
|
||||
|
||||
const Products = () => {
|
||||
const [page, setPage] = useState(0)
|
||||
const [products] = useQuery(
|
||||
getProducts,
|
||||
{
|
||||
skip: ITEMS_PER_PAGE * page,
|
||||
first: ITEMS_PER_PAGE,
|
||||
},
|
||||
{paginated: true},
|
||||
)
|
||||
|
||||
return (
|
||||
<div>
|
||||
{products.map((product) => (
|
||||
<p key={product.id}>
|
||||
<Link href="/products/[handle]" as={`/products/${product.handle}`}>
|
||||
<a>{product.name}</a>
|
||||
</Link>
|
||||
</p>
|
||||
))}
|
||||
<button disabled={page === 0} onClick={() => setPage(page - 1)}>
|
||||
Previous
|
||||
</button>
|
||||
<button disabled={products.length !== ITEMS_PER_PAGE} onClick={() => setPage(page + 1)}>
|
||||
Next
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const Page: BlitzPage = function () {
|
||||
return (
|
||||
<div>
|
||||
<h1>Products - Paginated</h1>
|
||||
<Suspense fallback={<div>Loading...</div>}>
|
||||
<Products />
|
||||
</Suspense>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default Page
|
||||
33
examples/store/app/products/pages/products/ssr.tsx
Normal file
33
examples/store/app/products/pages/products/ssr.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import {Link, BlitzPage, GetServerSideProps, ssrQuery} from '@blitzjs/core'
|
||||
import getProducts from 'app/products/queries/getProducts'
|
||||
import {Product} from 'db'
|
||||
|
||||
type PageProps = {
|
||||
products: Product[]
|
||||
}
|
||||
|
||||
export const getServerSideProps: GetServerSideProps<PageProps> = async ({req, res}) => {
|
||||
const products = await ssrQuery(getProducts, undefined, {req, res})
|
||||
|
||||
return {
|
||||
props: {products},
|
||||
}
|
||||
}
|
||||
|
||||
const Page: BlitzPage<PageProps> = function ({products}) {
|
||||
return (
|
||||
<div>
|
||||
<h1>Products</h1>
|
||||
<div id="products">
|
||||
{products.map((product) => (
|
||||
<p key={product.id}>
|
||||
<Link href="/products/[handle]" as={`/products/${product.handle}`}>
|
||||
<a>{product.name}</a>
|
||||
</Link>
|
||||
</p>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default Page
|
||||
7
examples/store/app/products/queries/getProduct.ts
Normal file
7
examples/store/app/products/queries/getProduct.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import db, {FindOneProductArgs} from 'db'
|
||||
|
||||
export default async function getProduct(args: FindOneProductArgs) {
|
||||
const product = await db.product.findOne(args)
|
||||
|
||||
return product
|
||||
}
|
||||
7
examples/store/app/products/queries/getProducts.ts
Normal file
7
examples/store/app/products/queries/getProducts.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import db, {FindManyProductArgs} from 'db'
|
||||
|
||||
export default async function getProducts(args?: FindManyProductArgs) {
|
||||
const products = await db.product.findMany(args)
|
||||
|
||||
return products
|
||||
}
|
||||
13
examples/store/blitz.config.js
Normal file
13
examples/store/blitz.config.js
Normal file
@@ -0,0 +1,13 @@
|
||||
module.exports = {
|
||||
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
|
||||
},
|
||||
}
|
||||
3
examples/store/cypress.json
Normal file
3
examples/store/cypress.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"baseUrl": "http://localhost:3099"
|
||||
}
|
||||
5
examples/store/cypress/fixtures/example.json
Normal file
5
examples/store/cypress/fixtures/example.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "Using fixtures to represent data",
|
||||
"email": "hello@cypress.io",
|
||||
"body": "Fixtures are a great way to mock data for responses to routes"
|
||||
}
|
||||
17
examples/store/cypress/integration/admin/index.test.ts
Normal file
17
examples/store/cypress/integration/admin/index.test.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
describe('admin#index page', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/admin')
|
||||
})
|
||||
|
||||
it('Has H1', () => {
|
||||
cy.visit('/admin')
|
||||
cy.contains('h1', 'Store Admin')
|
||||
})
|
||||
|
||||
it('goes to admin/products page', () => {
|
||||
cy.contains('a', 'Manage Products').click()
|
||||
cy.location('pathname').should('equal', '/admin/products')
|
||||
})
|
||||
})
|
||||
|
||||
export {}
|
||||
@@ -0,0 +1,50 @@
|
||||
import {insert, data, fields} from './helper'
|
||||
|
||||
describe('admin/products/[handle] page', () => {
|
||||
beforeEach(() => {
|
||||
insert()
|
||||
|
||||
cy.visit('/admin/products')
|
||||
cy.get('ul > li:last-child a').click()
|
||||
})
|
||||
|
||||
it('Has h1 and link back', () => {
|
||||
cy.contains('h1', 'Edit Product')
|
||||
cy.get('p > a').first().contains('Manage Products').click()
|
||||
cy.location('pathname').should('equal', '/admin/products')
|
||||
})
|
||||
|
||||
it('Has all fields, change ProductName', () => {
|
||||
cy.get('form > div > label').as('inputs')
|
||||
cy.get('@inputs').should('have.length', 4)
|
||||
|
||||
const random = Math.round(Math.random() * 100000).toString()
|
||||
|
||||
const count = {}
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const {label, type} = fields[i]
|
||||
const [element, inputType] = type.split('|')
|
||||
let item = data[i]
|
||||
|
||||
if (count[element] === undefined) count[element] = 0
|
||||
if (inputType) {
|
||||
cy.get('@inputs').get(element).eq(count[element]).should('have.attr', 'type', inputType)
|
||||
}
|
||||
|
||||
item += random
|
||||
|
||||
cy.get('@inputs').eq(i).contains(label)
|
||||
cy.get('@inputs').get(element).eq(count[element]).clear().type(item)
|
||||
cy.get('@inputs').get(element).eq(count[element]).should('have.value', item)
|
||||
|
||||
count[element]++
|
||||
}
|
||||
|
||||
cy.get('button').click()
|
||||
|
||||
cy.location('pathname').should('equal', '/admin/products')
|
||||
cy.get('ul > li:last-child').contains(data[0] + random)
|
||||
})
|
||||
})
|
||||
|
||||
export {}
|
||||
45
examples/store/cypress/integration/admin/products/helper.ts
Normal file
45
examples/store/cypress/integration/admin/products/helper.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
export const fields = [
|
||||
{label: 'Product Name', type: 'input|', uniq: true},
|
||||
{label: 'Handle', type: 'input|', uniq: true},
|
||||
{label: 'Description', type: 'textarea|'},
|
||||
{label: 'Price', type: 'input|'}, // TODO: Add input type here input|number
|
||||
]
|
||||
export const data = ['Apples', 'apples', 'Fresh apples', '32']
|
||||
|
||||
export const insert = (): string => {
|
||||
cy.visit('/admin/products/new')
|
||||
cy.get('form > div > label').as('inputs')
|
||||
cy.get('@inputs').should('have.length', 4)
|
||||
|
||||
const random = Math.round(Math.random() * 100000).toString()
|
||||
const name = data[0] + random
|
||||
|
||||
const count = {}
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const {label, type, uniq} = fields[i]
|
||||
const [element, inputType] = type.split('|')
|
||||
let item = data[i]
|
||||
|
||||
if (count[element] === undefined) count[element] = 0
|
||||
if (inputType) {
|
||||
cy.get('@inputs').get(element).eq(count[element]).should('have.attr', 'type', inputType)
|
||||
}
|
||||
|
||||
if (uniq) {
|
||||
item += random
|
||||
}
|
||||
|
||||
cy.get('@inputs').eq(i).contains(label)
|
||||
cy.get('@inputs').eq(i).type(item)
|
||||
cy.get('@inputs').get(element).eq(count[element]).should('have.value', item)
|
||||
|
||||
count[element]++
|
||||
}
|
||||
|
||||
cy.get('button').click()
|
||||
|
||||
cy.location('pathname').should('equal', '/admin/products')
|
||||
cy.get('ul > li:first-child').contains(name)
|
||||
|
||||
return name
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import {insert} from './helper'
|
||||
|
||||
describe('admin/products page', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/admin/products')
|
||||
})
|
||||
|
||||
it('Has H1', () => {
|
||||
cy.contains('h1', 'Products')
|
||||
})
|
||||
|
||||
it('goes to new product page', () => {
|
||||
cy.get('p > a').first().contains('Create Product').click()
|
||||
cy.location('pathname').should('equal', '/admin/products/new')
|
||||
})
|
||||
|
||||
it('goes to bas product page', () => {
|
||||
cy.get('p > a').last().contains('Admin').click()
|
||||
cy.location('pathname').should('equal', '/admin')
|
||||
})
|
||||
|
||||
// This is kind of redundant because this logic is handled in insert()
|
||||
it('shows latest created product', () => {
|
||||
const name = insert()
|
||||
cy.get('ul > li').contains(name)
|
||||
})
|
||||
})
|
||||
|
||||
export {}
|
||||
@@ -0,0 +1,19 @@
|
||||
import {insert} from './helper'
|
||||
|
||||
describe('admin/products/new page', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/admin/products/new')
|
||||
})
|
||||
|
||||
it('Has h1 and link back', () => {
|
||||
cy.contains('h1', 'Create a New Product')
|
||||
cy.get('p > a').first().contains('Manage Products').click()
|
||||
cy.location('pathname').should('equal', '/admin/products')
|
||||
})
|
||||
|
||||
it('Fills fields and creates product', () => {
|
||||
insert()
|
||||
})
|
||||
})
|
||||
|
||||
export {}
|
||||
1
examples/store/cypress/integration/index.d.ts
vendored
Normal file
1
examples/store/cypress/integration/index.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/// <reference types="Cypress" />
|
||||
22
examples/store/cypress/integration/index.test.ts
Normal file
22
examples/store/cypress/integration/index.test.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
describe('index page', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/')
|
||||
})
|
||||
|
||||
it('Has title and H1', () => {
|
||||
cy.contains('h1', 'Blitz Store Example')
|
||||
cy.title().should('eq', 'Blitz Example Store')
|
||||
})
|
||||
|
||||
it('goes to products page', () => {
|
||||
cy.contains('a', 'Static Product Listings').click()
|
||||
cy.location('pathname').should('equal', '/products')
|
||||
})
|
||||
|
||||
it('goes to admin page', () => {
|
||||
cy.contains('a', 'Admin Section (client-rendered)').click()
|
||||
cy.location('pathname').should('equal', '/admin/products')
|
||||
})
|
||||
})
|
||||
|
||||
export {}
|
||||
57
examples/store/cypress/integration/products.test.ts
Normal file
57
examples/store/cypress/integration/products.test.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
describe('products#index page', () => {
|
||||
it('Has H1', () => {
|
||||
cy.visit('/products')
|
||||
cy.contains('h1', 'Products')
|
||||
})
|
||||
})
|
||||
|
||||
describe('products#show page', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/products')
|
||||
})
|
||||
|
||||
it('goes to product page', () => {
|
||||
cy.get('#products > p > a').first().click()
|
||||
cy.location('pathname').should('match', /\/products\/\S+$/)
|
||||
})
|
||||
|
||||
it('has price and title', () => {
|
||||
cy.get('#products > p > a').first().click()
|
||||
cy.location('pathname').should('match', /\/products\/\S+$/)
|
||||
|
||||
cy.get('p').should('have.length', 2)
|
||||
cy.get('p')
|
||||
.last()
|
||||
.contains(/Price: \$[0-9]*/)
|
||||
|
||||
cy.get('h1').should('have.length', 1)
|
||||
})
|
||||
|
||||
it('goes to back to products page', () => {
|
||||
cy.get('#products > p > a').first().click()
|
||||
cy.location('pathname').should('match', /\/products\/\S+$/)
|
||||
|
||||
cy.get('a').first().click()
|
||||
cy.location('pathname').should('equal', '/products')
|
||||
})
|
||||
})
|
||||
|
||||
describe('products#ssr page', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/products/ssr')
|
||||
})
|
||||
|
||||
it('has title', () => {
|
||||
cy.get('h1').contains('Products')
|
||||
})
|
||||
|
||||
it('goes to back to products page', () => {
|
||||
cy.get('#products > p > a').first().click()
|
||||
cy.location('pathname').should('not.match', /\/products\/ssr$/)
|
||||
|
||||
cy.get('a').first().click()
|
||||
cy.location('pathname').should('equal', '/products')
|
||||
})
|
||||
})
|
||||
|
||||
export {}
|
||||
21
examples/store/cypress/plugins/index.js
Normal file
21
examples/store/cypress/plugins/index.js
Normal file
@@ -0,0 +1,21 @@
|
||||
/// <reference types="cypress" />
|
||||
// ***********************************************************
|
||||
// This example plugins/index.js can be used to load plugins
|
||||
//
|
||||
// You can change the location of this file or turn off loading
|
||||
// the plugins file with the 'pluginsFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/plugins-guide
|
||||
// ***********************************************************
|
||||
|
||||
// This function is called when a project is opened or re-opened (e.g. due to
|
||||
// the project's config changing)
|
||||
|
||||
/**
|
||||
* @type {Cypress.PluginConfig}
|
||||
*/
|
||||
module.exports = (on, config) => {
|
||||
// `on` is used to hook into various events Cypress emits
|
||||
// `config` is the resolved Cypress config
|
||||
}
|
||||
25
examples/store/cypress/support/commands.js
Normal file
25
examples/store/cypress/support/commands.js
Normal file
@@ -0,0 +1,25 @@
|
||||
// ***********************************************
|
||||
// This example commands.js shows you how to
|
||||
// create various custom commands and overwrite
|
||||
// existing commands.
|
||||
//
|
||||
// For more comprehensive examples of custom
|
||||
// commands please read more here:
|
||||
// https://on.cypress.io/custom-commands
|
||||
// ***********************************************
|
||||
//
|
||||
//
|
||||
// -- This is a parent command --
|
||||
// Cypress.Commands.add("login", (email, password) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a child command --
|
||||
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a dual command --
|
||||
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This will overwrite an existing command --
|
||||
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
|
||||
20
examples/store/cypress/support/index.js
Normal file
20
examples/store/cypress/support/index.js
Normal file
@@ -0,0 +1,20 @@
|
||||
// ***********************************************************
|
||||
// This example support/index.js is processed and
|
||||
// loaded automatically before your test files.
|
||||
//
|
||||
// This is a great place to put global configuration and
|
||||
// behavior that modifies Cypress.
|
||||
//
|
||||
// You can change the location of this file or turn off
|
||||
// automatically serving support files with the
|
||||
// 'supportFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/configuration
|
||||
// ***********************************************************
|
||||
|
||||
// Import commands.js using ES2015 syntax:
|
||||
import './commands'
|
||||
|
||||
// Alternatively you can use CommonJS syntax:
|
||||
// require('./commands')
|
||||
6
examples/store/db/index.ts
Normal file
6
examples/store/db/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import {PrismaClient} from '@prisma/client'
|
||||
export * from '@prisma/client'
|
||||
|
||||
const prisma = new PrismaClient()
|
||||
|
||||
export default prisma
|
||||
0
examples/store/db/migrations/.keep
Normal file
0
examples/store/db/migrations/.keep
Normal file
83
examples/store/db/migrations/20200429140440-init/README.md
Normal file
83
examples/store/db/migrations/20200429140440-init/README.md
Normal file
@@ -0,0 +1,83 @@
|
||||
# Migration `20200429140440-init`
|
||||
|
||||
This migration has been generated by Brandon Bayer at 4/29/2020, 2:04:40 PM.
|
||||
You can check out the [state of the schema](./schema.prisma) after the migration.
|
||||
|
||||
## Database Steps
|
||||
|
||||
```sql
|
||||
PRAGMA foreign_keys=OFF;
|
||||
|
||||
CREATE TABLE "quaint"."User" (
|
||||
"email" TEXT NOT NULL ,
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"name" TEXT ,
|
||||
"role" TEXT ,
|
||||
"storeId" INTEGER
|
||||
)
|
||||
|
||||
CREATE TABLE "quaint"."Product" (
|
||||
"description" TEXT ,
|
||||
"handle" TEXT NOT NULL ,
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"name" TEXT ,
|
||||
"price" INTEGER
|
||||
)
|
||||
|
||||
CREATE UNIQUE INDEX "quaint"."User.email" ON "User"("email")
|
||||
|
||||
CREATE UNIQUE INDEX "quaint"."Product.handle" ON "Product"("handle")
|
||||
|
||||
PRAGMA "quaint".foreign_key_check;
|
||||
|
||||
PRAGMA foreign_keys=ON;
|
||||
```
|
||||
|
||||
## Changes
|
||||
|
||||
```diff
|
||||
diff --git schema.prisma schema.prisma
|
||||
migration ..20200429140440-init
|
||||
--- datamodel.dml
|
||||
+++ datamodel.dml
|
||||
@@ -1,0 +1,37 @@
|
||||
+// This is your Prisma schema file,
|
||||
+// learn more about it in the docs: https://pris.ly/d/prisma-schema
|
||||
+
|
||||
+datasource sqlite {
|
||||
+ provider = "sqlite"
|
||||
+ url = "file:./db.sqlite"
|
||||
+}
|
||||
+
|
||||
+// SQLite is easy to start with, but if you use Postgres in production
|
||||
+// you should also use it in development with the following:
|
||||
+//datasource postgresql {
|
||||
+// provider = "postgresql"
|
||||
+// url = env("DATABASE_URL")
|
||||
+//}
|
||||
+
|
||||
+generator client {
|
||||
+ provider = "prisma-client-js"
|
||||
+}
|
||||
+
|
||||
+
|
||||
+// --------------------------------------
|
||||
+
|
||||
+model User {
|
||||
+ id Int @default(autoincrement()) @id
|
||||
+ email String @unique
|
||||
+ name String?
|
||||
+ role String?
|
||||
+ storeId Int?
|
||||
+}
|
||||
+
|
||||
+model Product {
|
||||
+ id Int @default(autoincrement()) @id
|
||||
+ handle String @unique
|
||||
+ name String?
|
||||
+ description String?
|
||||
+ price Int?
|
||||
+}
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
// This is your Prisma schema file,
|
||||
// learn more about it in the docs: https://pris.ly/d/prisma-schema
|
||||
|
||||
datasource sqlite {
|
||||
provider = "sqlite"
|
||||
url = "***"
|
||||
}
|
||||
|
||||
// SQLite is easy to start with, but if you use Postgres in production
|
||||
// you should also use it in development with the following:
|
||||
//datasource postgresql {
|
||||
// provider = "postgresql"
|
||||
// url = "***"
|
||||
//}
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------
|
||||
|
||||
model User {
|
||||
id Int @default(autoincrement()) @id
|
||||
email String @unique
|
||||
name String?
|
||||
role String?
|
||||
storeId Int?
|
||||
}
|
||||
|
||||
model Product {
|
||||
id Int @default(autoincrement()) @id
|
||||
handle String @unique
|
||||
name String?
|
||||
description String?
|
||||
price Int?
|
||||
}
|
||||
199
examples/store/db/migrations/20200429140440-init/steps.json
Normal file
199
examples/store/db/migrations/20200429140440-init/steps.json
Normal file
@@ -0,0 +1,199 @@
|
||||
{
|
||||
"version": "0.3.14-fixed",
|
||||
"steps": [
|
||||
{
|
||||
"tag": "CreateSource",
|
||||
"source": "sqlite"
|
||||
},
|
||||
{
|
||||
"tag": "CreateArgument",
|
||||
"location": {
|
||||
"tag": "Source",
|
||||
"source": "sqlite"
|
||||
},
|
||||
"argument": "provider",
|
||||
"value": "\"sqlite\""
|
||||
},
|
||||
{
|
||||
"tag": "CreateArgument",
|
||||
"location": {
|
||||
"tag": "Source",
|
||||
"source": "sqlite"
|
||||
},
|
||||
"argument": "url",
|
||||
"value": "\"file:./db.sqlite\""
|
||||
},
|
||||
{
|
||||
"tag": "CreateModel",
|
||||
"model": "User"
|
||||
},
|
||||
{
|
||||
"tag": "CreateField",
|
||||
"model": "User",
|
||||
"field": "id",
|
||||
"type": "Int",
|
||||
"arity": "Required"
|
||||
},
|
||||
{
|
||||
"tag": "CreateDirective",
|
||||
"location": {
|
||||
"path": {
|
||||
"tag": "Field",
|
||||
"model": "User",
|
||||
"field": "id"
|
||||
},
|
||||
"directive": "default"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "CreateArgument",
|
||||
"location": {
|
||||
"tag": "Directive",
|
||||
"path": {
|
||||
"tag": "Field",
|
||||
"model": "User",
|
||||
"field": "id"
|
||||
},
|
||||
"directive": "default"
|
||||
},
|
||||
"argument": "",
|
||||
"value": "autoincrement()"
|
||||
},
|
||||
{
|
||||
"tag": "CreateDirective",
|
||||
"location": {
|
||||
"path": {
|
||||
"tag": "Field",
|
||||
"model": "User",
|
||||
"field": "id"
|
||||
},
|
||||
"directive": "id"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "CreateField",
|
||||
"model": "User",
|
||||
"field": "email",
|
||||
"type": "String",
|
||||
"arity": "Required"
|
||||
},
|
||||
{
|
||||
"tag": "CreateDirective",
|
||||
"location": {
|
||||
"path": {
|
||||
"tag": "Field",
|
||||
"model": "User",
|
||||
"field": "email"
|
||||
},
|
||||
"directive": "unique"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "CreateField",
|
||||
"model": "User",
|
||||
"field": "name",
|
||||
"type": "String",
|
||||
"arity": "Optional"
|
||||
},
|
||||
{
|
||||
"tag": "CreateField",
|
||||
"model": "User",
|
||||
"field": "role",
|
||||
"type": "String",
|
||||
"arity": "Optional"
|
||||
},
|
||||
{
|
||||
"tag": "CreateField",
|
||||
"model": "User",
|
||||
"field": "storeId",
|
||||
"type": "Int",
|
||||
"arity": "Optional"
|
||||
},
|
||||
{
|
||||
"tag": "CreateModel",
|
||||
"model": "Product"
|
||||
},
|
||||
{
|
||||
"tag": "CreateField",
|
||||
"model": "Product",
|
||||
"field": "id",
|
||||
"type": "Int",
|
||||
"arity": "Required"
|
||||
},
|
||||
{
|
||||
"tag": "CreateDirective",
|
||||
"location": {
|
||||
"path": {
|
||||
"tag": "Field",
|
||||
"model": "Product",
|
||||
"field": "id"
|
||||
},
|
||||
"directive": "default"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "CreateArgument",
|
||||
"location": {
|
||||
"tag": "Directive",
|
||||
"path": {
|
||||
"tag": "Field",
|
||||
"model": "Product",
|
||||
"field": "id"
|
||||
},
|
||||
"directive": "default"
|
||||
},
|
||||
"argument": "",
|
||||
"value": "autoincrement()"
|
||||
},
|
||||
{
|
||||
"tag": "CreateDirective",
|
||||
"location": {
|
||||
"path": {
|
||||
"tag": "Field",
|
||||
"model": "Product",
|
||||
"field": "id"
|
||||
},
|
||||
"directive": "id"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "CreateField",
|
||||
"model": "Product",
|
||||
"field": "handle",
|
||||
"type": "String",
|
||||
"arity": "Required"
|
||||
},
|
||||
{
|
||||
"tag": "CreateDirective",
|
||||
"location": {
|
||||
"path": {
|
||||
"tag": "Field",
|
||||
"model": "Product",
|
||||
"field": "handle"
|
||||
},
|
||||
"directive": "unique"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "CreateField",
|
||||
"model": "Product",
|
||||
"field": "name",
|
||||
"type": "String",
|
||||
"arity": "Optional"
|
||||
},
|
||||
{
|
||||
"tag": "CreateField",
|
||||
"model": "Product",
|
||||
"field": "description",
|
||||
"type": "String",
|
||||
"arity": "Optional"
|
||||
},
|
||||
{
|
||||
"tag": "CreateField",
|
||||
"model": "Product",
|
||||
"field": "price",
|
||||
"type": "Int",
|
||||
"arity": "Optional"
|
||||
}
|
||||
]
|
||||
}
|
||||
6
examples/store/db/migrations/migrate.lock
Normal file
6
examples/store/db/migrations/migrate.lock
Normal file
@@ -0,0 +1,6 @@
|
||||
# IF THERE'S A GIT CONFLICT IN THIS FILE, DON'T SOLVE IT MANUALLY!
|
||||
# INSTEAD EXECUTE `prisma migrate fix`
|
||||
# Prisma Migrate lockfile v1
|
||||
# Read more about conflict resolution here: TODO
|
||||
|
||||
20200429140440-init
|
||||
37
examples/store/db/schema.prisma
Normal file
37
examples/store/db/schema.prisma
Normal file
@@ -0,0 +1,37 @@
|
||||
// This is your Prisma schema file,
|
||||
// learn more about it in the docs: https://pris.ly/d/prisma-schema
|
||||
|
||||
datasource sqlite {
|
||||
provider = "sqlite"
|
||||
url = "file:./db.sqlite"
|
||||
}
|
||||
|
||||
// SQLite is easy to start with, but if you use Postgres in production
|
||||
// you should also use it in development with the following:
|
||||
//datasource postgresql {
|
||||
// provider = "postgresql"
|
||||
// url = env("DATABASE_URL")
|
||||
//}
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------
|
||||
|
||||
model User {
|
||||
id Int @default(autoincrement()) @id
|
||||
email String @unique
|
||||
name String?
|
||||
role String?
|
||||
storeId Int?
|
||||
}
|
||||
|
||||
model Product {
|
||||
id Int @default(autoincrement()) @id
|
||||
handle String @unique
|
||||
name String?
|
||||
description String?
|
||||
price Int?
|
||||
}
|
||||
0
examples/store/integrations/.keep
Normal file
0
examples/store/integrations/.keep
Normal file
0
examples/store/jobs/.keep
Normal file
0
examples/store/jobs/.keep
Normal file
10
examples/store/now.json
Normal file
10
examples/store/now.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"env": {
|
||||
"DATABASE_URL": "@store-example-db"
|
||||
},
|
||||
"build": {
|
||||
"env": {
|
||||
"DATABASE_URL": "@store-example-db"
|
||||
}
|
||||
}
|
||||
}
|
||||
28
examples/store/package.json
Normal file
28
examples/store/package.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "store",
|
||||
"version": "0.9.2-canary.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "blitz db migrate && blitz build",
|
||||
"cy:open": "cypress open",
|
||||
"cy:run": "cypress run",
|
||||
"test:start": "blitz db migrate && blitz start --production -p 3099",
|
||||
"test": "start-server-and-test test:start http://localhost:3099 cy:run"
|
||||
},
|
||||
"dependencies": {
|
||||
"@prisma/cli": "2.0.0-beta.3",
|
||||
"@prisma/client": "2.0.0-beta.3",
|
||||
"blitz": "0.9.2-canary.1",
|
||||
"final-form": "4.19.1",
|
||||
"react": "0.0.0-experimental-e5d06e34b",
|
||||
"react-dom": "0.0.0-experimental-e5d06e34b",
|
||||
"react-final-form": "6.4.0",
|
||||
"typescript": "3.8.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "16.9.34",
|
||||
"cypress": "4.4.1",
|
||||
"eslint-plugin-cypress": "2.10.3",
|
||||
"start-server-and-test": "1.11.0"
|
||||
}
|
||||
}
|
||||
BIN
examples/store/public/favicon.ico
Executable file
BIN
examples/store/public/favicon.ico
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 556 B |
BIN
examples/store/public/logo.png
Normal file
BIN
examples/store/public/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
10
examples/store/public/zeit.svg
Normal file
10
examples/store/public/zeit.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg width="82" height="16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="url(#prefix__paint0_linear)" d="M9.018 0l9.019 16H0L9.018 0z"/>
|
||||
<path fill="#333" fill-rule="evenodd" d="M51.634 12.028h-6.492V2.052h6.492v1.256H46.61v3.007h4.37V7.57h-4.37v3.202h5.024v1.255zm-14.063 0h-7.235v-1.096l5.342-7.624h-5.253V2.052h7.058v1.097l-5.342 7.623h5.43v1.256zm21.88 0h6.333v-1.256h-2.423V3.308h2.423V2.052h-6.332v1.256h2.441v7.465h-2.441v1.255zm18.22 0h-1.468v-8.72h-3.36V2.052h8.225v1.256H77.67v8.72z" clip-rule="evenodd"/>
|
||||
<defs>
|
||||
<linearGradient id="prefix__paint0_linear" x1="28.022" x2="16.189" y1="22.991" y2="8.569" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#fff"/>
|
||||
<stop offset="1"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 794 B |
20
examples/store/tsconfig.json
Normal file
20
examples/store/tsconfig.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"baseUrl": "./",
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": false,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve"
|
||||
},
|
||||
"exclude": ["node_modules"],
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"]
|
||||
}
|
||||
0
examples/store/utils/.keep
Normal file
0
examples/store/utils/.keep
Normal file
4
examples/tailwind/.babelrc.js
Normal file
4
examples/tailwind/.babelrc.js
Normal file
@@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
presets: ["next/babel"],
|
||||
plugins: [],
|
||||
}
|
||||
10
examples/tailwind/.eslintrc.js
Normal file
10
examples/tailwind/.eslintrc.js
Normal file
@@ -0,0 +1,10 @@
|
||||
module.exports = {
|
||||
extends: ["react-app", "plugin:jsx-a11y/recommended"],
|
||||
plugins: ["jsx-a11y"],
|
||||
rules: {
|
||||
"import/no-anonymous-default-export": "error",
|
||||
"import/no-webpack-loader-syntax": "off",
|
||||
"react/react-in-jsx-scope": "off", // React is always in scope with Blitz
|
||||
"jsx-a11y/anchor-is-valid": "off", //Doesn't play well with Blitz/Next <Link> usage
|
||||
},
|
||||
}
|
||||
54
examples/tailwind/.gitignore
vendored
Normal file
54
examples/tailwind/.gitignore
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
# dependencies
|
||||
node_modules
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.pnp.*
|
||||
.npm
|
||||
web_modules/
|
||||
|
||||
# blitz
|
||||
/.blitz/
|
||||
/.next/
|
||||
*.sqlite
|
||||
.now
|
||||
.blitz-console-history
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
|
||||
# local env files
|
||||
.env
|
||||
.envrc
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Testing
|
||||
coverage
|
||||
*.lcov
|
||||
.nyc_output
|
||||
lib-cov
|
||||
|
||||
# Caches
|
||||
*.tsbuildinfo
|
||||
.eslintcache
|
||||
.node_repl_history
|
||||
.yarn-integrity
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
1
examples/tailwind/.npmrc
Normal file
1
examples/tailwind/.npmrc
Normal file
@@ -0,0 +1 @@
|
||||
save-exact=true
|
||||
5
examples/tailwind/.prettierignore
Normal file
5
examples/tailwind/.prettierignore
Normal file
@@ -0,0 +1,5 @@
|
||||
.gitkeep
|
||||
.env
|
||||
*.ico
|
||||
*.lock
|
||||
|
||||
11
examples/tailwind/README.md
Normal file
11
examples/tailwind/README.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Blitz Tailwind Example
|
||||
|
||||
## Getting Started
|
||||
|
||||
1. Start the dev server
|
||||
|
||||
```
|
||||
blitz start
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see Tailwind in use
|
||||
0
examples/tailwind/app/components/.keep
Normal file
0
examples/tailwind/app/components/.keep
Normal file
21
examples/tailwind/app/components/ErrorBoundary.tsx
Normal file
21
examples/tailwind/app/components/ErrorBoundary.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import React from "react"
|
||||
|
||||
export default class ErrorBoundary extends React.Component<{
|
||||
fallback: (error: any) => React.ReactNode
|
||||
}> {
|
||||
state = { hasError: false, error: null }
|
||||
|
||||
static getDerivedStateFromError(error: any) {
|
||||
return {
|
||||
hasError: true,
|
||||
error,
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.hasError) {
|
||||
return this.props.fallback(this.state.error)
|
||||
}
|
||||
return this.props.children
|
||||
}
|
||||
}
|
||||
29
examples/tailwind/app/components/nav.js
Normal file
29
examples/tailwind/app/components/nav.js
Normal file
@@ -0,0 +1,29 @@
|
||||
import { Link } from "blitz"
|
||||
|
||||
const links = [
|
||||
{ href: "https://github.com/blitz-js/blitz", label: "GitHub" },
|
||||
{ href: "https://github.com/blitz-js/blitz/blob/canary/USER_GUIDE.md", label: "Docs" },
|
||||
]
|
||||
|
||||
export default function Nav() {
|
||||
return (
|
||||
<nav>
|
||||
<ul className="flex justify-between items-center p-8">
|
||||
<li>
|
||||
<Link href="/">
|
||||
<a className="text-blue-500 no-underline">Home</a>
|
||||
</Link>
|
||||
</li>
|
||||
<ul className="flex justify-between items-center">
|
||||
{links.map(({ href, label }) => (
|
||||
<li key={`${href}${label}`} className="ml-4">
|
||||
<a href={href} className="btn-blue no-underline">
|
||||
{label}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</ul>
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
0
examples/tailwind/app/layouts/.keep
Normal file
0
examples/tailwind/app/layouts/.keep
Normal file
5
examples/tailwind/app/pages/_app.tsx
Normal file
5
examples/tailwind/app/pages/_app.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
import "app/styles/index.css"
|
||||
|
||||
export default function MyApp({ Component, pageProps }) {
|
||||
return <Component {...pageProps} />
|
||||
}
|
||||
23
examples/tailwind/app/pages/_document.tsx
Normal file
23
examples/tailwind/app/pages/_document.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import { Document, Html, DocumentHead, Main, NextScript /*DocumentContext*/ } from "@blitzjs/core"
|
||||
|
||||
class MyDocument extends Document {
|
||||
// Only uncomment if you need to customize this behaviour
|
||||
// static async getInitialProps(ctx: DocumentContext) {
|
||||
// const initialProps = await Document.getInitialProps(ctx)
|
||||
// return {...initialProps}
|
||||
// }
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Html lang="en">
|
||||
<DocumentHead />
|
||||
<body>
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
</Html>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default MyDocument
|
||||
14
examples/tailwind/app/pages/index.tsx
Normal file
14
examples/tailwind/app/pages/index.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import Nav from "app/components/nav"
|
||||
|
||||
const TailWindExamplePage = () => {
|
||||
return (
|
||||
<div>
|
||||
<Nav />
|
||||
<div className="hero">
|
||||
<h1 className="title">Blitz.js + Tailwind CSS</h1>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default TailWindExamplePage
|
||||
3
examples/tailwind/app/styles/button.css
Normal file
3
examples/tailwind/app/styles/button.css
Normal file
@@ -0,0 +1,3 @@
|
||||
.btn-blue {
|
||||
@apply bg-blue-500 text-white font-bold py-2 px-4 rounded;
|
||||
}
|
||||
23
examples/tailwind/app/styles/index.css
Normal file
23
examples/tailwind/app/styles/index.css
Normal file
@@ -0,0 +1,23 @@
|
||||
@import "./button.css";
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
.hero {
|
||||
width: 100%;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
padding-top: 80px;
|
||||
line-height: 1.15;
|
||||
font-size: 48px;
|
||||
}
|
||||
|
||||
.title,
|
||||
.description {
|
||||
text-align: center;
|
||||
}
|
||||
13
examples/tailwind/blitz.config.js
Normal file
13
examples/tailwind/blitz.config.js
Normal file
@@ -0,0 +1,13 @@
|
||||
module.exports = {
|
||||
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
|
||||
},
|
||||
}
|
||||
6
examples/tailwind/db/index.ts
Normal file
6
examples/tailwind/db/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { PrismaClient } from "@prisma/client"
|
||||
export * from "@prisma/client"
|
||||
|
||||
const prisma = new PrismaClient()
|
||||
|
||||
export default prisma
|
||||
0
examples/tailwind/db/migrations/.keep
Normal file
0
examples/tailwind/db/migrations/.keep
Normal file
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user