Compare commits
92 Commits
use-author
...
v0.30.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
11fc19fee2 | ||
|
|
fb4a63acba | ||
|
|
c3f8c7d07d | ||
|
|
51519166ef | ||
|
|
113a3a04f3 | ||
|
|
eba2a8081d | ||
|
|
16d2cae5b8 | ||
|
|
c2bd242d46 | ||
|
|
99966ab192 | ||
|
|
387e6e420b | ||
|
|
d39b461ddd | ||
|
|
0b7d463e7f | ||
|
|
87c4ee058c | ||
|
|
177c2b0519 | ||
|
|
31411baff5 | ||
|
|
2c4b803e7b | ||
|
|
117f0fda74 | ||
|
|
f613e08866 | ||
|
|
b696831e9f | ||
|
|
bd1781b99e | ||
|
|
9077397abe | ||
|
|
8e8fcffa2a | ||
|
|
364fb5e618 | ||
|
|
7678181bcf | ||
|
|
180d4cc4a1 | ||
|
|
2484b3a692 | ||
|
|
f1e223cd47 | ||
|
|
1a4dd4a2fe | ||
|
|
7f3fcf1489 | ||
|
|
219f069dba | ||
|
|
09bd6751fa | ||
|
|
f03fe566e5 | ||
|
|
4ed750a8f7 | ||
|
|
39eb1cef9b | ||
|
|
0a3c975b98 | ||
|
|
1b4119bb50 | ||
|
|
f0d880a4f2 | ||
|
|
11a11be20b | ||
|
|
0273e58749 | ||
|
|
75c3dc635e | ||
|
|
00e54948f2 | ||
|
|
ffcd0b4243 | ||
|
|
6bc26128ee | ||
|
|
f8b599aff4 | ||
|
|
fa09904d3a | ||
|
|
7c6c7dfdb5 | ||
|
|
ca850480ae | ||
|
|
f9006974bf | ||
|
|
5f6d296e37 | ||
|
|
5697173d5a | ||
|
|
69e5da6794 | ||
|
|
66e521e14b | ||
|
|
2a25bab41c | ||
|
|
c2fa1486df | ||
|
|
19cc7a1510 | ||
|
|
b1ed61f96d | ||
|
|
a101964603 | ||
|
|
eeeab00f1b | ||
|
|
42bf665f44 | ||
|
|
abf4795395 | ||
|
|
d48891a144 | ||
|
|
57f1e7d703 | ||
|
|
2d3e46fd79 | ||
|
|
984de2cab1 | ||
|
|
fca7791c47 | ||
|
|
c389a7ad6b | ||
|
|
a4320fd974 | ||
|
|
157c397959 | ||
|
|
d7804089e1 | ||
|
|
04b0cd356c | ||
|
|
cd615f58bd | ||
|
|
c49da040b5 | ||
|
|
da6685cd2d | ||
|
|
0f4de7761e | ||
|
|
3114e8bc4a | ||
|
|
e2383fbb5f | ||
|
|
a4ea513a76 | ||
|
|
9e1860d8b3 | ||
|
|
05673fbdb5 | ||
|
|
a2cdcf4a74 | ||
|
|
9ff56beebf | ||
|
|
b238bae52c | ||
|
|
caf3d260c8 | ||
|
|
b108c5720f | ||
|
|
8ccc744c5f | ||
|
|
b0ed47e1b8 | ||
|
|
ae43a07b4d | ||
|
|
0ba81b6474 | ||
|
|
190bc65c53 | ||
|
|
85bd4ec286 | ||
|
|
1792cc6788 | ||
|
|
dc88bc68d2 |
@@ -1587,7 +1587,8 @@
|
||||
"avatar_url": "https://avatars3.githubusercontent.com/u/43112535?v=4",
|
||||
"profile": "http://Dal.Design",
|
||||
"contributions": [
|
||||
"code"
|
||||
"code",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1975,6 +1976,160 @@
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "LBrian",
|
||||
"name": "Brian Liu",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/3888780?v=4",
|
||||
"profile": "https://brianypliu.com",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "beerose",
|
||||
"name": "Aleksandra Sikora",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/9019397?v=4",
|
||||
"profile": "http://aleksandra.codes",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "JuanM04",
|
||||
"name": "JuanM04",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/16712703?v=4",
|
||||
"profile": "https://juanm04.com",
|
||||
"contributions": [
|
||||
"code",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "arenddeboer",
|
||||
"name": "Arend de Boer",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/7022204?v=4",
|
||||
"profile": "https://github.com/arenddeboer",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "fmilani",
|
||||
"name": "Felipe Milani",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1580375?v=4",
|
||||
"profile": "https://github.com/fmilani",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "jxe",
|
||||
"name": "Joe Edelman",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/13018?v=4",
|
||||
"profile": "http://nxhx.org",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "garytube",
|
||||
"name": "Gary",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/3823504?v=4",
|
||||
"profile": "https://github.com/garytube",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "oliverloops",
|
||||
"name": "Oliver Lopez ",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/33361399?v=4",
|
||||
"profile": "http://oliverloops.com",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "DecadentIpsum",
|
||||
"name": "Andreas Zaralis",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/32861532?v=4",
|
||||
"profile": "https://decadentIpsum.me",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "davetorbeck",
|
||||
"name": "David Torbeck",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/5829885?v=4",
|
||||
"profile": "https://github.com/davetorbeck",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "gusgard",
|
||||
"name": "Gustavo Gard",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/2577356?v=4",
|
||||
"profile": "https://github.com/gusgard",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Immortalin",
|
||||
"name": "Immortalin",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/7126128?v=4",
|
||||
"profile": "https://narrationbox.com",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "cristianbgp",
|
||||
"name": "Cristian Granda",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/8507974?v=4",
|
||||
"profile": "https://cristianbgp.com",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "deniseyu",
|
||||
"name": "Denise Yu",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/8420094?v=4",
|
||||
"profile": "https://deniseyu.io",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "andreadellacorte",
|
||||
"name": "Andrea Della Corte",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/295683?v=4",
|
||||
"profile": "http://dellacorte.me",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "aditsachde",
|
||||
"name": "Adit Sachde",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/23707194?v=4",
|
||||
"profile": "http://aditsachde.com",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "hirenchauhan2",
|
||||
"name": "Hiren Chauhan",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/8999668?v=4",
|
||||
"profile": "https://github.com/hirenchauhan2",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
|
||||
23
.github/ISSUE_TEMPLATE/bug_report.md
vendored
23
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,23 +0,0 @@
|
||||
---
|
||||
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.
|
||||
31
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
31
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
name: Bug Report
|
||||
about: Something is not working right. Or error messages are unclear.
|
||||
labels: "kind/bug, status/triage"
|
||||
issue_body: true
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: Thanks for taking the time to file a bug report! Please fill out this form as completely as possible.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: What is the problem?
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: What are detailed steps to reproduce this?
|
||||
value: "1."
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Run `blitz -v` and paste the output here:"
|
||||
value: |
|
||||
```
|
||||
PASTE_HERE
|
||||
```
|
||||
validations:
|
||||
required: true
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: "Please include below any other applicable logs and screenshots that show your problem:"
|
||||
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -2,7 +2,7 @@
|
||||
name: Feature/change request
|
||||
about: Something new or better!
|
||||
title: ""
|
||||
labels: ""
|
||||
labels: "status/triage"
|
||||
assignees: ""
|
||||
---
|
||||
|
||||
|
||||
67
.github/workflows/codeql-analysis.yml
vendored
Normal file
67
.github/workflows/codeql-analysis.yml
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ canary, master ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ canary ]
|
||||
schedule:
|
||||
- cron: '31 19 * * 5'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'javascript' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
|
||||
# Learn more:
|
||||
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
4
.github/workflows/main.yml
vendored
4
.github/workflows/main.yml
vendored
@@ -48,7 +48,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
node_version: [12, 14]
|
||||
node_version: [12, 15]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
@@ -91,7 +91,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
node_version: [12, 14]
|
||||
node_version: [12, 15]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -25,3 +25,5 @@ dist
|
||||
.tsbuildinfo
|
||||
.nvmrc
|
||||
**/.test*
|
||||
examples/auth2
|
||||
.idea
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
# 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.
|
||||
@@ -1 +0,0 @@
|
||||
This document has moved here: https://blitzjs.com/docs/maintainers
|
||||
@@ -1,3 +0,0 @@
|
||||
# Manifesto
|
||||
|
||||
[The Manifesto has been moved to Blitzjs.com](https://blitzjs.com/docs/manifesto)
|
||||
78
README.md
78
README.md
@@ -6,7 +6,7 @@
|
||||
<img alt="" src="https://img.shields.io/badge/Join%20our%20community-6700EB.svg?style=for-the-badge&labelColor=000000&logoWidth=20&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAQ9SURBVHgB7d3dVdtAEIbhcSpICUoH0IEogQqSVBBSAU4FSSpIOoAORAfQgSghHXzZ1U/YcMD4R9rZmf2ec3y448LyiNf27iLiGIAmPLrweC9Un3DhrzG6EarLNP09nlwJ1SOZ/lQr5N80/S/p2QMVCBf5N17XCfm1Y/rBHqjAG9PPHvBsz+mf9WAP+HLA9M/YA14cOP2payH7jpj+VCtk1wnTP+vj7xCy6cTpn7EHLMLp059iD1iD8eveJbVCNsSLheX1YA/YgOWnf8YeKB3Wmf7Ud6Fy4f/FHmtpxbl3YlC4MJ/Cj0bWdwPnPbARg+L0S54XQHS32WwuxClzd4CM0z9rPfeAuTtA5ulPXYQ7wZ04Y+oOoDD9KZc9YOoOoDj9s4dwFzgXR6w1wIPoOvPWA9buAHEJ173o3gWiy3AnuBUHLEbgmYwvAk1/wuM8vAgexThzbwPDkx7/DHwVXfFOxP2GmsKd4Ab6zPeAyU8CI7AHFmH2BRCBPXAyk18GzUrqAXCTiR4ssyj0VFw/oCU8+e+RZ33AWz6KMaYbIIWxB+JSLs1bsbkeMN0AqakHvoku9oA2sAfqBvbAQdw0QArsgb25aYBUQT3QgT2gB+yBuqGcHij2UCqXDZACe2Anlw2QYg/QAOyBuoE98CL3DZDCuK4/rh/Q7oGL6U+TOvcNkJoijN8X1C48+T+g75eQDrAH/qmqAVJgDwyqaoAUe4AGYA/UDZX3QLUNkEIZPRCd5+6BahsgVUgPROwBTSijB7jpVAvGHriHvmw9wAZ4BpX1ABvgmakHtPcbRuwBTWAPULgAV9D/jKDY9YRvwvgEaurD44uQHvAol7qBW7WKluVtIHiUS7GyvA0s6CiXDnxrpQfsgbqBS7GKk/2jYHCrVlGyfxTMrVo0ALdq1Q3sgSKofh0M9oA61a+D2QM0AHugbmAPqClmSRjK2apVVQ8UsySsoK1aHdgDesCtWnUDeyCrIpeFg1u3sylyWTi3btMA7IG6gT2wuuK3hoE9sKrit4YVslWLPaAN7IG6ocKt2zmY2h4O9sDiTG0PZw/QANy6XTewBxZj9ogYVHy025LMHhEz9cBn0We6B0yfERReBLfhx0/R1YQHPx/QBPbA0VwcEwf2wNFcHBPHHjiem3MC2QPHcXdSaJjA+KfgTPQ8hhfjBzHC40mhlzJ+Xq9lK4a4PCs43AVaGTed5mZq+iOXZwWHi3AnOj2wFWNcnxYe7gTxLtBKHuamP/J+Wnh8a5irB7ZC5Yk9gPX1QuXC+usHWqGyhYvUYR0a7zboUOFCNVhnk0krZAOW7wFOvzXhom2xnEbIHizTA1wEYhWW6YFGyC6c1gOcfg9wfA80Qj7g8B7g9HuCww+haIR8wf49wOn3Cvv9k8tGyC/s7gFOv3fY3QONkH+v9MBWqB7PeqDn9FcIT//kcitUn6kHOu/T/xfWzlQy3dEHhwAAAABJRU5ErkJggg==">
|
||||
</a>
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
||||
<a aria-label="All Contributors" href="#contributors-"><img alt="" src="https://img.shields.io/badge/all_contributors-209-17BB8A.svg?style=for-the-badge&labelColor=000000"></a>
|
||||
<a aria-label="All Contributors" href="#contributors-"><img alt="" src="https://img.shields.io/badge/all_contributors-226-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">
|
||||
@@ -22,15 +22,15 @@
|
||||
<h1 align="center">The Fullstack React Framework</h1>
|
||||
|
||||
<h5 align="center">"Zero-API" Data Layer — Built on Next.js — Inspired by Ruby on Rails</h3>
|
||||
<h3 align="center">Makes you far more productive than you ever dreamed was possible 😉</h3>
|
||||
<h3 align="center"><a href="https://blitzjs.com" target="_blank">Read the Documentation</a></h3>
|
||||
<br>
|
||||
|
||||
“Zero-API” data layer **lets you import server code directly into your React components** instead of having to manually add API endpoints and do client-side fetching and caching.
|
||||
|
||||
Includes everything you need for production apps. **Everything end-to-end from the database to the frontend.**
|
||||
New Blitz apps come with **all the boring stuff already set up for you!** Like ESLint, Prettier, Jest, user sign up, log in, and password reset.
|
||||
|
||||
Provides **helpful defaults and conventions** for things like routing, file structure, and authentication while also being extremely flexible.
|
||||
|
||||
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!
|
||||
|
||||
<br>
|
||||
|
||||
@@ -49,7 +49,7 @@ _You can alternatively use [`npx`](https://www.npmjs.com/package/npx)_
|
||||
1. `blitz new myAppName`
|
||||
2. `cd myAppName`
|
||||
3. `blitz dev`
|
||||
4. View your baby app at http://localhost:3000
|
||||
4. View your brand new app at http://localhost:3000
|
||||
|
||||
<br><br>
|
||||
|
||||
@@ -57,32 +57,10 @@ _You can alternatively use [`npx`](https://www.npmjs.com/package/npx)_
|
||||
<img alt="Bytes Newsletter" src="https://files-8wtskjofb.vercel.app/smarter-16x1.jpg">
|
||||
</a>
|
||||
|
||||
<br><br>
|
||||
|
||||

|
||||
|
||||
<br><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>
|
||||
⚡️ Highly secure authentication <br>
|
||||
⚡️ Authorization you can use on both server and client<br>
|
||||
⚡️ Recipes for easily adding libraries like Tailwind, CSS-in-JS, etc.<br>
|
||||
|
||||
**Other key features coming:**<br>
|
||||
⚡️ Model validation you can use on both server and client<br>
|
||||
⚡️ React native support<br>
|
||||
⚡️ GUI so you don't have to use the CLI<br>
|
||||
|
||||
<br>
|
||||
|
||||
### The Foundational Principles
|
||||
|
||||
@@ -128,35 +106,38 @@ Your financial contributions help ensure Blitz continues to be developed and mai
|
||||
### 🌱 Seedling Sponsors
|
||||
|
||||
<a aria-label="React Bricks" href="https://reactbricks.com/?utm_source=blitzjs&utm_medium=sponsorship&utm_campaign=blitzjs_sponsorship">
|
||||
<img alt="" src="https://reactbricks.com/reactbricks_icon.svg" width="30px"/>
|
||||
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/canary/assets/reactbricks_icon.svg" width="40px"/>
|
||||
</a>
|
||||
<a aria-label="Andreas Asprou" href="https://andreas.fyi">
|
||||
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/canary/assets/andreas.jpg" width="30px"/>
|
||||
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/canary/assets/andreas.jpg" width="40px"/>
|
||||
</a>
|
||||
<a aria-label="Robert Malko" href="https://github.com/malkomalko">
|
||||
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/canary/assets/rob_blitz.jpg" width="40px"/>
|
||||
</a>
|
||||
|
||||
|
||||
### 🥉 Bronze Sponsors
|
||||
|
||||
<a aria-label="Render.com" href="https://render.com?utm_source=BlitzJS&utm_medium=sponsorship&utm_campaign=BlitzJS_Sponsorship_2020">
|
||||
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/canary/assets/render-logo-color2.png" width="110px">
|
||||
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/canary/assets/render-logo-color2.png" width="200px">
|
||||
</a>
|
||||
|
||||
### 🥈 Silver Sponsors
|
||||
|
||||
<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="200px">
|
||||
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/canary/assets/Fauna_Logo_Blue.png" width="300px">
|
||||
</a>
|
||||
|
||||
### 🏆 Gold Sponsors
|
||||
|
||||
<a aria-label="Your Company" href="#">
|
||||
<img alt="" src="https://dummyimage.com/1000x330/efe8ff/000000.png&text=Your+Logo+Here" width="300px">
|
||||
<img alt="" src="https://dummyimage.com/1000x330/efe8ff/000000.png&text=Your+Logo+Here" width="400px">
|
||||
</a>
|
||||
|
||||
### 💎 Diamond Sponsors
|
||||
|
||||
<a aria-label="Your Company" href="#">
|
||||
<img alt="" src="https://dummyimage.com/1000x330/efe8ff/000000.png&text=Your+Logo+Here" width="400px">
|
||||
<img alt="" src="https://dummyimage.com/1000x330/efe8ff/000000.png&text=Your+Logo+Here" width="500px">
|
||||
</a>
|
||||
|
||||
<br>
|
||||
@@ -177,7 +158,7 @@ Your financial contributions help ensure Blitz continues to be developed and mai
|
||||
|
||||
## Maintainers (Level 2) ✨
|
||||
|
||||
_Code ownership, pull request approvals and merging, etc_ (see [MAINTAINERS.md](./MAINTAINERS.md))
|
||||
_Code ownership, pull request approvals and merging, etc_ (see [Maintainers L2](https://blitzjs.com/docs/maintainers#level-2-maintainers))
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
<!-- markdownlint-disable -->
|
||||
@@ -186,6 +167,7 @@ _Code ownership, pull request approvals and merging, etc_ (see [MAINTAINERS.md](
|
||||
<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>
|
||||
<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 />SuperJSON</td>
|
||||
<td align="center"><a href="https://juanm04.com"><img src="https://avatars0.githubusercontent.com/u/16712703?v=4" width="100px;" alt=""/><br /><sub><b>Juan Martín Seery</b></sub></a><br />Website/Docs</td>
|
||||
</tr>
|
||||
</table>
|
||||
<!-- markdownlint-enable -->
|
||||
@@ -195,7 +177,7 @@ _Code ownership, pull request approvals and merging, etc_ (see [MAINTAINERS.md](
|
||||
|
||||
## Maintainers (Level 1) ✨
|
||||
|
||||
_Issue triage, pull request triage, community encouragement and moderation, etc_ (see [MAINTAINERS.md](./MAINTAINERS.md))
|
||||
_Issue triage, pull request triage, community encouragement and moderation, etc_ (see [Maintainers L1](https://blitzjs.com/docs/maintainers#level-1-maintainers))
|
||||
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
@@ -212,7 +194,6 @@ _Issue triage, pull request triage, community encouragement and moderation, etc_
|
||||
<tr>
|
||||
<td align="center"><a href="https://twitter.com/jdavenport97"><img src="https://avatars2.githubusercontent.com/u/1329874?v=4" width="100px;" alt=""/><br /><sub><b>Jamie Davenport</b></sub></a></td>
|
||||
<td align="center"><a href="https://twitter.com/myrondavis"><img src="https://avatars2.githubusercontent.com/u/1430136?v=4" width="100px;" alt=""/><br /><sub><b>Myron Davis</b></sub></a></td>
|
||||
<td align="center"><a href="https://flavioander.com/"><img src="https://avatars2.githubusercontent.com/u/14948074?s=460&u=31d7ea58b5c5cd9f724d684ed578f68896c4af71&v=4" width="100px;" alt=""/><br /><sub><b>Flavio Andrade</b></sub></a></td>
|
||||
<td align="center"><a href="https://twitter.com/NaReto1125_"><img src="https://avatars.githubusercontent.com/reo777" width="100px;" alt=""/><br /><sub><b>Reo Ishiyama</b></sub></a></td>
|
||||
<td align="center"><a href="https://github.com/malkomalko"><img src="https://avatars.githubusercontent.com/malkomalko" width="100px;" alt=""/><br /><sub><b>Robert Malko</b></sub></a></td>
|
||||
</tr>
|
||||
@@ -444,7 +425,7 @@ Thanks to these wonderful people ([emoji key](https://allcontributors.org/docs/e
|
||||
<td align="center"><a href="http://kattcorp.com"><img src="https://avatars1.githubusercontent.com/u/459267?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Alex Johansson</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=KATT" title="Code">💻</a></td>
|
||||
<td align="center"><a href="http://davidmazza.com"><img src="https://avatars0.githubusercontent.com/u/120893?v=4?s=100" width="100px;" alt=""/><br /><sub><b>David Mazza</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=dmzza" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/rayandrews"><img src="https://avatars1.githubusercontent.com/u/4437323?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ray Andrew</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=rayandrews" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=rayandrews" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="http://Dal.Design"><img src="https://avatars3.githubusercontent.com/u/43112535?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Abdullah Mzaien</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=Mzaien" title="Code">💻</a></td>
|
||||
<td align="center"><a href="http://Dal.Design"><img src="https://avatars3.githubusercontent.com/u/43112535?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Abdullah Mzaien</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=Mzaien" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=Mzaien" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="http://kwao.io"><img src="https://avatars2.githubusercontent.com/u/8839514?v=4?s=100" width="100px;" alt=""/><br /><sub><b>William Kwao</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=williamkwao" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -499,6 +480,29 @@ Thanks to these wonderful people ([emoji key](https://allcontributors.org/docs/e
|
||||
<td align="center"><a href="https://alistair.cloud"><img src="https://avatars.githubusercontent.com/u/25351731?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Alistair Smith</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=alii" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://rodrigoehlers.com"><img src="https://avatars.githubusercontent.com/u/19683042?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Rodrigo Ehlers</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=rodrigoehlers" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://www.builtopen.com/"><img src="https://avatars.githubusercontent.com/u/1734057?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Michael Ford</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=mtford90" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://brianypliu.com"><img src="https://avatars.githubusercontent.com/u/3888780?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Brian Liu</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=LBrian" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="http://aleksandra.codes"><img src="https://avatars.githubusercontent.com/u/9019397?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Aleksandra Sikora</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=beerose" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://juanm04.com"><img src="https://avatars.githubusercontent.com/u/16712703?v=4?s=100" width="100px;" alt=""/><br /><sub><b>JuanM04</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=JuanM04" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=JuanM04" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/arenddeboer"><img src="https://avatars.githubusercontent.com/u/7022204?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Arend de Boer</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=arenddeboer" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/fmilani"><img src="https://avatars.githubusercontent.com/u/1580375?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Felipe Milani</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=fmilani" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="http://nxhx.org"><img src="https://avatars.githubusercontent.com/u/13018?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Joe Edelman</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=jxe" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/garytube"><img src="https://avatars.githubusercontent.com/u/3823504?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Gary</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=garytube" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="http://oliverloops.com"><img src="https://avatars.githubusercontent.com/u/33361399?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Oliver Lopez </b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=oliverloops" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://decadentIpsum.me"><img src="https://avatars.githubusercontent.com/u/32861532?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Andreas Zaralis</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=DecadentIpsum" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/davetorbeck"><img src="https://avatars.githubusercontent.com/u/5829885?v=4?s=100" width="100px;" alt=""/><br /><sub><b>David Torbeck</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=davetorbeck" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/gusgard"><img src="https://avatars.githubusercontent.com/u/2577356?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Gustavo Gard</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=gusgard" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://narrationbox.com"><img src="https://avatars.githubusercontent.com/u/7126128?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Immortalin</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=Immortalin" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://cristianbgp.com"><img src="https://avatars.githubusercontent.com/u/8507974?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Cristian Granda</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=cristianbgp" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://deniseyu.io"><img src="https://avatars.githubusercontent.com/u/8420094?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Denise Yu</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=deniseyu" title="Code">💻</a></td>
|
||||
<td align="center"><a href="http://dellacorte.me"><img src="https://avatars.githubusercontent.com/u/295683?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Andrea Della Corte</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=andreadellacorte" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="http://aditsachde.com"><img src="https://avatars.githubusercontent.com/u/23707194?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Adit Sachde</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=aditsachde" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/hirenchauhan2"><img src="https://avatars.githubusercontent.com/u/8999668?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Hiren Chauhan</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=hirenchauhan2" title="Code">💻</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
10
SECURITY.md
Normal file
10
SECURITY.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
TODO
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Email Brandon Bayer at b@bayer.ws to report a vulnerablity, and he will follow up with you asap.
|
||||
|
||||
1
assets/reactbricks_icon.svg
Normal file
1
assets/reactbricks_icon.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 980 979.97"><path d="M139.38,0c-185.82-3.54-185.82,277,0,273.46H251.44C437.25,277,437.25-3.5,251.44,0ZM729.25,353.24c-184.92-2.62-184.92,276.1,0,273.48H841.31c184.92,2.62,184.92-276.1,0-273.48ZM138.69,706.46c-184.92-2.61-184.92,276.1,0,273.49H250.75c184.91,2.61,184.91-276.1,0-273.49Z" fill="#b43278"/><path d="M583.47,0c-185.82-3.54-185.82,277,0,273.46H840.61c185.81,3.54,185.81-277,0-273.46ZM138.7,353.24c-184.92-2.62-184.92,276.1,0,273.48H395.85c184.92,2.62,184.92-276.1,0-273.48ZM584.13,706.46c-184.91-2.61-184.91,276.1,0,273.49H841.3c184.92,2.61,184.92-276.1,0-273.49Z" fill="#f65a8e"/></svg>
|
||||
|
After Width: | Height: | Size: 650 B |
BIN
assets/rob_blitz.jpg
Normal file
BIN
assets/rob_blitz.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.8 MiB |
@@ -53,7 +53,7 @@ export default passportAuth({
|
||||
update: {email},
|
||||
})
|
||||
|
||||
const publicData = {userId: user.id, roles: [user.role], source: "twitter"}
|
||||
const publicData = {userId: user.id, role: user.role, source: "twitter"}
|
||||
done(null, {publicData})
|
||||
},
|
||||
),
|
||||
@@ -92,7 +92,7 @@ export default passportAuth({
|
||||
|
||||
const publicData = {
|
||||
userId: user.id,
|
||||
roles: [user.role as Role],
|
||||
role: user.role as Role,
|
||||
source: "github",
|
||||
githubUsername: profile.username,
|
||||
}
|
||||
@@ -131,7 +131,7 @@ export default passportAuth({
|
||||
|
||||
const publicData = {
|
||||
userId: user.id,
|
||||
roles: [user.role],
|
||||
role: user.role,
|
||||
source: "auth0",
|
||||
githubUsername: profile.username,
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ export default resolver.pipe(resolver.zod(Login), async ({email, password}, ctx)
|
||||
// This throws an error if credentials are invalid
|
||||
const user = await authenticateUser(email, password)
|
||||
|
||||
await ctx.session.$create({userId: user.id, roles: [user.role as Role]})
|
||||
await ctx.session.$create({userId: user.id, role: user.role as Role})
|
||||
|
||||
return user
|
||||
})
|
||||
|
||||
@@ -10,6 +10,6 @@ export default resolver.pipe(resolver.zod(Signup), async ({email, password}, ctx
|
||||
select: {id: true, name: true, email: true, role: true},
|
||||
})
|
||||
|
||||
await ctx.session.$create({userId: user.id, roles: [user.role as Role]})
|
||||
await ctx.session.$create({userId: user.id, role: user.role as Role})
|
||||
return user
|
||||
})
|
||||
|
||||
@@ -8,11 +8,17 @@ const LoginPage: BlitzPage = () => {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<LoginForm onSuccess={() => router.push("/")} />
|
||||
<LoginForm
|
||||
onSuccess={() => {
|
||||
const next = (router.query.next as string) ?? "/"
|
||||
router.push(next)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
LoginPage.redirectAuthenticatedTo = "/"
|
||||
LoginPage.getLayout = (page) => <Layout title="Log In">{page}</Layout>
|
||||
|
||||
export default LoginPage
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import {Head, useRouter, BlitzPage, useMutation} from "blitz"
|
||||
import {useRouter, BlitzPage} from "blitz"
|
||||
import Layout from "app/core/layouts/Layout"
|
||||
import {Form, FORM_ERROR} from "app/core/components/Form"
|
||||
import {LabeledTextField} from "app/core/components/LabeledTextField"
|
||||
import {SignupForm} from "app/auth/components/SignupForm"
|
||||
|
||||
const SignupPage: BlitzPage = () => {
|
||||
|
||||
@@ -2,7 +2,7 @@ import {useSession, useRouter, useMutation, Head} from "blitz"
|
||||
import logout from "app/auth/mutations/logout"
|
||||
|
||||
export default function Layout({title, children}: {title?: string; children: React.ReactNode}) {
|
||||
const session = useSession()
|
||||
const session = useSession({suspense: false})
|
||||
const router = useRouter()
|
||||
const [logoutMutation] = useMutation(logout)
|
||||
return (
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import {
|
||||
withBlitzAppRoot,
|
||||
AppProps,
|
||||
ErrorComponent,
|
||||
useRouter,
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
import {render} from "test/utils"
|
||||
|
||||
import Home from "./index"
|
||||
import {useCurrentUser} from "app/core/hooks/useCurrentUser"
|
||||
|
||||
jest.mock("app/core/hooks/useCurrentUser")
|
||||
const mockUseCurrentUser = useCurrentUser as jest.MockedFunction<typeof useCurrentUser>
|
||||
jest.mock("blitz", () => ({
|
||||
...jest.requireActual("blitz")!,
|
||||
useQuery: () => [
|
||||
{
|
||||
id: 1,
|
||||
name: "User",
|
||||
email: "user@email.com",
|
||||
role: "user",
|
||||
},
|
||||
],
|
||||
}))
|
||||
|
||||
test("renders blitz documentation link", () => {
|
||||
// This is an example of how to ensure a specific item is in the document
|
||||
@@ -12,12 +19,6 @@ test("renders blitz documentation link", () => {
|
||||
// when you remove the the default content from the page
|
||||
|
||||
// This is an example on how to mock api hooks when testing
|
||||
mockUseCurrentUser.mockReturnValue({
|
||||
id: 1,
|
||||
name: "User",
|
||||
email: "user@email.com",
|
||||
role: "user",
|
||||
})
|
||||
|
||||
const {getByText} = render(<Home />)
|
||||
const element = getByText(/powered by blitz/i)
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
import {Suspense} from "react"
|
||||
import {Head, Link, useSession, useRouterQuery, useMutation, invoke} from "blitz"
|
||||
import {
|
||||
Head,
|
||||
Link,
|
||||
useSession,
|
||||
useRouterQuery,
|
||||
useMutation,
|
||||
invoke,
|
||||
useQuery,
|
||||
BlitzPage,
|
||||
} from "blitz"
|
||||
import getUser from "app/users/queries/getUser"
|
||||
import trackView from "app/users/mutations/trackView"
|
||||
import Layout from "app/core/layouts/Layout"
|
||||
import {useCurrentUser} from "app/core/hooks/useCurrentUser"
|
||||
// import getUsers from "app/users/queries/getUsers"
|
||||
|
||||
const CurrentUserInfo = () => {
|
||||
const currentUser = useCurrentUser()
|
||||
const session = useSession()
|
||||
const [currentUser] = useQuery(getUser, {where: {id: session.userId!}})
|
||||
|
||||
return <pre>{JSON.stringify(currentUser, null, 2)}</pre>
|
||||
}
|
||||
|
||||
// const Users = () => {
|
||||
// const [users] = useQuery(getUsers, {})
|
||||
//
|
||||
// return <pre style={{maxWidth: "30rem"}}>{JSON.stringify(users, null, 2)}</pre>
|
||||
// }
|
||||
|
||||
const UserStuff = () => {
|
||||
const session = useSession()
|
||||
const query = useRouterQuery()
|
||||
const [trackViewMutation] = useMutation(trackView)
|
||||
|
||||
if (session.isLoading) return <div>Loading...</div>
|
||||
|
||||
return (
|
||||
<div>
|
||||
{!session.userId && (
|
||||
@@ -48,11 +48,6 @@ const UserStuff = () => {
|
||||
<Suspense fallback="Loading...">
|
||||
<CurrentUserInfo />
|
||||
</Suspense>
|
||||
{/*
|
||||
<Suspense fallback="Loading...">
|
||||
<Users />
|
||||
</Suspense>
|
||||
*/}
|
||||
<button
|
||||
onClick={async () => {
|
||||
try {
|
||||
@@ -81,7 +76,7 @@ const UserStuff = () => {
|
||||
)
|
||||
}
|
||||
|
||||
const Home = () => (
|
||||
const Home: BlitzPage = () => (
|
||||
<Layout>
|
||||
<div className="container">
|
||||
<Head>
|
||||
@@ -94,7 +89,9 @@ const Home = () => (
|
||||
<img src="/logo.png" alt="blitz.js" />
|
||||
</div>
|
||||
|
||||
<UserStuff />
|
||||
<Suspense fallback={"Loading..."}>
|
||||
<UserStuff />
|
||||
</Suspense>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
@@ -238,4 +235,6 @@ const Home = () => (
|
||||
</Layout>
|
||||
)
|
||||
|
||||
Home.suppressFirstRenderFlicker = true
|
||||
|
||||
export default Home
|
||||
|
||||
@@ -10,6 +10,8 @@ export const Project = () => {
|
||||
const [deleteProjectMutation, {isSuccess}] = useMutation(deleteProject)
|
||||
const [project] = useQuery(getProject, {id: projectId}, {enabled: !isSuccess})
|
||||
|
||||
if (!project) return null
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
@@ -57,6 +59,7 @@ const ShowProjectPage: BlitzPage = () => {
|
||||
)
|
||||
}
|
||||
|
||||
ShowProjectPage.authenticate = true
|
||||
ShowProjectPage.getLayout = (page) => <Layout>{page}</Layout>
|
||||
|
||||
export default ShowProjectPage
|
||||
|
||||
@@ -65,6 +65,7 @@ const EditProjectPage: BlitzPage = () => {
|
||||
)
|
||||
}
|
||||
|
||||
EditProjectPage.authenticate = true
|
||||
EditProjectPage.getLayout = (page) => <Layout>{page}</Layout>
|
||||
|
||||
export default EditProjectPage
|
||||
|
||||
@@ -61,6 +61,7 @@ const ProjectsPage: BlitzPage = () => {
|
||||
)
|
||||
}
|
||||
|
||||
ProjectsPage.authenticate = {redirectTo: "/login"}
|
||||
ProjectsPage.getLayout = (page) => <Layout>{page}</Layout>
|
||||
|
||||
export default ProjectsPage
|
||||
|
||||
@@ -40,6 +40,7 @@ const NewProjectPage: BlitzPage = () => {
|
||||
)
|
||||
}
|
||||
|
||||
NewProjectPage.authenticate = true
|
||||
NewProjectPage.getLayout = (page) => <Layout title={"Create New Project"}>{page}</Layout>
|
||||
|
||||
export default NewProjectPage
|
||||
|
||||
@@ -6,7 +6,7 @@ type GetUserInput = {
|
||||
}
|
||||
|
||||
export default async function getUser({where}: GetUserInput, ctx: Ctx) {
|
||||
ctx.session.$authorize()
|
||||
if (!ctx.session.userId) return null
|
||||
|
||||
const user = await db.user.findFirst({where})
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ describe("index page", () => {
|
||||
const user = createRandomUser()
|
||||
|
||||
cy.signup(user)
|
||||
cy.wait(1000)
|
||||
|
||||
cy.location("pathname").should("equal", "/")
|
||||
cy.contains("button", "Logout")
|
||||
@@ -28,9 +29,10 @@ describe("index page", () => {
|
||||
const user = createRandomUser()
|
||||
|
||||
cy.signup(user)
|
||||
cy.wait(1000)
|
||||
|
||||
cy.wait(500)
|
||||
cy.contains("button", "Logout").click()
|
||||
cy.wait(1000)
|
||||
cy.contains("a", /login/i).click()
|
||||
|
||||
cy.contains("Email").find("input").type(user.email)
|
||||
@@ -38,7 +40,7 @@ describe("index page", () => {
|
||||
cy.contains("button", /login/i).click()
|
||||
|
||||
cy.location("pathname").should("equal", "/")
|
||||
cy.wait(500)
|
||||
cy.wait(1000)
|
||||
cy.contains("button", "Logout")
|
||||
})
|
||||
|
||||
@@ -46,6 +48,7 @@ describe("index page", () => {
|
||||
const user = createRandomUser()
|
||||
|
||||
cy.signup(user)
|
||||
cy.wait(1000)
|
||||
|
||||
cy.contains("button", "Logout").click()
|
||||
|
||||
@@ -60,9 +63,11 @@ describe("index page", () => {
|
||||
|
||||
cy.contains("button", "Track view").click()
|
||||
cy.contains("button", "Track view").click()
|
||||
cy.wait(1000)
|
||||
cy.contains('"views": 2')
|
||||
|
||||
cy.signup(user)
|
||||
cy.wait(1000)
|
||||
|
||||
cy.contains('"views": 2')
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@examples/auth",
|
||||
"version": "0.30.0-canary.6",
|
||||
"version": "0.30.3",
|
||||
"scripts": {
|
||||
"dev": "blitz dev",
|
||||
"build": "blitz build",
|
||||
@@ -12,7 +12,7 @@
|
||||
"cy:run": "cypress run || cypress run",
|
||||
"test": "prisma generate && yarn test:jest && yarn test:e2e",
|
||||
"test:jest": "jest",
|
||||
"test:server": "blitz prisma migrate deploy --preview-feature && blitz build && blitz start -p 3099",
|
||||
"test:server": "cross-env NODE_ENV=test blitz prisma migrate deploy --preview-feature && blitz build && cross-env NODE_ENV=test blitz start -p 3099",
|
||||
"test:e2e": "cross-env NODE_ENV=test start-server-and-test test:server http://localhost:3099 cy:run"
|
||||
},
|
||||
"browserslist": [
|
||||
@@ -39,13 +39,13 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@prisma/client": "2.16.0",
|
||||
"blitz": "0.30.0-canary.6",
|
||||
"@prisma/client": "2.17.0",
|
||||
"blitz": "0.30.3",
|
||||
"final-form": "4.20.1",
|
||||
"passport-auth0": "1.4.0",
|
||||
"passport-github2": "0.1.12",
|
||||
"passport-twitter": "1.0.4",
|
||||
"prisma": "2.16.0",
|
||||
"prisma": "2.17.0",
|
||||
"react": "0.0.0-experimental-3310209d0",
|
||||
"react-dom": "0.0.0-experimental-3310209d0",
|
||||
"react-error-boundary": "3.1.0",
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import React from "react"
|
||||
import {RouterContext, BlitzRouter} from "blitz"
|
||||
import {render as defaultRender} from "@testing-library/react"
|
||||
import {renderHook as defaultRenderHook} from "@testing-library/react-hooks"
|
||||
@@ -67,6 +66,7 @@ export const mockRouter: BlitzRouter = {
|
||||
params: {},
|
||||
query: {},
|
||||
isReady: true,
|
||||
isLocaleDomain: false,
|
||||
push: jest.fn(),
|
||||
replace: jest.fn(),
|
||||
reload: jest.fn(),
|
||||
|
||||
@@ -12,7 +12,7 @@ declare module "blitz" {
|
||||
isAuthorized: SimpleRolesIsAuthorized<Role>
|
||||
PublicData: {
|
||||
userId: User["id"]
|
||||
roles: Role[]
|
||||
role: Role
|
||||
views?: number
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ export default async function login(input: LoginInputType, {session}: Ctx) {
|
||||
// This throws an error if credentials are invalid
|
||||
const user = await authenticateUser(email, password)
|
||||
|
||||
await session.$create({userId: user.id, roles: [user.role]})
|
||||
await session.$create({userId: user.id})
|
||||
|
||||
return user
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ export default async function signup(input: SignupInputType, {session}: Ctx) {
|
||||
select: {id: true, name: true, email: true, role: true},
|
||||
})
|
||||
|
||||
await session.$create({userId: user.id, roles: [user.role]})
|
||||
await session.$create({userId: user.id})
|
||||
|
||||
return user
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@examples/custom-server",
|
||||
"version": "0.30.0-canary.6",
|
||||
"version": "0.30.3",
|
||||
"scripts": {
|
||||
"dev": "nodemon --watch server.js --exec 'blitz dev'",
|
||||
"build": "blitz build",
|
||||
@@ -40,10 +40,10 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@prisma/client": "2.16.0",
|
||||
"blitz": "0.30.0-canary.6",
|
||||
"@prisma/client": "2.17.0",
|
||||
"blitz": "0.30.3",
|
||||
"final-form": "4.20.1",
|
||||
"prisma": "2.16.0",
|
||||
"prisma": "2.17.0",
|
||||
"react": "0.0.0-experimental-3310209d0",
|
||||
"react-dom": "0.0.0-experimental-3310209d0",
|
||||
"react-error-boundary": "2.3.2",
|
||||
|
||||
@@ -67,6 +67,7 @@ export const mockRouter: BlitzRouter = {
|
||||
params: {},
|
||||
query: {},
|
||||
isReady: true,
|
||||
isLocaleDomain: false,
|
||||
push: jest.fn(),
|
||||
replace: jest.fn(),
|
||||
reload: jest.fn(),
|
||||
|
||||
@@ -10,7 +10,6 @@ declare module "blitz" {
|
||||
isAuthorized: typeof simpleRolesIsAuthorized
|
||||
PublicData: {
|
||||
userId: number
|
||||
roles: string[]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ export default async function login(input: LoginInputType, { session }: Ctx) {
|
||||
// This throws an error if credentials are invalid
|
||||
const user = await authenticateUser(email, password)
|
||||
|
||||
await session.$create({ userId: user.id, roles: [user.role] })
|
||||
await session.$create({ userId: user.id })
|
||||
|
||||
return user
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ export default async function signup(input: SignupInputType, { session }: Ctx) {
|
||||
)
|
||||
console.log("Create user result:", user)
|
||||
|
||||
await session.$create({ userId: user.id, roles: [user.role] })
|
||||
await session.$create({ userId: user.id })
|
||||
|
||||
return user
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@examples/fauna",
|
||||
"version": "0.30.0-canary.6",
|
||||
"version": "0.30.3",
|
||||
"scripts": {
|
||||
"dev": "blitz dev",
|
||||
"build": "blitz build",
|
||||
@@ -28,7 +28,7 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"blitz": "0.30.0-canary.6",
|
||||
"blitz": "0.30.3",
|
||||
"final-form": "4.20.1",
|
||||
"graphql": "15.5.0",
|
||||
"graphql-request": "3.4.0",
|
||||
|
||||
@@ -78,6 +78,7 @@ export const mockRouter: BlitzRouter = {
|
||||
params: {},
|
||||
query: {},
|
||||
isReady: true,
|
||||
isLocaleDomain: false,
|
||||
push: jest.fn(),
|
||||
replace: jest.fn(),
|
||||
reload: jest.fn(),
|
||||
|
||||
@@ -9,7 +9,6 @@ declare module "blitz" {
|
||||
isAuthorized: typeof simpleRolesIsAuthorized
|
||||
PublicData: {
|
||||
userId: string
|
||||
roles: string[]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "no-prisma",
|
||||
"version": "0.30.0-canary.6",
|
||||
"version": "0.30.3",
|
||||
"scripts": {
|
||||
"dev": "blitz dev",
|
||||
"build": "blitz build",
|
||||
@@ -27,7 +27,7 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"blitz": "0.30.0-canary.6",
|
||||
"blitz": "0.30.3",
|
||||
"knex": "0.21.16",
|
||||
"react": "0.0.0-experimental-3310209d0",
|
||||
"react-dom": "0.0.0-experimental-3310209d0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@examples/plain-js",
|
||||
"version": "0.30.0-canary.6",
|
||||
"version": "0.30.3",
|
||||
"scripts": {
|
||||
"dev": "blitz dev",
|
||||
"build": "blitz prisma migrate deploy --preview-feature && blitz build",
|
||||
@@ -30,9 +30,9 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@prisma/client": "2.16.0",
|
||||
"blitz": "0.30.0-canary.6",
|
||||
"prisma": "2.16.0",
|
||||
"@prisma/client": "2.17.0",
|
||||
"blitz": "0.30.3",
|
||||
"prisma": "2.17.0",
|
||||
"react": "0.0.0-experimental-3310209d0",
|
||||
"react-dom": "0.0.0-experimental-3310209d0"
|
||||
},
|
||||
|
||||
@@ -17,33 +17,32 @@ generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------
|
||||
|
||||
model User {
|
||||
id Int @default(autoincrement()) @id
|
||||
id Int @id @default(autoincrement())
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
email String @unique
|
||||
name String?
|
||||
role String?
|
||||
storeId Int?
|
||||
email String @unique
|
||||
name String?
|
||||
role String?
|
||||
storeId Int?
|
||||
}
|
||||
|
||||
model Product {
|
||||
id Int @default(autoincrement()) @id
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
id Int @id @default(autoincrement())
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
handle String @unique
|
||||
name String?
|
||||
description String?
|
||||
price Int?
|
||||
|
||||
variants Variant[]
|
||||
variants Variant[]
|
||||
}
|
||||
|
||||
model Variant {
|
||||
id Int @default(autoincrement()) @id
|
||||
id Int @id @default(autoincrement())
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
name String
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@examples/store",
|
||||
"version": "0.30.0-canary.6",
|
||||
"version": "0.30.3",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "blitz prisma migrate deploy --preview-feature && blitz build",
|
||||
@@ -20,10 +20,10 @@
|
||||
"trailingComma": "all"
|
||||
},
|
||||
"dependencies": {
|
||||
"@prisma/client": "2.16.0",
|
||||
"blitz": "0.30.0-canary.6",
|
||||
"@prisma/client": "2.17.0",
|
||||
"blitz": "0.30.3",
|
||||
"final-form": "4.20.1",
|
||||
"prisma": "2.16.0",
|
||||
"prisma": "2.17.0",
|
||||
"react": "0.0.0-experimental-3310209d0",
|
||||
"react-dom": "0.0.0-experimental-3310209d0",
|
||||
"react-error-boundary": "3.1.0",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": "0.30.0-canary.6",
|
||||
"version": "0.30.3",
|
||||
"packages": ["packages/*"],
|
||||
"npmClient": "yarn",
|
||||
"useWorkspaces": true,
|
||||
|
||||
@@ -77,7 +77,6 @@
|
||||
"@types/passport": "1.0.5",
|
||||
"@types/pluralize": "0.0.29",
|
||||
"@types/prettier": "2.1.6",
|
||||
"@types/npm-run": "5.0.0",
|
||||
"@types/pump": "1.1.0",
|
||||
"@types/pumpify": "1.4.1",
|
||||
"@types/react": "17.0.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@blitzjs/babel-preset",
|
||||
"version": "0.30.0-canary.6",
|
||||
"version": "0.30.3",
|
||||
"license": "MIT",
|
||||
"main": "dist/index.js",
|
||||
"typings": "dist/index.d.ts",
|
||||
@@ -36,6 +36,6 @@
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"babel-plugin-superjson-next": "0.2.1"
|
||||
"babel-plugin-superjson-next": "0.2.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
require("@testing-library/jest-dom")
|
||||
|
||||
jest.setTimeout(10000)
|
||||
|
||||
afterAll(async () => {
|
||||
try {
|
||||
await global._blitz_prismaClient.$disconnect()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "blitz",
|
||||
"description": "Blitz is a Rails-like framework for monolithic, full-stack React apps — built on Next.js",
|
||||
"version": "0.30.0-canary.6",
|
||||
"version": "0.30.3",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"clean": "rimraf dist",
|
||||
@@ -43,20 +43,20 @@
|
||||
"url": "https://github.com/blitz-js/blitz"
|
||||
},
|
||||
"dependencies": {
|
||||
"@blitzjs/babel-preset": "0.30.0-canary.6",
|
||||
"@blitzjs/cli": "0.30.0-canary.6",
|
||||
"@blitzjs/config": "0.30.0-canary.6",
|
||||
"@blitzjs/core": "0.30.0-canary.6",
|
||||
"@blitzjs/display": "0.30.0-canary.6",
|
||||
"@blitzjs/generator": "0.30.0-canary.6",
|
||||
"@blitzjs/installer": "0.30.0-canary.6",
|
||||
"@blitzjs/server": "0.30.0-canary.6",
|
||||
"@blitzjs/babel-preset": "0.30.3",
|
||||
"@blitzjs/cli": "0.30.3",
|
||||
"@blitzjs/config": "0.30.3",
|
||||
"@blitzjs/core": "0.30.3",
|
||||
"@blitzjs/display": "0.30.3",
|
||||
"@blitzjs/generator": "0.30.3",
|
||||
"@blitzjs/installer": "0.30.3",
|
||||
"@blitzjs/server": "0.30.3",
|
||||
"@testing-library/jest-dom": "5.11.9",
|
||||
"@testing-library/react": "^11.2.3",
|
||||
"@testing-library/react-hooks": "^4.0.1",
|
||||
"@types/jest": "^26.0.20",
|
||||
"envinfo": "^7.7.3",
|
||||
"eslint-config-blitz": "0.30.0-canary.6",
|
||||
"eslint-config-blitz": "0.30.3",
|
||||
"jest": "^26.6.3",
|
||||
"jest-environment-jsdom-fourteen": "^1.0.1",
|
||||
"jest-watch-typeahead": "^0.6.1",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@blitzjs/cli",
|
||||
"description": "Blitz.js CLI",
|
||||
"version": "0.30.0-canary.6",
|
||||
"version": "0.30.3",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"b": "./bin/run",
|
||||
@@ -32,14 +32,14 @@
|
||||
"/lib"
|
||||
],
|
||||
"dependencies": {
|
||||
"@blitzjs/display": "0.30.0-canary.6",
|
||||
"@blitzjs/repl": "0.30.0-canary.6",
|
||||
"@blitzjs/display": "0.30.3",
|
||||
"@blitzjs/repl": "0.30.3",
|
||||
"@oclif/command": "1.8.0",
|
||||
"@oclif/config": "1.17.0",
|
||||
"@oclif/plugin-autocomplete": "0.3.0",
|
||||
"@oclif/plugin-help": "3.2.1",
|
||||
"@oclif/plugin-not-found": "1.2.4",
|
||||
"@prisma/sdk": "2.16.0",
|
||||
"@prisma/sdk": "2.17.0",
|
||||
"@salesforce/lazy-require": "0.4.0",
|
||||
"camelcase": "^6.2.0",
|
||||
"chalk": "4.1.0",
|
||||
@@ -64,13 +64,13 @@
|
||||
"v8-compile-cache": "2.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@blitzjs/generator": "0.30.0-canary.6",
|
||||
"@blitzjs/installer": "0.30.0-canary.6",
|
||||
"@blitzjs/server": "0.30.0-canary.6",
|
||||
"@blitzjs/generator": "0.30.3",
|
||||
"@blitzjs/installer": "0.30.3",
|
||||
"@blitzjs/server": "0.30.3",
|
||||
"@oclif/dev-cli": "1.26.0",
|
||||
"@oclif/test": "1.2.8",
|
||||
"nock": "13.0.6",
|
||||
"prisma": "2.16.0",
|
||||
"prisma": "2.17.0",
|
||||
"stdout-stderr": "0.1.13"
|
||||
},
|
||||
"jest": {
|
||||
|
||||
@@ -37,6 +37,14 @@ export class Dev extends Command {
|
||||
|
||||
try {
|
||||
const dev = (await import("@blitzjs/server")).dev
|
||||
const {getConfig} = await import("@blitzjs/config")
|
||||
|
||||
const blitzConfig = getConfig()
|
||||
if (blitzConfig.cli?.clearConsoleOnBlitzDev !== false) {
|
||||
const {log} = await import("@blitzjs/display")
|
||||
log.clearConsole()
|
||||
}
|
||||
|
||||
await dev(config)
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
|
||||
@@ -140,11 +140,11 @@ export class Generate extends Command {
|
||||
# will generate the proper database model for a Task.`)}
|
||||
> blitz generate model task \\
|
||||
name:string \\
|
||||
completed:boolean:default[false] \\
|
||||
completed:boolean:default=false \\
|
||||
belongsTo:project?
|
||||
> blitz generate all tasks \\
|
||||
name:string \\
|
||||
completed:boolean:default[false] \\
|
||||
completed:boolean:default=false \\
|
||||
belongsTo:project?
|
||||
`,
|
||||
`${chalk.dim(`# Sometimes you want just a single query with no generated
|
||||
|
||||
@@ -13,6 +13,6 @@
|
||||
"noEmit": false,
|
||||
"lib": ["dom", "dom.iterable", "ES2018"]
|
||||
},
|
||||
"include": ["src/**/*", "types"],
|
||||
"include": ["src/**/*", "types", "../../types"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@blitzjs/config",
|
||||
"version": "0.30.0-canary.6",
|
||||
"version": "0.30.3",
|
||||
"description": "Loads the blitz app config",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
@@ -27,6 +27,9 @@ export interface BlitzConfig extends Record<string, unknown> {
|
||||
isomorphicResolverImports?: boolean
|
||||
reactMode?: string
|
||||
}
|
||||
cli?: {
|
||||
clearConsoleOnBlitzDev?: boolean
|
||||
}
|
||||
_meta: {
|
||||
packageName: string
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@blitzjs/core",
|
||||
"description": "Blitz.js core functionality",
|
||||
"version": "0.30.0-canary.6",
|
||||
"version": "0.30.3",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"clean": "rimraf dist",
|
||||
@@ -40,20 +40,21 @@
|
||||
"url": "https://github.com/blitz-js/blitz"
|
||||
},
|
||||
"dependencies": {
|
||||
"@blitzjs/display": "0.30.0-canary.6",
|
||||
"@blitzjs/display": "0.30.3",
|
||||
"@types/htmlescape": "^1.1.1",
|
||||
"@types/secure-password": "3.1.0",
|
||||
"b64-lite": "^1.4.0",
|
||||
"bad-behavior": "^1.0.1",
|
||||
"cookie-session": "^1.4.0",
|
||||
"cross-spawn": "7.0.3",
|
||||
"htmlescape": "^1.1.1",
|
||||
"lodash": "^4.17.20",
|
||||
"lodash-es": "^4.17.20",
|
||||
"npm-run": "^5.0.1",
|
||||
"npm-which": "^3.0.1",
|
||||
"passport": "0.4.1",
|
||||
"react-query": "2.5.12",
|
||||
"secure-password": "4.0.0",
|
||||
"superjson": "1.6.0",
|
||||
"superjson": "1.7.2",
|
||||
"zod": ">=1.0.0"
|
||||
},
|
||||
"gitHead": "d3b9fce0bdd251c2b1890793b0aa1cd77c1c0922"
|
||||
|
||||
@@ -1,11 +1,97 @@
|
||||
import React from "react"
|
||||
import {AppProps} from "."
|
||||
import React, {ComponentPropsWithoutRef, useEffect} from "react"
|
||||
import {Head} from "./nextjs"
|
||||
import {publicDataStore} from "./public-data-store"
|
||||
import {useAuthorizeIf} from "./supertokens"
|
||||
import {AppProps, BlitzPage} from "./types"
|
||||
|
||||
export function withBlitzAppRoot(WrappedComponent: React.ComponentType<any>) {
|
||||
const BlitzAppRoot = (props: AppProps) => {
|
||||
// props comes afterwards so the can override the default ones.
|
||||
return <WrappedComponent {...(props as any)} />
|
||||
const customCSS = `
|
||||
body::before {
|
||||
content: "";
|
||||
display: block;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 99999;
|
||||
background-color: white;
|
||||
}
|
||||
BlitzAppRoot.displayName = `BlitzAppRoot`
|
||||
return BlitzAppRoot
|
||||
|
||||
.blitz-first-render-complete body::before {
|
||||
display: none;
|
||||
}
|
||||
`
|
||||
const noscriptCSS = `
|
||||
body::before {
|
||||
content: none
|
||||
}
|
||||
`
|
||||
|
||||
const NoPageFlicker = () => {
|
||||
return (
|
||||
<Head>
|
||||
<style dangerouslySetInnerHTML={{__html: customCSS}} />
|
||||
<noscript>
|
||||
<style dangerouslySetInnerHTML={{__html: noscriptCSS}} />
|
||||
</noscript>
|
||||
</Head>
|
||||
)
|
||||
}
|
||||
|
||||
export function withBlitzInnerWrapper(Page: BlitzPage) {
|
||||
const BlitzInnerRoot = (props: ComponentPropsWithoutRef<BlitzPage>) => {
|
||||
useAuthorizeIf(Page.authenticate === true)
|
||||
|
||||
return <Page {...props} />
|
||||
}
|
||||
for (let [key, value] of Object.entries(Page)) {
|
||||
;(BlitzInnerRoot as any)[key] = value
|
||||
}
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
BlitzInnerRoot.displayName = `BlitzInnerRoot`
|
||||
}
|
||||
return BlitzInnerRoot
|
||||
}
|
||||
|
||||
export function withBlitzAppRoot(UserAppRoot: React.ComponentType<any>) {
|
||||
const BlitzOuterRoot = (props: AppProps) => {
|
||||
if (typeof window !== "undefined") {
|
||||
if (publicDataStore.getData().userId) {
|
||||
if (props.Component.redirectAuthenticatedTo) {
|
||||
window.location.replace(props.Component.redirectAuthenticatedTo)
|
||||
}
|
||||
} else {
|
||||
const authenticate = props.Component.authenticate
|
||||
if (
|
||||
authenticate &&
|
||||
typeof authenticate === "object" &&
|
||||
typeof authenticate.redirectTo === "string"
|
||||
) {
|
||||
const url = new URL(authenticate.redirectTo, window.location.href)
|
||||
url.searchParams.append("next", window.location.pathname)
|
||||
window.location.replace(url.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const noPageFlicker =
|
||||
props.Component.suppressFirstRenderFlicker ||
|
||||
props.Component.authenticate !== undefined ||
|
||||
props.Component.redirectAuthenticatedTo
|
||||
|
||||
useEffect(() => {
|
||||
document.documentElement.classList.add("blitz-first-render-complete")
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
{noPageFlicker && <NoPageFlicker />}
|
||||
<UserAppRoot {...props} Component={withBlitzInnerWrapper(props.Component)} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
BlitzOuterRoot.displayName = `BlitzOuterRoot`
|
||||
}
|
||||
return BlitzOuterRoot
|
||||
}
|
||||
|
||||
@@ -11,13 +11,13 @@ export type BlitzRuntimeData = {
|
||||
export function _getBlitzRuntimeData(): BlitzRuntimeData {
|
||||
const config = getConfig()
|
||||
return {
|
||||
sessionCookiePrefix: config._meta.packageName.replace(/[^a-zA-Z0-9-_]/g, "_"),
|
||||
sessionCookiePrefix: (config._meta.packageName || "blitz").replace(/[^a-zA-Z0-9-_]/g, "_"),
|
||||
suspenseEnabled: config.experimental?.reactMode !== "legacy",
|
||||
}
|
||||
}
|
||||
|
||||
export function getBlitzRuntimeData() {
|
||||
if (isClient && process.env.JEST_WORKER_ID === "undefined") {
|
||||
if (isClient && !process.env.JEST_WORKER_ID) {
|
||||
return window.__BLITZ_DATA__
|
||||
} else {
|
||||
if (!global.__BLITZ_DATA__) {
|
||||
@@ -29,9 +29,7 @@ export function getBlitzRuntimeData() {
|
||||
|
||||
// Automatically deserialize __BLITZ_DATA__ in a browser environment
|
||||
if (isClient) {
|
||||
if (document.getElementById("__BLITZ_DATA__")) {
|
||||
deserializeAndSetBlitzDataOnWindow()
|
||||
}
|
||||
deserializeAndSetBlitzDataOnWindow()
|
||||
}
|
||||
|
||||
export function deserializeAndSetBlitzDataOnWindow() {
|
||||
@@ -41,7 +39,10 @@ export function deserializeAndSetBlitzDataOnWindow() {
|
||||
)
|
||||
window.__BLITZ_DATA__ = data
|
||||
} catch (e) {
|
||||
console.error("Error deserializing __BLITZ__DATA__", e)
|
||||
console.error(
|
||||
"Error deserializing __BLITZ__DATA__. Make sure you have a custom _document.js/tsx page that uses <BlitzScript/>",
|
||||
e,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,10 @@ export const COOKIE_SESSION_TOKEN = () =>
|
||||
export const COOKIE_REFRESH_TOKEN = () =>
|
||||
`${getBlitzRuntimeData().sessionCookiePrefix}_sIdRefreshToken`
|
||||
export const COOKIE_CSRF_TOKEN = () => `${getBlitzRuntimeData().sessionCookiePrefix}_sAntiCsrfToken`
|
||||
// TODO remove before 1.0 -
|
||||
// This is here for legacy compatability (misspelling)
|
||||
export const COOKIE_LEGACY_CSRF_TOKEN = () =>
|
||||
`${getBlitzRuntimeData().sessionCookiePrefix}_sAntiCrfToken`
|
||||
export const COOKIE_PUBLIC_DATA_TOKEN = () =>
|
||||
`${getBlitzRuntimeData().sessionCookiePrefix}_sPublicDataToken`
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import SuperJson from "superjson"
|
||||
|
||||
const errorProps = ["message", "code", "meta"]
|
||||
const errorProps = ["name", "message", "code", "statusCode", "meta"]
|
||||
if (process.env.JEST_WORKER_ID === undefined) {
|
||||
SuperJson.allowErrorProps(...errorProps)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import {NextComponentType, NextPage, NextPageContext} from "next"
|
||||
import {AppProps as NextAppProps} from "next/app"
|
||||
|
||||
export * from "./nextjs"
|
||||
export * from "./types"
|
||||
export * from "./errors"
|
||||
export * from "./constants"
|
||||
@@ -30,49 +28,18 @@ export {
|
||||
export {
|
||||
getAntiCSRFToken,
|
||||
useSession,
|
||||
SessionConfig, // new
|
||||
useAuthenticatedSession,
|
||||
useAuthorize,
|
||||
useRedirectAuthenticated,
|
||||
SessionConfig,
|
||||
SessionContext,
|
||||
AuthenticatedSessionContext,
|
||||
ClientSession,
|
||||
AuthenticatedClientSession,
|
||||
} from "./supertokens"
|
||||
|
||||
export {SecurePassword, hash256, generateToken} from "./auth-utils"
|
||||
|
||||
// --------------------
|
||||
// Exports from Next.js
|
||||
// --------------------
|
||||
export {default as Head} from "next/head"
|
||||
|
||||
export {default as Link, LinkProps} from "next/link"
|
||||
|
||||
export {default as Router} from "next/router"
|
||||
|
||||
export {default as Image, ImageProps} from "next/image"
|
||||
|
||||
export {
|
||||
default as Document,
|
||||
Html,
|
||||
Head as DocumentHead,
|
||||
Main,
|
||||
DocumentContext,
|
||||
DocumentInitialProps,
|
||||
} from "next/document"
|
||||
|
||||
export {default as dynamic} from "next/dynamic"
|
||||
|
||||
export {default as ErrorComponent, ErrorProps} from "next/error"
|
||||
|
||||
export {default as getConfig} from "next/config"
|
||||
|
||||
export type BlitzComponentType<C = NextPageContext, IP = {}, P = {}> = NextComponentType<C, IP, P>
|
||||
|
||||
export interface AppProps<P = {}> extends NextAppProps<P> {
|
||||
Component: BlitzComponentType<NextPageContext, any, P> & {
|
||||
getLayout?: (component: JSX.Element) => JSX.Element
|
||||
}
|
||||
}
|
||||
export type BlitzPage<P = {}, IP = P> = NextPage<P, IP> & {
|
||||
getLayout?: (component: JSX.Element) => JSX.Element
|
||||
}
|
||||
export {isLocalhost} from "./utils/index"
|
||||
export {prettyMs} from "./utils/pretty-ms"
|
||||
|
||||
|
||||
25
packages/core/src/nextjs.ts
Normal file
25
packages/core/src/nextjs.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
// --------------------
|
||||
// Exports from Next.js
|
||||
// --------------------
|
||||
export {default as Head} from "next/head"
|
||||
|
||||
export {default as Link, LinkProps} from "next/link"
|
||||
|
||||
export {default as Router} from "next/router"
|
||||
|
||||
export {default as Image, ImageProps} from "next/image"
|
||||
|
||||
export {
|
||||
default as Document,
|
||||
Html,
|
||||
Head as DocumentHead,
|
||||
Main,
|
||||
DocumentContext,
|
||||
DocumentInitialProps,
|
||||
} from "next/document"
|
||||
|
||||
export {default as dynamic} from "next/dynamic"
|
||||
|
||||
export {default as ErrorComponent, ErrorProps} from "next/error"
|
||||
|
||||
export {default as getConfig} from "next/config"
|
||||
@@ -94,11 +94,11 @@ export function passportAuth(config: BlitzPassportConfig) {
|
||||
}
|
||||
assert(
|
||||
typeof result === "object" && result !== null,
|
||||
`Your '${strategyName}' passport verify callback returned empty data. Ensure you call 'done(null, {publicData: {userId: 1, roles: ['myRole']}})')`,
|
||||
`Your '${strategyName}' passport verify callback returned empty data. Ensure you call 'done(null, {publicData: {userId: 1}})' along with any other publicData fields you need)`,
|
||||
)
|
||||
assert(
|
||||
(result as any).publicData,
|
||||
`'publicData' is missing from your '${strategyName}' passport verify callback. Ensure you call 'done(null, {publicData: {userId: 1, roles: ['myRole']}})')`,
|
||||
`'publicData' is missing from your '${strategyName}' passport verify callback. Ensure you call 'done(null, {publicData: {userId: 1}})' along with any other publicData fields you need)`,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {exec} from "npm-run"
|
||||
import {spawn} from "cross-spawn"
|
||||
import which from "npm-which"
|
||||
|
||||
interface Constructor<T = unknown> {
|
||||
new (...args: never[]): T
|
||||
@@ -33,15 +34,15 @@ export const enhancePrisma = <TPrismaClientCtor extends Constructor>(
|
||||
"You are calling db.$reset() in a production environment. We think you probably didn't mean to do that, so we are throwing this error instead of destroying your life's work.",
|
||||
)
|
||||
}
|
||||
await new Promise<void>((res, rej) =>
|
||||
exec("prisma migrate reset --force --skip-generate --preview-feature", function (err) {
|
||||
if (err) {
|
||||
rej(err)
|
||||
} else {
|
||||
res()
|
||||
}
|
||||
}),
|
||||
)
|
||||
const prismaBin = which(process.cwd()).sync("prisma")
|
||||
await new Promise((res, rej) => {
|
||||
const process = spawn(
|
||||
prismaBin,
|
||||
["migrate", "reset", "--force", "--skip-generate", "--preview-feature"],
|
||||
{stdio: "inherit"},
|
||||
)
|
||||
process.on("exit", (code) => (code === 0 ? res(0) : rej(code)))
|
||||
})
|
||||
global._blitz_prismaClient.$disconnect()
|
||||
}
|
||||
|
||||
|
||||
@@ -6,8 +6,7 @@ import {parsePublicDataToken} from "./utils/tokens"
|
||||
|
||||
class PublicDataStore {
|
||||
private eventKey = `${LOCALSTORAGE_PREFIX}publicDataUpdated`
|
||||
// TODO remove `as any` after https://github.com/blitz-js/blitz/pull/1788 merged
|
||||
readonly emptyPublicData: PublicData = {userId: null, roles: []} as any
|
||||
readonly emptyPublicData: PublicData = {userId: null}
|
||||
readonly observable = BadBehavior<PublicData>()
|
||||
|
||||
constructor() {
|
||||
|
||||
@@ -91,7 +91,7 @@ export const executeRpcCall = <TInput, TResult>(
|
||||
}
|
||||
if (response.headers.get(HEADER_SESSION_CREATED)) {
|
||||
clientDebug("Session created")
|
||||
queryCache.clear()
|
||||
await queryCache.invalidateQueries("")
|
||||
}
|
||||
if (response.headers.get(HEADER_CSRF_ERROR)) {
|
||||
const err = new CSRFTokenMismatchError()
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import {useState} from "react"
|
||||
import {COOKIE_CSRF_TOKEN} from "./constants"
|
||||
import {useEffect, useState} from "react"
|
||||
import {getBlitzRuntimeData} from "./blitz-data"
|
||||
import {COOKIE_CSRF_TOKEN, COOKIE_LEGACY_CSRF_TOKEN} from "./constants"
|
||||
import {AuthenticationError} from "./errors"
|
||||
import {Ctx} from "./middleware"
|
||||
import {publicDataStore} from "./public-data-store"
|
||||
import {IsAuthorizedArgs, PublicData} from "./types"
|
||||
import {isServer} from "./utils"
|
||||
import {readCookie} from "./utils/cookie"
|
||||
import {useIsomorphicLayoutEffect} from "./utils/hooks"
|
||||
|
||||
export interface SessionModel extends Record<any, any> {
|
||||
handle: string
|
||||
@@ -21,6 +23,7 @@ export type SessionConfig = {
|
||||
method?: "essential" | "advanced"
|
||||
sameSite?: "none" | "lax" | "strict"
|
||||
domain?: string
|
||||
publicDataKeysToSyncAcrossSessions?: string[]
|
||||
getSession: (handle: string) => Promise<SessionModel | null>
|
||||
getSessions: (userId: PublicData["userId"]) => Promise<SessionModel[]>
|
||||
createSession: (session: SessionModel) => Promise<SessionModel>
|
||||
@@ -29,7 +32,7 @@ export type SessionConfig = {
|
||||
isAuthorized: (data: {ctx: Ctx; args: any[]}) => boolean
|
||||
}
|
||||
|
||||
export interface SessionContextBase extends PublicData {
|
||||
export interface SessionContextBase {
|
||||
$handle: string | null
|
||||
$publicData: unknown
|
||||
$authorize(...args: IsAuthorizedArgs): asserts this is AuthenticatedSessionContext
|
||||
@@ -45,40 +48,86 @@ export interface SessionContextBase extends PublicData {
|
||||
}
|
||||
|
||||
// Could be anonymous
|
||||
export interface SessionContext extends SessionContextBase {
|
||||
export interface SessionContext extends SessionContextBase, Partial<PublicData> {
|
||||
userId: PublicData["userId"] | null
|
||||
$publicData: Partial<PublicData>
|
||||
}
|
||||
|
||||
export interface AuthenticatedSessionContext extends SessionContextBase {
|
||||
export interface AuthenticatedSessionContext extends SessionContextBase, PublicData {
|
||||
userId: PublicData["userId"]
|
||||
$publicData: PublicData
|
||||
}
|
||||
|
||||
export const getAntiCSRFToken = () => readCookie(COOKIE_CSRF_TOKEN())
|
||||
export const getAntiCSRFToken = () =>
|
||||
readCookie(COOKIE_CSRF_TOKEN()) || readCookie(COOKIE_LEGACY_CSRF_TOKEN())
|
||||
|
||||
export interface PublicDataWithLoading extends PublicData {
|
||||
export interface ClientSession extends Partial<PublicData> {
|
||||
userId: PublicData["userId"] | null
|
||||
isLoading: boolean
|
||||
}
|
||||
|
||||
export interface AuthenticatedClientSession extends PublicData {
|
||||
isLoading: boolean
|
||||
}
|
||||
|
||||
interface UseSessionOptions {
|
||||
initialPublicData?: PublicData
|
||||
suspense?: boolean | null
|
||||
}
|
||||
|
||||
export const useSession = (options: UseSessionOptions = {}): PublicDataWithLoading => {
|
||||
const [publicData, setPublicData] = useState(
|
||||
options.initialPublicData ?? publicDataStore.emptyPublicData,
|
||||
)
|
||||
const [isLoading, setIsLoading] = useState(!options.initialPublicData)
|
||||
export const useSession = (options: UseSessionOptions = {}): ClientSession => {
|
||||
const suspense = options?.suspense ?? getBlitzRuntimeData().suspenseEnabled
|
||||
|
||||
useIsomorphicLayoutEffect(() => {
|
||||
let initialState: ClientSession
|
||||
if (options.initialPublicData) {
|
||||
initialState = {...options.initialPublicData, isLoading: false}
|
||||
} else if (suspense) {
|
||||
if (isServer) {
|
||||
throw new Promise((_) => {})
|
||||
} else {
|
||||
initialState = {...publicDataStore.getData(), isLoading: false}
|
||||
}
|
||||
} else {
|
||||
initialState = {...publicDataStore.emptyPublicData, isLoading: true}
|
||||
}
|
||||
|
||||
const [session, setSession] = useState(initialState)
|
||||
|
||||
useEffect(() => {
|
||||
// Initialize on mount
|
||||
setPublicData(publicDataStore.getData())
|
||||
setIsLoading(false)
|
||||
const subscription = publicDataStore.observable.subscribe(setPublicData)
|
||||
setSession({...publicDataStore.getData(), isLoading: false})
|
||||
const subscription = publicDataStore.observable.subscribe((data) =>
|
||||
setSession({...data, isLoading: false}),
|
||||
)
|
||||
return subscription.unsubscribe
|
||||
}, [])
|
||||
|
||||
return {...publicData, isLoading}
|
||||
return session
|
||||
}
|
||||
|
||||
export const useAuthenticatedSession = (
|
||||
options: UseSessionOptions = {},
|
||||
): AuthenticatedClientSession => {
|
||||
useAuthorize()
|
||||
return useSession(options)
|
||||
}
|
||||
|
||||
export const useAuthorize = () => {
|
||||
useAuthorizeIf(true)
|
||||
}
|
||||
|
||||
export const useAuthorizeIf = (condition?: boolean) => {
|
||||
useEffect(() => {
|
||||
if (condition && !publicDataStore.getData().userId) {
|
||||
const error = new AuthenticationError()
|
||||
delete error.stack
|
||||
throw error
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const useRedirectAuthenticated = (to: string) => {
|
||||
if (typeof window !== "undefined" && publicDataStore.getData().userId) {
|
||||
window.location.replace(to)
|
||||
}
|
||||
}
|
||||
|
||||
26
packages/core/src/suspense.ts
Normal file
26
packages/core/src/suspense.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
export const suspend = <T>(promise: Promise<T>) => {
|
||||
let result: any
|
||||
let status = "pending"
|
||||
|
||||
const suspender = promise.then(
|
||||
(response) => {
|
||||
status = "success"
|
||||
result = response
|
||||
},
|
||||
(error) => {
|
||||
status = "error"
|
||||
result = error
|
||||
},
|
||||
)
|
||||
|
||||
return (): T => {
|
||||
switch (status) {
|
||||
case "pending":
|
||||
throw suspender
|
||||
case "error":
|
||||
throw result
|
||||
default:
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,13 @@
|
||||
import {IncomingMessage, ServerResponse} from "http"
|
||||
import {AppProps as NextAppProps} from "next/app"
|
||||
import {NextRouter} from "next/router"
|
||||
import {NextApiRequest, NextApiResponse, NextComponentType, NextPageContext} from "next/types"
|
||||
import {
|
||||
NextApiRequest,
|
||||
NextApiResponse,
|
||||
NextComponentType,
|
||||
NextPage,
|
||||
NextPageContext,
|
||||
} from "next/types"
|
||||
import {AuthenticateOptions, Strategy} from "passport"
|
||||
import {MutateOptions, MutationResult} from "react-query"
|
||||
import {BlitzRuntimeData} from "./blitz-data"
|
||||
@@ -23,9 +30,20 @@ export {
|
||||
} from "next"
|
||||
export type BlitzApiRequest = NextApiRequest
|
||||
export type BlitzApiResponse = NextApiResponse
|
||||
export type BlitzComponentType = NextComponentType
|
||||
export type BlitzPageContext = NextPageContext
|
||||
|
||||
export type BlitzComponentType<C = NextPageContext, IP = {}, P = {}> = NextComponentType<C, IP, P>
|
||||
|
||||
export interface AppProps<P = {}> extends NextAppProps<P> {
|
||||
Component: BlitzComponentType<NextPageContext, any, P> & BlitzPage
|
||||
}
|
||||
export type BlitzPage<P = {}, IP = P> = NextPage<P, IP> & {
|
||||
getLayout?: (component: JSX.Element) => JSX.Element
|
||||
authenticate?: boolean | {redirectTo?: string}
|
||||
suppressFirstRenderFlicker?: boolean
|
||||
redirectAuthenticatedTo?: string
|
||||
}
|
||||
|
||||
export interface BlitzRouter extends NextRouter {
|
||||
query: ReturnType<typeof useRouterQuery>
|
||||
params: ReturnType<typeof useParams>
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
usePaginatedQuery as usePaginatedReactQuery,
|
||||
useQuery as useReactQuery,
|
||||
} from "react-query"
|
||||
import {isClient} from "./utils"
|
||||
import {useSession} from "./supertokens"
|
||||
import {FirstParam, PromiseReturnType, QueryFn} from "./types"
|
||||
import {useRouter} from "./use-router"
|
||||
@@ -20,6 +21,13 @@ import {
|
||||
useDefaultQueryConfig,
|
||||
} from "./utils/react-query-utils"
|
||||
|
||||
type QueryLazyOptions = {suspense: unknown} | {enabled: unknown}
|
||||
type QueryNonLazyOptions =
|
||||
| {suspense: true; enabled?: never}
|
||||
| {suspense?: never; enabled: true}
|
||||
| {suspense: true; enabled: true}
|
||||
| {suspense?: never; enabled?: never}
|
||||
|
||||
// -------------------------
|
||||
// useQuery
|
||||
// -------------------------
|
||||
@@ -28,17 +36,30 @@ type RestQueryResult<TResult> = Omit<QueryResult<TResult>, "data"> & QueryCacheF
|
||||
export function useQuery<T extends QueryFn, TResult = PromiseReturnType<T>>(
|
||||
queryFn: T,
|
||||
params: FirstParam<T>,
|
||||
options?: QueryOptions<TResult>,
|
||||
): [TResult, RestQueryResult<TResult>] {
|
||||
options?: QueryOptions<TResult> & QueryNonLazyOptions,
|
||||
): [TResult, RestQueryResult<TResult>]
|
||||
export function useQuery<T extends QueryFn, TResult = PromiseReturnType<T>>(
|
||||
queryFn: T,
|
||||
params: FirstParam<T>,
|
||||
options: QueryOptions<TResult> & QueryLazyOptions,
|
||||
): [TResult | undefined, RestQueryResult<TResult>]
|
||||
export function useQuery<T extends QueryFn, TResult = PromiseReturnType<T>>(
|
||||
queryFn: T,
|
||||
params: FirstParam<T>,
|
||||
options: QueryOptions<TResult> = {},
|
||||
) {
|
||||
if (typeof queryFn === "undefined") {
|
||||
throw new Error("useQuery is missing the first argument - it must be a query function")
|
||||
}
|
||||
|
||||
// TODO - useSession here is a tempory fix for logout query invalidation until RQ v3
|
||||
// https://github.com/blitz-js/blitz/issues/1711
|
||||
// NOTE: bug did not present in local dev build. Only via npm bundle
|
||||
useSession()
|
||||
const routerIsReady = useRouter().isReady
|
||||
const suspense =
|
||||
options?.enabled === false || options?.enabled === null ? false : options?.suspense
|
||||
const session = useSession({suspense})
|
||||
if (session.isLoading) {
|
||||
options.enabled = false
|
||||
}
|
||||
|
||||
const routerIsReady = useRouter().isReady && isClient
|
||||
const enhancedResolverRpcClient = sanitize(queryFn)
|
||||
const queryKey = getQueryKey(queryFn, params)
|
||||
|
||||
@@ -59,7 +80,7 @@ export function useQuery<T extends QueryFn, TResult = PromiseReturnType<T>>(
|
||||
...getQueryCacheFunctions<FirstParam<T>, TResult, T>(queryFn, params),
|
||||
}
|
||||
|
||||
return [data as TResult, rest as RestQueryResult<TResult>]
|
||||
return [data, rest as RestQueryResult<TResult>]
|
||||
}
|
||||
|
||||
// -------------------------
|
||||
@@ -71,14 +92,29 @@ type RestPaginatedResult<TResult> = Omit<PaginatedQueryResult<TResult>, "resolve
|
||||
export function usePaginatedQuery<T extends QueryFn, TResult = PromiseReturnType<T>>(
|
||||
queryFn: T,
|
||||
params: FirstParam<T>,
|
||||
options?: QueryOptions<TResult>,
|
||||
): [TResult, RestPaginatedResult<TResult>] {
|
||||
options?: QueryOptions<TResult> & QueryNonLazyOptions,
|
||||
): [TResult, RestPaginatedResult<TResult>]
|
||||
export function usePaginatedQuery<T extends QueryFn, TResult = PromiseReturnType<T>>(
|
||||
queryFn: T,
|
||||
params: FirstParam<T>,
|
||||
options: QueryOptions<TResult> & QueryLazyOptions,
|
||||
): [TResult | undefined, RestPaginatedResult<TResult>]
|
||||
export function usePaginatedQuery<T extends QueryFn, TResult = PromiseReturnType<T>>(
|
||||
queryFn: T,
|
||||
params: FirstParam<T>,
|
||||
options: QueryOptions<TResult> = {},
|
||||
) {
|
||||
if (typeof queryFn === "undefined") {
|
||||
throw new Error("usePaginatedQuery is missing the first argument - it must be a query function")
|
||||
}
|
||||
|
||||
// TODO - useSession here is a tempory fix for logout query invalidation until RQ v3
|
||||
useSession()
|
||||
const suspense =
|
||||
options?.enabled === false || options?.enabled === null ? false : options?.suspense
|
||||
const session = useSession({suspense})
|
||||
if (session.isLoading) {
|
||||
options.enabled = false
|
||||
}
|
||||
|
||||
const routerIsReady = useRouter().isReady
|
||||
const enhancedResolverRpcClient = sanitize(queryFn)
|
||||
const queryKey = getQueryKey(queryFn, params)
|
||||
@@ -100,7 +136,7 @@ export function usePaginatedQuery<T extends QueryFn, TResult = PromiseReturnType
|
||||
...getQueryCacheFunctions<FirstParam<T>, TResult, T>(queryFn, params),
|
||||
}
|
||||
|
||||
return [resolvedData as TResult, rest as RestPaginatedResult<TResult>]
|
||||
return [resolvedData, rest as RestPaginatedResult<TResult>]
|
||||
}
|
||||
|
||||
// -------------------------
|
||||
@@ -108,7 +144,7 @@ export function usePaginatedQuery<T extends QueryFn, TResult = PromiseReturnType
|
||||
// -------------------------
|
||||
type RestInfiniteResult<TResult, TMoreVariable> = Omit<
|
||||
InfiniteQueryResult<TResult, TMoreVariable>,
|
||||
"resolvedData"
|
||||
"data"
|
||||
> &
|
||||
QueryCacheFunctions<TResult>
|
||||
|
||||
@@ -127,13 +163,36 @@ export function useInfiniteQuery<
|
||||
queryFn: T,
|
||||
params: (fetchMoreResult: TFetchMoreResult) => FirstParam<T>,
|
||||
options: InfiniteQueryConfig<TResult, TFetchMoreResult>,
|
||||
): [TResult[], RestInfiniteResult<TResult, TFetchMoreResult>] {
|
||||
): [TResult[], RestInfiniteResult<TResult, TFetchMoreResult> & QueryNonLazyOptions]
|
||||
export function useInfiniteQuery<
|
||||
T extends QueryFn,
|
||||
TFetchMoreResult = any,
|
||||
TResult = PromiseReturnType<T>
|
||||
>(
|
||||
queryFn: T,
|
||||
params: (fetchMoreResult: TFetchMoreResult) => FirstParam<T>,
|
||||
options: InfiniteQueryConfig<TResult, TFetchMoreResult> & QueryLazyOptions,
|
||||
): [TResult[] | undefined, RestInfiniteResult<TResult, TFetchMoreResult>]
|
||||
export function useInfiniteQuery<
|
||||
T extends QueryFn,
|
||||
TFetchMoreResult = any,
|
||||
TResult = PromiseReturnType<T>
|
||||
>(
|
||||
queryFn: T,
|
||||
params: (fetchMoreResult: TFetchMoreResult) => FirstParam<T>,
|
||||
options: InfiniteQueryConfig<TResult, TFetchMoreResult>,
|
||||
) {
|
||||
if (typeof queryFn === "undefined") {
|
||||
throw new Error("useInfiniteQuery is missing the first argument - it must be a query function")
|
||||
}
|
||||
|
||||
// TODO - useSession here is a tempory fix for logout query invalidation until RQ v3
|
||||
useSession()
|
||||
const suspense =
|
||||
options?.enabled === false || options?.enabled === null ? false : options?.suspense
|
||||
const session = useSession({suspense})
|
||||
if (session.isLoading) {
|
||||
options.enabled = false
|
||||
}
|
||||
|
||||
const routerIsReady = useRouter().isReady
|
||||
const enhancedResolverRpcClient = sanitize(queryFn)
|
||||
const queryKey = getQueryKey(queryFn, params)
|
||||
@@ -158,5 +217,5 @@ export function useInfiniteQuery<
|
||||
...getQueryCacheFunctions<FirstParam<T>, TResult, T>(queryFn, params),
|
||||
}
|
||||
|
||||
return [data as TResult[], rest as RestInfiniteResult<TResult, TFetchMoreResult>]
|
||||
return [data, rest as RestInfiniteResult<TResult, TFetchMoreResult>]
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import {queryCache, QueryKey} from "react-query"
|
||||
import {serialize} from "superjson"
|
||||
import {getBlitzRuntimeData} from "../blitz-data"
|
||||
import {EnhancedResolverRpcClient, QueryFn, Resolver} from "../types"
|
||||
import {isClient, isServer} from "."
|
||||
import {isClient} from "."
|
||||
import {requestIdleCallback} from "./request-idle-callback"
|
||||
|
||||
type MutateOptions = {
|
||||
@@ -60,13 +60,7 @@ export const validateQueryFn = <TInput, TResult>(
|
||||
export const sanitize = <TInput, TResult>(
|
||||
queryFn: Resolver<TInput, TResult> | EnhancedResolverRpcClient<TInput, TResult>,
|
||||
) => {
|
||||
if (isServer) {
|
||||
// Prevents logging garbage during static pre-rendering
|
||||
return emptyQueryFn as EnhancedResolverRpcClient<TInput, TResult>
|
||||
}
|
||||
|
||||
validateQueryFn(queryFn)
|
||||
|
||||
return queryFn as EnhancedResolverRpcClient<TInput, TResult>
|
||||
}
|
||||
|
||||
|
||||
@@ -54,8 +54,8 @@ describe("supertokens", () => {
|
||||
|
||||
expect(result.current).toEqual({
|
||||
isLoading: false,
|
||||
roles: ["foo"],
|
||||
userId: "bar",
|
||||
roles: ["foo"],
|
||||
})
|
||||
|
||||
act(() => {
|
||||
@@ -64,8 +64,8 @@ describe("supertokens", () => {
|
||||
|
||||
expect(result.current).toEqual({
|
||||
isLoading: false,
|
||||
roles: ["baz"],
|
||||
userId: "boo",
|
||||
roles: ["baz"],
|
||||
})
|
||||
})
|
||||
|
||||
@@ -86,8 +86,8 @@ describe("supertokens", () => {
|
||||
|
||||
expect(result.current).toEqual({
|
||||
isLoading: false,
|
||||
roles: ["foo"],
|
||||
userId: "bar",
|
||||
roles: ["foo"],
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -27,6 +27,7 @@ const mockRouter: NextRouter = {
|
||||
asPath: "/",
|
||||
query: {},
|
||||
isReady: true,
|
||||
isLocaleDomain: false,
|
||||
push: jest.fn(),
|
||||
replace: jest.fn(),
|
||||
reload: jest.fn(),
|
||||
|
||||
@@ -22,14 +22,15 @@ describe("useQuery", () => {
|
||||
const setupHook = (
|
||||
params: any,
|
||||
queryFn: (...args: any) => any,
|
||||
options: Parameters<typeof useQuery>[2] = {} as any,
|
||||
): [{data?: any; setQueryData?: any}, Function] => {
|
||||
let res = {}
|
||||
function TestHarness() {
|
||||
const [data, {setQueryData}] = useQuery(queryFn, params)
|
||||
const [data, {setQueryData}] = useQuery(queryFn, params, options as any)
|
||||
Object.assign(res, {data, setQueryData})
|
||||
return (
|
||||
<div id="harness">
|
||||
<span>{data ? "Ready" : "Missing Dependency"}</span>
|
||||
<span>{data ? "Ready" : "No data"}</span>
|
||||
<span>{data}</span>
|
||||
</div>
|
||||
)
|
||||
@@ -73,7 +74,27 @@ describe("useQuery", () => {
|
||||
console.error = jest.fn()
|
||||
expect(() => setupHook("test", upcase)).toThrowErrorMatchingSnapshot()
|
||||
})
|
||||
|
||||
it("suspense disabled if enabled is false", async () => {
|
||||
setupHook("test", enhanceQueryFn(upcase), {enabled: false})
|
||||
await screen.findByText("No data")
|
||||
})
|
||||
|
||||
it("suspense disabled if enabled is null", async () => {
|
||||
setupHook("test", enhanceQueryFn(upcase), {enabled: null})
|
||||
await screen.findByText("No data")
|
||||
})
|
||||
|
||||
it("suspense disabled if enabled is false and suspense set", async () => {
|
||||
setupHook("test", enhanceQueryFn(upcase), {enabled: false, suspense: true})
|
||||
await screen.findByText("No data")
|
||||
})
|
||||
})
|
||||
|
||||
// it("works with options other than enabled & suspense without type error", () => {
|
||||
// const queryFn = ((() => true) as unknown) as () => Promise<boolean>
|
||||
// useQuery(queryFn, undefined, {refetchInterval: 10000})
|
||||
// })
|
||||
})
|
||||
|
||||
describe("useInfiniteQuery", () => {
|
||||
@@ -89,7 +110,7 @@ describe("useInfiniteQuery", () => {
|
||||
Object.assign(res, {groupedData})
|
||||
return (
|
||||
<div id="harness">
|
||||
<span>{groupedData ? "Ready" : "Missing Dependency"}</span>
|
||||
<span>{groupedData ? "Ready" : "No data"}</span>
|
||||
<div>
|
||||
{groupedData.map((data: any) => (
|
||||
<div>{data}</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"include": ["src", "types", "test"],
|
||||
"include": ["src", "types", "test", "../../types"],
|
||||
"exclude": ["node_modules"],
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@blitzjs/display",
|
||||
"version": "0.30.0-canary.6",
|
||||
"version": "0.30.3",
|
||||
"description": "Display package for the Blitz CLI",
|
||||
"homepage": "https://github.com/blitz-js/blitz#readme",
|
||||
"license": "MIT",
|
||||
@@ -35,7 +35,7 @@
|
||||
"url": "git+https://github.com/blitz-js/blitz.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"@blitzjs/display": "0.30.0-canary.6",
|
||||
"@blitzjs/display": "0.30.3",
|
||||
"chalk": "^4.1.0",
|
||||
"console-table-printer": "^2.7.5",
|
||||
"ora": "^5.3.0",
|
||||
|
||||
@@ -56,6 +56,9 @@ const withX = (str: string) => {
|
||||
const withProgress = (str: string) => {
|
||||
return withCaret(str)
|
||||
}
|
||||
const withError = (str: string) => {
|
||||
return withX(c.red.bold(str))
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a branded purple message to stdout.
|
||||
@@ -77,6 +80,14 @@ const clearLine = (msg?: string) => {
|
||||
msg && process.stdout.write(msg)
|
||||
}
|
||||
|
||||
const clearConsole = () => {
|
||||
if (process.platform === "win32") {
|
||||
process.stdout.write("\x1B[2J\x1B[0f")
|
||||
} else {
|
||||
process.stdout.write("\x1B[2J\x1B[3J\x1B[H")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a red error message to stderr.
|
||||
*
|
||||
@@ -166,9 +177,7 @@ const variable = (val: any) => {
|
||||
* If the DEBUG env var is set this will write to the console
|
||||
* @param str msg
|
||||
*/
|
||||
const debug = (str: string) => {
|
||||
process.env.DEBUG && console.log(str)
|
||||
}
|
||||
const debug = require("debug")("blitz")
|
||||
|
||||
declare module globalThis {
|
||||
let _blitz_baseLogger: Logger
|
||||
@@ -210,8 +219,10 @@ export const log = {
|
||||
withCheck,
|
||||
withX,
|
||||
withProgress,
|
||||
withError,
|
||||
branded,
|
||||
clearLine,
|
||||
clearConsole,
|
||||
error,
|
||||
warning,
|
||||
meta,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "eslint-config-blitz",
|
||||
"version": "0.30.0-canary.6",
|
||||
"version": "0.30.3",
|
||||
"description": "Blitz.js eslint config",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@blitzjs/file-pipeline",
|
||||
"version": "0.30.0-canary.6",
|
||||
"version": "0.30.3",
|
||||
"description": "Display package for the Blitz CLI",
|
||||
"homepage": "https://github.com/blitz-js/blitz#readme",
|
||||
"license": "MIT",
|
||||
@@ -32,7 +32,7 @@
|
||||
"url": "git+https://github.com/blitz-js/blitz.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"@blitzjs/display": "0.30.0-canary.6",
|
||||
"@blitzjs/display": "0.30.3",
|
||||
"chalk": "^4.1.0",
|
||||
"chokidar": "3.5.1",
|
||||
"flush-write-stream": "2.0.0",
|
||||
@@ -43,6 +43,7 @@
|
||||
"parallel-transform": "1.2.0",
|
||||
"pump": "3.0.0",
|
||||
"pumpify": "2.0.1",
|
||||
"rimraf": "3.0.2",
|
||||
"through2": "4.0.2",
|
||||
"vinyl": "2.2.1",
|
||||
"vinyl-file": "3.0.0",
|
||||
|
||||
@@ -38,6 +38,7 @@ export const watch = (includePaths: string[] | string, options: chokidar.WatchOp
|
||||
fswatcher.on("add", processEvent("add"))
|
||||
fswatcher.on("change", processEvent("change"))
|
||||
fswatcher.on("unlink", processEvent("unlink"))
|
||||
fswatcher.on("error", (error) => console.log(`Watcher error: ${error}`))
|
||||
|
||||
return {stream, fswatcher}
|
||||
}
|
||||
@@ -53,8 +54,8 @@ function getWatcher(watching: boolean, cwd: string, include: string[], ignore: s
|
||||
ignoreInitial: true,
|
||||
alwaysStat: true,
|
||||
awaitWriteFinish: {
|
||||
stabilityThreshold: 2000,
|
||||
pollInterval: 100,
|
||||
stabilityThreshold: 200,
|
||||
pollInterval: 50,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
13
packages/file-pipeline/src/helpers/rimraf-promise.ts
Normal file
13
packages/file-pipeline/src/helpers/rimraf-promise.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import rimrafCallback, {Options} from "rimraf"
|
||||
|
||||
export function rimraf(f: string, opts: Options = {}) {
|
||||
return new Promise<void>((res, rej) => {
|
||||
rimrafCallback(f, opts, (error) => {
|
||||
if (error) {
|
||||
rej(error)
|
||||
} else {
|
||||
res()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as fs from "fs-extra"
|
||||
import {relative, resolve} from "path"
|
||||
import {transform} from "../../transform"
|
||||
import {EventedFile} from "../../types"
|
||||
import {rimraf} from "../rimraf-promise"
|
||||
|
||||
function getDestPath(folder: string, file: EventedFile) {
|
||||
return resolve(folder, relative(file.cwd, file.path))
|
||||
@@ -11,11 +11,11 @@ function getDestPath(folder: string, file: EventedFile) {
|
||||
* Deletes a file in the stream from the filesystem
|
||||
* @param folder The destination folder
|
||||
*/
|
||||
export function unlink(folder: string, unlinkFile = fs.unlink, pathExists = fs.pathExists) {
|
||||
export function unlink(folder: string, unlinkFile = rimraf) {
|
||||
return transform.file(async (file) => {
|
||||
if (file.event === "unlink" || file.event === "unlinkDir") {
|
||||
const destPath = getDestPath(folder, file)
|
||||
if (await pathExists(destPath)) await unlinkFile(destPath)
|
||||
await unlinkFile(destPath, {glob: false})
|
||||
}
|
||||
|
||||
return file
|
||||
|
||||
@@ -6,9 +6,8 @@ import {unlink} from "."
|
||||
describe("unlink", () => {
|
||||
it("should unlink the correct path", async () => {
|
||||
const unlinkFile = jest.fn(() => Promise.resolve())
|
||||
const pathExists = jest.fn(() => Promise.resolve(true))
|
||||
|
||||
const unlinkStream = unlink(normalize("/dest"), unlinkFile, pathExists)
|
||||
const unlinkStream = unlink(normalize("/dest"), unlinkFile)
|
||||
|
||||
unlinkStream.write(
|
||||
new File({
|
||||
@@ -21,10 +20,7 @@ describe("unlink", () => {
|
||||
|
||||
await take(unlinkStream, 1)
|
||||
|
||||
// Test the file exists before attempting to unlink it
|
||||
expect(pathExists).toHaveBeenCalledWith(resolve(normalize("/dest/bar/baz.tz")))
|
||||
|
||||
// Remove the correct file
|
||||
expect(unlinkFile).toHaveBeenCalledWith(resolve(normalize("/dest/bar/baz.tz")))
|
||||
expect(unlinkFile).toHaveBeenCalledWith(resolve(normalize("/dest/bar/baz.tz")), {glob: false})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -4,3 +4,4 @@ export * from "./events"
|
||||
export {transform} from "./transform"
|
||||
export {FileCache} from "./helpers/file-cache"
|
||||
export {RouteCache} from "./helpers/route-cache"
|
||||
export {rimraf} from "./helpers/rimraf-promise"
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import {ensureDir, pathExists, remove} from "fs-extra"
|
||||
import {join} from "path"
|
||||
import {Transform} from "stream"
|
||||
import {createDisplay} from "../display"
|
||||
import {ERROR_THROWN, READY} from "../events"
|
||||
import {rimraf} from "../helpers/rimraf-promise"
|
||||
import {createPipeline} from "../pipeline"
|
||||
import {pipe, through} from "../streams"
|
||||
import {Stage} from "../types"
|
||||
@@ -42,7 +43,9 @@ export async function transformFiles(
|
||||
clean: requestClean,
|
||||
} = options
|
||||
|
||||
if (requestClean) await clean(dest)
|
||||
if (requestClean) await rimraf(dest, {glob: false})
|
||||
// Fix windows EPERM issues
|
||||
if (process.platform === "win32") await rimraf(join(dest, ".next"), {glob: false})
|
||||
|
||||
const display = createDisplay()
|
||||
return await new Promise((resolve, reject) => {
|
||||
@@ -74,10 +77,3 @@ export async function transformFiles(
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async function clean(path: string) {
|
||||
if (await pathExists(path)) {
|
||||
await remove(path)
|
||||
}
|
||||
return await ensureDir(path)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@blitzjs/generator",
|
||||
"version": "0.30.0-canary.6",
|
||||
"version": "0.30.3",
|
||||
"description": "File generation for the Blitz CLI",
|
||||
"homepage": "https://github.com/blitz-js/blitz#readme",
|
||||
"license": "MIT",
|
||||
@@ -36,7 +36,7 @@
|
||||
"dependencies": {
|
||||
"@babel/core": "7.12.10",
|
||||
"@babel/plugin-transform-typescript": "7.12.1",
|
||||
"@blitzjs/display": "0.30.0-canary.6",
|
||||
"@blitzjs/display": "0.30.3",
|
||||
"@types/jscodeshift": "0.7.2",
|
||||
"chalk": "^4.1.0",
|
||||
"cross-spawn": "7.0.3",
|
||||
@@ -44,11 +44,11 @@
|
||||
"enquirer": "2.3.6",
|
||||
"fs-extra": "^9.1.0",
|
||||
"fs-readdir-recursive": "1.1.0",
|
||||
"npm-run": "^5.0.1",
|
||||
"got": "^11.8.1",
|
||||
"jscodeshift": "0.11.0",
|
||||
"mem-fs": "1.2.0",
|
||||
"mem-fs-editor": "8.0.0",
|
||||
"npm-which": "^3.0.1",
|
||||
"pluralize": "^8.0.0",
|
||||
"recast": "0.20.4",
|
||||
"username": "^5.1.0",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {log} from "@blitzjs/display"
|
||||
import {spawn} from "npm-run"
|
||||
import {spawn} from "cross-spawn"
|
||||
import which from "npm-which"
|
||||
import path from "path"
|
||||
import {Generator, GeneratorOptions} from "../generator"
|
||||
import {Field} from "../prisma/field"
|
||||
@@ -91,7 +92,8 @@ export class ModelGenerator extends Generator<ModelGeneratorOptions> {
|
||||
const shouldMigrate = await this.prismaMigratePrompt()
|
||||
if (shouldMigrate) {
|
||||
await new Promise<void>((res, rej) => {
|
||||
const child = spawn("prisma", ["migrate", "dev", "--preview-feature"], {stdio: "inherit"})
|
||||
const prismaBin = which(process.cwd()).sync("prisma")
|
||||
const child = spawn(prismaBin, ["migrate", "dev", "--preview-feature"], {stdio: "inherit"})
|
||||
child.on("exit", (code) => (code === 0 ? res() : rej()))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ const fallbackIfUndef = <T extends any>(defaultValue: T, input?: T) => {
|
||||
return input
|
||||
}
|
||||
|
||||
const defaultValueTest = /\[([\w]+)\]/
|
||||
const defaultValueTest = /=([\w]+)$/
|
||||
const builtInGenerators = ["autoincrement", "now", "uuid", "cuid"]
|
||||
|
||||
class MissingFieldNameError extends Error {}
|
||||
|
||||
@@ -66,6 +66,26 @@ Here is the starting structure of your app.
|
||||
```
|
||||
__name__
|
||||
├── app/
|
||||
│ ├── api/
|
||||
│ ├── auth/
|
||||
│ │ ├── components/
|
||||
│ │ │ ├── LoginForm.tsx
|
||||
│ │ │ └── SignupForm.tsx
|
||||
│ │ ├── mutations/
|
||||
│ │ │ ├── changePassword.ts
|
||||
│ │ │ ├── forgotPassword.test.ts
|
||||
│ │ │ ├── forgotPassword.ts
|
||||
│ │ │ ├── login.ts
|
||||
│ │ │ ├── logout.ts
|
||||
│ │ │ ├── resetPassword.test.ts
|
||||
│ │ │ ├── resetPassword.ts
|
||||
│ │ │ └── signup.ts
|
||||
│ │ ├── pages/
|
||||
│ │ │ ├── forgot-password.tsx
|
||||
│ │ │ ├── login.tsx
|
||||
│ │ │ ├── reset-password.tsx
|
||||
│ │ │ └── signup.tsx
|
||||
│ │ └── validations.ts
|
||||
│ ├── core/
|
||||
│ │ ├── components/
|
||||
│ │ │ ├── Form.tsx
|
||||
@@ -80,20 +100,6 @@ __name__
|
||||
│ │ ├── _document.tsx
|
||||
│ │ ├── index.test.tsx
|
||||
│ │ └── index.tsx
|
||||
│ ├── api/
|
||||
│ ├── auth/
|
||||
│ │ ├── components/
|
||||
│ │ │ ├── LoginForm.tsx
|
||||
│ │ │ └── SignupForm.tsx
|
||||
│ │ ├── mutations/
|
||||
│ │ │ ├── changePassword.ts
|
||||
│ │ │ ├── login.ts
|
||||
│ │ │ ├── logout.ts
|
||||
│ │ │ └── signup.ts
|
||||
│ │ ├── pages/
|
||||
│ │ │ ├── login.tsx
|
||||
│ │ │ └── signup.tsx
|
||||
│ │ └── validations.ts
|
||||
│ └── users/
|
||||
│ └── queries/
|
||||
│ └── getCurrentUser.ts
|
||||
@@ -102,6 +108,8 @@ __name__
|
||||
│ ├── schema.prisma
|
||||
│ └── seeds.ts
|
||||
├── integrations/
|
||||
├── mailers/
|
||||
│ └── forgotPasswordMailer.ts
|
||||
├── public/
|
||||
│ ├── favicon.ico*
|
||||
│ └── logo.png
|
||||
@@ -114,6 +122,7 @@ __name__
|
||||
├── jest.config.js
|
||||
├── package.json
|
||||
├── tsconfig.json
|
||||
├── types.d.ts
|
||||
├── types.ts
|
||||
└── yarn.lock
|
||||
```
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { ReactNode, PropsWithoutRef } from "react"
|
||||
import { ReactNode, PropsWithoutRef } from "react"
|
||||
import { Form as FinalForm, FormProps as FinalFormProps } from "react-final-form"
|
||||
import * as z from "zod"
|
||||
export { FORM_ERROR } from "final-form"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, {PropsWithoutRef} from "react"
|
||||
import {useField} from "react-final-form"
|
||||
import { forwardRef, PropsWithoutRef } from "react"
|
||||
import { useField } from "react-final-form"
|
||||
|
||||
export interface LabeledTextFieldProps extends PropsWithoutRef<JSX.IntrinsicElements["input"]> {
|
||||
/** Field name. */
|
||||
@@ -11,13 +11,13 @@ export interface LabeledTextFieldProps extends PropsWithoutRef<JSX.IntrinsicElem
|
||||
outerProps?: PropsWithoutRef<JSX.IntrinsicElements["div"]>
|
||||
}
|
||||
|
||||
export const LabeledTextField = React.forwardRef<HTMLInputElement, LabeledTextFieldProps>(
|
||||
({name, label, outerProps, ...props}, ref) => {
|
||||
export const LabeledTextField = forwardRef<HTMLInputElement, LabeledTextFieldProps>(
|
||||
({ name, label, outerProps, ...props }, ref) => {
|
||||
const {
|
||||
input,
|
||||
meta: {touched, error, submitError, submitting},
|
||||
meta: { touched, error, submitError, submitting },
|
||||
} = useField(name, {
|
||||
parse: props.type === "number" ? Number : undefined
|
||||
parse: props.type === "number" ? Number : undefined,
|
||||
})
|
||||
|
||||
const normalizedError = Array.isArray(error) ? error.join(", ") : error || submitError
|
||||
@@ -30,7 +30,7 @@ export const LabeledTextField = React.forwardRef<HTMLInputElement, LabeledTextFi
|
||||
</label>
|
||||
|
||||
{touched && normalizedError && (
|
||||
<div role="alert" style={{color: "red"}}>
|
||||
<div role="alert" style={{ color: "red" }}>
|
||||
{normalizedError}
|
||||
</div>
|
||||
)}
|
||||
@@ -53,7 +53,7 @@ export const LabeledTextField = React.forwardRef<HTMLInputElement, LabeledTextFi
|
||||
`}</style>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
export default LabeledTextField
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState, ReactNode, PropsWithoutRef } from "react"
|
||||
import { useState, ReactNode, PropsWithoutRef } from "react"
|
||||
import { Formik, FormikProps } from "formik"
|
||||
import * as z from "zod"
|
||||
|
||||
|
||||
@@ -1,57 +1,55 @@
|
||||
import React, { PropsWithoutRef } from "react";
|
||||
import { useField, useFormikContext, ErrorMessage } from "formik";
|
||||
import { forwardRef, PropsWithoutRef } from "react"
|
||||
import { useField, useFormikContext, ErrorMessage } from "formik"
|
||||
|
||||
export interface LabeledTextFieldProps
|
||||
extends PropsWithoutRef<JSX.IntrinsicElements["input"]> {
|
||||
export interface LabeledTextFieldProps extends PropsWithoutRef<JSX.IntrinsicElements["input"]> {
|
||||
/** Field name. */
|
||||
name: string;
|
||||
name: string
|
||||
/** Field label. */
|
||||
label: string;
|
||||
label: string
|
||||
/** Field type. Doesn't include radio buttons and checkboxes */
|
||||
type?: "text" | "password" | "email" | "number";
|
||||
outerProps?: PropsWithoutRef<JSX.IntrinsicElements["div"]>;
|
||||
type?: "text" | "password" | "email" | "number"
|
||||
outerProps?: PropsWithoutRef<JSX.IntrinsicElements["div"]>
|
||||
}
|
||||
|
||||
export const LabeledTextField = React.forwardRef<
|
||||
HTMLInputElement,
|
||||
LabeledTextFieldProps
|
||||
>(({ name, label, outerProps, ...props }, ref) => {
|
||||
const [input] = useField(name);
|
||||
const { isSubmitting } = useFormikContext();
|
||||
export const LabeledTextField = forwardRef<HTMLInputElement, LabeledTextFieldProps>(
|
||||
({ name, label, outerProps, ...props }, ref) => {
|
||||
const [input] = useField(name)
|
||||
const { isSubmitting } = useFormikContext()
|
||||
|
||||
return (
|
||||
<div {...outerProps}>
|
||||
<label>
|
||||
{label}
|
||||
<input {...input} disabled={isSubmitting} {...props} ref={ref} />
|
||||
</label>
|
||||
return (
|
||||
<div {...outerProps}>
|
||||
<label>
|
||||
{label}
|
||||
<input {...input} disabled={isSubmitting} {...props} ref={ref} />
|
||||
</label>
|
||||
|
||||
<ErrorMessage name={name}>
|
||||
{(msg) => (
|
||||
<div role="alert" style={{ color: "red" }}>
|
||||
{msg}
|
||||
</div>
|
||||
)}
|
||||
</ErrorMessage>
|
||||
<ErrorMessage name={name}>
|
||||
{(msg) => (
|
||||
<div role="alert" style={{ color: "red" }}>
|
||||
{msg}
|
||||
</div>
|
||||
)}
|
||||
</ErrorMessage>
|
||||
|
||||
<style jsx>{`
|
||||
label {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: start;
|
||||
font-size: 1rem;
|
||||
}
|
||||
input {
|
||||
font-size: 1rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 3px;
|
||||
border: 1px solid purple;
|
||||
appearance: none;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
<style jsx>{`
|
||||
label {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: start;
|
||||
font-size: 1rem;
|
||||
}
|
||||
input {
|
||||
font-size: 1rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 3px;
|
||||
border: 1px solid purple;
|
||||
appearance: none;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
export default LabeledTextField;
|
||||
export default LabeledTextField
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState, ReactNode, PropsWithoutRef } from "react"
|
||||
import { useState, ReactNode, PropsWithoutRef } from "react"
|
||||
import { FormProvider, useForm, UseFormOptions } from "react-hook-form"
|
||||
import * as z from "zod"
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { PropsWithoutRef } from "react"
|
||||
import { forwardRef, PropsWithoutRef } from "react"
|
||||
import { useFormContext } from "react-hook-form"
|
||||
|
||||
export interface LabeledTextFieldProps extends PropsWithoutRef<JSX.IntrinsicElements["input"]> {
|
||||
@@ -11,7 +11,7 @@ export interface LabeledTextFieldProps extends PropsWithoutRef<JSX.IntrinsicElem
|
||||
outerProps?: PropsWithoutRef<JSX.IntrinsicElements["div"]>
|
||||
}
|
||||
|
||||
export const LabeledTextField = React.forwardRef<HTMLInputElement, LabeledTextFieldProps>(
|
||||
export const LabeledTextField = forwardRef<HTMLInputElement, LabeledTextFieldProps>(
|
||||
({ label, outerProps, ...props }, ref) => {
|
||||
const {
|
||||
register,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import React from "react"
|
||||
import { AuthenticationError, Link, useMutation } from "blitz"
|
||||
import { LabeledTextField } from "app/core/components/LabeledTextField"
|
||||
import { Form, FORM_ERROR } from "app/core/components/Form"
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import React from "react"
|
||||
import { useMutation } from "blitz"
|
||||
import { LabeledTextField } from "app/core/components/LabeledTextField"
|
||||
import { Form, FORM_ERROR } from "app/core/components/Form"
|
||||
|
||||
@@ -23,7 +23,7 @@ export default resolver.pipe(resolver.zod(Login), async ({ email, password }, ct
|
||||
// This throws an error if credentials are invalid
|
||||
const user = await authenticateUser(email, password)
|
||||
|
||||
await ctx.session.$create({ userId: user.id, roles: [user.role as Role] })
|
||||
await ctx.session.$create({ userId: user.id, role: user.role as Role })
|
||||
|
||||
return user
|
||||
})
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user