1
0
mirror of synced 2026-02-04 03:01:17 -05:00

Compare commits

..

28 Commits

Author SHA1 Message Date
github-actions[bot]
4e3c25d9a0 Version Packages (beta) (#3839)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-10-11 16:24:43 +08:00
Estevan Jantsk
2ade7268e2 Add blitz export command (#3885)
Co-authored-by: Fran Zekan <zekan.fran369@gmail.com>
Co-authored-by: Aleksandra <alexsandra.sikora@gmail.com>
2022-10-11 16:18:54 +08:00
Fran Zekan
aa34661fac Fix queryKeyGeneration when using invalidateQuery (#3728)
Co-authored-by: beerose <alexsandra.sikora@gmail.com>
2022-10-11 16:01:47 +08:00
Siddharth Suresh
d98e4bac4e Migrate blitz routes command (#3890) 2022-10-11 15:50:30 +08:00
Isuru Maldeniya
0473020555 update prisma-ast version in blitz generator package (#3882)
Co-authored-by: Isuru Maldeniya <isuru.m@synergentl.com>
Co-authored-by: beerose <alexsandra.sikora@gmail.com>
2022-10-04 18:30:33 +08:00
Jannik Schmidtke
8a88dec873 Add a .vscode/settings.json file to specify a TypeScript version (#3876) 2022-10-04 15:35:03 +08:00
Fran Zekan
d6717b9d3c Try to load prisma.schema path from pkg json (#3874)
* Try to load prisma.schema path from pkg json
2022-10-03 13:02:41 -04:00
Daniel Oltmanns
eb970f7bbc fix detecting blitz.config.(ts|js) config file (#3701)
* fix detecting blit.config.(ts|js) config file
2022-10-03 12:41:57 -04:00
Dillon Raphael
8e0c9d76b5 Migrate recipes (#3784) 2022-10-03 09:21:45 -04:00
portalninja
bd09db7533 remove views property from Session's publicData in types.ts file (#3872)
* fix: remove views from public Session data

* add changeset

* Update .changeset/strong-keys-lie.md

Co-authored-by: Aleksandra <alexsandra.sikora@gmail.com>
2022-10-03 11:29:20 +08:00
João Jesus
83281a8469 remove {} from single values in the eslint config in package.json (#3877)
* refactor(js-templates): remove {} from single values inside package.json

* Add changeset

Co-authored-by: beerose <alexsandra.sikora@gmail.com>
2022-10-03 11:05:13 +08:00
Siddharth Suresh
15d22af24a Migrate console (#3863)
Co-authored-by: Aleksandra <alexsandra.sikora@gmail.com>
2022-09-29 22:22:07 +02:00
Siddharth Suresh
1c3106eacf (meta) add @siddhsuresh as maintainer (#3868) 2022-09-29 16:47:09 +02:00
Fran Zekan
af58e2b239 CLI Version check fixes (#3675)
Co-authored-by: Dillon Raphael <dillon@creatorsneverdie.com>
Co-authored-by: Aleksandra <alexsandra.sikora@gmail.com>
2022-09-29 08:59:40 +02:00
Dillon Raphael
430f6ec782 Only generate the prisma client if it's not found (#3855) 2022-09-29 08:34:40 +02:00
Aleksandra
e2c18895da Add client testing utilities and index.test.tsx back to toolkit (#3857) 2022-09-28 16:38:58 -04:00
Aleksandra
3b3c245fb1 Update sponsors list (#3865) 2022-09-26 10:37:56 +02:00
Siddharth Suresh
7b00c17376 Add CI action that checks if changeset was added (#3850) 2022-09-23 14:22:12 +02:00
Dillon Raphael
b43c1a81cc Remove random user git config when generating a new project (#3852) 2022-09-22 12:49:48 +02:00
Aleksandra
1742eb45db Use prefetchInfiniteQuery from @tanstack/react-query for prefetching infinite Blitz queries (#3847)
* Use prefetchInfiniteQuery from @tanstack/react-query for prefetching infinite Blitz queries
2022-09-19 15:23:57 -04:00
Aleksandra
824a9b5e29 Update generator templates to stop breaking no-floating-promises rule (#3848)
* Update generator templates to stop breaking no-floating-promises rule
2022-09-19 15:07:42 -04:00
Aleksandra
9db6c88555 Fix help CLI command not working correctly (#3849)
* Fix help CLI command not working correctly
2022-09-19 14:57:53 -04:00
Jan Wilhelm
25f4526f7e Add API Route handler to the middleware stack (#3830)
Co-authored-by: Aleksandra <alexsandra.sikora@gmail.com>
2022-09-19 10:41:42 +02:00
Dillon Raphael
9fe0cc5468 Throw redirect errors when component has mounted in blitz-auth (#3842)
* Throw redirect errors when component has mounted

* Create heavy-cobras-own.md

* clean up useEffect

* Add mounted check for AuthenticationError

* remove clean up
2022-09-16 16:33:34 -04:00
Dillon Raphael
0edeaa37a3 Update regex for wildcard blitz route in webpack config (#3843)
* update regex for wildcard blitz route in webpack config

* Create itchy-spoons-tan.md

* fix regex

* Update itchy-spoons-tan.md
2022-09-16 14:05:31 -04:00
Estevan Jantsk
bf4aaf1de6 move useCurrentUser from core/hooks to users/hooks (#3831)
Co-authored-by: Aleksandra <alexsandra.sikora@gmail.com>
Co-authored-by: Dillon Raphael <dillon@creatorsneverdie.com>
2022-09-15 15:56:09 +02:00
Siddharth Suresh
b3b4c21501 convert invokeWithMiddleware to resolver call in codemod (#3764) 2022-09-14 14:38:04 -04:00
Dillon Raphael
757789c43b CI Fixes (#3834) 2022-09-14 09:42:27 -04:00
320 changed files with 10761 additions and 497 deletions

View File

@@ -3689,7 +3689,9 @@
"profile": "https://github.com/siddhsuresh",
"contributions": [
"doc",
"code"
"code",
"test",
"maintenance"
]
},
{
@@ -3716,4 +3718,4 @@
],
"contributorsPerLine": 7,
"skipCi": true
}
}

View File

@@ -0,0 +1,5 @@
---
"@blitzjs/generator": patch
---
Update prisma-ast dependency to prevent Blitz generator from failing when Prisma keywords are used as model names

View File

@@ -0,0 +1,5 @@
---
"blitz": patch
---
Fix `blitz --help` CLI command not being found

View File

@@ -0,0 +1,5 @@
---
"@blitzjs/generator": patch
---
Fix `no-floating-promises` lint errors after generating pages with Blitz generator by adding `await` to `router.push` calls in the templates

View File

@@ -0,0 +1,5 @@
---
"@blitzjs/generator": patch
---
Load schema.prisma path from `package.json` instead of assuming it's `db/schema.prisma`

View File

@@ -0,0 +1,5 @@
---
"blitz": patch
---
Add `blitz routes` CLI command back to toolkit

View File

@@ -0,0 +1,5 @@
---
"@blitzjs/next": patch
---
Fix prefetching infinite Blitz queries.

View File

@@ -0,0 +1,5 @@
---
"@blitzjs/generator": patch
---
Move `useCurrentUser` hook from `core/hooks` to `users/hooks` folder

View File

@@ -0,0 +1,7 @@
---
"@blitzjs/auth": patch
"@blitzjs/next": patch
"blitz": patch
---
Fix auth related React hydration errors by not redirecting until after component mount.

View File

@@ -0,0 +1,5 @@
---
"@blitzjs/generator": patch
---
Remove the random user (noop@blitzjs.com) & use user's default git account when commiting a new generated blitz project.

View File

@@ -0,0 +1,5 @@
---
"blitz": patch
---
Add a global Blitz version check when generating a new Blitz project to ensure users use the latest Blitz.

View File

@@ -0,0 +1,5 @@
---
"blitz": patch
---
Add `blitz export` CLI command to toolkit

View File

@@ -0,0 +1,6 @@
---
"@blitzjs/rpc": patch
"blitz": patch
---
Allow for custom page extensions for the wildcard blitz route. For example [...blitz].api.ts. For more information vist https://nextjs.org/docs/api-reference/next.config.js/custom-page-extensions

View File

@@ -0,0 +1,5 @@
---
"@blitzjs/codemod": patch
---
Unwrap `invokeWithMiddleware` so the query or mutation is called directly when running the codemod

View File

@@ -0,0 +1,5 @@
---
"blitz": patch
---
Only generate the prisma client if it's not found in node_modules when running a blitz cli command.

View File

@@ -0,0 +1,5 @@
---
"@blitzjs/generator": patch
---
Fix eslint config in new JavaScript app templates

View File

@@ -20,12 +20,38 @@
"test-no-suspense": "0.0.0",
"test-trailing-slash": "0.0.0",
"test-middleware": "0.0.0",
"test-react-query-utils": "0.0.0"
"test-react-query-utils": "0.0.0",
"toolkit-app-passport": "1.0.1-alpha.16",
"test-get-initial-props": "0.0.0",
"@blitzjs/recipe-base-web": "0.34.0-canary.0",
"@blitzjs/recipe-bulma": "0.34.0-canary.0",
"@blitzjs/recipe-bumbag-ui": "0.34.0-canary.0",
"@blitzjs/recipe-chakra-ui": "0.34.0-canary.0",
"@blitzjs/recipe-emotion": "0.34.0-canary.0",
"@blitzjs/recipe-gh-action-yarn-mariadb": "0.34.0-canary.0",
"@blitzjs/recipe-gh-action-yarn-postgres": "0.34.0-canary.0",
"@blitzjs/recipe-ghost": "0.40.0-canary.9",
"@blitzjs/recipe-graphql": "0.34.0-canary.0",
"@blitzjs/recipe-logrocket": "0.34.0-canary.0",
"@blitzjs/recipe-material-ui": "0.34.0-canary.0",
"@blitzjs/recipe-next-ui": "0.34.0-canary.0",
"@blitzjs/recipe-passenger": "0.34.0-canary.0",
"@blitzjs/recipe-quirrel": "0.34.0-canary.0",
"@blitzjs/recipe-reflexjs": "0.34.0-canary.0",
"@blitzjs/recipe-render": "0.34.0-canary.0",
"@blitzjs/recipe-secureheaders": "0.34.0-canary.0",
"@blitzjs/recipe-stitches": "0.34.0-canary.0",
"@blitzjs/recipe-styled-components": "0.34.0-canary.0",
"@blitzjs/recipe-tailwind": "0.34.0-canary.0",
"@blitzjs/recipe-theme-ui": "0.34.0-canary.0",
"@blitzjs/recipe-vanilla-extract": "0.34.0-canary.0"
},
"changesets": [
"afraid-dancers-juggle",
"big-phones-bow",
"blue-flowers-peel",
"blue-pigs-tan",
"breezy-bees-beg",
"breezy-cameras-double",
"breezy-moose-behave",
"bright-mangos-run",
@@ -49,7 +75,9 @@
"dirty-planets-chew",
"early-lamps-itch",
"eleven-humans-sort",
"eleven-lobsters-drop",
"empty-berries-rule",
"empty-pants-search",
"empty-turkeys-wave",
"fair-carrots-guess",
"fair-kangaroos-clean",
@@ -58,6 +86,7 @@
"fast-clocks-push",
"fast-trainers-kneel",
"few-dogs-fetch",
"few-shrimps-leave",
"flat-bees-approve",
"fluffy-mangos-begin",
"fluffy-mice-wash",
@@ -79,13 +108,20 @@
"great-terms-rescue",
"green-papayas-do",
"green-pillows-hammer",
"happy-bees-lick",
"happy-hotels-visit",
"happy-paws-join",
"healthy-rice-shout",
"heavy-apes-judge",
"heavy-cobras-own",
"hip-buttons-dance",
"honest-candles-yawn",
"honest-cherries-push",
"hot-cups-rhyme",
"hot-drinks-approve",
"hungry-baboons-swim",
"itchy-houses-marry",
"itchy-spoons-tan",
"kind-walls-suffer",
"late-steaks-give",
"lazy-maps-sort",
@@ -108,6 +144,7 @@
"nervous-beds-travel",
"nervous-dolls-rule",
"new-coats-turn",
"new-olives-protect",
"nice-deers-dream",
"nice-starfishes-live",
"nine-birds-confess",
@@ -117,7 +154,9 @@
"ninety-rice-tickle",
"olive-bees-buy",
"olive-feet-rhyme",
"olive-kings-invent",
"olive-sheep-rhyme",
"orange-mirrors-tap",
"orange-zebras-reflect",
"perfect-eyes-repeat",
"perfect-trains-double",
@@ -163,14 +202,18 @@
"sour-mails-lick",
"spicy-beds-float",
"spotty-dingos-stare",
"spotty-lies-visit",
"spotty-peas-hope",
"spotty-zoos-film",
"stale-jobs-drum",
"strong-apes-reply",
"strong-keys-lie",
"stupid-walls-sell",
"sweet-kiwis-cross",
"swift-drinks-dress",
"tall-meals-learn",
"tame-keys-reply",
"tame-pumpkins-nail",
"tasty-maps-fetch",
"tasty-news-collect",
"ten-hairs-listen",
@@ -178,11 +221,13 @@
"tender-pianos-check",
"thick-parrots-float",
"thirty-countries-build",
"thirty-spies-applaud",
"three-lies-pull",
"tidy-clouds-smoke",
"tough-toes-pull",
"twelve-hornets-sip",
"twelve-lemons-smile",
"twelve-needles-worry",
"twenty-beans-pump",
"two-carpets-rhyme",
"two-eyes-knock",
@@ -194,6 +239,7 @@
"violet-bags-leave",
"violet-lions-help",
"weak-suns-shave",
"wicked-badgers-smoke",
"wicked-ghosts-cough",
"wicked-rings-walk",
"wise-eels-visit",

View File

@@ -0,0 +1,5 @@
---
"@blitzjs/codemod": patch
---
Fix detecting `blitz.config.(ts|js)` config file when running the codemod.

View File

@@ -0,0 +1,5 @@
---
"blitz": patch
---
Add `blitz console` CLI command back to toolkit

View File

@@ -0,0 +1,5 @@
---
"@blitzjs/generator": patch
---
Remove `views` property from `Session.PublicData` in `types.ts` file

View File

@@ -0,0 +1,6 @@
---
"@blitzjs/rpc": patch
"blitz": patch
---
Fix invalidateQuery generating wrong param when no param argument is passed

View File

@@ -0,0 +1,6 @@
---
"@blitzjs/rpc": patch
"blitz": patch
---
Migrate over recipe functionality from legacy framework & expose recipe builder helper functions that find and modify next.config.js, blitz-server & blitz-client.

View File

@@ -0,0 +1,7 @@
---
"blitz": patch
"@blitzjs/next": patch
"@blitzjs/generator": patch
---
Add client testing utilities and a sample test to a new blitz app template

View File

@@ -0,0 +1,5 @@
---
"@blitzjs/next": patch
---
Treat API Route handler as a middleware. This allows outer middlewares to completely wrap queries and mutations.

26
.github/workflows/changeset_check.yml vendored Normal file
View File

@@ -0,0 +1,26 @@
name: Changeset
on:
pull_request:
types: [opened, synchronize, labeled, unlabeled]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
changeset:
if: ${{ github.event.label.name != 'no-changeset' }}
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Setup node
uses: actions/setup-node@v2
with:
node-version: 16
- name: Check if changeset is added
run: |
npx @changesets/cli@2.12.0 status --since=origin/${GITHUB_BASE_REF}

View File

@@ -127,8 +127,8 @@ jobs:
shell: bash
run: |
cd ./integration-tests
tree -J -d -L 1 | jq -c '.[0].contents | map(.name | tostring)'
folders=$(tree -J -d -L 1 | jq -c '.[0].contents | map(.name | tostring)')
tree -J -d -L 1 | jq -c '.[0].contents | map(.name | tostring) | map(select(. != "utils"))'
folders=$(tree -J -d -L 1 | jq -c '.[0].contents | map(.name | tostring) | map(select(. != "utils"))')
echo "::set-output name=folders::$folders"
Integration-Tests:
@@ -158,6 +158,10 @@ jobs:
node-version: ${{ matrix.NODE_VERSION }}
cache: "pnpm"
- name: Link Blitz CLI
run: pnpm link ./packages/blitz
shell: bash
- name: Install dependencies
run: pnpm install --frozen-lockfile
shell: bash

3
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"typescript.tsdk": "node_modules/typescript/lib"
}

View File

@@ -76,6 +76,9 @@ Your financial contributions help ensure Blitz continues to be developed and mai
</a></td>
<td><a aria-label="MeetKai" href="https://meetkai.com/?ref=blitzjs">
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/main/assets/meetkai.png" width="40px"/>
</a></td>
<td><a aria-label="Simon Lammes" href="https://github.com/simon-lammes">
<img alt="" src="https://avatars.githubusercontent.com/u/46446421?v=4" width="40px"/>
</a></td>
</tr>
</table>
@@ -195,6 +198,14 @@ _Issue triage, pull request triage, community encouragement and moderation, etc_
</sub>
</a>
</td>
<td align="center">
<a href="https://siddharthsuresh.vercel.app/">
<img src="https://avatars.githubusercontent.com/u/83594610?v=4" width="100px;" alt="Siddharth Suresh avatar" /><br />
<sub>
<b>Siddharth Suresh</b>
</sub>
</a>
</td>
</tr>
</table>
<!-- markdownlint-enable -->
@@ -712,7 +723,7 @@ Thanks to these wonderful people ([emoji key](https://allcontributors.org/docs/e
<td align="center"><a href="https://chaiwattsw.com/"><img src="https://avatars.githubusercontent.com/u/30198386?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Chaiwat Trisuwan</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=chaiwattsw" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=chaiwattsw" title="Code">💻</a></td>
<td align="center"><a href="oltdaniel.eu"><img src="https://avatars.githubusercontent.com/u/53529846?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Daniel Oltmanns</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=oltdaniel" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=oltdaniel" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/edrickleong"><img src="https://avatars.githubusercontent.com/u/10529706?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Edrick Leong</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=edrickleong" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=edrickleong" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=edrickleong" title="Tests">⚠️</a></td>
<td align="center"><a href="https://github.com/siddhsuresh"><img src="https://avatars.githubusercontent.com/u/83594610?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Siddharth Suresh</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=siddhsuresh" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=siddhsuresh" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/siddhsuresh"><img src="https://avatars.githubusercontent.com/u/83594610?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Siddharth Suresh</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=siddhsuresh" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=siddhsuresh" title="Code">💻</a><a href="https://github.com/blitz-js/blitz/commits?author=siddhsuresh" title="Tests">⚠️</a> <a href="#maintenance-siddhsuresh" title="Maintenance">🚧</a></td>
<td align="center"><a href="http://jins.dev"><img src="https://avatars.githubusercontent.com/u/39466936?v=4?s=100" width="100px;" alt=""/><br /><sub><b>JH.Lee</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=orionmiz" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=orionmiz" title="Tests">⚠️</a> <a href="#maintenance-orionmiz" title="Maintenance">🚧</a> <a href="https://github.com/blitz-js/blitz/commits?author=orionmiz" title="Documentation">📖</a></td>
</tr>
<tr>

View File

@@ -92,8 +92,6 @@ __name__
│ │ ├── components/
│ │ │ ├── Form.tsx
│ │ │ └── LabeledTextField.tsx
│ │ ├── hooks/
│ │ │ └── useCurrentUser.ts
│ │ └── layouts/
│ │ └── Layout.tsx
│ ├── pages/
@@ -103,6 +101,8 @@ __name__
│ │ ├── index.test.tsx
│ │ └── index.tsx
│ └── users/
│ ├── hooks/
│ │ └── useCurrentUser.ts
│ └── queries/
│ └── getCurrentUser.ts
├── db/

View File

@@ -29,7 +29,7 @@
"@blitzjs/rpc": "workspace:*",
"@hookform/resolvers": "2.8.8",
"@prisma/client": "4.0.0",
"blitz": "workspace:2.0.0-beta.4",
"blitz": "workspace:2.0.0-beta.5",
"next": "12.2.5",
"openid-client": "5.1.8",
"prisma": "4.0.0",

View File

@@ -2,7 +2,7 @@ import { Suspense } from "react"
import Image from "next/image"
import Link from "next/link"
import Layout from "app/core/layouts/Layout"
import { useCurrentUser } from "app/core/hooks/useCurrentUser"
import { useCurrentUser } from "app/users/hooks/useCurrentUser"
import logout from "app/auth/mutations/logout"
import logo from "public/logo.png"
import { useMutation } from "@blitzjs/rpc"

View File

@@ -92,8 +92,6 @@ __name__
│ │ ├── components/
│ │ │ ├── Form.tsx
│ │ │ └── LabeledTextField.tsx
│ │ ├── hooks/
│ │ │ └── useCurrentUser.ts
│ │ └── layouts/
│ │ └── Layout.tsx
│ ├── pages/
@@ -103,6 +101,8 @@ __name__
│ │ ├── index.test.tsx
│ │ └── index.tsx
│ └── users/
│ ├── hooks/
│ │ └── useCurrentUser.ts
│ └── queries/
│ └── getCurrentUser.ts
├── db/

View File

@@ -29,7 +29,7 @@
"@blitzjs/rpc": "workspace:*",
"@hookform/resolvers": "2.8.8",
"@prisma/client": "4.0.0",
"blitz": "workspace:2.0.0-beta.4",
"blitz": "workspace:2.0.0-beta.5",
"next": "12.2.5",
"prisma": "4.0.0",
"react": "18.2.0",

View File

@@ -2,7 +2,7 @@ import { Suspense } from "react"
import Image from "next/image"
import Link from "next/link"
import Layout from "app/core/layouts/Layout"
import { useCurrentUser } from "app/core/hooks/useCurrentUser"
import { useCurrentUser } from "app/users/hooks/useCurrentUser"
import logout from "app/auth/mutations/logout"
import logo from "public/logo.png"
import { useMutation } from "@blitzjs/rpc"
@@ -271,4 +271,6 @@ const Home: BlitzPage = () => {
)
}
Home.authenticate = true
export default Home

View File

@@ -0,0 +1,27 @@
import { useCurrentUser } from "app/users/hooks/useCurrentUser"
import { render } from "test/utils"
import Home from "../pages/index"
jest.mock("app/users/hooks/useCurrentUser")
const mockUseCurrentUser = useCurrentUser as jest.MockedFunction<typeof useCurrentUser>
describe("renders blitz documentation link", () => {
it("test", () => {
// This is an example of how to ensure a specific item is in the document
// But it's disabled by default (by test.skip) so the test doesn't fail
// 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 linkElement = getByText(/Documentation/i)
expect(linkElement).toBeInTheDocument()
})
})

View File

@@ -0,0 +1,104 @@
import { render as defaultRender } from "@testing-library/react"
import { renderHook as defaultRenderHook } from "@testing-library/react-hooks"
import { NextRouter } from "next/router"
import { BlitzProvider, RouterContext } from "@blitzjs/next"
import { QueryClient } from "@blitzjs/rpc"
export * from "@testing-library/react"
// --------------------------------------------------------------------------------
// This file customizes the render() and renderHook() test functions provided
// by React testing library. It adds a router context wrapper with a mocked router.
//
// You should always import `render` and `renderHook` from this file
//
// This is the place to add any other context providers you need while testing.
// --------------------------------------------------------------------------------
// --------------------------------------------------
// render()
// --------------------------------------------------
// Override the default test render with our own
//
// You can override the router mock like this:
//
// const { baseElement } = render(<MyComponent />, {
// router: { pathname: '/my-custom-pathname' },
// });
// --------------------------------------------------
const queryClient = new QueryClient()
export function render(
ui: RenderUI,
{ wrapper, router, dehydratedState, ...options }: RenderOptions = {}
) {
if (!wrapper) {
// Add a default context wrapper if one isn't supplied from the test
wrapper = ({ children }: { children: React.ReactNode }) => (
<BlitzProvider dehydratedState={dehydratedState} client={queryClient}>
<RouterContext.Provider value={{ ...mockRouter, ...router }}>
{children}
</RouterContext.Provider>
</BlitzProvider>
)
}
return defaultRender(ui, { wrapper, ...options })
}
// --------------------------------------------------
// renderHook()
// --------------------------------------------------
// Override the default test renderHook with our own
//
// You can override the router mock like this:
//
// const result = renderHook(() => myHook(), {
// router: { pathname: '/my-custom-pathname' },
// });
// --------------------------------------------------
export function renderHook(
hook: RenderHook,
{ wrapper, router, dehydratedState, ...options }: RenderOptions = {}
) {
if (!wrapper) {
// Add a default context wrapper if one isn't supplied from the test
wrapper = ({ children }: { children: React.ReactNode }) => (
<BlitzProvider dehydratedState={dehydratedState} client={queryClient}>
<RouterContext.Provider value={{ ...mockRouter, ...router }}>
{children}
</RouterContext.Provider>
</BlitzProvider>
)
}
return defaultRenderHook(hook, { wrapper, ...options })
}
export const mockRouter: NextRouter = {
basePath: "",
pathname: "/",
route: "/",
asPath: "/",
query: {},
isReady: true,
isLocaleDomain: false,
isPreview: false,
push: jest.fn(),
replace: jest.fn(),
reload: jest.fn(),
back: jest.fn(),
prefetch: jest.fn(),
beforePopState: jest.fn(),
events: {
on: jest.fn(),
off: jest.fn(),
emit: jest.fn(),
},
isFallback: false,
}
type DefaultParams = Parameters<typeof defaultRender>
type RenderUI = DefaultParams[0]
type RenderOptions = DefaultParams[1] & { router?: Partial<NextRouter>; dehydratedState?: unknown }
type DefaultHookParams = Parameters<typeof defaultRenderHook>
type RenderHook = DefaultHookParams[0]

View File

@@ -1,7 +1,7 @@
import {setupBlitzServer} from "@blitzjs/next"
import {AuthServerPlugin, PrismaStorage} from "@blitzjs/auth"
import {simpleRolesIsAuthorized} from "@blitzjs/auth"
import db from "../prisma/index"
import db from "../db/index"
const {gSSP, gSP, api} = setupBlitzServer({
plugins: [

View File

@@ -2,7 +2,6 @@ import {enhancePrisma} from "blitz"
import {PrismaClient} from "@prisma/client"
const EnhancedPrisma = enhancePrisma(PrismaClient)
export * from "@prisma/client"
const prisma = new EnhancedPrisma()
export default prisma

View File

@@ -1,20 +1,16 @@
import prisma from "./index"
import { SecurePassword } from "@blitzjs/auth"
import {SecurePassword} from "@blitzjs/auth"
const seed = async () => {
// await prisma.$reset()
const hashedPassword = await SecurePassword.hash("abcd1234")
await prisma.user.create({
data: {
email: "test@test.com",
hashedPassword,
role: "user",
},
}).catch(console.error)
process.exit(0);
})
process.exit(0)
}
seed()

View File

@@ -3,16 +3,17 @@
"version": "0.0.0",
"private": true,
"prisma": {
"seed": "ts-node --compiler-options {\"module\":\"CommonJS\"} prisma/seed.ts"
"seed": "ts-node --compiler-options {\"module\":\"CommonJS\"} db/seed.ts",
"schema": "db/schema.prisma"
},
"scripts": {
"start:dev": "pnpm run prisma:start && next dev",
"test": "pnpm run prisma:start && vitest run",
"start:dev": "pnpm run prisma:start && blitz dev",
"test": "vitest run",
"test-watch": "vitest",
"start": "next start",
"start": "blitz start",
"lint": "next lint",
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf .next",
"prisma:start": "prisma generate && prisma migrate dev",
"prisma:start": "blitz prisma migrate deploy",
"prisma:studio": "prisma studio"
},
"dependencies": {
@@ -20,7 +21,7 @@
"@blitzjs/config": "workspace:*",
"@blitzjs/next": "workspace:*",
"@prisma/client": "4.0.0",
"blitz": "workspace:*",
"blitz": "workspace:2.0.0-beta.5",
"lowdb": "3.0.0",
"next": "12.2.5",
"prisma": "4.0.0",

View File

@@ -1,5 +1,5 @@
import {api} from "../../app/blitz-server"
import prisma from "../../prisma/index"
import prisma from "../../db/index"
import {SecurePassword} from "@blitzjs/auth"
import {Role} from "../../types"

View File

@@ -1,11 +1,22 @@
import {describe, it, expect, beforeAll, afterAll} from "vitest"
import {killApp, findPort, launchApp, nextBuild, nextStart} from "../../utils/next-test-utils"
import {
killApp,
findPort,
launchApp,
nextBuild,
nextStart,
runBlitzCommand,
blitzLaunchApp,
blitzBuild,
blitzStart,
} from "../../utils/next-test-utils"
import webdriver from "../../utils/next-webdriver"
import {join} from "path"
import seed from "../prisma/seed"
import fetch from "node-fetch"
import {fromBase64} from "b64-lite"
import seed from "../db/seed"
import prisma from "../db"
let app: any
let appPort: number
@@ -126,9 +137,9 @@ describe("Auth Tests", () => {
describe("dev mode", () => {
beforeAll(async () => {
try {
await runBlitzCommand(["prisma", "migrate", "reset", "--force"])
appPort = await findPort()
app = await launchApp(appDir, appPort, {cwd: process.cwd()})
// await seed()
app = await blitzLaunchApp(appPort, {cwd: process.cwd()})
} catch (error) {
console.log(error)
}
@@ -140,9 +151,11 @@ describe("Auth Tests", () => {
describe("server mode", () => {
beforeAll(async () => {
try {
await nextBuild(appDir)
await runBlitzCommand(["prisma", "generate"])
await runBlitzCommand(["prisma", "migrate", "deploy"])
await blitzBuild()
appPort = await findPort()
app = await nextStart(appDir, appPort, {cwd: process.cwd()})
app = await blitzStart(appPort, {cwd: process.cwd()})
} catch (err) {
console.log(err)
}

View File

@@ -1,5 +1,5 @@
import {SimpleRolesIsAuthorized} from "@blitzjs/auth"
import {User} from "./prisma"
import {User} from "./db"
export type Role = "ADMIN" | "USER"

View File

@@ -1,7 +0,0 @@
import prisma from "./index"
const seed = async () => {
await prisma.$reset()
}
export default seed

View File

@@ -4,7 +4,7 @@
"private": true,
"scripts": {
"start:dev": "pnpm run prisma:start && next dev",
"test": "pnpm run prisma:start && vitest run",
"test": "vitest run",
"test-watch": "vitest",
"start": "next start",
"lint": "next lint",

View File

@@ -1,5 +1,15 @@
import {describe, it, expect, beforeAll, afterAll} from "vitest"
import {killApp, findPort, launchApp, nextBuild, nextStart} from "../../utils/next-test-utils"
import {
killApp,
findPort,
launchApp,
nextBuild,
nextStart,
runBlitzCommand,
blitzLaunchApp,
blitzBuild,
blitzStart,
} from "../../utils/next-test-utils"
import webdriver from "../../utils/next-webdriver"
import {join} from "path"
@@ -40,8 +50,9 @@ describe("getInitialProps Tests", () => {
describe("dev mode", () => {
beforeAll(async () => {
try {
await runBlitzCommand(["prisma", "migrate", "reset", "--force"])
appPort = await findPort()
app = await launchApp(appDir, appPort, {cwd: process.cwd()})
app = await blitzLaunchApp(appPort, {cwd: process.cwd()})
} catch (error) {
console.log(error)
}
@@ -53,9 +64,11 @@ describe("getInitialProps Tests", () => {
describe("server mode", () => {
beforeAll(async () => {
try {
await nextBuild(appDir)
await runBlitzCommand(["prisma", "generate"])
await runBlitzCommand(["prisma", "migrate", "deploy"])
await blitzBuild()
appPort = await findPort()
app = await nextStart(appDir, appPort, {cwd: process.cwd()})
app = await blitzStart(appPort, {cwd: process.cwd()})
} catch (err) {
console.log(err)
}

View File

@@ -0,0 +1,8 @@
import {defineConfig} from "vitest/config"
export default defineConfig({
test: {
testTimeout: 5000 * 60 * 2,
hookTimeout: 100000,
},
})

View File

@@ -6,6 +6,9 @@ import {
nextBuild,
nextStart,
fetchViaHTTP,
blitzLaunchApp,
blitzBuild,
blitzStart,
} from "../../utils/next-test-utils"
import {join} from "path"
@@ -46,7 +49,7 @@ describe("Middleware Tests", () => {
beforeAll(async () => {
try {
appPort = await findPort()
app = await launchApp(appDir, appPort, {cwd: process.cwd()})
app = await blitzLaunchApp(appPort, {cwd: process.cwd()})
} catch (error) {
console.log(error)
}
@@ -58,9 +61,9 @@ describe("Middleware Tests", () => {
describe("server mode", () => {
beforeAll(async () => {
try {
await nextBuild(appDir)
await blitzBuild()
appPort = await findPort()
app = await nextStart(appDir, appPort, {cwd: process.cwd()})
app = await blitzStart(appPort, {cwd: process.cwd()})
} catch (err) {
console.log(err)
}

View File

@@ -3,6 +3,7 @@ import {defineConfig} from "vitest/config"
export default defineConfig({
test: {
testTimeout: 5000 * 60 * 2,
hookTimeout: 100000,
},
})

View File

@@ -1,7 +1,7 @@
import {setupBlitzServer} from "@blitzjs/next"
import {AuthServerPlugin, PrismaStorage} from "@blitzjs/auth"
import {simpleRolesIsAuthorized} from "@blitzjs/auth"
import db from "../prisma/index"
import db from "../db/index"
const {gSSP, gSP, api} = setupBlitzServer({
plugins: [

View File

@@ -2,9 +2,12 @@
"name": "test-no-suspense",
"version": "0.0.0",
"private": true,
"prisma": {
"schema": "db/schema.prisma"
},
"scripts": {
"start:dev": "pnpm run prisma:start && next dev",
"test": "pnpm run prisma:start && vitest run",
"test": "vitest run",
"test-watch": "vitest",
"start": "next start",
"lint": "next lint",

View File

@@ -1,7 +0,0 @@
import prisma from "./index"
const seed = async () => {
await prisma.$reset()
}
export default seed

View File

@@ -1,5 +1,15 @@
import {describe, it, expect, beforeAll, afterAll} from "vitest"
import {killApp, findPort, launchApp, nextBuild, nextStart} from "../../utils/next-test-utils"
import {
killApp,
findPort,
launchApp,
nextBuild,
nextStart,
runBlitzCommand,
blitzLaunchApp,
blitzBuild,
blitzStart,
} from "../../utils/next-test-utils"
import webdriver from "../../utils/next-webdriver"
import {join} from "path"
@@ -30,8 +40,9 @@ describe("No Suspense Tests", () => {
describe("dev mode", () => {
beforeAll(async () => {
try {
await runBlitzCommand(["prisma", "migrate", "reset", "--force"])
appPort = await findPort()
app = await launchApp(appDir, appPort, {cwd: process.cwd()})
app = await blitzLaunchApp(appPort, {cwd: process.cwd()})
} catch (error) {
console.log(error)
}
@@ -43,9 +54,11 @@ describe("No Suspense Tests", () => {
describe("server mode", () => {
beforeAll(async () => {
try {
await nextBuild(appDir)
await runBlitzCommand(["prisma", "generate"])
await runBlitzCommand(["prisma", "migrate", "deploy"])
await blitzBuild()
appPort = await findPort()
app = await nextStart(appDir, appPort, {cwd: process.cwd()})
app = await blitzStart(appPort, {cwd: process.cwd()})
} catch (err) {
console.log(err)
}

View File

@@ -1,5 +1,5 @@
import {SimpleRolesIsAuthorized} from "@blitzjs/auth"
import {User} from "./prisma"
import {User} from "./db"
export type Role = "ADMIN" | "USER"

View File

@@ -1,12 +1,14 @@
/// <reference types="vitest" />
import { defineConfig } from 'vitest/config'
import react from '@vitejs/plugin-react'
import {defineConfig} from "vitest/config"
import react from "@vitejs/plugin-react"
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
test: {
environment: 'jsdom',
environment: "jsdom",
testTimeout: 5000 * 60 * 2,
hookTimeout: 100000,
},
})
})

View File

@@ -0,0 +1,6 @@
import {resolver} from "@blitzjs/rpc"
export default resolver.pipe(async (input, ctx) => {
await new Promise((r) => setTimeout(r, 4000))
return "thanks"
})

View File

@@ -0,0 +1,6 @@
const cache = {}
export default async function getSequence(key: string) {
cache[key] = cache[key] || 0
return cache[key]++
}

View File

@@ -1,7 +0,0 @@
import prisma from "./index"
const seed = async () => {
await prisma.$reset()
}
export default seed

View File

@@ -4,7 +4,7 @@
"private": true,
"scripts": {
"start:dev": "pnpm run prisma:start && next dev",
"test": "pnpm run prisma:start && vitest run",
"test": "vitest run",
"test-watch": "vitest",
"start": "next start",
"lint": "next lint",

View File

@@ -0,0 +1,55 @@
import React, {Suspense} from "react"
import {BlitzPage} from "@blitzjs/next"
import {invalidateQuery, useQuery} from "@blitzjs/rpc"
import getSequence from "../app/queries/getSequence"
const useQueryOptions = {
refetchInterval: 0,
refetchOnMount: false,
refetchOnReconnect: false,
refetchOnWindowFocus: false,
}
const PageWithInvalidateQuery: React.FC = () => {
const [query1, {isFetching: isQ1Fetching}] = useQuery(getSequence, "query1", useQueryOptions)
const [query2, {isFetching: isQ2Fetching}] = useQuery(getSequence, "query2", useQueryOptions)
const isFetching = isQ1Fetching || isQ2Fetching
const onRevalidateBoth = async () => {
await invalidateQuery(getSequence)
}
const onRevalidateFirst = async () => {
await invalidateQuery(getSequence, "query1")
}
return (
<div>
<h1>Hello from PageWithInvalidateQuery</h1>
<button id="invalidate-both" onClick={onRevalidateBoth}>
Both
</button>
<button id="invalidate-first" onClick={onRevalidateFirst}>
First
</button>
{isFetching && <h3>Loading...</h3>}
{!isFetching && (
<div id="data">
<h2 id="data-first">{query1}</h2>
<h2 id="data-second">{query2}</h2>
</div>
)}
</div>
)
}
const PageWithInvalidateQueryPage: BlitzPage = () => {
return (
<Suspense fallback={<h1>Loading...</h1>}>
<PageWithInvalidateQuery />
</Suspense>
)
}
export default PageWithInvalidateQueryPage

View File

@@ -0,0 +1,26 @@
import {useInfiniteQuery} from "@blitzjs/rpc"
import {gSSP} from "../app/blitz-server"
import testQuery from "../app/queries/getInfiniteData"
export const getServerSideProps = gSSP(async ({ctx}) => {
const {prefetchInfiniteQuery} = ctx
await prefetchInfiniteQuery(testQuery, {
name: "hello world",
})
return {props: {}}
})
const PageWithPrefetchInfQuery = () => {
const [data] = useInfiniteQuery(
testQuery,
(pageParams) => ({...pageParams, name: "hello world"}),
{
suspense: false,
getNextPageParam: (lastPage) => lastPage,
},
)
return <div id="data">{data ? data : "no-data"}</div>
}
export default PageWithPrefetchInfQuery

View File

@@ -1,14 +1,18 @@
import {describe, it, expect, beforeAll, afterAll} from "vitest"
import {killApp, findPort, launchApp, nextBuild, nextStart} from "../../utils/next-test-utils"
import {
killApp,
findPort,
runBlitzCommand,
blitzLaunchApp,
blitzBuild,
blitzStart,
} from "../../utils/next-test-utils"
import webdriver from "../../utils/next-webdriver"
import {join} from "path"
let app: any
let appPort: number
const appDir = join(__dirname, "../")
const runTests = (mode?: string) => {
const runTests = () => {
describe("get query data", () => {
it(
"should work",
@@ -27,13 +31,73 @@ const runTests = (mode?: string) => {
5000 * 60 * 2,
)
})
describe("prefetch infinite query", () => {
it(
"should work",
async () => {
const browser = await webdriver(appPort, "/page-with-prefetch-inf-query")
browser.waitForElementByCss("#data", 0)
const newText = await browser.elementByCss("#data").text()
expect(newText).not.toMatch("no-data")
expect(newText).toMatch("thanks")
if (browser) await browser.close()
},
5000 * 60 * 2,
)
})
describe("invalidate query", () => {
it(
"should work",
async () => {
const browser = await webdriver(appPort, "/page-with-invalidate")
const getData = async () => {
const q1 = await browser.elementByCss("#data-first").text()
const q2 = await browser.elementByCss("#data-second").text()
return {q1: parseInt(q1), q2: parseInt(q2)}
}
browser.waitForElementByCss("#data", 0)
const initialData = await getData()
expect(initialData.q1).equal(0)
expect(initialData.q2).equal(0)
browser.elementByCss("#invalidate-both").click() // sometimes first one returns the same value
await new Promise((r) => setTimeout(r, 100))
browser.elementByCss("#invalidate-both").click()
browser.waitForElementByCss("#data", 0)
const bothData = await getData()
expect(bothData.q1).greaterThan(initialData.q1)
expect(bothData.q2).greaterThan(initialData.q2)
browser.elementByCss("#invalidate-first").click()
browser.waitForElementByCss("#data", 0)
const afterSecond = await getData()
expect(afterSecond.q1).equal(bothData.q1 + 1)
expect(afterSecond.q2).equal(bothData.q2)
if (browser) await browser.close()
},
5000 * 60 * 2,
)
})
}
describe("React Query Utils Tests", () => {
describe("dev mode", () => {
beforeAll(async () => {
try {
await runBlitzCommand(["prisma", "migrate", "reset", "--force"])
appPort = await findPort()
app = await launchApp(appDir, appPort, {cwd: process.cwd()})
app = await blitzLaunchApp(appPort, {cwd: process.cwd()})
} catch (error) {
console.log(error)
}
@@ -45,9 +109,11 @@ describe("React Query Utils Tests", () => {
describe("server mode", () => {
beforeAll(async () => {
try {
await nextBuild(appDir)
await runBlitzCommand(["prisma", "generate"])
await runBlitzCommand(["prisma", "migrate", "deploy"])
await blitzBuild()
appPort = await findPort()
app = await nextStart(appDir, appPort, {cwd: process.cwd()})
app = await blitzStart(appPort, {cwd: process.cwd()})
} catch (err) {
console.log(err)
}

View File

@@ -1,7 +0,0 @@
import prisma from "./index"
const seed = async () => {
await prisma.$reset()
}
export default seed

View File

@@ -4,7 +4,7 @@
"private": true,
"scripts": {
"start:dev": "pnpm run prisma:start && next dev",
"test": "pnpm run prisma:start && vitest run",
"test": "vitest run",
"test-watch": "vitest",
"start": "next start",
"lint": "next lint",

View File

@@ -1,5 +1,15 @@
import {describe, it, expect, beforeAll, afterAll} from "vitest"
import {killApp, findPort, launchApp, nextBuild, nextStart} from "../../utils/next-test-utils"
import {
killApp,
findPort,
launchApp,
nextBuild,
nextStart,
runBlitzCommand,
blitzLaunchApp,
blitzBuild,
blitzStart,
} from "../../utils/next-test-utils"
import webdriver from "../../utils/next-webdriver"
import {join} from "path"
@@ -29,8 +39,9 @@ describe("Trailing Slash Tests", () => {
describe("dev mode", () => {
beforeAll(async () => {
try {
await runBlitzCommand(["prisma", "migrate", "reset", "--force"])
appPort = await findPort()
app = await launchApp(appDir, appPort, {cwd: process.cwd()})
app = await blitzLaunchApp(appPort, {cwd: process.cwd()})
} catch (error) {
console.log(error)
}
@@ -42,9 +53,11 @@ describe("Trailing Slash Tests", () => {
describe("server mode", () => {
beforeAll(async () => {
try {
await nextBuild(appDir)
await runBlitzCommand(["prisma", "generate"])
await runBlitzCommand(["prisma", "migrate", "deploy"])
await blitzBuild()
appPort = await findPort()
app = await nextStart(appDir, appPort, {cwd: process.cwd()})
app = await blitzStart(appPort, {cwd: process.cwd()})
} catch (err) {
console.log(err)
}

View File

@@ -0,0 +1,8 @@
import {defineConfig} from "vitest/config"
export default defineConfig({
test: {
testTimeout: 5000 * 60 * 2,
hookTimeout: 100000,
},
})

View File

@@ -57,6 +57,7 @@ const BlitzProvider = ({
return children
}
const compose =
(...rest) =>
(x: React.ComponentType<any>) =>

View File

@@ -141,6 +141,180 @@ export function getCommandBin(
return path.resolve(rootFolder, bin)
}
export function runBlitzCommand(argv, options: RunNextCommandOptions = {}) {
const nextnextbin = getCommandBin("blitz", options.cwd)
const blitzBin = path.join(nextnextbin, "dist/index.cjs")
const cwd = options.cwd || process.cwd()
// Let Next.js decide the environment
const env = {
...process.env,
NODE_ENV: "production" as const,
__NEXT_TEST_MODE: "true",
...options.env,
}
return new Promise((resolve, reject) => {
console.log(`Running command "blitz ${argv.join(" ")}"`)
const instance = spawn("node", [blitzBin, ...argv], {
cwd,
env,
stdio: ["ignore", "pipe", "pipe"],
})
if (typeof options.instance === "function") {
options.instance(instance)
}
let mergedStdio = ""
let stderrOutput = ""
if (options.stderr) {
instance.stderr?.on("data", function (chunk) {
mergedStdio += chunk
stderrOutput += chunk
if (options.stderr === "log") {
console.log(chunk.toString())
}
})
} else {
instance.stderr?.on("data", function (chunk) {
mergedStdio += chunk
})
}
let stdoutOutput = ""
if (options.stdout) {
instance.stdout?.on("data", function (chunk) {
mergedStdio += chunk
stdoutOutput += chunk
if (options.stdout === "log") {
console.log(chunk.toString())
}
})
} else {
instance.stdout?.on("data", function (chunk) {
mergedStdio += chunk
})
}
instance.on("close", (code, signal) => {
if (!options.stderr && !options.stdout && !options.ignoreFail && code !== 0) {
return reject(new Error(`command failed with code ${code}\n${mergedStdio}`))
}
resolve({
code,
signal,
stdout: stdoutOutput,
stderr: stderrOutput,
})
})
instance.on("error", (err: any) => {
err.stdout = stdoutOutput
err.stderr = stderrOutput
reject(err)
})
})
}
export function runBlitzCommandDev(argv, stdOut, opts: RunNextCommandDevOptions = {}) {
const nextnextbin = getCommandBin("blitz", opts.cwd)
const blitzBin = path.join(nextnextbin, "dist/index.cjs")
const cwd = opts.cwd
const env = {
...process.env,
NODE_ENV: undefined,
__NEXT_TEST_MODE: "true",
...opts.env,
}
return new Promise<void>((resolve, reject) => {
const instance = spawn("node", [blitzBin, ...argv], {
cwd,
env,
} as {})
let didResolve = false
function handleStdout(data) {
const message = data.toString()
const bootupMarkers = {
dev: /compiled .*successfully/i,
start: /started server/i,
}
if (
(opts.bootupMarker && opts.bootupMarker.test(message)) ||
bootupMarkers[opts.nextStart || stdOut ? "start" : "dev"].test(message)
) {
if (!didResolve) {
didResolve = true
resolve(stdOut ? message : instance)
}
}
if (typeof opts.onStdout === "function") {
opts.onStdout(message)
}
if (opts.stdout !== false) {
process.stdout.write(message)
}
}
function handleStderr(data) {
const message = data.toString()
if (typeof opts.onStderr === "function") {
opts.onStderr(message)
}
if (opts.stderr !== false) {
process.stderr.write(message)
}
}
instance.stdout?.on("data", handleStdout)
instance.stderr?.on("data", handleStderr)
instance.on("close", () => {
instance.stdout?.removeListener("data", handleStdout)
instance.stderr?.removeListener("data", handleStderr)
if (!didResolve) {
didResolve = true
resolve()
}
})
instance.on("error", (err) => {
reject(err)
})
})
}
// Blitz Utils
export function blitzLaunchApp(port, opts: RunNextCommandDevOptions) {
return runBlitzCommandDev(["dev", "-p", port], undefined, opts)
}
export function blitzBuild(args = [], opts = {}): any {
return runBlitzCommand(["build", ...args], opts)
}
export function nextLint(args = [], opts = {}) {
return runBlitzCommand(["lint", ...args], opts)
}
export function blitzStart(port, opts = {}) {
return runBlitzCommandDev(["start", "-p", port], undefined, {
...opts,
nextStart: true,
})
}
//Next Utils
export function runNextCommand(argv, options: RunNextCommandOptions = {}) {
const nextnextbin = getCommandBin("next", options.cwd)
const nextBin = path.join(nextnextbin, "dist/bin/next")
@@ -295,7 +469,6 @@ export function runNextCommandDev(argv, stdOut, opts: RunNextCommandDevOptions =
})
}
// Launch the app in dev mode.
export function launchApp(dir, port, opts: RunNextCommandDevOptions) {
return runNextCommandDev([dir, "-p", port], undefined, opts)
}
@@ -312,10 +485,6 @@ export function nextExportDefault(dir, opts = {}) {
return runNextCommand(["export", dir], opts)
}
export function nextLint(dir, args = [], opts = {}) {
return runNextCommand(["lint", dir, ...args], opts)
}
export function nextStart(dir, port, opts = {}) {
return runNextCommandDev(["start", "-p", port, dir], undefined, {
...opts,
@@ -683,20 +852,20 @@ function runSuite(suiteName, context, options) {
}
if (env === "prod") {
context.appPort = await findPort()
const {stdout, stderr, code} = await nextBuild(appDir, [], {
const {stdout, stderr, code} = await blitzBuild([], {
stderr: true,
stdout: true,
})
context.stdout = stdout
context.stderr = stderr
context.code = code
context.server = await nextStart(context.appDir, context.appPort, {
context.server = await blitzStart(context.appPort, {
onStderr,
onStdout,
})
} else if (env === "dev") {
context.appPort = await findPort()
context.server = await launchApp(context.appDir, context.appPort, {
context.server = await blitzLaunchApp(context.appPort, {
onStderr,
onStdout,
})

View File

@@ -5,7 +5,8 @@
"workspaces": [
"apps/*",
"packages/*",
"integration-tests/*"
"integration-tests/*",
"recipes/*"
],
"scripts": {
"preinstall": "npx only-allow pnpm",
@@ -38,7 +39,7 @@
"wait-on": "6.0.1"
},
"npmClient": "pnpm",
"packageManager": "pnpm@7.4.0-1",
"packageManager": "pnpm@7.11.0",
"manypkg": {
"ignoredRules": [
"EXTERNAL_MISMATCH"

View File

@@ -1,5 +1,23 @@
# @blitzjs/auth
## 2.0.0-beta.10
### Patch Changes
- 9fe0cc54: Fix auth related React hydration errors by not redirecting until after component mount.
- Updated dependencies [9db6c885]
- Updated dependencies [d98e4bac]
- Updated dependencies [9fe0cc54]
- Updated dependencies [af58e2b2]
- Updated dependencies [2ade7268]
- Updated dependencies [0edeaa37]
- Updated dependencies [430f6ec7]
- Updated dependencies [15d22af2]
- Updated dependencies [aa34661f]
- Updated dependencies [8e0c9d76]
- Updated dependencies [e2c18895]
- blitz@2.0.0-beta.5
## 2.0.0-beta.4
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@blitzjs/auth",
"version": "2.0.0-beta.4",
"version": "2.0.0-beta.10",
"scripts": {
"build": "unbuild",
"predev": "wait-on -d 250 ../blitz/dist/index-server.d.ts",
@@ -26,7 +26,7 @@
"@types/secure-password": "3.1.1",
"b64-lite": "1.4.0",
"bad-behavior": "1.0.1",
"blitz": "2.0.0-beta.4",
"blitz": "2.0.0-beta.5",
"cookie": "0.4.1",
"cookie-session": "2.0.0",
"debug": "4.3.3",
@@ -40,7 +40,7 @@
"url": "0.11.0"
},
"devDependencies": {
"@blitzjs/config": "workspace:2.0.0-beta.4",
"@blitzjs/config": "workspace:2.0.0-beta.10",
"@testing-library/react": "13.0.0",
"@testing-library/react-hooks": "7.0.2",
"@types/cookie": "0.4.1",

View File

@@ -165,7 +165,13 @@ export const useSession = (options: UseSessionOptions = {}): ClientSession => {
}
export const useAuthorizeIf = (condition?: boolean) => {
if (isClient && condition && !getPublicDataStore().getData().userId) {
const [mounted, setMounted] = useState(false)
useEffect(() => {
setMounted(true)
}, [])
if (isClient && condition && !getPublicDataStore().getData().userId && mounted) {
const error = new AuthenticationError()
error.stack = null!
throw error
@@ -184,7 +190,13 @@ export const useAuthenticatedSession = (
}
export const useRedirectAuthenticated = (to: UrlObject | string) => {
if (isClient && getPublicDataStore().getData().userId) {
const [mounted, setMounted] = useState(false)
useEffect(() => {
setMounted(true)
}, [])
if (isClient && getPublicDataStore().getData().userId && mounted) {
const error = new RedirectError(to)
error.stack = null!
throw error
@@ -248,6 +260,11 @@ export function getAuthValues<TProps = any>(
function withBlitzAuthPlugin<TProps = any>(Page: ComponentType<TProps> | BlitzPage<TProps>) {
const AuthRoot = (props: ComponentProps<any>) => {
useSession({suspense: false})
const [mounted, setMounted] = useState(false)
useEffect(() => {
setMounted(true)
}, [])
let {authenticate, redirectAuthenticatedTo} = getAuthValues(Page, props)
@@ -273,10 +290,12 @@ function withBlitzAuthPlugin<TProps = any>(Page: ComponentType<TProps> | BlitzPa
? redirectAuthenticatedTo
: formatWithValidation(redirectAuthenticatedTo)
debug("[BlitzAuthInnerRoot] redirecting to", redirectUrl)
const error = new RedirectError(redirectUrl)
error.stack = null!
throw error
if (mounted) {
debug("[BlitzAuthInnerRoot] redirecting to", redirectUrl)
const error = new RedirectError(redirectUrl)
error.stack = null!
throw error
}
}
} else {
debug("[BlitzAuthInnerRoot] logged out")
@@ -288,10 +307,13 @@ function withBlitzAuthPlugin<TProps = any>(Page: ComponentType<TProps> | BlitzPa
const url = new URL(redirectTo, window.location.href)
url.searchParams.append("next", window.location.pathname)
debug("[BlitzAuthInnerRoot] redirecting to", url.toString())
const error = new RedirectError(url.toString())
error.stack = null!
throw error
if (mounted) {
debug("[BlitzAuthInnerRoot] redirecting to", url.toString())
const error = new RedirectError(url.toString())
error.stack = null!
throw error
}
}
}
}

View File

@@ -1,5 +1,18 @@
# @blitzjs/next
## 2.0.0-beta.10
### Patch Changes
- 1742eb45: Fix prefetching infinite Blitz queries.
- 9fe0cc54: Fix auth related React hydration errors by not redirecting until after component mount.
- e2c18895: Add client testing utilities and a sample test to a new blitz app template
- 25f4526f: Treat API Route handler as a middleware. This allows outer middlewares to completely wrap queries and mutations.
- Updated dependencies [0edeaa37]
- Updated dependencies [aa34661f]
- Updated dependencies [8e0c9d76]
- @blitzjs/rpc@2.0.0-beta.10
## 2.0.0-beta.4
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@blitzjs/next",
"version": "2.0.0-beta.4",
"version": "2.0.0-beta.10",
"scripts": {
"build": "unbuild",
"dev": "pnpm predev && pnpm watch unbuild src --wait=0.2",
@@ -24,7 +24,7 @@
"eslint.js"
],
"dependencies": {
"@blitzjs/rpc": "2.0.0-beta.4",
"@blitzjs/rpc": "2.0.0-beta.10",
"@tanstack/react-query": "4.0.10",
"@types/hoist-non-react-statics": "3.3.1",
"debug": "4.3.3",
@@ -34,7 +34,7 @@
"supports-color": "8.1.1"
},
"devDependencies": {
"@blitzjs/config": "workspace:2.0.0-beta.4",
"@blitzjs/config": "workspace:2.0.0-beta.10",
"@testing-library/dom": "8.13.0",
"@testing-library/jest-dom": "5.16.3",
"@testing-library/react": "13.0.0",
@@ -44,7 +44,7 @@
"@types/react": "18.0.17",
"@types/react-dom": "17.0.14",
"@types/testing-library__react-hooks": "4.0.0",
"blitz": "2.0.0-beta.4",
"blitz": "2.0.0-beta.5",
"cross-spawn": "7.0.3",
"find-up": "4.1.0",
"next": "12.2.5",

View File

@@ -81,6 +81,7 @@ class ErrorBoundary extends React.Component<
state = initialState
updatedWithError = false
resetErrorBoundary = (...args: Array<unknown>) => {
this.props.onReset?.(...args)
this.reset()
@@ -94,6 +95,7 @@ class ErrorBoundary extends React.Component<
async componentDidCatch(error: Error, info: React.ErrorInfo) {
if (error instanceof RedirectError) {
debug("Redirecting from ErrorBoundary to", error.url)
await (this.context as Router)?.push(error.url)
return
}

View File

@@ -1,10 +1,5 @@
import "./global"
import type {
ClientPlugin,
BlitzProvider as BlitzProviderType,
UnionToIntersection,
Simplify,
} from "blitz"
import type {ClientPlugin, BlitzProviderComponentType, UnionToIntersection, Simplify} from "blitz"
import Head from "next/head"
import React, {ReactNode} from "react"
import {QueryClient, QueryClientProvider, Hydrate, HydrateOptions} from "@tanstack/react-query"
@@ -22,14 +17,14 @@ export * from "./router-context"
export {Routes} from ".blitz"
const compose =
(...rest: BlitzProviderType[]) =>
(...rest: BlitzProviderComponentType[]) =>
(x: React.ComponentType<any>) =>
rest.reduceRight((y, f) => f(y), x)
const buildWithBlitz = <TPlugins extends readonly ClientPlugin<object>[]>(plugins: TPlugins) => {
const providers = plugins.reduce((acc, plugin) => {
return plugin.withProvider ? acc.concat(plugin.withProvider) : acc
}, [] as BlitzProviderType[])
}, [] as BlitzProviderComponentType[])
const withPlugins = compose(...providers)

View File

@@ -116,8 +116,13 @@ const prefetchQueryFactory = (
queryClient = new QueryClient({defaultOptions})
}
const queryKey = infinite ? getInfiniteQueryKey(fn, input) : getQueryKey(fn, input)
await queryClient.prefetchQuery(queryKey, () => fn(input, ctx))
if (infinite) {
await queryClient.prefetchInfiniteQuery(getInfiniteQueryKey(fn, input), () =>
fn(input, ctx),
)
} else {
await queryClient.prefetchQuery(getQueryKey(fn, input), () => fn(input, ctx))
}
},
}
}
@@ -140,7 +145,8 @@ export const setupBlitzServer = ({plugins, onError}: SetupBlitzOptions) => {
const {getClient, prefetchQuery} = prefetchQueryFactory(ctx)
ctx.prefetchQuery = prefetchQuery
ctx.prefetchInfiniteQuery = (...args) => prefetchQuery(...args, true)
ctx.prefetchInfiniteQuery = (fn, input, defaultOptions = {}) =>
prefetchQuery(fn, input, defaultOptions, true)
try {
const result = await handler({req, res, ctx, ...rest})
@@ -187,8 +193,10 @@ export const setupBlitzServer = ({plugins, onError}: SetupBlitzOptions) => {
): NextApiHandler<TResult | void> =>
async (req, res) => {
try {
await handleRequestWithMiddleware(req, res, middlewares)
return handler(req, res, res.blitzCtx)
return await handleRequestWithMiddleware(req, res, [
...middlewares,
(req, res) => handler(req, res, res.blitzCtx),
])
} catch (error: any) {
onError?.(error)
return res.status(400).send(error)

View File

@@ -1,5 +1,15 @@
# @blitzjs/rpc
## 2.0.0-beta.10
### Patch Changes
- 0edeaa37: Allow for custom page extensions for the wildcard blitz route. For example [...blitz].api.ts. For more information vist https://nextjs.org/docs/api-reference/next.config.js/custom-page-extensions
- aa34661f: Fix invalidateQuery generating wrong param when no param argument is passed
- 8e0c9d76: Migrate over recipe functionality from legacy framework & expose recipe builder helper functions that find and modify next.config.js, blitz-server & blitz-client.
- Updated dependencies [9fe0cc54]
- @blitzjs/auth@2.0.0-beta.10
## 2.0.0-beta.4
### Patch Changes

View File

@@ -1,9 +1,9 @@
{
"name": "@blitzjs/rpc",
"version": "2.0.0-beta.4",
"version": "2.0.0-beta.10",
"scripts": {
"build": "unbuild",
"predev": "wait-on -d 250 ../blitz/dist/index-server.d.ts && wait-on -d 250 ../blitz-auth/dist/index-browser.d.ts",
"predev": "wait-on -d 400 ../blitz/dist/index-server.d.ts && wait-on -d 400 ../blitz-auth/dist/index-browser.d.ts",
"dev": "pnpm run predev && watch unbuild src --wait=0.2",
"lint": "eslint . --fix",
"test": "vitest run",
@@ -20,7 +20,7 @@
"dist/**"
],
"dependencies": {
"@blitzjs/auth": "2.0.0-beta.4",
"@blitzjs/auth": "2.0.0-beta.10",
"@tanstack/react-query": "4.0.10",
"b64-lite": "1.4.0",
"bad-behavior": "1.0.1",
@@ -30,11 +30,11 @@
"supports-color": "8.1.1"
},
"devDependencies": {
"@blitzjs/config": "workspace:2.0.0-beta.4",
"@blitzjs/config": "workspace:2.0.0-beta.10",
"@types/debug": "4.1.7",
"@types/react": "18.0.17",
"@types/react-dom": "17.0.14",
"blitz": "2.0.0-beta.4",
"blitz": "2.0.0-beta.5",
"next": "12.2.5",
"react": "18.2.0",
"react-dom": "18.2.0",

View File

@@ -0,0 +1,77 @@
import {describe, expect, it} from "vitest"
import superJson from "superjson"
import {getQueryKey, getQueryKeyFromUrlAndParams} from "./react-query-utils"
import {RpcClient} from "./rpc"
const API_ENDPOINT = "http://localhost:3000"
const constructData = (arg: any) => {
return {
data: arg,
expected: superJson.serialize(arg),
}
}
describe("react-query-utils", () => {
describe("getQueryKeyFromUrlAndParams", () => {
it("returns a query key with string arg", () => {
const {data, expected} = constructData("RandomString")
expect(getQueryKeyFromUrlAndParams(API_ENDPOINT, data)).toEqual([API_ENDPOINT, expected])
})
it("returns a query key with object arg", () => {
const {data, expected} = constructData({id: 1, name: "test", field: undefined})
expect(getQueryKeyFromUrlAndParams(API_ENDPOINT, data)).toEqual([API_ENDPOINT, expected])
})
it("returns a query key with undefined arg", () => {
const {data, expected} = constructData(undefined)
expect(getQueryKeyFromUrlAndParams(API_ENDPOINT, data)).toEqual([API_ENDPOINT, expected])
})
it("returns a query key with null arg", () => {
const {data, expected} = constructData(null)
expect(getQueryKeyFromUrlAndParams(API_ENDPOINT, data)).toEqual([API_ENDPOINT, expected])
})
it("if no argument is passed it returns only url", () => {
const queryKey = getQueryKeyFromUrlAndParams(API_ENDPOINT)
expect(queryKey).toEqual([API_ENDPOINT])
})
})
describe("getQueryKey", () => {
// @ts-expect-error - we just need these 3 params
const query: RpcClient<{}, null> = {
_resolverName: "randomQuery",
_resolverType: "query",
_routePath: API_ENDPOINT,
}
it("returns a query key with string arg", () => {
const {data, expected} = constructData("RandomString")
expect(getQueryKey(query, data)).toEqual([API_ENDPOINT, expected])
})
it("returns a query key with object arg", () => {
const {data, expected} = constructData({id: 1, name: "test", field: undefined})
expect(getQueryKey(query, data)).toEqual([API_ENDPOINT, expected])
})
it("returns a query key with undefined arg", () => {
const {data, expected} = constructData(undefined)
expect(getQueryKey(query, data)).toEqual([API_ENDPOINT, expected])
})
it("returns a query key with null arg", () => {
const {data, expected} = constructData(null)
expect(getQueryKey(query, data)).toEqual([API_ENDPOINT, expected])
})
it("if no argument is passed it returns only url", () => {
const queryKey = getQueryKey(query)
expect(queryKey).toEqual([API_ENDPOINT])
})
})
})

View File

@@ -1,4 +1,4 @@
import {QueryClient, QueryFilters} from "@tanstack/react-query"
import {QueryClient} from "@tanstack/react-query"
import {serialize} from "superjson"
import {isClient, isServer, AsyncFunc} from "blitz"
import {ResolverType, RpcClient} from "./rpc"
@@ -124,24 +124,29 @@ const sanitize =
export const sanitizeQuery = sanitize("query")
export const sanitizeMutation = sanitize("mutation")
export const getQueryKeyFromUrlAndParams = (url: string, params: unknown) => {
const queryKey = [url]
type BlitzQueryKey = [string] | [string, any]
export const getQueryKeyFromUrlAndParams = (
url: string,
...params: [unknown] | []
): BlitzQueryKey => {
const queryKey: BlitzQueryKey = [url]
if (params.length === 1) {
const param = params[0]
queryKey.push(serialize(typeof param === "function" ? param() : param) as any)
}
const args = typeof params === "function" ? (params as Function)() : params
queryKey.push(serialize(args) as any)
return queryKey as [string, any]
return queryKey
}
export function getQueryKey<TInput, TResult, T extends AsyncFunc>(
resolver: T | Resolver<TInput, TResult> | RpcClient<TInput, TResult>,
params?: TInput,
...params: [TInput] | []
) {
if (typeof resolver === "undefined") {
throw new Error("getQueryKey is missing the first argument - it must be a resolver function")
}
return getQueryKeyFromUrlAndParams(sanitizeQuery(resolver)._routePath, params)
return getQueryKeyFromUrlAndParams(sanitizeQuery(resolver)._routePath, ...params)
}
export function getInfiniteQueryKey<TInput, TResult, T extends AsyncFunc>(
@@ -158,17 +163,23 @@ export function getInfiniteQueryKey<TInput, TResult, T extends AsyncFunc>(
return [...queryKey, "infinite"]
}
export function invalidateQuery<TInput, TResult, T extends AsyncFunc>(
type InvalidateQueryTypeWithParams = <TInput, TResult, T extends AsyncFunc>(
resolver: T | Resolver<TInput, TResult> | RpcClient<TInput, TResult>,
params?: TInput,
) {
...params: [TInput]
) => Promise<void>
type InvalidateQueryTypeAllQueries = <TInput, TResult, T extends AsyncFunc>(
resolver: T | Resolver<TInput, TResult> | RpcClient<TInput, TResult>,
) => Promise<void>
type InvalidateQueryType = InvalidateQueryTypeWithParams & InvalidateQueryTypeAllQueries
export const invalidateQuery: InvalidateQueryType = (resolver, ...params: []) => {
if (typeof resolver === "undefined") {
throw new Error(
"invalidateQuery is missing the first argument - it must be a resolver function",
)
}
const fullQueryKey = getQueryKey(resolver, params)
const fullQueryKey = getQueryKey(resolver, ...params)
return getQueryClient().invalidateQueries(fullQueryKey)
}

View File

@@ -88,7 +88,7 @@ export function installWebpackConfig({
webpackConfig.resolve.alias["npm-which"] = false
webpackConfig.resolve.alias["cross-spawn"] = false
webpackConfig.module.rules.push({
test: /[\\/]\[\[\.\.\.blitz]]\.[jt]sx?$/,
test: /[\\/]\[\[\.\.\.blitz]]?.+\.[jt]sx?$/,
use: [
{
loader: loaderServer,

View File

@@ -1,5 +1,30 @@
# blitz
## 2.0.0-beta.5
### Patch Changes
- 9db6c885: Fix `blitz --help` CLI command not being found
- d98e4bac: Add `blitz routes` CLI command back to toolkit
- 9fe0cc54: Fix auth related React hydration errors by not redirecting until after component mount.
- af58e2b2: Add a global Blitz version check when generating a new Blitz project to ensure users use the latest Blitz.
- 2ade7268: Add `blitz export` CLI command to toolkit
- 0edeaa37: Allow for custom page extensions for the wildcard blitz route. For example [...blitz].api.ts. For more information vist https://nextjs.org/docs/api-reference/next.config.js/custom-page-extensions
- 430f6ec7: Only generate the prisma client if it's not found in node_modules when running a blitz cli command.
- 15d22af2: Add `blitz console` CLI command back to toolkit
- aa34661f: Fix invalidateQuery generating wrong param when no param argument is passed
- 8e0c9d76: Migrate over recipe functionality from legacy framework & expose recipe builder helper functions that find and modify next.config.js, blitz-server & blitz-client.
- e2c18895: Add client testing utilities and a sample test to a new blitz app template
- Updated dependencies [04730205]
- Updated dependencies [824a9b5e]
- Updated dependencies [d6717b9d]
- Updated dependencies [bf4aaf1d]
- Updated dependencies [b43c1a81]
- Updated dependencies [83281a84]
- Updated dependencies [bd09db75]
- Updated dependencies [e2c18895]
- @blitzjs/generator@2.0.0-beta.10
## 2.0.0-beta.4
### Patch Changes

View File

@@ -1,7 +1,7 @@
import {BuildConfig} from "unbuild"
const config: BuildConfig = {
entries: ["./src/index-browser", "./src/index-server", "./src/cli/index"],
entries: ["./src/index-browser", "./src/index-server", "./src/cli/index", "./src/installer"],
externals: ["index-browser.cjs", "index-browser.mjs", "index.cjs", "zod", "react"],
declaration: true,
rollup: {

1
packages/blitz/installer.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
export * from "./dist/installer"

View File

@@ -0,0 +1 @@
module.exports = require("./dist/installer.cjs")

View File

@@ -1,13 +1,14 @@
{
"name": "blitz",
"version": "2.0.0-beta.4",
"version": "2.0.0-beta.5",
"scripts": {
"build": "unbuild",
"dev": "watch unbuild src --wait=0.2",
"dev": "pnpm run predev && watch unbuild src --wait=0.2",
"lint": "eslint . --fix",
"test": "vitest run",
"test-watch": "vitest",
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist"
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist",
"predev": "wait-on -d 250 ../generator/dist/index.d.ts"
},
"main": "./dist/index-server.cjs",
"module": "./dist/index-server.mjs",
@@ -23,39 +24,54 @@
"blitz": "bin/blitz"
},
"dependencies": {
"@blitzjs/generator": "2.0.0-beta.4",
"@blitzjs/generator": "2.0.0-beta.10",
"@mrleebo/prisma-ast": "0.2.6",
"@types/global-agent": "2.1.1",
"arg": "5.0.1",
"ast-types": "0.14.2",
"boxen": "7.0.0",
"chalk": "^4.1.0",
"chokidar": "3.5.3",
"console-table-printer": "2.10.0",
"cross-spawn": "7.0.3",
"debug": "4.3.3",
"detect-port": "1.3.0",
"diff": "5.0.0",
"dotenv": "16.0.0",
"dotenv-expand": "8.0.3",
"envinfo": "7.8.1",
"esbuild": "0.14.34",
"esbuild-register": "3.3.3",
"find-up": "4.1.0",
"fs-extra": "10.0.1",
"global-agent": "3.0.0",
"globby": "13.1.2",
"got": "^11.8.1",
"hasbin": "1.2.3",
"ink": "3.2.0",
"ink-spinner": "4.0.3",
"jscodeshift": "0.13.0",
"node-fetch": "3.2.3",
"npm-which": "3.0.1",
"ora": "5.3.0",
"os-name": "5.0.1",
"p-event": "4.2.0",
"pkg-dir": "5.0.0",
"progress": "2.0.3",
"prompts": "2.4.2",
"recast": "0.20.5",
"resolve-cwd": "3.0.0",
"resolve-from": "5.0.0",
"superjson": "1.9.1",
"supports-color": "8.1.1",
"tar": "6.1.11",
"ts-node": "10.7.0",
"tsconfig-paths": "4.0.0",
"tslog": "3.3.1",
"watchpack": "2.1.1"
},
"devDependencies": {
"@blitzjs/config": "workspace:2.0.0-beta.4",
"@blitzjs/config": "workspace:2.0.0-beta.10",
"@types/cookie": "0.4.1",
"@types/cross-spawn": "6.0.2",
"@types/debug": "4.1.7",
@@ -64,8 +80,10 @@
"@types/express": "4.17.13",
"@types/fs-extra": "9.0.13",
"@types/hasbin": "1.2.0",
"@types/jscodeshift": "0.11.2",
"@types/node-fetch": "2.6.1",
"@types/npm-which": "3.0.1",
"@types/progress": "2.0.5",
"@types/prompts": "2.0.14",
"@types/react": "18.0.17",
"@types/react-dom": "17.0.14",

View File

@@ -0,0 +1,47 @@
import {CliCommand} from "../index"
import arg from "arg"
import chalk from "chalk"
import {log} from "../../logging"
import {runRepl, getDbFolder} from "../utils/next-console"
const args = arg(
{
// Types
"--only-db": Boolean,
// Aliases
"-d": "--only-db",
},
{
permissive: true,
},
)
const replOptions = {
prompt: "⚡️ > ",
useColors: true,
}
const consoleREPL: CliCommand = async () => {
process.env.CLI_COMMAND_CONSOLE = "true"
log.branded("You have entered the Blitz console")
console.log(chalk.yellow("Tips: - Exit by typing .exit or pressing Ctrl-D"))
console.log(chalk.yellow(` - Use your db like this: ${getDbFolder()}.project.findMany()`))
console.log(chalk.yellow(" - Use your queries/mutations like this: getProjects({})"))
const {register} = require("esbuild-register/dist/node")
const {unregister} = register({
target: "es6",
})
const onlyDb = args["--only-db"] as boolean
if (onlyDb) {
console.log(chalk.green(`Loading only ${getDbFolder()} module`))
}
await runRepl(replOptions, onlyDb)
unregister()
}
export {consoleREPL}

View File

@@ -0,0 +1,319 @@
import arg from "arg"
import {CliCommand} from "../index"
import prompts from "prompts"
import {bootstrap} from "global-agent"
import {baseLogger, log} from "../../logging"
const debug = require("debug")("blitz:cli")
import {join, resolve} from "path"
import {Stream} from "stream"
import {promisify} from "util"
import {RecipeCLIFlags, RecipeExecutor} from "../../installer"
import {setupTsnode} from "../utils/setup-ts-node"
interface GlobalAgent {
HTTP_PROXY?: string
HTTPS_PROXY?: string
NO_PROXY?: string
}
declare global {
var GLOBAL_AGENT: GlobalAgent
}
const args = arg(
{
// Types
"--help": Boolean,
"--env": String,
"--yes": Boolean,
// Aliases
"-e": "--env",
"-y": "--yes",
},
{
permissive: true,
},
)
const pipeline = promisify(Stream.pipeline)
const got = async (url: string) => {
return require("got")(url).catch((e: any) => {
if (e.response.statusCode === 403) {
baseLogger({displayDateTime: false}).error(e.response.body)
} else {
return e
}
})
}
const gotJSON = async (url: string) => {
debug("[gotJSON] Downloading json from ", url)
const res = await got(url)
return JSON.parse(res.body)
}
const isUrlValid = async (url: string) => {
return (await got(url).catch((e) => e)).statusCode === 200
}
const requireJSON = (file: string) => {
return JSON.parse(require("fs-extra").readFileSync(file).toString("utf-8"))
}
const checkLockFileExists = (filename: string) => {
return require("fs-extra").existsSync(resolve(filename))
}
const GH_ROOT = "https://github.com/"
const API_ROOT = "https://api.github.com/repos/"
const RAW_ROOT = "https://raw.githubusercontent.com/"
const CODE_ROOT = "https://codeload.github.com/"
export enum RecipeLocation {
Local,
Remote,
}
interface RecipeMeta {
path: string
subdirectory?: string
location: RecipeLocation
}
interface Tree {
path: string
mode: string
type: string
sha: string
size: number
url: string
}
interface GithubRepoAPITrees {
sha: string
url: string
tree: Tree[]
truncated: boolean
}
const getOfficialRecipeList = async (): Promise<string[]> => {
return await gotJSON(`${API_ROOT}blitz-js/blitz/git/trees/main?recursive=1`).then(
(release: GithubRepoAPITrees) =>
release.tree.reduce((recipesList: string[], item) => {
const filePath = item.path.split("/")
const [directory, recipeName] = filePath
if (
directory === "recipes" &&
filePath.length === 2 &&
item.type === "tree" &&
recipeName
) {
recipesList.push(recipeName)
}
return recipesList
}, []),
)
}
const normalizeRecipePath = (recipeArg: string): RecipeMeta => {
const isNativeRecipe = /^([\w\-_]*)$/.test(recipeArg)
const isUrlRecipe = recipeArg.startsWith(GH_ROOT)
const isGitHubShorthandRecipe = /^([\w-_]*)\/([\w-_]*)$/.test(recipeArg)
if (isNativeRecipe || isUrlRecipe || isGitHubShorthandRecipe) {
let repoUrl
let subdirectory
switch (true) {
case isUrlRecipe:
repoUrl = recipeArg
break
case isNativeRecipe:
repoUrl = `${GH_ROOT}blitz-js/blitz`
subdirectory = `recipes/${recipeArg}`
break
case isGitHubShorthandRecipe:
repoUrl = `${GH_ROOT}${recipeArg}`
break
default:
throw new Error(
"should be impossible, the 3 cases are the only way to get into this switch",
)
}
return {
path: repoUrl,
subdirectory,
location: RecipeLocation.Remote,
}
} else {
return {
path: recipeArg,
location: RecipeLocation.Local,
}
}
}
const cloneRepo = async (
repoFullName: string,
defaultBranch: string,
subdirectory?: string,
): Promise<string> => {
debug("[cloneRepo] starting...")
const recipeDir = join(process.cwd(), ".blitz", "recipe-install")
// clean up from previous run in case of error
require("rimraf").sync(recipeDir)
require("fs-extra").mkdirsSync(recipeDir)
process.chdir(recipeDir)
debug("Extracting recipe to ", recipeDir)
const repoName = repoFullName.split("/")[1]
// `tar` top-level filter is `${repoName}-${defaultBranch}`, and then we want to get our recipe path
// within that folder
const extractPath = subdirectory ? [`${repoName}-${defaultBranch}/${subdirectory}`] : undefined
const depth = subdirectory ? subdirectory.split("/").length + 1 : 1
await pipeline(
require("got").stream(`${CODE_ROOT}${repoFullName}/tar.gz/${defaultBranch}`),
require("tar").extract({strip: depth}, extractPath),
)
return recipeDir
}
const installRecipeAtPath = async (
recipePath: string,
...runArgs: Parameters<RecipeExecutor<any>["run"]>
) => {
const recipe = require(recipePath).default as RecipeExecutor<any>
await recipe.run(...runArgs)
}
const setupProxySupport = async () => {
const httpProxy = process.env.http_proxy || process.env.HTTP_PROXY
const httpsProxy = process.env.https_proxy || process.env.HTTPS_PROXY
const noProxy = process.env.no_proxy || process.env.NO_PROXY
if (httpProxy || httpsProxy) {
globalThis.GLOBAL_AGENT = {
HTTP_PROXY: httpProxy,
HTTPS_PROXY: httpsProxy,
NO_PROXY: noProxy,
}
bootstrap()
}
}
const install: CliCommand = async () => {
setupTsnode()
let selectedRecipe: string | null = args._[1] ? `${args._[1]}` : null
await setupProxySupport()
if (!selectedRecipe) {
const officialRecipeList = await getOfficialRecipeList()
const res = await prompts({
type: "select",
name: "recipeName",
message: "Select a recipe to install",
choices: officialRecipeList.map((r) => {
return {title: r, value: r}
}),
})
selectedRecipe = res.recipeName
}
if (selectedRecipe) {
const recipeInfo = normalizeRecipePath(selectedRecipe)
// Take all the args after the recipe string
//
// ['material-ui', '--yes', 'prop=true']
// --> ['material-ui', 'prop=true']
// --> ['prop=true']
// --> { prop: 'true' }
const cliArgs = args._.filter((arg) => !arg.startsWith("--"))
.slice(2)
.reduce(
(acc, arg) => ({
...acc,
[`${arg.split("=")[0]}`]: arg.split("=")[1] ? JSON.parse(`"${arg.split("=")[1]}"`) : true, // if no value is provided, assume it's a boolean flag
}),
{},
)
const cliFlags: RecipeCLIFlags = {
yesToAll: args["--yes"] || false,
}
const chalk = (await import("chalk")).default
if (recipeInfo.location === RecipeLocation.Remote) {
const apiUrl = recipeInfo.path.replace(GH_ROOT, API_ROOT)
const rawUrl = recipeInfo.path.replace(GH_ROOT, RAW_ROOT)
const repoInfo = await gotJSON(apiUrl)
const packageJsonPath = join(
`${rawUrl}`,
repoInfo.default_branch,
recipeInfo.subdirectory ?? "",
"package.json",
)
if (!(await isUrlValid(packageJsonPath))) {
debug("Url is invalid for ", packageJsonPath)
baseLogger({displayDateTime: false}).error(`Could not find recipe "${args._[1]}"\n`)
console.log(`${chalk.bold("Please provide one of the following:")}
1. The name of a recipe to install (e.g. "tailwind")
${chalk.dim("- Available recipes listed at https://github.com/blitz-js/blitz/tree/main/recipes")}
2. The full name of a GitHub repository (e.g. "blitz-js/example-recipe"),
3. A full URL to a Github repository (e.g. "https://github.com/blitz-js/example-recipe"), or
4. A file path to a locally-written recipe.\n`)
process.exit(1)
} else {
let spinner = log.spinner(`Cloning GitHub repository for ${selectedRecipe} recipe`).start()
const recipeRepoPath = await cloneRepo(
repoInfo.full_name,
repoInfo.default_branch,
recipeInfo.subdirectory,
)
spinner.stop()
spinner = log.spinner("Installing package.json dependencies").start()
let pkgManager = "npm"
let installArgs = ["install", "--legacy-peer-deps", "--ignore-scripts"]
if (checkLockFileExists("yarn.lock")) {
pkgManager = "yarn"
installArgs = ["install", "--ignore-scripts"]
} else if (checkLockFileExists("pnpm-lock.yaml")) {
pkgManager = "pnpm"
installArgs = ["install", "--ignore-scripts"]
}
await new Promise((resolve) => {
const installProcess = require("cross-spawn")(pkgManager, installArgs)
installProcess.on("exit", resolve)
})
spinner.stop()
const recipePackageMain = requireJSON("./package.json").main
const recipeEntry = resolve(recipePackageMain)
process.chdir(process.cwd())
await installRecipeAtPath(recipeEntry, cliArgs, cliFlags)
require("rimraf").sync(recipeRepoPath)
}
} else {
try {
await installRecipeAtPath(resolve(`${args._[1]}`), cliArgs, cliFlags)
} catch (err) {
if (err instanceof Error) {
throw new Error(err.message)
}
console.log(err)
}
}
}
}
export {install}

View File

@@ -10,7 +10,8 @@ import {runPrisma} from "../../utils/run-prisma"
import {checkLatestVersion} from "../utils/check-latest-version"
import {codegenTasks} from "../utils/codegen-tasks"
const forms: Record<AppGeneratorOptions["form"], string> = {
type NotUndefined<T> = T extends undefined ? never : T
const forms: Record<NotUndefined<AppGeneratorOptions["form"]>, string> = {
finalform: "React Final Form (recommended)",
hookform: "React Hook Form",
formik: "Formik",
@@ -225,7 +226,7 @@ const determinePkgManagerToInstallDeps = async () => {
}
const newApp: CliCommand = async (argv) => {
const shouldUpgrade = false // !args["--skip-upgrade"]
const shouldUpgrade = !args["--skip-upgrade"]
if (shouldUpgrade) {
await checkLatestVersion()
}

View File

@@ -0,0 +1,52 @@
import arg from "arg"
import {resolve} from "path"
import {CliCommand} from "../../index"
import {ServerConfig} from "../../utils/config"
const nextExport: CliCommand = async () => {
const nextArgs = arg(
{
// Types
"--help": Boolean,
"--outdir": String,
// Aliases
"-h": "--help",
"-o": "--outdir",
},
{
permissive: true,
},
)
const config: ServerConfig = {
rootFolder: process.cwd(),
...(nextArgs["--outdir"] && {outdir: resolve(nextArgs["--outdir"])}),
env: process.env.NODE_ENV === "production" ? "prod" : "dev",
}
const getHelp = async () => {
if (nextArgs["--help"]) {
console.log(`
Description
Exports your Blitz app as a static application. Make sure to run "blitz build" before!
Usage
$ blitz export [options] <dir>
<dir> represents the directory of the Blitz.js application. If no directory is provided, the current directory will be used.
Options
--help -h — help
-o — set the output dir (default: '/out')
`)
process.exit(0)
}
}
await getHelp()
await import("../../utils/next-commands").then((i) => i.blitzExport(config))
}
export {nextExport}

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