From e465983cacdddb33a8d931cafa8cdc28ec63ed43 Mon Sep 17 00:00:00 2001 From: Alex Dolan Date: Tue, 23 Mar 2021 18:26:21 -0500 Subject: [PATCH 01/67] updated prisma migrate usage in docs (#419) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: JuanM04 Co-authored-by: Andreas Bollig Co-authored-by: Abu Uzayr Co-authored-by: François Best Co-authored-by: Faraz Patankar Co-authored-by: Eric Vicenti Co-authored-by: Alex --- app/core/layouts/ContentsLayout.js | 2 +- app/core/layouts/DocumentationLayout.js | 2 +- app/core/navs/documentation.js | 570 ------------------ app/core/navs/documentation.json | 5 +- app/navs/documentation.js | 242 -------- app/pages/docs/api-routes.mdx | 2 +- app/pages/docs/database-overview.mdx | 9 +- app/pages/docs/deploy-heroku.mdx | 2 +- app/pages/docs/deploy-render.mdx | 6 +- app/pages/docs/deploy-vercel.mdx | 4 +- app/pages/docs/error-handling.mdx | 6 +- app/pages/docs/error-pages.mdx | 66 ++ app/pages/docs/how-the-community-operates.mdx | 2 +- app/pages/docs/image-optimization.mdx | 42 +- app/pages/docs/link.mdx | 4 +- app/pages/docs/postgres.mdx | 4 +- app/pages/docs/router.mdx | 32 +- app/pages/docs/shallow-routing.mdx | 67 ++ app/pages/index.js | 2 +- blitz.config.js | 33 + package.json | 6 +- postcss.config.js | 2 +- scripts/generate-documentation-nav.js | 38 -- yarn.lock | 22 +- 24 files changed, 256 insertions(+), 914 deletions(-) delete mode 100644 app/core/navs/documentation.js delete mode 100644 app/navs/documentation.js create mode 100644 app/pages/docs/shallow-routing.mdx delete mode 100644 scripts/generate-documentation-nav.js diff --git a/app/core/layouts/ContentsLayout.js b/app/core/layouts/ContentsLayout.js index dd0c0fd..b9035fa 100644 --- a/app/core/layouts/ContentsLayout.js +++ b/app/core/layouts/ContentsLayout.js @@ -183,7 +183,7 @@ export function ContentsLayout({children, meta, tableOfContents: toc}) { rel="noopener noreferrer" className="flex items-center py-2 text-sm" > - Idea for improving this page? Edit it on Github. + Idea for improving this page? Edit it on GitHub. {(prev || next) && ( <> diff --git a/app/core/layouts/DocumentationLayout.js b/app/core/layouts/DocumentationLayout.js index dc15da4..2fee2e8 100644 --- a/app/core/layouts/DocumentationLayout.js +++ b/app/core/layouts/DocumentationLayout.js @@ -6,7 +6,7 @@ import {SocialCards} from "@/components/SocialCards" import {Title} from "@/components/Title" import {ContentsLayout} from "@/layouts/ContentsLayout" import {SidebarLayout} from "@/layouts/SidebarLayout" -import {documentationNav} from "@/navs/documentation" +import documentationNav from "@/navs/documentation.json" export function DocumentationLayout({children, ...props}) { const [navIsOpen, setNavIsOpen] = useState(false) diff --git a/app/core/navs/documentation.js b/app/core/navs/documentation.js deleted file mode 100644 index 4fed099..0000000 --- a/app/core/navs/documentation.js +++ /dev/null @@ -1,570 +0,0 @@ -export const documentationNav = [ - { - title: { - title: "Introduction", - iconPath: "/img/introduction.svg", - iconDarkPath: "/img/introduction-white.svg", - }, - pages: [ - { - title: "Get Started with Blitz", - sidebar_label: "Get Started", - href: "/docs/get-started", - }, - { - title: "Tutorial", - sidebar_label: "Tutorial", - href: "/docs/tutorial", - }, - { - title: "What is Next.js?", - sidebar_label: "What is Next.js?", - href: "/docs/what-is-nextjs", - }, - { - title: "Why use Blitz instead of Next.js?", - sidebar_label: "Why Blitz instead of Next.js?", - href: "/docs/why-blitz", - }, - { - title: "Tradeoffs", - sidebar_label: "Tradeoffs", - href: "/docs/tradeoffs", - }, - { - title: "Want Free Stickers?", - sidebar_label: "Free Stickers", - displayUrl: "/stickers", - titleAlign: "center", - hideFooter: true, - href: "/docs/stickers", - }, - ], - }, - { - title: { - title: "Community", - iconPath: "/img/people-purple.svg", - iconDarkPath: "/img/people-white.svg", - }, - pages: [ - { - title: "How the Community Operates", - sidebar_label: "How the Community Operates", - href: "/docs/how-the-community-operates", - }, - { - id: "manifesto", - title: "The Blitz.js Manifesto", - sidebar_label: "Manifesto", - href: "/docs/manifesto", - }, - { - title: "Community History", - sidebar_label: "History", - href: "/docs/community-history", - }, - { - title: "How to Contribute", - sidebar_label: "How to Contribute", - href: "/docs/contributing", - }, - { - title: "❤️ Blitz Maintainers", - sidebar_label: "Being a Maintainer", - href: "/docs/maintainers", - }, - { - id: "code-of-conduct", - title: "The Blitz Community Code of Conduct", - sidebar_label: "Code of Conduct", - href: "/docs/code-of-conduct", - }, - { - title: "Help Us Translate Blitz Docs", - sidebar_label: "Doc Translations", - href: "/docs/translations", - }, - ], - }, - { - title: { - title: "Basics", - iconPath: "/img/basics.svg", - iconDarkPath: "/img/basics-white.svg", - }, - pages: [ - { - id: "file-structure", - title: "File Structure", - sidebar_label: "File Structure", - href: "/docs/file-structure", - }, - { - title: "Root Component", - sidebar_label: "", - href: "/docs/app-component", - }, - { - title: "Built-In CSS Support", - sidebar_label: "Built-In CSS Support", - href: "/docs/css", - }, - { - title: "Image Component and Image Optimization", - sidebar_label: "Image Optimization", - href: "/docs/image-optimization", - }, - { - title: "Static Files & Images", - sidebar_label: "Static Files & Images", - href: "/docs/static-files", - }, - { - title: "Environment Variables", - sidebar_label: "Environment Variables", - href: "/docs/environment-variables", - }, - { - title: "Error Handing", - sidebar_label: "Error Handling", - href: "/docs/error-handling", - }, - { - title: "Testing", - sidebar_label: "Testing", - href: "/docs/testing", - }, - ], - }, - { - title: { - title: "Pages", - iconPath: "/img/pages.svg", - iconDarkPath: "/img/pages-white.svg", - }, - pages: [ - { - title: "Pages", - sidebar_label: "Pages", - href: "/docs/pages", - }, - { - title: "Redirects", - sidebar_label: "Redirects", - href: "/docs/redirects", - }, - { - title: "Error Pages", - sidebar_label: "Error Pages", - href: "/docs/error-pages", - }, - { - title: " Component", - sidebar_label: "", - href: "/docs/head-component", - }, - { - title: "", - sidebar_label: "", - href: "/docs/document-component", - }, - { - title: "Preview Mode", - sidebar_label: "Preview Mode", - href: "/docs/preview-mode", - }, - { - title: "Static HTML Export", - sidebar_label: "Static HTML Export", - href: "/docs/static-html-export", - }, - { - title: "Code Splitting", - sidebar_label: "Code Splitting", - href: "/docs/code-splitting", - }, - { - title: "getStaticProps API", - sidebar_label: "getStaticProps API", - href: "/docs/get-static-props", - }, - { - title: "getStaticPaths API", - sidebar_label: "getStaticPaths API", - href: "/docs/get-static-paths", - }, - { - title: "getServerSideProps API", - sidebar_label: "getServerSideProps API", - href: "/docs/get-server-side-props", - }, - ], - }, - { - title: { - title: "Routing", - iconPath: "/img/routing.svg", - iconDarkPath: "/img/routing-white.svg", - }, - pages: [ - { - title: "File-Based Routing", - sidebar_label: "File-Based Routing", - href: "/docs/routing", - }, - { - title: "Routing Conventions", - sidebar_label: "Conventions", - href: "/docs/routing-conventions", - }, - { - title: "Internationalized Routing", - sidebar_label: "Internationalized Routing", - href: "/docs/i18n-routing", - }, - { - title: "", - sidebar_label: "", - href: "/docs/link", - }, - { - title: "URL Params & Query", - sidebar_label: "URL Params & Query", - href: "/docs/route-params-query", - }, - { - title: "Router", - sidebar_label: "Router", - href: "/docs/router", - }, - { - title: "API Routes", - sidebar_label: "API Routes", - href: "/docs/api-routes", - }, - ], - }, - { - title: { - title: "Auth", - iconPath: "/img/shield-purple.svg", - iconDarkPath: "/img/shield-white.svg", - }, - pages: [ - { - title: "Auth Overview", - sidebar_label: "Overview", - href: "/docs/auth", - }, - { - title: "Session Management", - sidebar_label: "Session Management", - href: "/docs/session-management", - }, - { - title: "Authorization & Security", - sidebar_label: "Authorization & Security", - href: "/docs/authorization", - }, - { - title: "Auth Hooks & Utilities", - sidebar_label: "Hooks & Utilities", - href: "/docs/auth-utils", - }, - { - title: "Third Party Login with Passport.js", - sidebar_label: "Third Party Login w/Passport.js", - href: "/docs/passportjs", - }, - { - title: "How To Impersonate Other Users", - sidebar_label: "How To Impersonate Other Users", - href: "/docs/impersonation", - }, - ], - }, - { - title: { - title: "Database", - iconPath: "/img/database.svg", - iconDarkPath: "/img/database-white.svg", - }, - pages: [ - { - title: "Database Overview", - sidebar_label: "Overview", - href: "/docs/database-overview", - }, - { - title: "Run Postgres Locally", - sidebar_label: "Run Postgres Locally", - href: "/docs/postgres", - }, - { - title: "Database Seeds", - sidebar_label: "Seeds", - href: "/docs/database-seeds", - }, - { - title: "Prisma Utilities", - sidebar_label: "Prisma Utilities", - href: "/docs/prisma", - }, - { - title: "Using Fauna with Blitz.js", - sidebar_label: "Fauna", - href: "/docs/fauna", - }, - ], - }, - { - title: { - title: "Queries & Mutations", - iconPath: "/img/queries.svg", - iconDarkPath: "/img/queries-white.svg", - }, - pages: [ - { - title: "Query Resolvers", - sidebar_label: "Query Resolvers", - href: "/docs/query-resolvers", - }, - { - title: "Using Queries", - sidebar_label: "Use Queries", - href: "/docs/query-usage", - }, - { - title: "Mutation Resolvers", - sidebar_label: "Mutation Resolvers", - href: "/docs/mutation-resolvers", - }, - { - title: "Using Mutations", - sidebar_label: "Use Mutations", - href: "/docs/mutation-usage", - }, - { - title: "Client Utilities", - sidebar_label: "Client Utilities", - href: "/docs/resolver-client-utilities", - }, - { - title: "Server Utilities", - sidebar_label: "Server Utilities", - href: "/docs/resolver-server-utilities", - }, - { - title: "useQuery", - sidebar_label: "useQuery", - href: "/docs/use-query", - }, - { - title: "usePaginatedQuery", - sidebar_label: "usePaginatedQuery", - href: "/docs/use-paginated-query", - }, - { - title: "useInfiniteQuery", - sidebar_label: "useInfiniteQuery", - href: "/docs/use-infinite-query", - }, - { - title: "useMutation", - sidebar_label: "useMutation", - href: "/docs/use-mutation", - }, - ], - }, - { - title: { - title: "Backend Architecture", - iconPath: "/img/mutations.svg", - iconDarkPath: "/img/mutations-white.svg", - }, - pages: [ - { - title: "Background Processing with Quirrel", - sidebar_label: "Background Processing with Quirrel", - href: "/docs/background-processing-with-quirrel", - }, - { - title: "HTTP Middleware", - sidebar_label: "HTTP Middleware", - href: "/docs/middleware", - }, - { - title: "Custom Server", - sidebar_label: "Custom Server", - href: "/docs/custom-server", - }, - ], - }, - { - title: { - title: "Deploying to Production", - iconPath: "/img/deploying-to-production.svg", - iconDarkPath: "/img/deploying-to-production-white.svg", - }, - pages: [ - { - title: "Deploy to a Server on Render.com", - sidebar_label: "To Render.com", - href: "/docs/deploy-render", - }, - { - title: "Deploy to a Server on Heroku", - sidebar_label: "To Heroku", - href: "/docs/deploy-heroku", - }, - { - title: "Deploy Serverless to Vercel", - sidebar_label: "To Vercel", - href: "/docs/deploy-vercel", - }, - ], - }, - { - title: { - title: "Recipes", - iconPath: "/img/recipe-purple.svg", - iconDarkPath: "/img/recipe-white.svg", - }, - pages: [ - { - title: "Using Recipes", - sidebar_label: "Using Recipes", - href: "/docs/using-recipes", - }, - { - title: "Writing Your Own Recipes", - sidebar_label: "Writing Recipes", - href: "/docs/writing-recipes", - }, - ], - }, - { - title: { - title: "Configuration", - iconPath: "/img/config-purple.svg", - iconDarkPath: "/img/config-white.svg", - }, - pages: [ - { - title: "blitz.config.js", - sidebar_label: "blitz.config.js", - href: "/docs/blitz-config", - }, - { - title: "Custom Webpack Config", - sidebar_label: "Webpack Config", - href: "/docs/webpack-config", - }, - { - title: "Custom PostCSS Config", - sidebar_label: "PostCSS Config", - href: "/docs/postcss-config", - }, - { - id: "rpc-specification", - title: "RPC Specification", - sidebar_label: "RPC Specification", - href: "/docs/rpc-specification", - }, - { - title: "Measuring Performance", - sidebar_label: "Measuring Performance", - href: "/docs/measuring-performance", - }, - ], - }, - { - title: { - title: "CLI", - iconPath: "/img/terminal-purple.svg", - iconDarkPath: "/img/terminal-white.svg", - }, - pages: [ - { - title: "Blitz CLI Overview", - sidebar_label: "Overview", - href: "/docs/cli-overview", - }, - { - title: "blitz new", - sidebar_label: "blitz new", - href: "/docs/cli-new", - }, - { - title: "blitz dev", - sidebar_label: "blitz dev", - href: "/docs/cli-dev", - }, - { - title: "blitz start", - sidebar_label: "blitz start", - href: "/docs/cli-start", - }, - { - title: "blitz build", - sidebar_label: "blitz build", - href: "/docs/cli-build", - }, - { - title: "blitz export", - sidebar_label: "blitz export", - href: "/docs/cli-export", - }, - { - title: "blitz db", - sidebar_label: "blitz db", - href: "/docs/cli-db", - }, - { - title: "blitz prisma", - sidebar_label: "blitz prisma", - href: "/docs/cli-prisma", - }, - { - title: "blitz generate", - sidebar_label: "blitz generate", - href: "/docs/cli-generate", - }, - { - title: "blitz console", - sidebar_label: "blitz console", - href: "/docs/cli-console", - }, - { - title: "blitz install", - sidebar_label: "blitz install", - href: "/docs/cli-install", - }, - { - title: "blitz autocomplete", - sidebar_label: "blitz autocomplete", - href: "/docs/cli-autocomplete", - }, - { - title: "blitz routes", - sidebar_label: "blitz routes", - href: "/docs/cli-routes", - }, - ], - }, - { - title: { - title: "Templates", - iconPath: "/img/template-purple.svg", - iconDarkPath: "/img/template-white.svg", - }, - pages: [ - { - title: "Blitz Templating Language", - sidebar_label: "Blitz Templates", - href: "/docs/templates", - }, - ], - }, -] diff --git a/app/core/navs/documentation.json b/app/core/navs/documentation.json index 6e7c444..a20d78e 100644 --- a/app/core/navs/documentation.json +++ b/app/core/navs/documentation.json @@ -53,6 +53,7 @@ "head-component", "document-component", "preview-mode", + "static-html-export", "code-splitting", "get-static-props", "get-static-paths", @@ -72,7 +73,8 @@ "link", "route-params-query", "router", - "api-routes" + "api-routes", + "shallow-routing" ] }, { @@ -167,6 +169,7 @@ "cli-dev", "cli-start", "cli-build", + "cli-export", "cli-db", "cli-prisma", "cli-generate", diff --git a/app/navs/documentation.js b/app/navs/documentation.js deleted file mode 100644 index 2a2ca01..0000000 --- a/app/navs/documentation.js +++ /dev/null @@ -1,242 +0,0 @@ -import {Image} from "blitz" - -import {createPageList} from "@/utils/createPageList" - -const pages = createPageList( - // use compiled location - require.context( - `pages/docs/?meta=title,sidebar_label,shortTitle,published,displayUrl`, - false, - /\.mdx$/, - ), - "docs", -) - -const Title = ({title, iconPath, iconDarkPath}) => ( -
- {iconPath && ( -
- {title} -
- )} - {iconDarkPath && ( -
- {title} -
- )} -
- {title} -
-
-) - -export const documentationNav = [ - { - title: ( - - ), - pages: [ - pages["get-started"], - pages["tutorial"], - pages["what-is-nextjs"], - pages["why-blitz"], - pages["tradeoffs"], - pages["stickers"], - ], - }, - { - title: ( - <Title - title="Community" - iconPath="/img/people-purple.svg" - iconDarkPath="/img/people-white.svg" - /> - ), - pages: [ - pages["how-the-community-operates"], - pages["manifesto"], - pages["community-history"], - pages["contributing"], - pages["maintainers"], - pages["code-of-conduct"], - pages["translations"], - ], - }, - { - title: <Title title="Basics" iconPath="/img/basics.svg" iconDarkPath="/img/basics-white.svg" />, - pages: [ - pages["file-structure"], - pages["app-component"], - pages["css"], - pages["image-optimization"], - pages["static-files"], - pages["environment-variables"], - pages["error-handling"], - pages["testing"], - ], - }, - { - title: <Title title="Pages" iconPath="/img/pages.svg" iconDarkPath="/img/pages-white.svg" />, - pages: [ - pages["pages"], - pages["redirects"], - pages["error-pages"], - pages["head-component"], - pages["document-component"], - pages["preview-mode"], - pages["static-html-export"], - pages["code-splitting"], - pages["get-static-props"], - pages["get-static-paths"], - pages["get-server-side-props"], - ], - }, - { - title: ( - <Title title="Routing" iconPath="/img/routing.svg" iconDarkPath="/img/routing-white.svg" /> - ), - pages: [ - pages["routing"], - pages["routing-conventions"], - pages["i18n-routing"], - pages["link"], - pages["route-params-query"], - pages["router"], - pages["api-routes"], - ], - }, - { - title: ( - <Title title="Auth" iconPath="/img/shield-purple.svg" iconDarkPath="/img/shield-white.svg" /> - ), - pages: [ - pages["auth"], - pages["session-management"], - pages["authorization"], - pages["auth-utils"], - pages["passportjs"], - pages["impersonation"], - ], - }, - { - title: ( - <Title title="Database" iconPath="/img/database.svg" iconDarkPath="/img/database-white.svg" /> - ), - pages: [ - pages["database-overview"], - pages["postgres"], - pages["database-seeds"], - pages["prisma"], - pages["fauna"], - ], - }, - { - title: ( - <Title - title="Queries & Mutations" - iconPath="/img/queries.svg" - iconDarkPath="/img/queries-white.svg" - /> - ), - pages: [ - pages["query-resolvers"], - pages["query-usage"], - pages["mutation-resolvers"], - pages["mutation-usage"], - pages["resolver-client-utilities"], - pages["resolver-server-utilities"], - pages["use-query"], - pages["use-paginated-query"], - pages["use-infinite-query"], - pages["use-mutation"], - ], - }, - { - title: ( - <Title - title="Backend Architecture" - iconPath="/img/mutations.svg" - iconDarkPath="/img/mutations-white.svg" - /> - ), - pages: [ - pages["background-processing-with-quirrel"], - pages["middleware"], - pages["custom-server"], - ], - }, - { - title: ( - <Title - title="Deploying to Production" - iconPath="/img/deploying-to-production.svg" - iconDarkPath="/img/deploying-to-production-white.svg" - /> - ), - pages: [pages["deploy-render"], pages["deploy-heroku"], pages["deploy-vercel"]], - }, - { - title: ( - <Title - title="Recipes" - iconPath="/img/recipe-purple.svg" - iconDarkPath="/img/recipe-white.svg" - /> - ), - pages: [pages["using-recipes"], pages["writing-recipes"]], - }, - { - title: ( - <Title - title="Configuration" - iconPath="/img/config-purple.svg" - iconDarkPath="/img/config-white.svg" - /> - ), - pages: [ - pages["blitz-config"], - pages["webpack-config"], - pages["postcss-config"], - pages["rpc-specification"], - pages["measuring-performance"], - ], - }, - { - title: ( - <Title - title="CLI" - iconPath="/img/terminal-purple.svg" - iconDarkPath="/img/terminal-white.svg" - /> - ), - pages: [ - pages["cli-overview"], - pages["cli-new"], - pages["cli-dev"], - pages["cli-start"], - pages["cli-build"], - pages["cli-export"], - pages["cli-db"], - pages["cli-prisma"], - pages["cli-generate"], - pages["cli-console"], - pages["cli-install"], - pages["cli-autocomplete"], - pages["cli-routes"], - ], - }, - { - title: ( - <Title - title="Templates" - iconPath="/img/template-purple.svg" - iconDarkPath="/img/template-white.svg" - /> - ), - pages: [pages["templates"]], - }, -] diff --git a/app/pages/docs/api-routes.mdx b/app/pages/docs/api-routes.mdx index 2f38470..d141d7c 100644 --- a/app/pages/docs/api-routes.mdx +++ b/app/pages/docs/api-routes.mdx @@ -67,7 +67,7 @@ You can use the `getSession` function to get the session of the user. Here is an example using session in a API route `app/api/customRoute.tsx`: ```ts -import {getSession} from "blitz" +import {getSession, BlitzApiRequest, BlitzApiResponse} from "blitz" export default async function customRoute( req: BlitzApiRequest, diff --git a/app/pages/docs/database-overview.mdx b/app/pages/docs/database-overview.mdx index 4f601d3..5ef504c 100644 --- a/app/pages/docs/database-overview.mdx +++ b/app/pages/docs/database-overview.mdx @@ -40,8 +40,8 @@ model Task { > If you need, > [reference the Prisma Schema documentation](https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-schema/data-model) -2. Then run `blitz prisma migrate dev --preview-feature` in your terminal - to apply the changes +2. Then run `blitz prisma migrate dev` in your terminal to apply the + changes 3. Now you can import `db` from `db/index.ts` and create a model like this: - `db.project.create({data: {name: 'Hello'}})` @@ -67,6 +67,5 @@ datasource db { your setup, you may need to modify the URL. 3. Update `.env.test.local` as well to use PostgreSQL in your test runs. 4. Delete the `db/migrations` folder -5. Run `blitz prisma migrate dev --preview-feature`. This command will - create the database (if it does not already exist) and tables based on - your schema. +5. Run `blitz prisma migrate dev`. This command will create the database + (if it does not already exist) and tables based on your schema. diff --git a/app/pages/docs/deploy-heroku.mdx b/app/pages/docs/deploy-heroku.mdx index 8afcf4d..b86e440 100644 --- a/app/pages/docs/deploy-heroku.mdx +++ b/app/pages/docs/deploy-heroku.mdx @@ -62,7 +62,7 @@ heroku git:remote --app <APP_NAME> following content. ``` -release: npx blitz prisma migrate deploy --preview-feature +release: npx blitz prisma migrate deploy web: npm run start:production ``` diff --git a/app/pages/docs/deploy-render.mdx b/app/pages/docs/deploy-render.mdx index 52099af..1e49345 100644 --- a/app/pages/docs/deploy-render.mdx +++ b/app/pages/docs/deploy-render.mdx @@ -4,10 +4,10 @@ sidebar_label: To Render.com --- 1. Add one of the `render.yaml` files shown below -2. Push code to your github repo +2. Push code to your GitHub repo 3. Log in to [Render.com](https://render.com) 4. Click on the "YAML" menu item, then click the "New from YAML" button -5. Connect your github account then select your blitz app repo +5. Connect your GitHub account then select your blitz app repo 6. Click approve 7. Go to "Services", click on your new Blitz service, click on "Environment" @@ -49,7 +49,7 @@ services: buildCommand: yarn --frozen-lockfile --prod=false && blitz build && - blitz prisma migrate deploy --preview-feature + blitz prisma migrate deploy startCommand: blitz start envVars: - key: NODE_ENV diff --git a/app/pages/docs/deploy-vercel.mdx b/app/pages/docs/deploy-vercel.mdx index d48d6b0..05fcab9 100644 --- a/app/pages/docs/deploy-vercel.mdx +++ b/app/pages/docs/deploy-vercel.mdx @@ -94,8 +94,8 @@ This also assumes you already have a [Vercel](https://vercel.com) account. must add `&pgbouncer=true` to the end of your connection string for Prisma to work correctly. 4. Change your build script in package.json to be - `NODE_ENV=production blitz build && blitz prisma migrate deploy --preview-feature` - so that the production DB will be migrated on each deploy + `NODE_ENV=production blitz build && blitz prisma migrate deploy` so + that the production DB will be migrated on each deploy 5. Add your DB url as a secret environment variable [using the UI](https://vercel.com/blog/environment-variables-ui) or by running `vercel env add DATABASE_URL` diff --git a/app/pages/docs/error-handling.mdx b/app/pages/docs/error-handling.mdx index 9eeb1dc..0edd1e7 100644 --- a/app/pages/docs/error-handling.mdx +++ b/app/pages/docs/error-handling.mdx @@ -26,7 +26,8 @@ application. - Default `message`: "This could not be found" To use, import from `blitz` and use like any JavaScript Error. If you're -curious, you can [see the source code for these.](TODO) +curious, you can +[see the source code for these](https://github.com/blitz-js/blitz/blob/canary/packages/core/src/errors.ts). ```ts import {AuthenticationError} from "blitz" @@ -164,5 +165,4 @@ can handle the error in your form submit handler like this. ## Error Pages {#error-pages} -See the [Error Pages documentation](./error-pages) to learn about custom -404 and 500 error pages. +See the [Error Pages documentation](./error-pages) to learn about custom error pages and error fallback components. diff --git a/app/pages/docs/error-pages.mdx b/app/pages/docs/error-pages.mdx index a5856dd..7945fbc 100644 --- a/app/pages/docs/error-pages.mdx +++ b/app/pages/docs/error-pages.mdx @@ -56,6 +56,72 @@ export default Error > get an error with the call stack to know where the error originated > from. +## Customize the Error Boundary Fallback Component {#custom-error-boundary-component} + +The default `app/pages/_app.tsx` configures an error boundary which catches errors that happen during render, on the client side. This will also catch errors within `useQuery` or `useMutation` because they happen in suspense. + +In essence, the default configuration is: + +```tsx +export default function App({ Component, pageProps }: AppProps) { + return ( + <ErrorBoundary + FallbackComponent={RootErrorFallback} + {...etc} + > + {getLayout(<Component {...pageProps} />)} + </ErrorBoundary> + ) +} +``` + +The `RootErrorFallback` is rendered when a error happens within render, or a suspended mutation or query. By default: + +```tsx +function RootErrorFallback({ error, resetErrorBoundary }: ErrorFallbackProps) { + if (error instanceof AuthenticationError) { + return <LoginForm onSuccess={resetErrorBoundary} /> + } else if (error instanceof AuthorizationError) { + return ( + <ErrorComponent + statusCode={error.statusCode} + title="Sorry, you are not authorized to access this" + /> + ) + } else { + return ( + <ErrorComponent statusCode={error.statusCode || 400} title={error.message || error.name} /> + ) + } +} +``` + +### Authentication Error Component (401) {#custom-authentication-error-component} + +If your query, mutation, or render function `throw new AuthenticationError()` when the user is not authenticated, you can detect that within the error fallback: + +```tsx +function RootErrorFallback({ error, resetErrorBoundary }: ErrorFallbackProps) { + if (error instanceof AuthenticationError) { + return <MyCustomError error={error} onRetry={resetErrorBoundary} /> + } + return <ErrorComponent statusCode={error.statusCode || 400} title={error.message || error.name} /> +} +``` + +### Authorization Error Component (403) {#custom-authorization-error-component} + +Your query, mutation, or render function may call `throw new AuthorizationError()` when a user is authenticated but not authorized to do something. The error fallback may handle that: + +```tsx +function RootErrorFallback({ error, resetErrorBoundary }: ErrorFallbackProps) { + if (error instanceof AuthorizationError) { + return <MyCustomError error={error} onRetry={resetErrorBoundary} /> + } + return <ErrorComponent statusCode={error.statusCode || 400} title={error.message || error.name} /> +} +``` + ## Reusing the built-in error page {#reusing-the-built-in-error-page} You can render the built-in error page by importing `ErrorComponent`. diff --git a/app/pages/docs/how-the-community-operates.mdx b/app/pages/docs/how-the-community-operates.mdx index d364ecf..a16edef 100644 --- a/app/pages/docs/how-the-community-operates.mdx +++ b/app/pages/docs/how-the-community-operates.mdx @@ -57,7 +57,7 @@ smoothing rough edges and fixing bugs. However, anyone else is more than welcome to continue adding major features! Our [GitHub issues](https://github.com/blitz-js/blitz/issues) are where -you can find our roadmap. Many planned features have github issues, but +you can find our roadmap. Many planned features have GitHub issues, but not necessarily all of them do. ## How to Propose Features/Changes {#how-to-propose-features-changes} diff --git a/app/pages/docs/image-optimization.mdx b/app/pages/docs/image-optimization.mdx index 199aa4c..288e299 100644 --- a/app/pages/docs/image-optimization.mdx +++ b/app/pages/docs/image-optimization.mdx @@ -126,7 +126,7 @@ found, then `max-age` is used. If no `max-age` is found, then 60 seconds is used. You can configure [`deviceSizes`](#device-sizes) and -[`imageSizes`](#device-sizes) to reduce the total number of possible +[`imageSizes`](#image-sizes) to reduce the total number of possible generated images. ## Advanced {#advanced} @@ -213,20 +213,20 @@ The `Image` component requires the following properties. The path or URL to the source image. This is required. -When using an external URL, you must add it to [domains](#domains) in +When using an external URL, you must add it to [`domains`](#domains) in `blitz.config.js`. #### width The width of the image, in pixels. Must be an integer without a unit. -Required unless [layout="fill"`](#layout). +Required unless `layout="fill"`. #### height The height of the image, in pixels. Must be an integer without a unit. -Required unless [layout="fill"`](#layout). +Required unless `layout="fill"`. ### Optional Props {#optional-props} @@ -261,13 +261,13 @@ Try it out: ### loader {#loader-1} A custom function used to resolve URLs. Defaults to -[`images` object in `blitz.config.js`](/docs/basic-features/image-optimization.md#loader). +[`images` object in `blitz.config.js`](#loader). `loader` is a function returning a string, given the following parameters: -- [`src`](#src) -- [`width`](#width) -- [`quality`](#quality) +- `src` +- `width` +- `quality` ```js import {Image} from "blitz" @@ -305,7 +305,7 @@ is the best quality. Defaults to 75. #### priority -When true, the image will be considered high priority and +When `true`, the image will be considered high priority and [preload](https://web.dev/preload-responsive-images/). Should only be used when the image is visible above the fold. Defaults to @@ -320,21 +320,25 @@ optionally accepts the following advanced properties. The image fit when using `layout="fill"`. -[Learn more](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit) +[Learn more](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit). #### objectPosition The image position when using `layout="fill"`. -[Learn more](https://developer.mozilla.org/en-US/docs/Web/CSS/object-position) +[Learn more](https://developer.mozilla.org/en-US/docs/Web/CSS/object-position). #### loading -> **Attention**: This property is only meant for advanced usage. Switching -> an image to load with `eager` will normally **hurt performance**. -> -> We recommend using the [`priority`](#priority) property instead, which -> properly loads the image eagerly for nearly all use cases. +<!-- prettier-ignore-start --> +:::caution +This property is only meant for advanced usage. Switching an image to +load with `eager` will normally **hurt performance**. + +We recommend using the `priority` property instead, which properly loads +the image eagerly for nearly all use cases. +::: +<!-- prettier-ignore-end --> The loading behavior of the image. Defaults to `lazy`. @@ -343,12 +347,12 @@ distance from the viewport. When `eager`, load the image immediately. -[Learn more](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attr-loading) +[Learn more](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attr-loading). #### unoptimized -When true, the source image will be served as-is instead of changing -quality, size, or format. Defaults to false. +When `true`, the source image will be served as-is instead of changing +quality, size, or format. Defaults to `false`. ### Other Props {#other-props} diff --git a/app/pages/docs/link.mdx b/app/pages/docs/link.mdx index 9da3305..17acf23 100644 --- a/app/pages/docs/link.mdx +++ b/app/pages/docs/link.mdx @@ -48,8 +48,8 @@ export default Home `false` - [`scroll`](#disable-scrolling-to-the-top-of-the-page) - Scroll to the top of the page after a navigation. Defaults to `true` -- [`shallow`](#todo) - Update the path of the current page without - rerunning [`getStaticProps`](./get-static-props) or +- [`shallow`](./shallow-routing) - Update the path of the current page + without rerunning [`getStaticProps`](./get-static-props) or [`getServerSideProps`](./get-server-side-props). Defaults to `false` External URLs, and any links that don't require a route navigation using diff --git a/app/pages/docs/postgres.mdx b/app/pages/docs/postgres.mdx index c7878b9..5b509b8 100644 --- a/app/pages/docs/postgres.mdx +++ b/app/pages/docs/postgres.mdx @@ -80,5 +80,5 @@ Given these values your `DATABASE_URL` should look like this } ``` -4. Run `blitz prisma migrate dev --preview-feature` to get your new - database to the latest version of your migrations +4. Run `blitz prisma migrate dev` to get your new database to the latest + version of your migrations diff --git a/app/pages/docs/router.mdx b/app/pages/docs/router.mdx index 390d272..ac937fb 100644 --- a/app/pages/docs/router.mdx +++ b/app/pages/docs/router.mdx @@ -31,7 +31,7 @@ export default Thing > `useRouter` is a > [React Hook](https://reactjs.org/docs/hooks-intro.html), meaning it > cannot be used with classes. You can either use -> [withRouter](#withRouter) or wrap your class in a function component. +> [withRouter](#with-router) or wrap your class in a function component. ## `withRouter` {#with-router} @@ -58,15 +58,16 @@ Here's the definition of the `router` object returned by `useRouter` and route. Parameter types are `string` or `string[]`. - `asPath`: `String` - Actual path (including the query) shown in the browser -- [`push()`](#routerpush): Make page navigation -- [`replace()`](#routerreplace): Make page navigation without adding to +- [`push()`](#router-push): Make page navigation +- [`replace()`](#router-replace): Make page navigation without adding to browser history -- [`back()`](#routerback): Navigate to previous history location -- [`reload()`](#routerreload): Reload the page -- [`prefetch()`](#routerprefetch): Prefetch pages for faster client-side +- [`back()`](#router-back): Navigate to previous history location +- [`reload()`](#router-reload): Reload the page +- [`prefetch()`](#router-prefetch): Prefetch pages for faster client-side transitions -- [`events`](#routerevents): Subscribe to various router events -- [`beforePopState()`](#routerbeforepopstate): For advanced routing needs +- [`events`](#router-events): Subscribe to various router events +- [`beforePopState()`](#router-before-pop-state): For advanced routing + needs ### Router API {#router-api} @@ -88,8 +89,8 @@ Router.push(url, as, options) - `as` - Optional decorator for the URL that will be shown in the browser. Defaults to `url` - `options` - Optional object with the following configuration options: - - [`shallow`](#todo): Update the path of the current page without - rerunning [`getStaticProps`](./get-static-props) or + - [`shallow`](./shallow-routing): Update the path of the current page + without rerunning [`getStaticProps`](./get-static-props) or [`getServerSideProps`](./get-server-side-props). Defaults to `false` > You don't need to use `Router` for external URLs, @@ -161,7 +162,7 @@ Router.replace("/home") ``` The API for `Router.replace` is exactly the same as that used for -[`Router.push`](#routerpush). +[`Router.push`](#router-push). ### Router.back {#router-back} @@ -324,8 +325,8 @@ useEffect(() => { ### Router.beforePopState {#router-before-pop-state} -In some cases (for example, if using a [Custom Server](#todo)), you may -wish to listen to +In some cases (for example, if using a [Custom Server](./custom-server)), +you may wish to listen to [popstate](https://developer.mozilla.org/en-US/docs/Web/Events/popstate) and do something before the router acts on it. @@ -355,8 +356,9 @@ Router.beforePopState(({url, as, options}) => { name of a `page` - `as`: `String` - the url that will be shown in the browser - `options`: `Object` - Additional options sent by - [Router.push](#router.push) + [Router.push](#router-push) If the function you pass into `beforePopState` returns `false`, `Router` will not handle `popstate` and you'll be responsible for handling it, in -that case. See [Disabling file-system routing](#todo). +that case. See +[Disabling file-system routing](./custom-server#disabling-file-system-routing). diff --git a/app/pages/docs/shallow-routing.mdx b/app/pages/docs/shallow-routing.mdx new file mode 100644 index 0000000..61b24ab --- /dev/null +++ b/app/pages/docs/shallow-routing.mdx @@ -0,0 +1,67 @@ +--- +title: Shallow Routing +sidebar_label: Shallow Routing +--- + +Shallow routing allows you to change the URL without running data fetching +methods again, that includes [`getServerSideProps`](get-server-side-props) +and [`getStaticProps`](get-static-props). + +You'll receive the updated `pathname` and the `query` via the +[`router` object](router#router-object) (added by +[`useRouter`](router#use-router) or [`withRouter`](router#use-router)), +without losing state. + +To enable shallow routing, set the `shallow` option to `true`. Consider +the following example: + +```ts +import {useEffect} from "react" +import {useRouter} from "blitz" + +// Current URL is '/' +function Page() { + const router = useRouter() + + useEffect(() => { + // Always do navigations after the first render + router.push("/?counter=10", undefined, {shallow: true}) + }, []) + + useEffect(() => { + // The counter changed! + }, [router.query.counter]) +} + +export default Page +``` + +The URL will get updated to `/?counter=10` and the page won't get +replaced, only the state of the route is changed. + +You can also watch for URL changes via +[`componentDidUpdate`](https://reactjs.org/docs/react-component.html#componentdidupdate) +as shown below: + +```ts +componentDidUpdate(prevProps) { + const { pathname, query } = this.props.router + // verify props have changed to avoid an infinite loop + if (query.counter !== prevProps.router.query.counter) { + // fetch data based on the new query + } +} +``` + +## Caveats {#caveats} + +Shallow routing **only** works for same page URL changes. For example, +let's assume we have another page called `pages/about.ts`, and you run +this: + +```ts +router.push("/?counter=10", "/about?counter=10", {shallow: true}) +``` + +Since that's a new page, it'll unload the current page, load the new one +and wait for data fetching even though we asked to do shallow routing. diff --git a/app/pages/index.js b/app/pages/index.js index 49983aa..55c255b 100644 --- a/app/pages/index.js +++ b/app/pages/index.js @@ -69,7 +69,7 @@ const Home = ({randomContributors}) => { variant="outline" className="w-1/3 lg:w-auto rounded-r-xl" > - Github + GitHub </ButtonLink> </div> </div> diff --git a/blitz.config.js b/blitz.config.js index bb225d0..061b78a 100644 --- a/blitz.config.js +++ b/blitz.config.js @@ -1,3 +1,4 @@ +const fs = require("fs") const path = require("path") const querystring = require("querystring") const {createLoader} = require("simple-functional-loader") @@ -151,6 +152,38 @@ module.exports = withBundleAnalyzer({ ], }) + config.module.rules.push({ + test: /navs\/documentation\.json$/, + use: [ + createLoader(function (source) { + const documentation = JSON.parse(source) + let finalDocumentation = [] + + for (const category of documentation) { + let pages = [] + for (const page of category.pages) { + const pageFile = fs.readFileSync( + path.resolve(process.cwd(), "pages", "docs", `${page}.mdx`), + {encoding: "utf-8"}, + ) + const {data} = matter(pageFile) + + pages.push({ + ...data, + href: `/docs/${page}`, + }) + } + finalDocumentation.push({ + ...category, + pages, + }) + } + + return JSON.stringify(finalDocumentation) + }), + ], + }) + return config }, }) diff --git a/package.json b/package.json index f3d5c02..24fee83 100644 --- a/package.json +++ b/package.json @@ -2,14 +2,13 @@ "name": "blitz-site-v2", "version": "1.0.0", "scripts": { - "dev": "yarn gen-nav && blitz dev", + "dev": "blitz dev", "start": "blitz start", - "build": "yarn gen-nav && blitz build", + "build": "blitz build", "lint": "yarn lint:code && yarn lint:docs", "lint:code": "eslint --ignore-path .gitignore --ext .js,.ts,.tsx .", "lint:docs": "alex app/pages/docs/**/*.mdx", "english-slugify": "node scripts/english-slugify.js", - "gen-nav": "node scripts/generate-documentation-nav.js && prettier -w app/core/navs/*", "postinstall": "husky install" }, "lint-staged": { @@ -51,6 +50,7 @@ "@mdx-js/loader": "^1.6.22", "@next/bundle-analyzer": "^10.0.8", "@svgr/webpack": "^5.4.0", + "@tailwindcss/jit": "0.1.3", "@tailwindcss/typography": "0.4.0", "alex": "9.1.0", "autoprefixer": "^10.2.5", diff --git a/postcss.config.js b/postcss.config.js index 58bdd3e..bfe13b1 100644 --- a/postcss.config.js +++ b/postcss.config.js @@ -1,3 +1,3 @@ module.exports = { - plugins: ["tailwindcss", "postcss-nested", "autoprefixer"], + plugins: ["@tailwindcss/jit", "postcss-nested", "autoprefixer"], } diff --git a/scripts/generate-documentation-nav.js b/scripts/generate-documentation-nav.js deleted file mode 100644 index 990e6fd..0000000 --- a/scripts/generate-documentation-nav.js +++ /dev/null @@ -1,38 +0,0 @@ -const fs = require("fs/promises") -const path = require("path") -const matter = require("gray-matter") - -const getFile = (...filePath) => - fs.readFile(path.resolve(process.cwd(), ...filePath), {encoding: "utf-8"}) - -async function main() { - const documentation = JSON.parse(await getFile("app", "core", "navs", "documentation.json")) - let finalDocumentation = [] - - for (const category of documentation) { - let pages = [] - for (const page of category.pages) { - const pageFile = await getFile("app", "pages", "docs", `${page}.mdx`) - const {data} = matter(pageFile) - - pages.push({ - ...data, - href: `/docs/${page}`, - }) - } - finalDocumentation.push({ - ...category, - pages, - }) - } - - await fs.writeFile( - path.resolve(process.cwd(), "app", "core", "navs", "documentation.js"), - "export const documentationNav = " + JSON.stringify(finalDocumentation, null, 2), - {encoding: "utf-8"}, - ) - - console.log("Documentation Nav generated!") -} - -main() diff --git a/yarn.lock b/yarn.lock index 1253dab..dd600c9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2717,6 +2717,19 @@ dependencies: defer-to-connect "^2.0.0" +"@tailwindcss/jit@0.1.3": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@tailwindcss/jit/-/jit-0.1.3.tgz#50390d9ac95fee78ed23a7e2320ef20bd4a35354" + integrity sha512-7VAvHKNLJxbGWRKxo2Z+beiodag7vWPx8b/+Egd5fve4zFihsngeNt6RwQFnll+almjppRYefRC5Py5v5K+6vg== + dependencies: + chokidar "^3.5.1" + dlv "^1.1.3" + fast-glob "^3.2.5" + lodash.topath "^4.5.2" + object-hash "^2.1.1" + postcss-selector-parser "^6.0.4" + quick-lru "^5.1.1" + "@tailwindcss/typography@0.4.0": version "0.4.0" resolved "https://registry.yarnpkg.com/@tailwindcss/typography/-/typography-0.4.0.tgz#b80974ad6af93df7b06e1981cb4d79698b6ad5c7" @@ -4456,7 +4469,7 @@ checkpoint-client@1.1.18: node-fetch "2.6.1" uuid "8.3.0" -chokidar@3.5.1: +chokidar@3.5.1, chokidar@^3.5.1: version "3.5.1" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a" integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw== @@ -6266,7 +6279,7 @@ fast-deep-equal@^3.1.1: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== -fast-glob@3.2.5: +fast-glob@3.2.5, fast-glob@^3.2.5: version "3.2.5" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.5.tgz#7939af2a656de79a4f1901903ee8adcaa7cb9661" integrity sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg== @@ -9139,6 +9152,11 @@ lodash.toarray@^4.4.0: resolved "https://registry.yarnpkg.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561" integrity sha1-JMS/zWsvuji/0FlNsRedjptlZWE= +lodash.topath@^4.5.2: + version "4.5.2" + resolved "https://registry.yarnpkg.com/lodash.topath/-/lodash.topath-4.5.2.tgz#3616351f3bba61994a0931989660bd03254fd009" + integrity sha1-NhY1Hzu6YZlKCTGYlmC9AyVP0Ak= + lodash.union@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88" From eed14c6cbbb42e8c5bfc79606948b828d488154f Mon Sep 17 00:00:00 2001 From: tundera <tunderadev@gmail.com> Date: Mon, 5 Apr 2021 13:03:34 -0600 Subject: [PATCH 02/67] Update docs for upgrade to React Query v3 (#424) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: JuanM04 <me@juanm04.com> Co-authored-by: depfu[bot] <23717796+depfu[bot]@users.noreply.github.com> Co-authored-by: Brandon Bayer <b@bayer.ws> Co-authored-by: Andreas Bollig <andreas.bollig@gmail.com> Co-authored-by: Abu Uzayr <abu.uzayr@builtforfifty.com> Co-authored-by: François Best <francois@francoisbest.com> Co-authored-by: Faraz Patankar <farazpatankar@gmail.com> Co-authored-by: Eric Vicenti <ericvicenti@users.noreply.github.com> Co-authored-by: Layne Geck <layne.geck@gmail.com> Co-authored-by: gstranger <36181416+gstranger@users.noreply.github.com> Co-authored-by: Andrea Rizzello <10348930+andrearizzello@users.noreply.github.com> --- app/pages/docs/error-handling.mdx | 23 +- app/pages/docs/impersonation.mdx | 10 +- app/pages/docs/query-usage.mdx | 45 ++++ app/pages/docs/resolver-client-utilities.mdx | 6 +- app/pages/docs/testing.mdx | 29 ++ app/pages/docs/tutorial.mdx | 46 +++- app/pages/docs/use-infinite-query.mdx | 167 +++++++----- app/pages/docs/use-mutation.mdx | 85 +++--- app/pages/docs/use-paginated-query.mdx | 126 +++++++-- app/pages/docs/use-query.mdx | 262 ++++++++++++++----- 10 files changed, 563 insertions(+), 236 deletions(-) diff --git a/app/pages/docs/error-handling.mdx b/app/pages/docs/error-handling.mdx index 0edd1e7..df0f071 100644 --- a/app/pages/docs/error-handling.mdx +++ b/app/pages/docs/error-handling.mdx @@ -57,21 +57,17 @@ It looks something like this: ```tsx // app/pages/_app.tsx -import {AppProps, ErrorComponent} from "blitz" +import {AppProps, ErrorComponent, useQueryErrorResetBoundary} from "blitz" import {ErrorBoundary} from "react-error-boundary" -import {queryCache} from "react-query" import LoginForm from "app/auth/components/LoginForm" export default function App({Component, pageProps}: AppProps) { + // This ensures the Blitz useQuery hooks will automatically refetch + // data any time you reset the error boundary + const {reset} = useQueryErrorResetBoundary() + return ( - <ErrorBoundary - FallbackComponent={RootErrorFallback} - onReset={() => { - // This ensures the Blitz useQuery hooks will automatically refetch - // data any time you reset the error boundary - queryCache.resetErrorBoundaries() - }} - > + <ErrorBoundary FallbackComponent={RootErrorFallback} onReset={reset}> <Component {...pageProps} /> </ErrorBoundary> ) @@ -100,7 +96,9 @@ function RootErrorFallback({error, resetErrorBoundary}) { That means all errors will at least be caught at the root level. However, you can also add `<ErrorBoundary>` anywhere else in your app for more -localized error handiling. If an error is caught by an `<ErrorBoundary>` +localized error handling. To do this, declare a separate +`useQueryErrorResetBoundary` in your component and pass it along to the +local ErrorBoundary. If an error is caught by an `<ErrorBoundary>` somewhere down inside your app tree, then it will not reach the root ErrorBoundary unless you re-throw it. @@ -165,4 +163,5 @@ can handle the error in your form submit handler like this. ## Error Pages {#error-pages} -See the [Error Pages documentation](./error-pages) to learn about custom error pages and error fallback components. +See the [Error Pages documentation](./error-pages) to learn about custom +error pages and error fallback components. diff --git a/app/pages/docs/impersonation.mdx b/app/pages/docs/impersonation.mdx index 753ee9e..4a4ae94 100644 --- a/app/pages/docs/impersonation.mdx +++ b/app/pages/docs/impersonation.mdx @@ -94,8 +94,7 @@ export default resolver.pipe(resolver.authorize(), async (_, ctx) => { Add a form similar to this in order to switch users. ```tsx -import {useMutation} from "blitz" -import {queryCache} from "react-query" +import {useMutation, queryClient} from "blitz" import impersonateUser, { ImpersonateUserInput, } from "app/auth/mutations/impersonateUser" @@ -112,7 +111,7 @@ export const ImpersonateUserForm = () => { onSubmit={async (values) => { try { await impersonateUserMutation(values) - queryCache.clear() + queryClient.clear() } catch (error) { return { [FORM_ERROR]: @@ -132,8 +131,7 @@ Lastly, add the following component at the top of your Layout(s). ```tsx // app/core/components/ImpersonatingUserNotice.tsx -import {invoke, useSession} from "blitz" -import {queryCache} from "react-query" +import {invoke, useSession, queryClient} from "blitz" import stopImpersonating from "app/auth/mutations/stopImpersonating" export const ImpersonatingUserNotice = () => { @@ -147,7 +145,7 @@ export const ImpersonatingUserNotice = () => { className="appearance-none bg-transparent text-black uppercase" onClick={async () => { await invoke(stopImpersonating, {}) - queryCache.clear() + queryClient.clear() }} > Exit diff --git a/app/pages/docs/query-usage.mdx b/app/pages/docs/query-usage.mdx index c3bdef4..d548214 100644 --- a/app/pages/docs/query-usage.mdx +++ b/app/pages/docs/query-usage.mdx @@ -120,6 +120,51 @@ const project = await getProject({where: {id: props.id}}) </a> ``` +Blitz also supports prefetching multiple queries on the server and then +dehydrating those queries to the internal query client. This allows Blitz +to prerender markup that is immediately available on page load. Once +JavaScript is available in the browser, Blitz will hydrate those queries +and refetch them if they have become stale since the time they were +rendered on the server. + +The following example demonstrates how prefetching works in Blitz. + +```tsx +import {QueryClient, getQueryKey, useQuery, dehydrate} from "blitz" + +import getProject from "app/projects/queries/getProject" + +export const getStaticProps = async (context) => { + const queryClient = new QueryClient() + const queryKey = getQueryKey(getProject) + + await queryClient.prefetchQuery(queryKey, () => + getProject({where: {id: context.params?.projectId}}), + ) + + return { + props: { + dehydratedState: dehydrate(queryClient), + }, + } +} + +function ProjectPage({project}) { + const [project] = useQuery(getProject, {where: {id: props.id}}) + return <div>{project.name}</div> +} + +export default ProjectPage +``` + +To ensure data is not shared between users and requests, a new query +client is created for each page request. You can prefetch your data by +ivoking the `prefetchQuery` method on the newly-created client, passing in +the query key and resolver along with any relevant input arguments. Once +the data has been fetched, dehydrate the queries to the query client using +the `dehydrate` method and pass the result to the page via the +`dehydratedState` prop. + ## On the Server {#on-the-server} ### `getStaticProps` {#get-static-props} diff --git a/app/pages/docs/resolver-client-utilities.mdx b/app/pages/docs/resolver-client-utilities.mdx index 2e8d5cd..102f76b 100644 --- a/app/pages/docs/resolver-client-utilities.mdx +++ b/app/pages/docs/resolver-client-utilities.mdx @@ -108,7 +108,7 @@ This allows you to get the query key for a query ### Example {#example-2} ```tsx -import {getQueryKey} from "blitz" +import {getQueryKey, queryClient} from "blitz" import getProducts from "app/products/queries/getProducts" const Page = function ({products}) { @@ -117,10 +117,10 @@ const Page = function ({products}) { <button onClick={async () => { const queryKey = getQueryKey(getProducts) - queryCache.invalidateQueries(queryKey) + queryClient.invalidateQueries(queryKey) const queryKey = getQueryKey(getProducts, {where: {ordId: 1}}) - queryCache.invalidateQueries(queryKey) + queryClient.invalidateQueries(queryKey) }} > Invalidate diff --git a/app/pages/docs/testing.mdx b/app/pages/docs/testing.mdx index e79a438..d64d03b 100644 --- a/app/pages/docs/testing.mdx +++ b/app/pages/docs/testing.mdx @@ -66,3 +66,32 @@ as needed. - [@testing-library/jest-dom](https://testing-library.com/docs/ecosystem-jest-dom) - [@testing-library/react](https://testing-library.com/docs/react-testing-library/intro) - [@testing-library/react-hooks](https://react-hooks-testing-library.com/) + +## Testing Utilities {#testing-utilities} + +Blitz provides some basic testing utilities in the generated +`test/utils.tsx` file. These are intended for use with Jest and React +Testing Library to allow advanced configuration for your testing +environment. Customizing this file can be useful for testing things like +global providers as well as certain parts of your app which Blitz handles +internally such as routing and data querying. + +### `BlitzProvider` {#blitz-provider} + +This component works as the global provider for data queries and mutations +in your tests. Internally, it wraps the `QueryClient` component from +`react-query` and adds some additional configuration options. + +`BlitzProvider` accepts the following props: + +- `client` - The global query client used by React Query. By default the + `queryClient` exported from `blitz` is used unless you override it. + +- `contextSharing` - Controls whether or not the provider context can be + shared across multiple applications. This can be useful when working in + certain environments such as micro-frontends. Defaults to `false`. + +- `dehydratedState` - The server state passed along to the application + containing the results of server-side queries. Useful for testing + queries that need to be + [prerendered in markup and hydrated on the client](https://react-query.tanstack.com/guides/ssr#using-hydration). diff --git a/app/pages/docs/tutorial.mdx b/app/pages/docs/tutorial.mdx index 721b8d0..6147f4d 100644 --- a/app/pages/docs/tutorial.mdx +++ b/app/pages/docs/tutorial.mdx @@ -403,14 +403,22 @@ Jump over to `app/pages/questions/index.tsx`. Notice that a export const QuestionsList = () => { const router = useRouter() const page = Number(router.query.page) || 0 - const [{questions, hasMore}] = usePaginatedQuery(getQuestions, { - orderBy: {id: "asc"}, - skip: ITEMS_PER_PAGE * page, - take: ITEMS_PER_PAGE, - }) + const [{questions, hasMore}, {isPreviousData}] = usePaginatedQuery( + getQuestions, + { + orderBy: {id: "asc"}, + skip: ITEMS_PER_PAGE * page, + take: ITEMS_PER_PAGE, + }, + ) const goToPreviousPage = () => router.push({query: {page: page - 1}}) - const goToNextPage = () => router.push({query: {page: page + 1}}) + + const goToNextPage = () => { + if (!isPreviousData && hasMore) { + router.push({query: {page: page + 1}}) + } + } return ( <div> @@ -427,7 +435,10 @@ export const QuestionsList = () => { <button disabled={page === 0} onClick={goToPreviousPage}> Previous </button> - <button disabled={!hasMore} onClick={goToNextPage}> + <button + disabled={isPreviousData || !hasMore} + onClick={goToNextPage} + > Next </button> </div> @@ -445,14 +456,21 @@ with `question.text`: export const QuestionsList = () => { const router = useRouter() const page = Number(router.query.page) || 0 - const [{questions, hasMore}] = usePaginatedQuery(getQuestions, { - orderBy: {id: "asc"}, - skip: ITEMS_PER_PAGE * page, - take: ITEMS_PER_PAGE, - }) + const [{questions, hasMore}, {isPreviousData}] = usePaginatedQuery( + getQuestions, { + orderBy: {id: "asc"}, + skip: ITEMS_PER_PAGE * page, + take: ITEMS_PER_PAGE, + }, + ) const goToPreviousPage = () => router.push({query: {page: page - 1}}) - const goToNextPage = () => router.push({query: {page: page + 1}}) + + const goToNextPage = () => { + if (!isPreviousData && hasMore) { + router.push({query: {page: page + 1}}) + } + } return ( <div> @@ -470,7 +488,7 @@ export const QuestionsList = () => { <button disabled={page === 0} onClick={goToPreviousPage}> Previous </button> - <button disabled={!hasMore} onClick={goToNextPage}> + <button disabled={isPreviousData || !hasMore} onClick={goToNextPage}> Next </button> </div> diff --git a/app/pages/docs/use-infinite-query.mdx b/app/pages/docs/use-infinite-query.mdx index 9c08030..4dd35b3 100644 --- a/app/pages/docs/use-infinite-query.mdx +++ b/app/pages/docs/use-infinite-query.mdx @@ -7,41 +7,39 @@ sidebar_label: useInfiniteQuery ```ts import {useInfiniteQuery} from "blitz" -import getProjectsInfinite from "app/projects/queries/getProjectsInfinite" +import getProjects from "app/projects/queries/getProjects" function Projects(props) { const [ - groupedProjects, - {isFetching, isFetchingMore, fetchMore, canFetchMore}, - ] = useInfiniteQuery( - getProjectsInfinite, - (page = {take: 3, skip: 0}) => page, - { - getFetchMore: (lastGroup) => lastGroup.nextPage, - }, - ) + projectPages, + {isFetching, isFetchingNextPage, fetchNextPage, hasNextPage}, + ] = useInfiniteQuery(getProjects, (page = {take: 3, skip: 0}) => page, { + getNextPageParam: (lastPage) => lastPage.nextPage, + }) return ( <> - {groupedProjects.map((group, i) => ( + {projectPages.map((group, i) => ( <React.Fragment key={i}> - {group.projects.map((project) => ( + {page.projects.map((project) => ( <p key={project.id}>{project.name}</p> ))} </React.Fragment> ))} <div> <button - onClick={() => fetchMore()} - disabled={!canFetchMore || !!isFetchingMore} + onClick={() => fetchNextPage()} + disabled={!hasNextPage || !!isFetchingNextPage} > - {isFetchingMore + {isFetchingNextPage ? "Loading more..." - : canFetchMore + : hasNextPage ? "Load More" : "Nothing more to load"} </button> </div> - <div>{isFetching && !isFetchingMore ? "Fetching..." : null}</div> + <div> + {isFetching && !isFetchingNextPage ? "Fetching..." : null} + </div> </> ) } @@ -50,26 +48,43 @@ function Projects(props) { And here's the query to work with that: ```ts -export default async function getProjectsInfinite({ - where, - orderBy, - take, - skip, -}: GetProjectsInfiniteInput) { +import {paginate} from "blitz" +import db, {Prisma, Project} from "db" + +type GetProjectsInput = { + where?: Prisma.ProjectFindManyArgs["where"] + orderBy?: Prisma.ProjectFindManyArgs["orderBy"] + skip?: number + take?: number +} + +export default async function getProjects( + {where, orderBy, take, skip}: GetProjectsInput, + ctx: Record<any, unknown> = {}, +) { const projects = await db.project.findMany({ where, orderBy, take, skip, }) + if (ctx.referer) { + console.log("HTTP referer:", ctx.referer) + } - const count = await db.project.count() - const hasMore = skip! + take! < count - const nextPage = hasMore ? {take, skip: skip! + take!} : null + const {items: projects, hasMore, nextPage, count} = await paginate({ + skip, + take, + count: () => db.project.count({where}), + query: (paginateArgs) => + db.project.findMany({...paginateArgs, where, orderBy}), + }) return { projects, nextPage, + hasMore, + count, } } ``` @@ -79,18 +94,21 @@ export default async function getProjectsInfinite({ <!-- prettier-ignore --> ```js const [ - groupedQueryFunctionResults, + pagesOfQueryResults, { - isFetching, - failureCount, - refetch, - fetchMore, - canFetchMore, - setQueryData, + pageParams, + fetchNextPage, + fetchPreviousPage, + hasNextPage, + hasPreviousPage, + isFetchingNextPage, + isFetchingPreviousPage, + ...extras } -] = useQuery(queryResolver, getQueryInputArguments, { - getFetchMore: (lastPage, allPages) => fetchMoreVariable - ...queryOptions, +] = useInfiniteQuery(queryResolver, getQueryInputArguments, { + ...options, + getNextPageParam: (lastPage, allPages) => lastPage.nextPage, + getPreviousPageParam: (firstPage, allPages) => firstPage.nextPage, }) ``` @@ -98,64 +116,71 @@ const [ - `queryResolver:` A [Blitz query resolver](./query-resolvers) - **Required** -- `getQueryInputArguments: (fetchMoreVariable) => queryInputArguments` +- `getQueryInputArguments: (fetchNextPageVariable) => queryInputArguments` - **Required** - A function that accepts the current page options and returns the `queryInputArguments` - - On the first page load, `fetchMoreVariable` is `undefined`. - - For subsequent pages, `fetchMoreVariable` is whatever is returned from - `getFetchMore()` + - On the first page load, `fetchNextPageVariable` is `undefined`. + - For subsequent pages, `fetchNextPageVariable` is whatever is returned + from `getNextPageParam()` - `options` - Optional ### Options {#options} The options are identical to the options for the -[useQuery hook](./use-query) with the addition of the following: +[useQuery hook](./use-query#options) with the addition of the following: -- `getFetchMore: Function(lastPage, allPages) => fetchMoreVariable | Boolean` +- `getNextPageParam: (lastPage, allPages) => unknown | undefined` - When new data is received for this query, this function receives both the last page of the infinite list of data and the full array of all pages. - It should return a **single variable** that will be passed as the - argument to `getQueryInputArguments()` + argument to `getQueryInputArguments()`. + - Return `undefined` to indicate there is no next page available. +- `getPreviousPageParam: (firstPage, allPages) => unknown | undefined` + - When new data is received for this query, this function receives both + the first page of the infinite list of data and the full array of all + pages. + - It should return a **single variable** that will be passed as the + argument to your `getQueryInputArguments()`. + - Return `undefined` to indicate there is no previous page available. ### Returns {#returns} -`[groupedQueryFunctionResults, queryExtras]` +`[pagesOfQueryResults, queryExtras]` -##### `groupedQueryFunctionResults: Array<any>` +##### `pagesOfQueryResults: TData[]` -- Defaults to `[]`. -- The array will contain each "page" of data that has been requested +- Array containing all pages. ##### `queryExtras: Object` -- `isFetching: Boolean` - - Will be `true` if the query is currently fetching, including - background fetching. -- `failureCount: Integer` - - The failure count for the query. - - Incremented every time the query fails. - - Reset to `0` when the query succeeds. -- `refetch()` - `Function({ force, throwOnError }) => void` - - A function to manually refetch the query if it is stale. - - To bypass the stale check, you can pass the `force: true` option and - refetch it regardless of it's freshness - - If the query errors, the error will only be logged. If you want an - error to be thrown, pass the `throwOnError: true` option -- `fetchMore()` - - `Function(fetchMoreVariableOverride, { previous }) => Promise` +The query extras for `useInfiniteQuery` are identical to the query extas +for the [useQuery hook](./use-query#returns) with the addition of the +following: + +- `pageParams: unknown[]` + - Array containing all page params. +- `isFetchingNextPage: boolean` + - Will be `true` while fetching the next page with `fetchNextPage`. +- `isFetchingPreviousPage: boolean` + - Will be `true` while fetching the previous page with + `fetchPreviousPage`. +- `fetchNextPage: (options?: FetchNextPageOptions) => Promise<UseInfiniteQueryResult>` - This function allows you to fetch the next "page" of results. - - fetchMoreVariableOverride allows you to optionally override the fetch - more variable returned from your getCanFetchMore option to your query - function to retrieve the next page of results. - - previous option which will determine if the data you are fetching is - should be prepended instead of appended to your infinite list. e.g. - `fetchMore(nextPageVars, { previous: true })` -- `canFetchMore: Boolean` - - If using `paginated` mode, this will be `true` if there is more data - to be fetched (known via the required `getFetchMore` option function). + - `options.pageParam: unknown` allows you to manually specify a page + param instead of using `getNextPageParam`. +- `fetchPreviousPage: (options?: FetchPreviousPageOptions) => Promise<UseInfiniteQueryResult>` + - This function allows you to fetch the previous "page" of results. + - `options.pageParam: unknown` allows you to manually specify a page + param instead of using `getPreviousPageParam`. +- `hasNextPage: boolean` + - This will be `true` if there is a next page to be fetched (known via + the `getNextPageParam` option). +- `hasPreviousPage: boolean` + - This will be `true` if there is a previous page to be fetched (known + via the `getPreviousPageParam` option). - `setQueryData()` - `Function(newData, opts) => void` - A function to manually update the cache for a query. - `newData` can be an object of new data or a function that receives the diff --git a/app/pages/docs/use-mutation.mdx b/app/pages/docs/use-mutation.mdx index a7edd53..414aa08 100644 --- a/app/pages/docs/use-mutation.mdx +++ b/app/pages/docs/use-mutation.mdx @@ -39,29 +39,30 @@ function (props) { const [ invoke, { - status, + data, + error, + isError, isIdle, isLoading, + isPaused, isSuccess, - isError, - data, - error, - reset + mutate, + reset, + status, } ] = useMutation(mutationResolver, { - onMutate, - onSuccess, + mutationKey, onError, + onMutate, onSettled, - throwOnError, + onSuccess, useErrorBoundary, }) const promise = invoke(inputArguments, { - onSuccess, - onSettled, onError, - throwOnError, + onSettled, + onSuccess, }) ``` @@ -74,7 +75,12 @@ const promise = invoke(inputArguments, { ### Options {#options} -- `onMutate: Function(inputArguments) => Promise | snapshotValue` +- `mutationKey: string` + - Optional + - A mutation key can be set to inherit defaults set with + `queryClient.setMutationDefaults` or to identify the mutation in the + devtools. +- `onMutate: (variables: TVariables) => Promise<TContext | void> | TContext | void` - Optional - This function will fire before the mutation function is fired and is passed the same variables the mutation function would receive @@ -83,55 +89,62 @@ const promise = invoke(inputArguments, { - The value returned from this function will be passed to both the `onError` and `onSettled` functions in the event of a mutation failure and can be useful for rolling back optimistic updates. -- `onSuccess: Function(data, inputArguments) => Promise | undefined` +- `onSuccess: (data: TData, variables: TVariables, context?: TContext) => Promise<void> | void` - Optional - This function will fire when the mutation is successful and will be passed the mutation's result. - - Fires after the `mutate`-level `onSuccess` handler (if it is defined) - If a promise is returned, it will be awaited and resolved before proceeding -- `onError: Function(err, inputArguments, onMutateValue) => Promise | undefined` +- `onError: (err: TError, variables: TVariables, context?: TContext) => Promise<void> | void` - Optional - This function will fire if the mutation encounters an error and will be passed the error. - - Fires after the `mutate`-level `onError` handler (if it is defined) - If a promise is returned, it will be awaited and resolved before proceeding -- `onSettled: Function(data, error, inputArguments, onMutateValue) => Promise | undefined` +- `onSettled: (data: TData, error: TError, variables: TVariables, context?: TContext) => Promise<void> | void` - Optional - This function will fire when the mutation is either successfully fetched or encounters an error and be passed either the data or error - - Fires after the `mutate`-level `onSettled` handler (if it is defined) - If a promise is returned, it will be awaited and resolved before proceeding -- `throwOnError` - - Defaults to `true` - - Set this to `false` if failed mutations should not re-throw errors - from the mutation function to the `mutate` function. +- `retry: boolean | number | (failureCount: number, error: TError) => boolean` + - If `false`, failed mutations will not retry by default. + - If `true`, failed mutations will retry infinitely. + - If set to an `number`, e.g. `3`, failed mutations will retry until the + failed mutations count meets that number. +- `retryDelay: number | (retryAttempt: number, error: TError) => number` + - This function receives a `retryAttempt` integer and the actual Error + and returns the delay to apply before the next attempt in + milliseconds. + - A function like + `attempt => Math.min(attempt > 1 ? 2 ** attempt * 1000 : 1000, 30 * 1000)` + applies exponential backoff. + - A function like `attempt => attempt * 1000` applies linear backoff. - `useErrorBoundary` - - Defaults to false, typically you will not use this + - Defaults to the global query config's `useErrorBoundary` value, which + is `false` - Set this to true if you want mutation errors to be thrown in the render phase and propagate to the nearest error boundary ### Returns {#returns} -`[invoke, mutationExtras]` +`[mutate, mutationExtras]` -##### `invoke: Function(inputArguments, { onSuccess, onSettled, onError, throwOnError }) => Promise` +##### `mutate: (variables: TVariables, { onSuccess, onSettled, onError }) => Promise<TData>` -- The mutation function you can call with inputArguments to trigger the - mutation and optionally override the original mutation options. -- `inputArguments: any` +- The mutation function you can call with variables to trigger the + mutation and optionally override options passed to `useMutation`. +- `variables: TVariables` - Optional - - The inputArguments object to pass to the `mutationResolver`. + - The variables object to pass to the `mutationFn`. - Remaining options extend the same options described above in the `useMutation` hook. -- Lifecycle callbacks defined here will fire **after** those of the same - type defined in the `useMutation`-level options. +- If you make multiple requests, `onSuccess` will fire only after the + latest call you've made. ##### `mutationExtras: Object` -- `status: String` +- `status: string` - Will be: - `idle` initial status prior to the mutation function executing. - `loading` if the mutation is currently executing. @@ -139,11 +152,11 @@ const promise = invoke(inputArguments, { - `success` if the last mutation attempt was successful. - `isIdle`, `isLoading`, `isSuccess`, `isError`: boolean variables derived from `status` -- `data: undefined | Any` +- `data: undefined | unknown` - Defaults to `undefined` - - The last successfully resolved data for the mutation. -- `error: null | Error` + - The last successfully resolved data for the query. +- `error: null | TError` - The error object for the query, if an error was encountered. -- `reset: Function() => void` +- `reset: () => void` - A function to clean the mutation internal state (i.e., it resets the mutation to its initial state). diff --git a/app/pages/docs/use-paginated-query.mdx b/app/pages/docs/use-paginated-query.mdx index b4de461..65f15e7 100644 --- a/app/pages/docs/use-paginated-query.mdx +++ b/app/pages/docs/use-paginated-query.mdx @@ -21,15 +21,21 @@ const ITEMS_PER_PAGE = 3 const Projects = () => { const router = useRouter() const {page = 0} = useRouterQuery() - const [{projects, hasMore}] = usePaginatedQuery(getProjects, { - skip: ITEMS_PER_PAGE * Number(page), - take: ITEMS_PER_PAGE, - }) + const [{projects, hasMore}, {isPreviousData}] = usePaginatedQuery( + getProjects, + { + skip: ITEMS_PER_PAGE * Number(page), + take: ITEMS_PER_PAGE, + }, + ) const goToPreviousPage = () => router.push({query: {page: Number(page) - 1}}) - const goToNextPage = () => - router.push({query: {page: Number(page) + 1}}) + const goToNextPage = () => { + if (!isPreviousData && hasMore) { + router.push({query: {page: Number(page) + 1}}) + } + } return ( <div> @@ -46,7 +52,10 @@ const Projects = () => { <button disabled={page === 0} onClick={goToPreviousPage}> Previous </button> - <button disabled={!hasMore} onClick={goToNextPage}> + <button + disabled={isPreviousData || !hasMore} + onClick={goToNextPage} + > Next </button> </div> @@ -85,12 +94,28 @@ export default async function getProjects( const [ queryResult, { - latestData, - isFetching, + dataUpdatedAt, + error, + errorUpdatedAt, failureCount, + isError, + isFetched, + isFetchedAfterMount, + isFetching, + isIdle, + isLoading, + isLoadingError, + isPlaceholderData, + isPreviousData, + isRefetchError, + isStale, + isSuccess, refetch, + remove, + status, + setQueryData, } -] = useQuery(queryResolver, queryInputArguments, options) +] = usePaginatedQuery(queryResolver, queryInputArguments, options) ``` ### Arguments {#arguments} @@ -112,33 +137,86 @@ The options are identical to the options for the `[queryResult, queryExtras]` -##### `queryResult: Any` +##### `queryResult: TData` - Defaults to `undefined`. - The last successfully resolved data for the query. -- When fetching with new input arguments, the value will resolve to the - last known successful value, regardless of input arguments ##### `queryExtras: Object` -- `latestData: Any` +- `status: String` + - Will be: + - `idle` if the query is idle. This only happens if a query is + initialized with `enabled: false` and no initial data is available. + - `loading` if the query is in a "hard" loading state. This means + there is no cached data and the query is currently fetching, eg + `isFetching === true` + - `error` if the query attempt resulted in an error. The corresponding + `error` property has the error received from the attempted fetch + - `success` if the query has received a response with no errors and is + ready to display its data. The corresponding `data` property on the + query is the data received from the successful fetch or if the + query's `enabled` property is set to `false` and has not been + fetched yet `data` is the first `initialData` supplied to the query + on initialization. +- `isIdle: boolean` + - A derived boolean from the `status` variable above, provided for + convenience. +- `isLoading: boolean` + - A derived boolean from the `status` variable above, provided for + convenience. +- `isSuccess: boolean` + - A derived boolean from the `status` variable above, provided for + convenience. +- `isError: boolean` + - A derived boolean from the `status` variable above, provided for + convenience. +- `isLoadingError: boolean` + - Will be `true` if the query failed while fetching for the first time. +- `isRefetchError: boolean` + - Will be `true` if the query failed while refetching. +- `data: TData` - Defaults to `undefined`. - - The actual data object for this query and its specific input arguments - - When fetching an uncached query, this value will be `undefined` -- `isFetching: Boolean` + - The last successfully resolved data for the query. +- `dataUpdatedAt: number` + - The timestamp for when the query most recently returned the `status` + as `"success"`. +- `error: null | TError` + - Defaults to `null` + - The error object for the query, if an error was thrown. +- `errorUpdatedAt: number` + - The timestamp for when the query most recently returned the `status` + as `"error"`. +- `isStale: boolean` + - Will be `true` if the data in the cache is invalidated or if the data + is older than the given `staleTime`. +- `isPlaceholderData: boolean` + - Will be `true` if the data shown is the placeholder data. +- `isPreviousData: boolean` + - Will be `true` when data from the previous query is returned. +- `isFetched: boolean` + - Will be `true` if the query has been fetched. +- `isFetchedAfterMount: boolean` + - Will be `true` if the query has been fetched after the component + mounted. + - This property can be used to not show any previously cached data. +- `isFetching: boolean` + - Defaults to `true` so long as `enabled` is set to `false` - Will be `true` if the query is currently fetching, including background fetching. -- `failureCount: Integer` +- `failureCount: number` - The failure count for the query. - Incremented every time the query fails. - Reset to `0` when the query succeeds. -- `refetch: Function({ force, throwOnError }) => void` - - A function to manually refetch the query if it is stale. - - To bypass the stale check, you can pass the `force: true` option and - refetch it regardless of it's freshness +- `refetch: (options: { throwOnError: boolean, cancelRefetch: boolean }) => Promise<UseQueryResult>` + - A function to manually refetch the query. - If the query errors, the error will only be logged. If you want an error to be thrown, pass the `throwOnError: true` option -- `setQueryData()` - `Function(newData, opts) => void` + - If `cancelRefetch` is `true`, then the current request will be + cancelled before a new request is made +- `remove: () => void` + - A function to remove the query from the cache. +- `setQueryData()` - `Function(newData, opts) => Promise<void>` - A function to manually update the cache for a query. - `newData` can be an object of new data or a function that receives the old data and returns the new data @@ -148,4 +226,4 @@ The options are identical to the options for the ensure the data is correct. Disable refetch by passing an options object `{refetch: false}` as the second argument. - See the [Blitz mutation usage docs](./mutation-usage#setQueryData) for - example usage of `setQueryData()` + example usage of `setQueryData()`. diff --git a/app/pages/docs/use-query.mdx b/app/pages/docs/use-query.mdx index 6c42d0d..5f863c8 100644 --- a/app/pages/docs/use-query.mdx +++ b/app/pages/docs/use-query.mdx @@ -53,29 +53,53 @@ export default WrappedProject const [ queryResult, { - isFetching, + dataUpdatedAt, + error, + errorUpdatedAt, failureCount, + isError, + isFetched, + isFetchedAfterMount, + isFetching, + isIdle, + isLoading, + isLoadingError, + isPlaceholderData, + isPreviousData, + isRefetchError, + isStale, + isSuccess, refetch, + remove, + status, setQueryData, } ] = useQuery(queryResolver, queryInputArguments, { - enabled, - forceFetchOnMount, - retry, - retryDelay, - staleTime, cacheTime, + enabled, + initialData, + initialDataUpdatedAt + isDataEqual, + keepPreviousData, + notifyOnChangeProps, + notifyOnChangePropsExclusions, + onError, + onSettled, + onSuccess, + queryKeyHashFn, refetchInterval, refetchIntervalInBackground, - refetchOnWindowFocus, + refetchOnMount, refetchOnReconnect, - notifiyOnStatusChange, - onSuccess, - onError, - onSettled, + refetchOnWindowFocus, + retry, + retryOnMount, + retryDelay, + select, + staleTime, + structuralSharing, suspense, - initialData, - refetchOnMount, + useErrorBoundary, }) ``` @@ -91,117 +115,215 @@ const [ ### Options {#options} -- `enabled: Boolean` - - Set this to `false` to disable automatic refetching when the query - mounts or changes query keys. - - To refetch the query, use the `refetch` method returned from the - `useQuery` instance. -- `forceFetchOnMount: Boolean` - - Optional - - Defaults to `false` - - Set this to `true` to always fetch when the component mounts - (regardless of staleness). -- `retry: Boolean | Int | Function(failureCount, error) => Boolean` +- `enabled: boolean` + - Set this to `false` to disable this query from automatically running. + - Can be used for [Dependent Queries](./query-usage#dependent-queries). +- `retry: boolean | number | (failureCount: number, error: TError) => boolean` - If `false`, failed queries will not retry by default. - If `true`, failed queries will retry infinitely. - - If set to an `Int`, e.g. `3`, failed queries will retry until the + - If set to a `number`, e.g. `3`, failed queries will retry until the failed query count meets that number. - - If set to a function `(failureCount, error) => boolean` failed queries - will retry until the function returns false. -- `retryDelay: Function(retryAttempt: Int) => Int` - - This function receives a `retryAttempt` integer and returns the delay - to apply before the next attempt in milliseconds. +- `retryOnMount: boolean` + - If set to `false`, the query will not be retried on mount if it + contains an error. Defaults to `true`. +- `retryDelay: number | (retryAttempt: number, error: TError) => number` + - This function receives a `retryAttempt` integer and the actual Error + and returns the delay to apply before the next attempt in + milliseconds. - A function like `attempt => Math.min(attempt > 1 ? 2 ** attempt * 1000 : 1000, 30 * 1000)` applies exponential backoff. - A function like `attempt => attempt * 1000` applies linear backoff. -- `staleTime: Int | Infinity` - - The time in milliseconds that cache data remains fresh. After a - successful cache update, that cache data will become stale after this - duration. - - If set to `Infinity`, query will never go stale -- `cacheTime: Int | Infinity` +- `staleTime: number | Infinity` + - The time in milliseconds after data is considered stale. This value + only applies to the hook it is defined on. + - If set to `Infinity`, the data will never be considered stale +- `cacheTime: number | Infinity` - The time in milliseconds that unused/inactive cache data remains in memory. When a query's cache becomes unused or inactive, that cache - data will be garbage collected after this duration. + data will be garbage collected after this duration. When different + cache times are specified, the longest one will be used. - If set to `Infinity`, will disable garbage collection -- `refetchInterval: false | Integer` +- `refetchInterval: false | number` - Optional - If set to a number, all queries will continuously refetch at this frequency in milliseconds -- `refetchIntervalInBackground: Boolean` +- `refetchIntervalInBackground: boolean` - Optional - If set to `true`, queries that are set to continuously refetch with a `refetchInterval` will continue to refetch while their tab/window is in the background -- `refetchOnWindowFocus: Boolean` +- `refetchOnMount: boolean | "always"` + - Optional + - Defaults to `true` + - If set to `true`, the query will refetch on mount if the data is + stale. + - If set to `false`, the query will not refetch on mount. + - If set to `"always"`, the query will always refetch on mount. +- `refetchOnWindowFocus: boolean | "always"` + - Optional + - Defaults to `true` + - If set to `true`, the query will refetch on window focus if the data + is stale. + - If set to `false`, the query will not refetch on window focus. + - If set to `"always"`, the query will always refetch on window focus. +- `refetchOnReconnect: boolean | "always"` - Optional - - Set this to `false` to disable automatic refetching on window focus - (useful, when `refetchAllOnWindowFocus` is set to `true`). - - Set this to `true` to enable automatic refetching on window focus - (useful, when `refetchAllOnWindowFocus` is set to `false`. -- `refetchOnReconnect: Boolean` + - Defaults to `true` + - If set to `true`, the query will refetch on reconnect if the data is + stale. + - If set to `false`, the query will not refetch on reconnect. + - If set to `"always"`, the query will always refetch on reconnect. +- `notifyOnChangeProps: string[] | "tracked"` - Optional - - Set this to `true` or `false` to enable/disable automatic refetching - on reconnect for this query. -- `notifyOnStatusChange: Boolean` + - If set, the component will only re-render if any of the listed + properties change. + - If set to `['data', 'error']` for example, the component will only + re-render when the `data` or `error` properties change. + - If set to `"tracked"`, access to properties will be tracked, and the + component will only re-render when one of the tracked properties + change. +- `notifyOnChangePropsExclusions: string[]` - Optional - - Whether a change to the query status should re-render a component. - - If set to `false`, the component will only re-render when the actual - `data` or `error` changes. - - Defaults to `true`. -- `onSuccess: Function(data) => data` + - If set, the component will not re-render if any of the listed + properties change. + - If set to `['isStale']` for example, the component will not re-render + when the `isStale` property changes. +- `onSuccess: (data: TData) => void` - Optional - This function will fire any time the query successfully fetches new data. -- `onError: Function(err) => void` +- `onError: (error: TError) => void` - Optional - This function will fire if the query encounters an error and will be passed the error. -- `onSettled: Function(data, error) => data` +- `onSettled: (data?: TData, error?: TError) => void` - Optional - This function will fire any time the query is either successfully fetched or errors and be passed either the data or error -- `initialData: any | Function() => any` +- `select: (data: TData) => unknown` + - Optional + - This option can be used to transform or select a part of the data + returned by the query function. +- `suspense: boolean` + - Optional + - Set this to `false` to disable suspense mode. + - When `true`, `useQuery` will suspend when `status === 'loading'` + - When `true`, `useQuery` will throw runtime errors when + `status === 'error'` +- `initialData: TData | () => TData` - Optional - If set, this value will be used as the initial data for the query cache (as long as the query hasn't been created or cached yet) - If set to a function, the function will be called **once** during the shared/root query initialization, and be expected to synchronously return the initialData -- `refetchOnMount: Boolean` + - Initial data is considered stale by default unless a `staleTime` has + been set. + - `initialData` **is persisted** to the cache +- `initialDataUpdatedAt: number | (() => number | undefined)` - Optional - - Defaults to `true` - - If set to `false`, will disable additional instances of a query to - trigger background refetches -- `suspense: Boolean` + - If set, this value will be used as the time (in milliseconds) of when + the `initialData` itself was last updated. +- `placeholderData: TData | () => TData` - Optional - - Enabled by default. Set this to `false` to disable suspense mode. + - If set, this value will be used as the placeholder data for this + particular query observer while the query is still in the `loading` + data and no initialData has been provided. + - `placeholderData` is **not persisted** to the cache +- `keepPreviousData: boolean` + - Optional + - Defaults to `false` + - If set, any previous `data` will be kept when fetching new data + because the query key changed. +- `structuralSharing: boolean` + - Optional + - Defaults to `true` ### Returns {#returns} `[queryResult, queryExtras]` -##### `queryResult: Any` +##### `queryResult: TData` - Defaults to `undefined`. - The last successfully resolved data for the query. ##### `queryExtras: Object` -- `isFetching: Boolean` +- `status: String` + - Will be: + - `idle` if the query is idle. This only happens if a query is + initialized with `enabled: false` and no initial data is available. + - `loading` if the query is in a "hard" loading state. This means + there is no cached data and the query is currently fetching, eg + `isFetching === true` + - `error` if the query attempt resulted in an error. The corresponding + `error` property has the error received from the attempted fetch + - `success` if the query has received a response with no errors and is + ready to display its data. The corresponding `data` property on the + query is the data received from the successful fetch or if the + query's `enabled` property is set to `false` and has not been + fetched yet `data` is the first `initialData` supplied to the query + on initialization. +- `isIdle: boolean` + - A derived boolean from the `status` variable above, provided for + convenience. +- `isLoading: boolean` + - A derived boolean from the `status` variable above, provided for + convenience. +- `isSuccess: boolean` + - A derived boolean from the `status` variable above, provided for + convenience. +- `isError: boolean` + - A derived boolean from the `status` variable above, provided for + convenience. +- `isLoadingError: boolean` + - Will be `true` if the query failed while fetching for the first time. +- `isRefetchError: boolean` + - Will be `true` if the query failed while refetching. +- `data: TData` + - Defaults to `undefined`. + - The last successfully resolved data for the query. +- `dataUpdatedAt: number` + - The timestamp for when the query most recently returned the `status` + as `"success"`. +- `error: null | TError` + - Defaults to `null` + - The error object for the query, if an error was thrown. +- `errorUpdatedAt: number` + - The timestamp for when the query most recently returned the `status` + as `"error"`. +- `isStale: boolean` + - Will be `true` if the data in the cache is invalidated or if the data + is older than the given `staleTime`. +- `isPlaceholderData: boolean` + - Will be `true` if the data shown is the placeholder data. +- `isPreviousData: boolean` + - Will be `true` when `keepPreviousData` is set and data from the + previous query is returned. +- `isFetched: boolean` + - Will be `true` if the query has been fetched. +- `isFetchedAfterMount: boolean` + - Will be `true` if the query has been fetched after the component + mounted. + - This property can be used to not show any previously cached data. +- `isFetching: boolean` + - Defaults to `true` so long as `enabled` is set to `false` - Will be `true` if the query is currently fetching, including background fetching. -- `failureCount: Integer` +- `failureCount: number` - The failure count for the query. - Incremented every time the query fails. - Reset to `0` when the query succeeds. -- `refetch()` - `Function({ force, throwOnError }) => void` - - A function to manually refetch the query if it is stale. - - To bypass the stale check, you can pass the `force: true` option and - refetch it regardless of it's freshness +- `refetch: (options: { throwOnError: boolean, cancelRefetch: boolean }) => Promise<UseQueryResult>` + - A function to manually refetch the query. - If the query errors, the error will only be logged. If you want an error to be thrown, pass the `throwOnError: true` option + - If `cancelRefetch` is `true`, then the current request will be + cancelled before a new request is made +- `remove: () => void` + - A function to remove the query from the cache. - `setQueryData()` - `Function(newData, opts) => Promise<void>` - A function to manually update the cache for a query. - `newData` can be an object of new data or a function that receives the @@ -212,4 +334,4 @@ const [ ensure the data is correct. Disable refetch by passing an options object `{refetch: false}` as the second argument. - See the [Blitz mutation usage docs](./mutation-usage#setQueryData) for - example usage of `setQueryData()` + example usage of `setQueryData()`. From 08fb629ef03f5457dbbbd63b0146a58de1ec0f36 Mon Sep 17 00:00:00 2001 From: Simon Knott <info@simonknott.de> Date: Sat, 10 Apr 2021 22:23:51 +0200 Subject: [PATCH 03/67] Document Routes Manifest (#430) Co-authored-by: Brandon Bayer <b@bayer.ws> Co-authored-by: Jahred Hope <jahredhope@gmail.com> Co-authored-by: Fran Zekan <zekan.fran369@gmail.com> --- app/core/components/SponsorPack.js | 7 +++ app/core/navs/documentation.json | 4 +- app/core/styles/utilities.css | 4 ++ app/pages/docs/blitz-config.mdx | 6 +-- app/pages/docs/cli-codegen.mdx | 20 +++++++++ app/pages/docs/link.mdx | 39 +++++++++------- app/pages/docs/passportjs.mdx | 13 +++--- app/pages/docs/resolver-server-utilities.mdx | 6 +-- app/pages/docs/route-manifest.mdx | 47 ++++++++++++++++++++ app/pages/docs/router.mdx | 6 +-- app/pages/docs/use-paginated-query.mdx | 6 +-- app/pages/docs/why-blitz.mdx | 14 ++++++ 12 files changed, 131 insertions(+), 41 deletions(-) create mode 100644 app/pages/docs/cli-codegen.mdx create mode 100644 app/pages/docs/route-manifest.mdx diff --git a/app/core/components/SponsorPack.js b/app/core/components/SponsorPack.js index 0c3add2..c68d74c 100644 --- a/app/core/components/SponsorPack.js +++ b/app/core/components/SponsorPack.js @@ -50,6 +50,13 @@ const sponsors = [ tier: 4, cost: 100, }, + { + name: "userTrack", + href: "https://www.usertrack.net/?ref=blitzjs_web", + imageUrl: "https://i.imgur.com/UDBeazC.png", + tier: 4, + cost: 100, + }, ] const pack = { diff --git a/app/core/navs/documentation.json b/app/core/navs/documentation.json index a20d78e..2006562 100644 --- a/app/core/navs/documentation.json +++ b/app/core/navs/documentation.json @@ -74,7 +74,8 @@ "route-params-query", "router", "api-routes", - "shallow-routing" + "shallow-routing", + "route-manifest" ] }, { @@ -173,6 +174,7 @@ "cli-db", "cli-prisma", "cli-generate", + "cli-codegen", "cli-console", "cli-install", "cli-autocomplete", diff --git a/app/core/styles/utilities.css b/app/core/styles/utilities.css index 185872f..f7961a9 100644 --- a/app/core/styles/utilities.css +++ b/app/core/styles/utilities.css @@ -181,6 +181,10 @@ body.cursor-grabbing * { @apply text-black; } +.dark .admonition .admonition-content.admonition-content strong { + color: inherit; +} + .topic-select .topic-select__placeholder, .topic-select .topic-select__menu-list, .topic-select .topic-select__single-value { diff --git a/app/pages/docs/blitz-config.mdx b/app/pages/docs/blitz-config.mdx index 12f99d6..f389057 100644 --- a/app/pages/docs/blitz-config.mdx +++ b/app/pages/docs/blitz-config.mdx @@ -304,8 +304,8 @@ re-building as the value is inlined in the client-side bundles. ### Links {#links} -When linking to other pages using `blitz/link` and `blitz/router` the -`basePath` will be automatically applied. +When linking to other pages using `Link` and `Router` the `basePath` will +be automatically applied. For example using `/about` will automatically become `/docs/about` when `basePath` is set to `/docs`. @@ -314,7 +314,7 @@ For example using `/about` will automatically become `/docs/about` when export default function HomePage() { return ( <> - <Link href="/about"> + <Link href={Routes.About()}> <a>About Page</a> </Link> </> diff --git a/app/pages/docs/cli-codegen.mdx b/app/pages/docs/cli-codegen.mdx new file mode 100644 index 0000000..36f03d5 --- /dev/null +++ b/app/pages/docs/cli-codegen.mdx @@ -0,0 +1,20 @@ +--- +title: blitz codegen +sidebar_label: blitz codegen +--- + +**Alias: `blitz cg`** + +Use this command to generate the [Route Manifest](./route-manifest). + +#### Options + +None + +#### Example + +``` +> blitz codegen + +✔ Compiled +``` diff --git a/app/pages/docs/link.mdx b/app/pages/docs/link.mdx index 17acf23..5344555 100644 --- a/app/pages/docs/link.mdx +++ b/app/pages/docs/link.mdx @@ -5,21 +5,23 @@ sidebar_label: <Link> `<Link>` is used to navigate between pages -An example of linking to `/` and `/about`: +An example of linking to `Home` and `About`: ```jsx -import {Link} from "blitz" +import {Link, Routes} from "blitz" function Home() { return ( <ul> <li> - <Link href="/"> + <Link href={Routes.Home()}> // Option 1: use Route manifest + <Link href="/"> // Option 2: use string or location object <a>Home</a> </Link> </li> <li> - <Link href="/about"> + <Link href={Routes.About()}> // Option 1: use Route manifest + <Link href="/about"> // Option 2: use string or location object <a>About Us</a> </Link> </li> @@ -30,10 +32,15 @@ function Home() { export default Home ``` +The `Routes` object imported from `blitz` is automatically generated based +on the pages in your app. If your page component is named `About`, then +you can link to it with `Routes.About`. For more information, see the +[Route Manifest documentation](./route-manifest). + `Link` accepts the following props: - `href` - The path inside `pages` directory. This is the only required - prop + prop. - `as` - The path that will be rendered in the browser URL bar. Used for dynamic routes - [`passHref`](#if-the-child-is-a-custom-component-that-wraps-an-a-tag) - @@ -58,25 +65,23 @@ such cases instead. ## Dynamic routes {#dynamic-routes} -A `Link` to a dynamic route is a combination of the `href` and `as` props. -A link to the page `pages/post/[pid].js` will look like this: +A `Link` to a dynamic route works like the other links. A link to the page +`pages/post/[pid].js` will look like this: ```jsx -<Link href="/post/[pid]" as="/post/abc"> +<Link href={Routes.Post({pid: "abc"})}> <a>First Post</a> </Link> ``` -`href` is a file system path used by the page and it shouldn't change at -runtime. `as` on the other hand, will be dynamic most of the time -according to your needs. Here's an example of how to create a list of -links: +Here's an example of how to create a list of links: ```jsx const pids = ["id1", "id2", "id3"] { pids.map((pid) => ( - <Link href="/post/[pid]" as={`/post/${pid}`}> + <Link href={Routes.Post({pid})}> // using Routes Manifest + <Link href={`/posts/${pid}`}> // using location <a>Post {pid}</a> </Link> )) @@ -123,7 +128,7 @@ If the child of `Link` is a function component, in addition to using [`React.forwardRef`](https://reactjs.org/docs/react-api.html#reactforwardref): ```jsx -import {Link} from "blitz" +import {Link, Routes} from "blitz" // `onClick`, `href`, and `ref` need to be passed to the DOM element // for proper handling @@ -137,7 +142,7 @@ const MyButton = React.forwardRef(({onClick, href}, ref) => { function Home() { return ( - <Link href="/about" passHref> + <Link href={Routes.About()} passHref> <MyButton /> </Link> ) @@ -178,7 +183,7 @@ the `history` stack. You can use the `replace` prop to prevent adding a new entry, as in the following example: ```jsx -<Link href="/about" replace> +<Link href={Routes.About()} replace> <a>About us</a> </Link> ``` @@ -189,7 +194,7 @@ new entry, as in the following example: case you don't provide an `<a>` tag, consider the following example: ```jsx -<Link href="/about"> +<Link href={Routes.About()}> <img src="/static/image.png" alt="image" /> </Link> ``` diff --git a/app/pages/docs/passportjs.mdx b/app/pages/docs/passportjs.mdx index a1e4c43..a679620 100644 --- a/app/pages/docs/passportjs.mdx +++ b/app/pages/docs/passportjs.mdx @@ -93,13 +93,12 @@ export default passportAuth((ctx) => ({ errorRedirectUrl: "/", strategies: [ { - strategy: new TwitterStrategy( - { - consumerKey: process.env.TWITTER_CONSUMER_KEY as string, - consumerSecret: process.env.TWITTER_CONSUMER_SECRET as string, - /*...*/ - } - } + strategy: new TwitterStrategy({ + consumerKey: process.env.TWITTER_CONSUMER_KEY as string, + consumerSecret: process.env.TWITTER_CONSUMER_SECRET as string, + /*...*/ + }), + }, ], })) ``` diff --git a/app/pages/docs/resolver-server-utilities.mdx b/app/pages/docs/resolver-server-utilities.mdx index 0c3fbed..b829b7b 100644 --- a/app/pages/docs/resolver-server-utilities.mdx +++ b/app/pages/docs/resolver-server-utilities.mdx @@ -256,6 +256,7 @@ import { Link, BlitzPage, PromiseReturnType, + Routes, } from "blitz" import getProducts from "app/products/queries/getProducts" @@ -285,10 +286,7 @@ const Page: BlitzPage<PageProps> = function ({products}) { <div id="products"> {products.map((product) => ( <p key={product.id}> - <Link - href="/products/[handle]" - as={`/products/${product.handle}`} - > + <Link href={Routes.Product({handle: product.handle})}> <a>{product.name}</a> </Link> </p> diff --git a/app/pages/docs/route-manifest.mdx b/app/pages/docs/route-manifest.mdx new file mode 100644 index 0000000..929f078 --- /dev/null +++ b/app/pages/docs/route-manifest.mdx @@ -0,0 +1,47 @@ +--- +title: Route Manifest +sidebar_label: Route Manifest +--- + +Blitz generates a _Route Manifest_ for you. It allows you to refer to a +page by _name_ instead of _location_: + +```ts +// Assume you have a page at app/pages/products/[productId].tsx +export default function ProductPage() { ... + +// You can then use Routes... +import { Link, Routes } from "blitz" + +// ...to refer to it by name... +<Link href={Routes.ProductsPage({ productId: 123 })} /> + +// ...instead of looking up the location! +<Link href={`/products/${123}`} /> +``` + +The _Route Manifest_ is a purely optional feature. It has some advantages, +though: + +- improved expressiveness +- simplifies moving pages to new locations + +## Query Parameters {#query-parameters} + +Query parameters can be specified together with route parameters. + +```ts +// instead of ... +<Link href={`/products/${pid}?offerCode=capybara`} /> + +// ... you can do: +<Link href={Routes.Product({ pid, offerCode: "capybara" })} /> +``` + +## Generating the Manifest {#generating} + +The Route Manifest is generated into `node_modules/.blitz` directly from +your source code into. Both [`blitz build`](./cli-build) and +[`blitz dev`](./cli-dev) will automatically keep it up-to-date. +If you need to manually generate the route manifest, for example for a +typecheck in CI, use [`blitz codegen`](./cli-codegen). diff --git a/app/pages/docs/router.mdx b/app/pages/docs/router.mdx index ac937fb..7464463 100644 --- a/app/pages/docs/router.mdx +++ b/app/pages/docs/router.mdx @@ -115,11 +115,7 @@ Navigating `pages/post/[pid].js`, which is a dynamic route: import {Router} from "blitz" function Page() { - return ( - <span onClick={() => Router.push("/post/[pid]", "/post/abc")}> - Click me - </span> - ) + return <span onClick={() => Router.push("/post/abc")}>Click me</span> } ``` diff --git a/app/pages/docs/use-paginated-query.mdx b/app/pages/docs/use-paginated-query.mdx index 65f15e7..7ea51f8 100644 --- a/app/pages/docs/use-paginated-query.mdx +++ b/app/pages/docs/use-paginated-query.mdx @@ -13,6 +13,7 @@ import { usePaginatedQuery, useRouterQuery, useRouter, + Routes, } from "blitz" import getProjects from "app/products/queries/getProjects" @@ -41,10 +42,7 @@ const Projects = () => { <div> {projects.map((project) => ( <p key={project.id}> - <Link - href="/projects/[handle]" - as={`/projects/${project.handle}`} - > + <Link href={Routes.Project({handle: project.handle})}> <a>{project.name}</a> </Link> </p> diff --git a/app/pages/docs/why-blitz.mdx b/app/pages/docs/why-blitz.mdx index 6d556b5..a64dd39 100644 --- a/app/pages/docs/why-blitz.mdx +++ b/app/pages/docs/why-blitz.mdx @@ -123,3 +123,17 @@ Examples: - `app/products/pages/` could contain all the pages related to products - `app/admin/pages/` could contain all pages related to the backend admin section + +### 9. Route Manifest {#9-relaxed-restrictions} + +Next.js requires you to manually type out page locations. Blitz comes with +a [Route Manifest](./route-manifest), so you can do: + +```ts +<Link href={Routes.ProductsPage({ productId: 123 })} /> +// instead of +<Link href={`/products/${123}`} /> +``` + +This improves expressiveness and simplifies moving pages to other +location. From ff957b744f220d2c1ffeb360cc54575536108b81 Mon Sep 17 00:00:00 2001 From: JuanM04 <me@juanm04.com> Date: Mon, 12 Apr 2021 20:35:47 -0300 Subject: [PATCH 04/67] Updated dependencies --- package.json | 13 ++--- postcss.config.js | 2 +- tailwind.config.js | 1 + yarn.lock | 134 +++++++++++++++++++++------------------------ 4 files changed, 70 insertions(+), 80 deletions(-) diff --git a/package.json b/package.json index cd7f945..c83be60 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "dependencies": { "@docsearch/react": "1.0.0-alpha.28", "@octokit/rest": "18.5.2", - "@reach/rect": "0.14.0", + "@reach/rect": "0.15.0", "@sindresorhus/slugify": "1.1.0", "@visx/hierarchy": "1.7.0", "@visx/responsive": "1.7.0", @@ -28,7 +28,7 @@ "dlv": "1.1.3", "fathom-client": "3.0.0", "focus-visible": "5.2.0", - "framer-motion": "4.0.3", + "framer-motion": "4.1.3", "gray-matter": "4.0.2", "just-group-by": "1.0.0", "next-themes": "0.0.14", @@ -48,27 +48,26 @@ }, "devDependencies": { "@mdx-js/loader": "1.6.22", - "@next/bundle-analyzer": "10.0.9", + "@next/bundle-analyzer": "10.1.3", "@svgr/webpack": "5.5.0", - "@tailwindcss/jit": "0.1.17", "@tailwindcss/typography": "0.4.0", "alex": "9.1.0", "autoprefixer": "10.2.5", "babel-plugin-preval": "5.0.0", - "eslint": "7.23.0", + "eslint": "7.24.0", "eslint-plugin-simple-import-sort": "7.0.0", "file-loader": "6.2.0", "glob": "7.1.6", "husky": "6.0.0", "lint-staged": "10.5.4", "minimatch": "3.0.4", - "postcss": "8.2.8", + "postcss": "8.2.10", "postcss-nested": "5.0.5", "prettier": "2.2.1", "pretty-quick": "3.1.0", "remark-admonitions": "1.2.1", "simple-functional-loader": "1.2.1", - "tailwindcss": "2.0.4" + "tailwindcss": "2.1.1" }, "private": true } diff --git a/postcss.config.js b/postcss.config.js index bfe13b1..58bdd3e 100644 --- a/postcss.config.js +++ b/postcss.config.js @@ -1,3 +1,3 @@ module.exports = { - plugins: ["@tailwindcss/jit", "postcss-nested", "autoprefixer"], + plugins: ["tailwindcss", "postcss-nested", "autoprefixer"], } diff --git a/tailwind.config.js b/tailwind.config.js index 9d52cb8..d0de892 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -4,6 +4,7 @@ const {default: flattenColorPalette} = require("tailwindcss/lib/util/flattenColo const {toRgba} = require("tailwindcss/lib/util/withAlphaVariable") module.exports = { + mode: "jit", purge: { content: ["{app,pages,remark}/**/*.{js,jsx,ts,tsx}"], options: { diff --git a/yarn.lock b/yarn.lock index 8eb0994..9305286 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2202,10 +2202,10 @@ resolved "https://registry.yarnpkg.com/@mdx-js/util/-/util-1.6.22.tgz#219dfd89ae5b97a8801f015323ffa4b62f45718b" integrity sha512-H1rQc1ZOHANWBvPcW+JpGwr+juXSxM8Q8YCkm3GhZd8REu1fHR3z99CErO1p9pkcfcxZnMdIZdIsXkOHY0NilA== -"@next/bundle-analyzer@10.0.9": - version "10.0.9" - resolved "https://registry.yarnpkg.com/@next/bundle-analyzer/-/bundle-analyzer-10.0.9.tgz#e25d1c16b35348d8ed8e2d0608647cc519ce3911" - integrity sha512-xHU8BTtQty8g8Uq1TTiGyeOhX9Qo4q/jOmEcI9n5qQRlHB7DVNKmG4Jx5QoQ9DwWL7Rd0bPeFqvoTOlrqmIQ3g== +"@next/bundle-analyzer@10.1.3": + version "10.1.3" + resolved "https://registry.yarnpkg.com/@next/bundle-analyzer/-/bundle-analyzer-10.1.3.tgz#bf9a6883c66a210632954c957fe81fde04b35af5" + integrity sha512-25/I1+f83jJOryarNNGQ0cAlYdGfz5HFyQXmal4skKXhMKyWEBSKOu88tIZzsWktlhOEB8QC7O9KVgAlXPqTyw== dependencies: webpack-bundle-analyzer "4.3.0" @@ -2632,24 +2632,24 @@ resolved "https://registry.yarnpkg.com/@reach/observe-rect/-/observe-rect-1.2.0.tgz#d7a6013b8aafcc64c778a0ccb83355a11204d3b2" integrity sha512-Ba7HmkFgfQxZqqaeIWWkNK0rEhpxVQHIoVyW1YDSkGsGIXzcaW4deC8B0pZrNSSyLTdIk7y+5olKt5+g0GmFIQ== -"@reach/rect@0.14.0": - version "0.14.0" - resolved "https://registry.yarnpkg.com/@reach/rect/-/rect-0.14.0.tgz#ac0cb48814c2fbf8cff5a53edf3556c01fe33dc6" - integrity sha512-mXunIyFUCbzWlUVwMuI/nL3tLDsPWwe+5knXHQQG5cOqE9tosLt9PqVYQW5iMHdpl9pGqbx7yVXMZKp2hookJg== +"@reach/rect@0.15.0": + version "0.15.0" + resolved "https://registry.yarnpkg.com/@reach/rect/-/rect-0.15.0.tgz#c374b2971d250d7eeea6846fb4d64733b799f94e" + integrity sha512-Nu0zG4Xq8Z0C3Yr3wbjtj7fvII1q2KopHDStNtmL26oWPzlaZJ9A1K6rZSPIqxKkx1hvl6AUTeom7eHTIKhHjA== dependencies: "@reach/observe-rect" "1.2.0" - "@reach/utils" "0.14.0" + "@reach/utils" "0.15.0" prop-types "^15.7.2" + tiny-warning "^1.0.3" tslib "^2.1.0" -"@reach/utils@0.14.0": - version "0.14.0" - resolved "https://registry.yarnpkg.com/@reach/utils/-/utils-0.14.0.tgz#f3ff579c737c3e9528f6f940bc518452f2636810" - integrity sha512-QbSFO5p44qUCkOllJM06Lt5A/EsoVIYlB9Ij1vKEezy53keaa5bc879dD2Ahv+mMf+E1VppCeiL6YYvdpJmVVQ== +"@reach/utils@0.15.0": + version "0.15.0" + resolved "https://registry.yarnpkg.com/@reach/utils/-/utils-0.15.0.tgz#5b183d668f9bb900b2dec7a33c028a2a828d27b2" + integrity sha512-JHHN7T5ucFiuQbqkgv8ECbRWKfRiJxrO/xHR3fHf+f2C7mVs/KkJHhYtovS1iEapR4silygX9PY0+QUmHPOTYw== dependencies: - "@types/warning" "^3.0.0" + tiny-warning "^1.0.3" tslib "^2.1.0" - warning "^4.0.3" "@salesforce/lazy-require@0.4.0": version "0.4.0" @@ -2816,21 +2816,6 @@ dependencies: defer-to-connect "^2.0.0" -"@tailwindcss/jit@0.1.17": - version "0.1.17" - resolved "https://registry.yarnpkg.com/@tailwindcss/jit/-/jit-0.1.17.tgz#0fe54a6bd9473c3c1a5cb622ba771d69117a3d49" - integrity sha512-Of3NbM2Peex6iPkOap5JBmAPq6oVjKEKMysy6iH/9Vl0TjuEuB094Uc7yzJz8q6j6AQEImlaOGxwF+sdBlG2pw== - dependencies: - chokidar "^3.5.1" - dlv "^1.1.3" - fast-glob "^3.2.5" - lodash.topath "^4.5.2" - normalize-path "^3.0.0" - object-hash "^2.1.1" - parse-glob "^3.0.4" - postcss-selector-parser "^6.0.4" - quick-lru "^5.1.1" - "@tailwindcss/typography@0.4.0": version "0.4.0" resolved "https://registry.yarnpkg.com/@tailwindcss/typography/-/typography-0.4.0.tgz#b80974ad6af93df7b06e1981cb4d79698b6ad5c7" @@ -3187,11 +3172,6 @@ resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.3.tgz#9c088679876f374eb5983f150d4787aa6fb32d7e" integrity sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ== -"@types/warning@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/warning/-/warning-3.0.0.tgz#0d2501268ad8f9962b740d387c4654f5f8e23e52" - integrity sha1-DSUBJorY+ZYrdA04fEZU9fjiPlI= - "@types/yargs-parser@*": version "20.2.0" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.0.tgz#dd3e6699ba3237f0348cd085e4698780204842f9" @@ -6083,10 +6063,10 @@ eslint-visitor-keys@^2.0.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8" integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ== -eslint@7.23.0: - version "7.23.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.23.0.tgz#8d029d252f6e8cf45894b4bee08f5493f8e94325" - integrity sha512-kqvNVbdkjzpFy0XOszNwjkKzZ+6TcwCQ/h+ozlcIWwaimBBuhlQ4nN6kbiM2L+OjDcznkTJxzYfRFH92sx4a0Q== +eslint@7.24.0: + version "7.24.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.24.0.tgz#2e44fa62d93892bfdb100521f17345ba54b8513a" + integrity sha512-k9gaHeHiFmGCDQ2rEfvULlSLruz6tgfA8DEn+rY9/oYPFFTlz55mM/Q/Rij1b2Y42jwZiK3lXvNTw6w6TXzcKQ== dependencies: "@babel/code-frame" "7.12.11" "@eslint/eslintrc" "^0.4.0" @@ -6612,23 +6592,23 @@ fragment-cache@^0.2.1: dependencies: map-cache "^0.2.2" -framer-motion@4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-4.0.3.tgz#e20a32c6825dc08c98e130640cb6441ec2920b7d" - integrity sha512-5p9VPpgPctMJuNW8CqHSk9k/v3xAUpReWzlgKl1a1fsvqzcFgNGENZPfBwHuukanvMmh3trzO5jZNX0Kc8/pAA== +framer-motion@4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-4.1.3.tgz#fdde56885910f516233233073f8f0ed6aab6492e" + integrity sha512-WJ3iZiMgzR3cf/W5/o5OwGyNeXvjuIriItPnDtqbAGgd8rCq5ysAW3RS4cC2i2wVA9EfrlxWmosTt+lKhaVI3Q== dependencies: - framesync "5.2.3" + framesync "5.3.0" hey-listen "^1.0.8" - popmotion "9.3.4" + popmotion "9.3.5" style-value-types "4.1.4" tslib "^2.1.0" optionalDependencies: "@emotion/is-prop-valid" "^0.8.2" -framesync@5.2.3: - version "5.2.3" - resolved "https://registry.yarnpkg.com/framesync/-/framesync-5.2.3.tgz#73e26c83e108fb95f05515ee5c6fdf20c9fd47ab" - integrity sha512-5PxjYm5RxvsT68a9trOOL/61POxL7DcHrbx+j/50CR33mvBdp0aUTI+EfrMeVXprx3XOZ37NWg6r5ZQQOA2arA== +framesync@5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/framesync/-/framesync-5.3.0.tgz#0ecfc955e8f5a6ddc8fdb0cc024070947e1a0d9b" + integrity sha512-oc5m68HDO/tuK2blj7ZcdEBRx3p1PjrgHazL8GYEpvULhrtGIFbQArN6cQS2QhW8mitffaB+VYzMjDqBxxQeoA== dependencies: tslib "^2.1.0" @@ -9754,6 +9734,11 @@ nanoid@^3.1.20: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.20.tgz#badc263c6b1dcf14b71efaa85f6ab4c1d6cfc788" integrity sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw== +nanoid@^3.1.22: + version "3.1.22" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.22.tgz#b35f8fb7d151990a8aebd5aa5015c03cf726f844" + integrity sha512-/2ZUaJX2ANuLtTvqTlgqBQNJoQO398KyJgZloL0PZkC0dpysjncRUPsFe3DUPzz/y3h+u7C46np8RMuvF3jsSQ== + nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" @@ -10736,12 +10721,12 @@ pnp-webpack-plugin@1.6.4: dependencies: ts-pnp "^1.1.6" -popmotion@9.3.4: - version "9.3.4" - resolved "https://registry.yarnpkg.com/popmotion/-/popmotion-9.3.4.tgz#9dea29e752491396b0beebc360d9837598f24c7c" - integrity sha512-CwUJwVEkhXZg7ZCtWLrO2lK40g/J+cEwAV0bPxOq83g2UNrvN2HTcUDnlf/rq6QeMKJPwDvXd6R8SsJO5BjDXg== +popmotion@9.3.5: + version "9.3.5" + resolved "https://registry.yarnpkg.com/popmotion/-/popmotion-9.3.5.tgz#e821aff3424a021b0f2c93922db31c55cfe64149" + integrity sha512-Lr2rq8OP0j8D7CO2/6eO17ALeFCxjx1hfTGbMg+TLqFj+KZSGOoj6gRBVTzDINGqo6LQrORQSSSDaCL5OrB3bw== dependencies: - framesync "5.2.3" + framesync "5.3.0" hey-listen "^1.0.8" style-value-types "4.1.4" tslib "^2.1.0" @@ -10769,7 +10754,7 @@ postcss-js@^3.0.3: camelcase-css "^2.0.1" postcss "^8.1.6" -postcss-nested@5.0.5, postcss-nested@^5.0.5: +postcss-nested@5.0.5: version "5.0.5" resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-5.0.5.tgz#f0a107d33a9fab11d7637205f5321e27223e3603" integrity sha512-GSRXYz5bccobpTzLQZXOnSOfKl6TwVr5CyAQJUPub4nuRJSOECK5AqurxVgmtxP48p0Kc/ndY/YyS1yqldX0Ew== @@ -10806,13 +10791,13 @@ postcss@8.1.7, postcss@^8.1.6: nanoid "^3.1.16" source-map "^0.6.1" -postcss@8.2.8: - version "8.2.8" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.8.tgz#0b90f9382efda424c4f0f69a2ead6f6830d08ece" - integrity sha512-1F0Xb2T21xET7oQV9eKuctbM9S7BC0fetoHCc4H13z0PT6haiRLP4T0ZY4XWh7iLP0usgqykT6p9B2RtOf4FPw== +postcss@8.2.10: + version "8.2.10" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.10.tgz#ca7a042aa8aff494b334d0ff3e9e77079f6f702b" + integrity sha512-b/h7CPV7QEdrqIxtAf2j31U5ef05uBDuvoXv6L51Q4rcS1jdlXAVKJv+atCFdUXYl9dyTHGyoMzIepwowRJjFw== dependencies: colorette "^1.2.2" - nanoid "^3.1.20" + nanoid "^3.1.22" source-map "^0.6.1" postcss@^6.0.9: @@ -12822,29 +12807,36 @@ table@^6.0.4: slice-ansi "^4.0.0" string-width "^4.2.0" -tailwindcss@2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-2.0.4.tgz#cf13e62738c3a27065664e449d93b66ee2945506" - integrity sha512-WhgR0oiBxGOZ9jY0yVfaJCHnckR7U74Fs/BMsYxGdwGJQ5Hd/HlaKD26bEJFZOvYScJo0QcUj2ImldzedsG7Bw== +tailwindcss@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-2.1.1.tgz#642f6038c9283a8e1454da34585b8b7c1a1e8877" + integrity sha512-zZ6axGqpSZOCBS7wITm/WNHkBzDt5CIZlDlx0eCVldwTxFPELCVGbgh7Xpb3/kZp3cUxOmK7bZUjqhuMrbN6xQ== dependencies: "@fullhuman/postcss-purgecss" "^3.1.3" bytes "^3.0.0" chalk "^4.1.0" + chokidar "^3.5.1" color "^3.1.3" detective "^5.2.0" didyoumean "^1.2.1" + dlv "^1.1.3" + fast-glob "^3.2.5" fs-extra "^9.1.0" html-tags "^3.1.0" lodash "^4.17.21" + lodash.topath "^4.5.2" modern-normalize "^1.0.0" node-emoji "^1.8.1" + normalize-path "^3.0.0" object-hash "^2.1.1" + parse-glob "^3.0.4" postcss-functions "^3" postcss-js "^3.0.3" - postcss-nested "^5.0.5" + postcss-nested "5.0.5" postcss-selector-parser "^6.0.4" postcss-value-parser "^4.1.0" pretty-hrtime "^1.0.3" + quick-lru "^5.1.1" reduce-css-calc "^2.1.8" resolve "^1.20.0" @@ -13038,6 +13030,11 @@ tiny-emitter@^2.0.0: resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423" integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q== +tiny-warning@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" + integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== + tmp@0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" @@ -13932,13 +13929,6 @@ walker@^1.0.7, walker@~1.0.5: dependencies: makeerror "1.0.x" -warning@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3" - integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w== - dependencies: - loose-envify "^1.0.0" - watchpack@2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.1.1.tgz#e99630550fca07df9f90a06056987baa40a689c7" From 20e1fb4de1bdbe677a50ea5e7ad5cc23baeff507 Mon Sep 17 00:00:00 2001 From: sean-brydon <55134778+sean-brydon@users.noreply.github.com> Date: Tue, 13 Apr 2021 20:28:09 +0100 Subject: [PATCH 05/67] Uppercased "admin" in session-management.mdx (#441) --- app/pages/docs/session-management.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/pages/docs/session-management.mdx b/app/pages/docs/session-management.mdx index 9a2b180..0c3c0e2 100644 --- a/app/pages/docs/session-management.mdx +++ b/app/pages/docs/session-management.mdx @@ -232,7 +232,7 @@ declare module "blitz" { Then change all uses of `ctx.session.$create()` to pass in the new fields. ```ts -ctx.session.$create({userId: 1, role: "admin", orgId: 1}) +ctx.session.$create({userId: 1, role: "ADMIN", orgId: 1}) ``` You can also use `ctx.session.$setPublicData()` to update session data for From 195b9a0f250fe01730749e3b948ea4cfa7bf6174 Mon Sep 17 00:00:00 2001 From: Brandon Bayer <b@bayer.ws> Date: Wed, 14 Apr 2021 11:19:39 -0400 Subject: [PATCH 06/67] Few maintainers retire --- app/pages/docs/how-the-community-operates.mdx | 66 +------------------ 1 file changed, 2 insertions(+), 64 deletions(-) diff --git a/app/pages/docs/how-the-community-operates.mdx b/app/pages/docs/how-the-community-operates.mdx index a16edef..b9bcc32 100644 --- a/app/pages/docs/how-the-community-operates.mdx +++ b/app/pages/docs/how-the-community-operates.mdx @@ -167,20 +167,6 @@ For more details, see [Being an L2 Maintainer](./maintainers) <table> <tr> - <td align="center"> - <a href="https://github.com/aem"> - <img - src="https://avatars0.githubusercontent.com/u/1909883?v=4" - width="100px;" - alt="" - /> - <sub> - <b>Adam Markon</b> - </sub> - </a> - <br /> - CLI - </td> <td align="center"> <a href="http://robdrosenberg.com"> <img @@ -237,18 +223,6 @@ For more details, see [Being an L1 Maintainer](./maintainers) <table> <tr> - <td align="center"> - <a href="https://corey-brown.com"> - <img - src="https://avatars1.githubusercontent.com/u/12791148?v=4" - width="100px;" - alt="" - /> - <sub> - <b>Corey Brown</b> - </sub> - </a> - </td> <td align="center"> <a href="http://jeremyliberman.com/"> <img @@ -261,18 +235,6 @@ For more details, see [Being an L1 Maintainer](./maintainers) </sub> </a> </td> - <td align="center"> - <a href="http://jagascript.com"> - <img - src="https://avatars0.githubusercontent.com/u/4562878?v=4" - width="100px;" - alt="" - /> - <sub> - <b>Jaga Santagostino</b> - </sub> - </a> - </td> <td align="center"> <a href="https://twitter.com/nitaking_"> <img @@ -285,18 +247,6 @@ For more details, see [Being an L1 Maintainer](./maintainers) </sub> </a> </td> - <td align="center"> - <a href="https://twitter.com/sandulat"> - <img - src="https://avatars2.githubusercontent.com/u/7345874?v=4" - width="100px;" - alt="" - /> - <sub> - <b>Alexandru Stratulat</b> - </sub> - </a> - </td> <td align="center"> <a href="https://github.com/engelkes-finstreet"> <img @@ -309,20 +259,6 @@ For more details, see [Being an L1 Maintainer](./maintainers) </sub> </a> </td> - </tr> - <tr> - <td align="center"> - <a href="https://twitter.com/jdavenport97"> - <img - src="https://avatars2.githubusercontent.com/u/1329874?v=4" - width="100px;" - alt="" - /> - <sub> - <b>Jamie Davenport</b> - </sub> - </a> - </td> <td align="center"> <a href="https://twitter.com/myrondavis"> <img @@ -359,6 +295,8 @@ For more details, see [Being an L1 Maintainer](./maintainers) </sub> </a> </td> + </tr> + <tr> <td align="center"> <a href="https://kevinlangleyjr.com"> <img From 1d1f0d7de19dcfc0fe0201424844f82207222ccf Mon Sep 17 00:00:00 2001 From: Alessandro <alessandro.buonerba@ovoenergy.com> Date: Wed, 14 Apr 2021 16:36:03 +0100 Subject: [PATCH 07/67] Add myself as L1 Maintainer (#443) --- app/pages/docs/how-the-community-operates.mdx | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/pages/docs/how-the-community-operates.mdx b/app/pages/docs/how-the-community-operates.mdx index b9bcc32..418829a 100644 --- a/app/pages/docs/how-the-community-operates.mdx +++ b/app/pages/docs/how-the-community-operates.mdx @@ -309,5 +309,17 @@ For more details, see [Being an L1 Maintainer](./maintainers) </sub> </a> </td> + <td align="center"> + <a href="https://kevinlangleyjr.com"> + <img + src="https://avatars.githubusercontent.com/u/28837891?v=4" + width="100px;" + alt="" + /> + <sub> + <b>Alessandro Buonerba</b> + </sub> + </a> + </td> </tr> </table> From f0c1fd16acecc3a521ac45c90326c50cac3ee88d Mon Sep 17 00:00:00 2001 From: laubonghaudoi <laubonghaudoi@icloud.com> Date: Wed, 14 Apr 2021 08:36:35 -0700 Subject: [PATCH 08/67] Fix the first two issues of #436 (#439) --- app/pages/docs/tutorial.mdx | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/app/pages/docs/tutorial.mdx b/app/pages/docs/tutorial.mdx index 422393f..935c224 100644 --- a/app/pages/docs/tutorial.mdx +++ b/app/pages/docs/tutorial.mdx @@ -276,8 +276,9 @@ blitz generate resource choice text votes:int:default=0 belongsTo:question ``` If you get an error run `blitz prisma format` -Again, hit **Enter** when prompted to run the migration and enter a name -for the migration. +Note that this doesn't require a database migration because we haven't added the +`Choice` field to the `Question` model yet. So we are choosing `false` when +prompted to run the migration: ``` CREATE app/choices/queries/getChoice.ts @@ -298,7 +299,7 @@ CREATE app/choices/mutations/updateChoice.ts > questionId Int > } -? Run 'prisma migrate dev' to update your database? (Y/n) › true +? Run 'prisma migrate dev' to update your database? (Y/n) › false ``` #### Lastly let's update the `Question` model to have a relationship back to `Choice`. @@ -316,9 +317,31 @@ model Question { } ``` -And then run `blitz prisma generate` to update prisma client for that -schema change. This doesn't require a database migration because there's -not an actual field being added to the `Question` model in the database. +Now we can run the migration to update our database: + +```sh +blitz prisma migrate dev +``` + +And again, enter a name for the migration, like "add choice": + +``` +Environment variables loaded from .env +Prisma schema loaded from db/schema.prisma +Datasource "db": SQLite database "db.sqlite" at "file:./db.sqlite" + +✔ Name of migration … add choice +The following migration(s) have been created and applied from new schema changes: + +migrations/ + └─ 20210412175528_add_choice/ + └─ migration.sql + +Your database is now in sync with your schema. +``` + +Now our database is ready and a Prisma client is also generated. Lets move on to +play with the Prisma client! ## Playing with the Prisma database client {#playing-with-the-prisma-database-client} From bf68c8d89c8b2ac411132ef87e8c2fa66cbde4e8 Mon Sep 17 00:00:00 2001 From: Alessandro <a.buonerba@hotmail.com> Date: Thu, 15 Apr 2021 15:10:12 +0100 Subject: [PATCH 09/67] Edit Dieman's personal website (#445) --- .alexrc.js | 11 ++++++++++- app/pages/docs/how-the-community-operates.mdx | 2 +- app/pages/docs/tutorial.mdx | 13 +++++++------ 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/.alexrc.js b/.alexrc.js index 71913eb..bd676be 100644 --- a/.alexrc.js +++ b/.alexrc.js @@ -1,4 +1,13 @@ // Use a "maybe" level of profanity instead of the default "unlikely". exports.profanitySureness = 1 -exports.allow = ["simple", "special", "invalid", "he-she", "her-him", "herself-himself", "obvious"] +exports.allow = [ + "simple", + "special", + "invalid", + "he-she", + "her-him", + "herself-himself", + "obvious", + "easy", +] diff --git a/app/pages/docs/how-the-community-operates.mdx b/app/pages/docs/how-the-community-operates.mdx index 418829a..6c184b2 100644 --- a/app/pages/docs/how-the-community-operates.mdx +++ b/app/pages/docs/how-the-community-operates.mdx @@ -310,7 +310,7 @@ For more details, see [Being an L1 Maintainer](./maintainers) </a> </td> <td align="center"> - <a href="https://kevinlangleyjr.com"> + <a href="https://buonerba.dev"> <img src="https://avatars.githubusercontent.com/u/28837891?v=4" width="100px;" diff --git a/app/pages/docs/tutorial.mdx b/app/pages/docs/tutorial.mdx index 935c224..760ebf4 100644 --- a/app/pages/docs/tutorial.mdx +++ b/app/pages/docs/tutorial.mdx @@ -274,11 +274,12 @@ pages for the `Choice` model: ```sh blitz generate resource choice text votes:int:default=0 belongsTo:question ``` + If you get an error run `blitz prisma format` -Note that this doesn't require a database migration because we haven't added the -`Choice` field to the `Question` model yet. So we are choosing `false` when -prompted to run the migration: +Note that this doesn't require a database migration because we haven't +added the `Choice` field to the `Question` model yet. So we are choosing +`false` when prompted to run the migration: ``` CREATE app/choices/queries/getChoice.ts @@ -317,7 +318,7 @@ model Question { } ``` -Now we can run the migration to update our database: +Now we can run the migration to update our database: ```sh blitz prisma migrate dev @@ -340,8 +341,8 @@ migrations/ Your database is now in sync with your schema. ``` -Now our database is ready and a Prisma client is also generated. Lets move on to -play with the Prisma client! +Now our database is ready and a Prisma client is also generated. Lets move +on to play with the Prisma client! ## Playing with the Prisma database client {#playing-with-the-prisma-database-client} From dc28c78237840a642c13270f7ce94d70343a395a Mon Sep 17 00:00:00 2001 From: Brandon Bayer <b@bayer.ws> Date: Fri, 16 Apr 2021 15:32:11 -0400 Subject: [PATCH 10/67] update hero code sample to use new Routes object --- app/core/components/home/HeroCode.js | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/app/core/components/home/HeroCode.js b/app/core/components/home/HeroCode.js index e6fb156..024f77e 100644 --- a/app/core/components/home/HeroCode.js +++ b/app/core/components/home/HeroCode.js @@ -7,11 +7,11 @@ import tokenize from "../../macros/tokenize.macro.js" const pageTokenized = tokenize.jsx( `// app/pages/projects/new.tsx -import { Link, useRouter, useMutation, BlitzPage } from "blitz" +import { Link, Routes, useRouter, useMutation, BlitzPage } from "blitz" import Layout from "app/core/layouts/Layout" // Notice how we import the server function directly import createProject, {CreateProject} from "app/projects/mutations/createProject" -import { ProjectForm, FORM_ERROR } from "app/projects/components/ProjectForm" +import { ProjectForm } from "app/projects/components/ProjectForm" const NewProjectPage: BlitzPage = () => { const router = useRouter() @@ -25,13 +25,10 @@ const NewProjectPage: BlitzPage = () => { submitText="Create Project" schema={CreateProject} onSubmit={async (values) => { - try { - // This is equivalent to calling the server function directly - const project = await createProjectMutation(values) - router.push("/projects/" + project.id) - } catch (error) { - return { [FORM_ERROR]: error.toString() } - } + // This is equivalent to calling the server function directly + const project = await createProjectMutation(values) + // Notice the 'Routes' object Blitz provides for routing + router.push(Routes.ProjectsPage({projectId: project.id}})) }} /> </div> From fb9d45d822c9a76f7246a4f02057447b8745bf6f Mon Sep 17 00:00:00 2001 From: Tommaso Bruno <bruno.tommaso31@gmail.com> Date: Fri, 16 Apr 2021 23:53:45 +0100 Subject: [PATCH 11/67] new maintainer TommasoBruno added (#444) * new maintainer TommasoBruno added * href TommasoBruno maintainer changed --- app/pages/docs/how-the-community-operates.mdx | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/pages/docs/how-the-community-operates.mdx b/app/pages/docs/how-the-community-operates.mdx index 6c184b2..60aa7e4 100644 --- a/app/pages/docs/how-the-community-operates.mdx +++ b/app/pages/docs/how-the-community-operates.mdx @@ -321,5 +321,17 @@ For more details, see [Being an L1 Maintainer](./maintainers) </sub> </a> </td> + <td align="center"> + <a href="https://dev.to/tommasobruno99"> + <img + src="https://avatars.githubusercontent.com/u/61512591?v=4" + width="100px;" + alt="" + /> + <sub> + <b>Tommaso Bruno</b> + </sub> + </a> + </td> </tr> </table> From b27cfb5eaf0e0b339678cd798c4d5cbf8ddb726f Mon Sep 17 00:00:00 2001 From: Antony <info@antonykamp.de> Date: Sat, 17 Apr 2021 17:28:27 +0200 Subject: [PATCH 12/67] Added `--inspect` flag to CLI (#447) Added to `cli-start.mdx` & `cli-dev.mdx` --- app/pages/docs/cli-dev.mdx | 1 + app/pages/docs/cli-start.mdx | 1 + 2 files changed, 2 insertions(+) diff --git a/app/pages/docs/cli-dev.mdx b/app/pages/docs/cli-dev.mdx index 3b80c3e..260407b 100644 --- a/app/pages/docs/cli-dev.mdx +++ b/app/pages/docs/cli-dev.mdx @@ -14,6 +14,7 @@ Starts the Blitz development server. | `--hostname` | `-H` | Set the hostname to use for the server. | `"localhost"` | | `--port` | `-p` | Set the port you'd like the server to listen on. | `3000` | | `--no-incremental-build` | | Disable incremental build and start from a fresh cache | `false` | +| `--inspect` | | Enable the Node.js inspector | `false` | #### Examples diff --git a/app/pages/docs/cli-start.mdx b/app/pages/docs/cli-start.mdx index 3abdee1..4a050b6 100644 --- a/app/pages/docs/cli-start.mdx +++ b/app/pages/docs/cli-start.mdx @@ -13,6 +13,7 @@ Starts the Blitz production server. | ------------ | --------- | ------------------------------------------------ | ------------- | | `--hostname` | `-H` | Set the hostname to use for the server. | `"localhost"` | | `--port` | `-p` | Set the port you'd like the server to listen on. | `3000` | +| `--inspect` | | Enable the Node.js inspector | `false` | #### Examples From 82436ec0678e19c73c9c61ff701e0569a9fe4a67 Mon Sep 17 00:00:00 2001 From: Justin Hall <justin.r.hall@gmail.com> Date: Sun, 18 Apr 2021 13:15:05 -0600 Subject: [PATCH 13/67] Fix minor typo in query-usage.mdx (#449) --- app/pages/docs/query-usage.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/pages/docs/query-usage.mdx b/app/pages/docs/query-usage.mdx index d548214..a65d404 100644 --- a/app/pages/docs/query-usage.mdx +++ b/app/pages/docs/query-usage.mdx @@ -159,7 +159,7 @@ export default ProjectPage To ensure data is not shared between users and requests, a new query client is created for each page request. You can prefetch your data by -ivoking the `prefetchQuery` method on the newly-created client, passing in +invoking the `prefetchQuery` method on the newly-created client, passing in the query key and resolver along with any relevant input arguments. Once the data has been fetched, dehydrate the queries to the query client using the `dehydrate` method and pass the result to the page via the From 6ce3d922be92a3eb5c3f8e964b41d3fcc57bd670 Mon Sep 17 00:00:00 2001 From: JuanM04 <me@juanm04.com> Date: Mon, 19 Apr 2021 08:04:06 -0300 Subject: [PATCH 14/67] Added a note about broken symlinks --- app/pages/docs/contributing.mdx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/pages/docs/contributing.mdx b/app/pages/docs/contributing.mdx index 6449a50..ff89efa 100644 --- a/app/pages/docs/contributing.mdx +++ b/app/pages/docs/contributing.mdx @@ -269,3 +269,9 @@ If you run into symlink and EPERM errors when trying to run Preconstruct on Windows, you may need to enable [Windows Developer Mode](https://www.howtogeek.com/292914/what-is-developer-mode-in-windows-10/) so that Preconstruct can create symlinks. + +#### Missing files in Windows + +If you have errors about missing files even after you run `yarn build`, +try cloning again your repository win the configuration `core.symlinks` +set to `true` like this: `git clone -c core.symlinks=true <URL>`. From fcf53b24eee8eb56f3513283816e87f6bb25dea7 Mon Sep 17 00:00:00 2001 From: JuanM04 <me@juanm04.com> Date: Mon, 19 Apr 2021 08:34:13 -0300 Subject: [PATCH 15/67] Typo in contributing.mdx --- app/pages/docs/contributing.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/pages/docs/contributing.mdx b/app/pages/docs/contributing.mdx index ff89efa..8b2645b 100644 --- a/app/pages/docs/contributing.mdx +++ b/app/pages/docs/contributing.mdx @@ -273,5 +273,5 @@ so that Preconstruct can create symlinks. #### Missing files in Windows If you have errors about missing files even after you run `yarn build`, -try cloning again your repository win the configuration `core.symlinks` +try cloning again your repository with the configuration `core.symlinks` set to `true` like this: `git clone -c core.symlinks=true <URL>`. From 00986a812eda0a78c0562afd5f37c7cf5e4a2c3e Mon Sep 17 00:00:00 2001 From: Fatih Altinok <fatihaltinok@live.com> Date: Mon, 19 Apr 2021 19:43:10 +0300 Subject: [PATCH 16/67] Improve deploy-vercel.mdx consistency and clarity (#450) --- app/pages/docs/deploy-vercel.mdx | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/app/pages/docs/deploy-vercel.mdx b/app/pages/docs/deploy-vercel.mdx index 05fcab9..77348ae 100644 --- a/app/pages/docs/deploy-vercel.mdx +++ b/app/pages/docs/deploy-vercel.mdx @@ -15,7 +15,7 @@ very expensive database to support any amount of scale. ## Serverless Peculiarities for SQL Databases {#peculiarities} -There are two main issues when it comes to using SQL databases in a +There are three main issues when it comes to using SQL databases in a serverless environment: ### Problem 1: Separate Locations for App and Database {#locations} @@ -35,35 +35,34 @@ outside your API handler (this is the default setup in Blitz apps). This optimizes performance because subsequent requests to the same lambda already have an active database connection. -However, this can result in a large number of idle connections. A larger -number than what your database can support. For reference, the lowest -database tier on Digital Ocean has a limit of 22 connections. +However, this can result in a large number of idle connections, larger +than what your database can support. For reference, the lowest database +tier on Digital Ocean has a limit of 22 connections. -**Solution:** The solution to this problem is to use a connection pool in -front of your database. Pgbouncer is the most popular connection pool for -postgres. Both Digital Ocean and Heroku have a feature to simply add -pgbouncer. +**Solution:** Use a connection pool in front of your database. PgBouncer +is the most popular connection pool for PostgreSQL. Both Digital Ocean and +Heroku have a feature to simply add PgBouncer. ### Problem 3: Database Throughput {#database-throughput} -Pgbouncer for PostgreSQL is advertised as allowing you to have +PgBouncer for PostgreSQL is advertised as allowing you to have 5,000-10,000 active database connections, but **it's critical to -understand that those are idle connections.** **Pgbouncer does not +understand that those are idle connections.** **PgBouncer does not increase your core database capacity.** If your database has a max of 22 connections, then you can only have 22 simultaneous transactions, -regardless of whether you have pgbouncer in front of it or not. +regardless of whether you have PgBouncer in front of it or not. **Solution:** Increase your database size to meet the traffic needs for your app. But be aware this can become very expensive. -Note: slow database transactions will greatly decrease your throughput. If -it seems you are running out of connections before you should based on +Note: Slow database transactions will greatly decrease your throughput. If +it seems you are running out of connections before you should, based on your app traffic, then turn on logging for your database queries and look for long transactions. ## Databases to Consider {#databases} -1. PostgreSQL + Pgbouncer +1. PostgreSQL + PgBouncer 2. [PlanetScaleDB](https://www.planetscale.com) - This is MySQL but seems to have solved the connection limit issue for serverless environments @@ -90,7 +89,7 @@ This also assumes you already have a [Vercel](https://vercel.com) account. [read the Prisma docs on this](https://www.prisma.io/docs/reference/database-connectors/postgresql#connection-details). 1. Make sure you get the connection string to your connection pool, not directly to the DB. - 2. If using pgBouncer (default connection pool on Digital Ocean), you + 2. If using PgBouncer (default connection pool on Digital Ocean), you must add `&pgbouncer=true` to the end of your connection string for Prisma to work correctly. 4. Change your build script in package.json to be From 15baf5e316a1a207570357fc5a504903fc427235 Mon Sep 17 00:00:00 2001 From: JuanM04 <me@juanm04.com> Date: Mon, 19 Apr 2021 19:11:05 -0300 Subject: [PATCH 17/67] Upgraded dependencies --- package.json | 8 +- ...english-slugify.js => english-slugify.mjs} | 6 +- yarn.lock | 1275 +++++++---------- 3 files changed, 528 insertions(+), 761 deletions(-) rename scripts/{english-slugify.js => english-slugify.mjs} (91%) diff --git a/package.json b/package.json index c83be60..3742b12 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "lint": "yarn lint:code && yarn lint:docs", "lint:code": "eslint --ignore-path .gitignore --ext .js,.ts,.tsx .", "lint:docs": "alex app/pages/docs/**/*.mdx", - "english-slugify": "node scripts/english-slugify.js", + "english-slugify": "node scripts/english-slugify.mjs", "prepare": "husky install" }, "lint-staged": { @@ -20,15 +20,15 @@ "@docsearch/react": "1.0.0-alpha.28", "@octokit/rest": "18.5.2", "@reach/rect": "0.15.0", - "@sindresorhus/slugify": "1.1.0", + "@sindresorhus/slugify": "2.0.0", "@visx/hierarchy": "1.7.0", "@visx/responsive": "1.7.0", - "blitz": "0.33.1", + "blitz": "0.34.0", "clsx": "1.1.1", "dlv": "1.1.3", "fathom-client": "3.0.0", "focus-visible": "5.2.0", - "framer-motion": "4.1.3", + "framer-motion": "4.1.5", "gray-matter": "4.0.2", "just-group-by": "1.0.0", "next-themes": "0.0.14", diff --git a/scripts/english-slugify.js b/scripts/english-slugify.mjs similarity index 91% rename from scripts/english-slugify.js rename to scripts/english-slugify.mjs index 8904b02..bb664f9 100644 --- a/scripts/english-slugify.js +++ b/scripts/english-slugify.mjs @@ -1,6 +1,6 @@ -const slugify = require("@sindresorhus/slugify") -const fs = require("fs/promises") -const path = require("path") +import slugify from "@sindresorhus/slugify" +import fs from "fs/promises" +import path from "path" async function main() { const docsPath = path.resolve(process.cwd(), "app", "pages", "docs") diff --git a/yarn.lock b/yarn.lock index 9305286..744f9aa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -137,6 +137,27 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.13.11.tgz#9c8fe523c206979c9a81b1e12fe50c1254f1aa35" integrity sha512-BwKEkO+2a67DcFeS3RLl0Z3Gs2OvdXewuWjc1Hfokhb5eQWP9YRYH1/+VrVZvql2CfjOiNGqSAFOYt4lsqTHzg== +"@babel/core@7.12.10": + version "7.12.10" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.12.10.tgz#b79a2e1b9f70ed3d84bbfb6d8c4ef825f606bccd" + integrity sha512-eTAlQKq65zHfkHZV0sIVODCPGVgoo1HdBlbSLi9CqOzuZanMv2ihzY+4paiKr1mH+XmYESMAmJ/dpZ68eN6d8w== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.12.10" + "@babel/helper-module-transforms" "^7.12.1" + "@babel/helpers" "^7.12.5" + "@babel/parser" "^7.12.10" + "@babel/template" "^7.12.7" + "@babel/traverse" "^7.12.10" + "@babel/types" "^7.12.10" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.1" + json5 "^2.1.2" + lodash "^4.17.19" + semver "^5.4.1" + source-map "^0.5.0" + "@babel/core@7.12.9": version "7.12.9" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.12.9.tgz#fd450c4ec10cdbb980e2928b7aa7a28484593fc8" @@ -159,28 +180,6 @@ semver "^5.4.1" source-map "^0.5.0" -"@babel/core@7.13.1": - version "7.13.1" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.13.1.tgz#7ddd027176debe40f13bb88bac0c21218c5b1ecf" - integrity sha512-FzeKfFBG2rmFtGiiMdXZPFt/5R5DXubVi82uYhjGX4Msf+pgYQMCFIqFXZWs5vbIYbf14VeBIgdGI03CDOOM1w== - dependencies: - "@babel/code-frame" "^7.12.13" - "@babel/generator" "^7.13.0" - "@babel/helper-compilation-targets" "^7.13.0" - "@babel/helper-module-transforms" "^7.13.0" - "@babel/helpers" "^7.13.0" - "@babel/parser" "^7.13.0" - "@babel/template" "^7.12.13" - "@babel/traverse" "^7.13.0" - "@babel/types" "^7.13.0" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.1.2" - lodash "^4.17.19" - semver "7.0.0" - source-map "^0.5.0" - "@babel/core@^7.1.0", "@babel/core@^7.7.5": version "7.13.10" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.13.10.tgz#07de050bbd8193fcd8a3c27918c0890613a94559" @@ -234,6 +233,15 @@ jsesc "^2.5.1" source-map "^0.5.0" +"@babel/generator@^7.12.10", "@babel/generator@^7.13.0", "@babel/generator@^7.13.9": + version "7.13.9" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.13.9.tgz#3a7aa96f9efb8e2be42d38d80e2ceb4c64d8de39" + integrity sha512-mHOOmY0Axl/JCTkxTU6Lf5sWOg/v8nUa+Xkt4zMTftX0wqmb6Sh7J8gvcehBw7q0AhrhAR+FDacKjCZ2X8K+Sw== + dependencies: + "@babel/types" "^7.13.0" + jsesc "^2.5.1" + source-map "^0.5.0" + "@babel/generator@^7.12.17": version "7.12.17" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.12.17.tgz#9ef1dd792d778b32284411df63f4f668a9957287" @@ -243,15 +251,6 @@ jsesc "^2.5.1" source-map "^0.5.0" -"@babel/generator@^7.13.0", "@babel/generator@^7.13.9", "@babel/generator@^7.4.0": - version "7.13.9" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.13.9.tgz#3a7aa96f9efb8e2be42d38d80e2ceb4c64d8de39" - integrity sha512-mHOOmY0Axl/JCTkxTU6Lf5sWOg/v8nUa+Xkt4zMTftX0wqmb6Sh7J8gvcehBw7q0AhrhAR+FDacKjCZ2X8K+Sw== - dependencies: - "@babel/types" "^7.13.0" - jsesc "^2.5.1" - source-map "^0.5.0" - "@babel/helper-annotate-as-pure@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz#5bf0d495a3f757ac3bda48b5bf3b3ba309c72ba3" @@ -614,7 +613,7 @@ "@babel/traverse" "^7.12.17" "@babel/types" "^7.12.17" -"@babel/helpers@^7.13.0", "@babel/helpers@^7.13.10": +"@babel/helpers@^7.13.10": version "7.13.10" resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.13.10.tgz#fd8e2ba7488533cdeac45cc158e9ebca5e3c7df8" integrity sha512-4VO883+MWPDUVRF3PhiLBUFHoX/bsLTGFpFK/HqvvfBZz2D57u9XzPVNFVBTc0PW/CWR9BXTOKt8NF4DInUHcQ== @@ -641,7 +640,7 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.13.0", "@babel/parser@^7.13.10", "@babel/parser@^7.4.3": +"@babel/parser@^7.1.0", "@babel/parser@^7.13.0", "@babel/parser@^7.13.10": version "7.13.11" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.11.tgz#f93ebfc99d21c1772afbbaa153f47e7ce2f50b88" integrity sha512-PhuoqeHoO9fc4ffMEVk4qb/w/s2iOSWohvbHxLtxui0eBg3Lg5gN1U8wp1V1u61hOWkPQJJyJzGH6Y+grwkq8Q== @@ -651,6 +650,11 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.12.5.tgz#b4af32ddd473c0bfa643bd7ff0728b8e71b81ea0" integrity sha512-FVM6RZQ0mn2KCf1VUED7KepYeUWoVShczewOCfm3nzoBybaih51h+sYVVGthW9M6lPByEPTQf+xm27PBdlpwmQ== +"@babel/parser@^7.12.10", "@babel/parser@^7.13.15": + version "7.13.15" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.15.tgz#8e66775fb523599acb6a289e12929fa5ab0954d8" + integrity sha512-b9COtcAlVEQljy/9fbcMHpG+UIW9ReF+gpaxDHTlZd0c6/UU9ng8zdySAW9sRTzpvcdCHn6bUcbuYUgGzLAWVQ== + "@babel/parser@^7.12.13", "@babel/parser@^7.12.17", "@babel/parser@^7.12.7": version "7.12.17" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.12.17.tgz#bc85d2d47db38094e5bb268fc761716e7d693848" @@ -1419,7 +1423,7 @@ dependencies: regenerator-runtime "^0.13.4" -"@babel/runtime@^7.12.5": +"@babel/runtime@^7.12.5", "@babel/runtime@^7.6.2": version "7.13.10" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.13.10.tgz#47d42a57b6095f4468da440388fdbad8bebf0d7d" integrity sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw== @@ -1435,7 +1439,7 @@ "@babel/parser" "^7.10.4" "@babel/types" "^7.10.4" -"@babel/template@^7.12.13", "@babel/template@^7.12.7", "@babel/template@^7.3.3", "@babel/template@^7.4.0": +"@babel/template@^7.12.13", "@babel/template@^7.12.7", "@babel/template@^7.3.3": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.12.13.tgz#530265be8a2589dbb37523844c5bcb55947fb327" integrity sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA== @@ -1444,7 +1448,7 @@ "@babel/parser" "^7.12.13" "@babel/types" "^7.12.13" -"@babel/traverse@^7.1.0", "@babel/traverse@^7.13.0", "@babel/traverse@^7.4.3": +"@babel/traverse@^7.1.0", "@babel/traverse@^7.13.0": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.13.0.tgz#6d95752475f86ee7ded06536de309a65fc8966cc" integrity sha512-xys5xi5JEhzC3RzEmSGrs/b3pJW/o87SypZ+G/PhaE7uqVQNv/jlmVIBXuoh5atqQ434LfXV+sf23Oxj0bchJQ== @@ -1474,6 +1478,20 @@ globals "^11.1.0" lodash "^4.17.19" +"@babel/traverse@^7.12.10": + version "7.13.15" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.13.15.tgz#c38bf7679334ddd4028e8e1f7b3aa5019f0dada7" + integrity sha512-/mpZMNvj6bce59Qzl09fHEs8Bt8NnpEDQYleHUPZQ3wXUMvXi+HJPLars68oAbmp839fGoOkv2pSL2z9ajCIaQ== + dependencies: + "@babel/code-frame" "^7.12.13" + "@babel/generator" "^7.13.9" + "@babel/helper-function-name" "^7.12.13" + "@babel/helper-split-export-declaration" "^7.12.13" + "@babel/parser" "^7.13.15" + "@babel/types" "^7.13.14" + debug "^4.1.0" + globals "^11.1.0" + "@babel/traverse@^7.12.17", "@babel/traverse@^7.12.9": version "7.12.17" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.12.17.tgz#40ec8c7ffb502c4e54c7f95492dc11b88d718619" @@ -1498,7 +1516,7 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" -"@babel/types@^7.0.0", "@babel/types@^7.13.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.0": +"@babel/types@^7.0.0", "@babel/types@^7.13.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.13.0.tgz#74424d2816f0171b4100f0ab34e9a374efdf7f80" integrity sha512-hE+HE8rnG1Z6Wzo+MhaKE5lM5eMx71T4EHJgku2E3xIfaULhDcxiiRxUYgwX8qwP1BBSlag+TdGOt6JAidIZTA== @@ -1516,6 +1534,15 @@ lodash "^4.17.19" to-fast-properties "^2.0.0" +"@babel/types@^7.12.10", "@babel/types@^7.13.14": + version "7.13.14" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.13.14.tgz#c35a4abb15c7cd45a2746d78ab328e362cbace0d" + integrity sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ== + dependencies: + "@babel/helper-validator-identifier" "^7.12.11" + lodash "^4.17.19" + to-fast-properties "^2.0.0" + "@babel/types@^7.12.13", "@babel/types@^7.12.17", "@babel/types@^7.12.7": version "7.12.17" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.12.17.tgz#9d711eb807e0934c90b8b1ca0eb1f7230d150963" @@ -1539,21 +1566,21 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@blitzjs/babel-preset@0.33.1": - version "0.33.1" - resolved "https://registry.yarnpkg.com/@blitzjs/babel-preset/-/babel-preset-0.33.1.tgz#dfbb6fe1d5c989cfb2feb124e70071992c1b660f" - integrity sha512-VjVPUjMFVPZ8MuKKZt8fEF0zzAbIhxCXxkBG5Jh9KHq2TGf+vn1iYp7PqvnoBhzdwhS5PjTXFT11CTPEixOPMQ== +"@blitzjs/babel-preset@0.34.0": + version "0.34.0" + resolved "https://registry.yarnpkg.com/@blitzjs/babel-preset/-/babel-preset-0.34.0.tgz#728f99aa2f85860f5d06fa2a6ab55727b99ca591" + integrity sha512-oFVh7oXb3qh8IAlNLjSS/NeXSnAZYDrXDjRz5C4t6wTIzGkGhbVFBXKX9Jb6mrGJCbWeNo667rmCRZhQB0NxNg== dependencies: "@babel/helper-module-imports" "^7.0.0" babel-plugin-superjson-next "0.2.2" -"@blitzjs/cli@0.33.1": - version "0.33.1" - resolved "https://registry.yarnpkg.com/@blitzjs/cli/-/cli-0.33.1.tgz#8a25202a03bfd99d84f58e5e1041cb760b7fc5ed" - integrity sha512-TZ9m7qIgFqrsG2kIw4ZMQB7nJgV4JUABVEs3aCQ+p9iza4fnb/qx2/5JyRJu7QaqcDdZViDrCU5wfHgAl4pvwQ== +"@blitzjs/cli@0.34.0": + version "0.34.0" + resolved "https://registry.yarnpkg.com/@blitzjs/cli/-/cli-0.34.0.tgz#f81e7d9bf0b8bf1d88d1cdeeb427462399a57c39" + integrity sha512-Qtq5Cyy8QcX0KCzCALaBIloRl00Lp/aaw4hPm5JF+dpzyqhcnmdhq4OonwU6ebx7IYg+Bg2OIPButGBKjHHb9Q== dependencies: - "@blitzjs/display" "0.33.1" - "@blitzjs/repl" "0.33.1" + "@blitzjs/display" "0.34.0" + "@blitzjs/repl" "0.34.0" "@oclif/command" "1.8.0" "@oclif/config" "1.17.0" "@oclif/plugin-autocomplete" "0.3.0" @@ -1582,21 +1609,21 @@ tsconfig-paths "3.9.0" v8-compile-cache "2.2.0" -"@blitzjs/config@0.33.1": - version "0.33.1" - resolved "https://registry.yarnpkg.com/@blitzjs/config/-/config-0.33.1.tgz#6530a5f64c6b8bbdecc2dd59e2bc077e73953f6d" - integrity sha512-zK0y0exmUrPPEclBh9OcXNN3TRA2XpinFFyuonhmLxN4Vh1qw3bx3iiJdP9xN4yCUIXlBAU++dBaHDZOciJsIg== +"@blitzjs/config@0.34.0": + version "0.34.0" + resolved "https://registry.yarnpkg.com/@blitzjs/config/-/config-0.34.0.tgz#72b1f48e3d1b036571635ee84f82f4dbd3afa957" + integrity sha512-7F1zyCeVQA6yr0GVbCK8/NCDCsddmpR8JKWK1SPperbmddVSPUbeHGdYGlF0xzGZdaBEU+uhofmYSRR/KchbQA== dependencies: fs-extra "^9.1.0" pkg-dir "^5.0.0" -"@blitzjs/core@0.33.1": - version "0.33.1" - resolved "https://registry.yarnpkg.com/@blitzjs/core/-/core-0.33.1.tgz#8f98c52e41b6f5f117c46cb719021b5ad9e75cc5" - integrity sha512-TC2K64XjKb1AGMNWfJPJOLGgWYAeLxlTkHFR7IUXn4wnCDcGXbiwUz/2HVTET7INPdDu5v3st7xNmmrBV6DoJA== +"@blitzjs/core@0.34.0": + version "0.34.0" + resolved "https://registry.yarnpkg.com/@blitzjs/core/-/core-0.34.0.tgz#34f639d87f9db673c793df625d36184df8df4932" + integrity sha512-jt92Ui2vPWl/AGmIbPa91A7ULeLVrAntlDnSiqFkrMgd0LwmUOvAP4BAt4EXujiFei7emht49sAt5j2/Z3SyCA== dependencies: - "@blitzjs/config" "0.33.1" - "@blitzjs/display" "0.33.1" + "@blitzjs/config" "0.34.0" + "@blitzjs/display" "0.34.0" "@types/secure-password" "3.1.0" b64-lite "^1.4.0" bad-behavior "^1.0.1" @@ -1608,33 +1635,33 @@ jsonwebtoken "8.5.1" lodash.frompairs "4.0.1" nanoid "^3.1.20" - next "10.0.9" + next "npm:@blitzjs/next@0.34.0" npm-which "^3.0.1" null-loader "4.0.1" passport "0.4.1" - react-query "2.5.12" + react-query "3.13.9" secure-password "4.0.0" superjson "1.7.2" -"@blitzjs/display@0.33.1": - version "0.33.1" - resolved "https://registry.yarnpkg.com/@blitzjs/display/-/display-0.33.1.tgz#cd163a6f3353ba7cc59a8f6e2e82f8d3b1f07d82" - integrity sha512-JCVhqivxjebKJXIzy5rPe6Hv8V5tWPtBOO6DTsE/6FZezI4VYY2sgN20g00Ac2XH14hIzsSu9doHiTIk9tJzvQ== +"@blitzjs/display@0.34.0": + version "0.34.0" + resolved "https://registry.yarnpkg.com/@blitzjs/display/-/display-0.34.0.tgz#f46ed3d3774d0170eb52af71d4c89da09cac9e8f" + integrity sha512-+tHVyEe5umCnwlRoPqzryfZLuDqSORUXNiXP8z3Ufrpjo6hr4Yl0DxGbtcCz4TgBx91OvbVyDI68bkHFePj0Iw== dependencies: - "@blitzjs/config" "0.33.1" - "@blitzjs/display" "0.33.1" + "@blitzjs/config" "0.34.0" + "@blitzjs/display" "0.34.0" chalk "^4.1.0" console-table-printer "^2.7.5" ora "^5.3.0" readline "1.3.0" tslog "^3.1.1" -"@blitzjs/file-pipeline@0.33.1": - version "0.33.1" - resolved "https://registry.yarnpkg.com/@blitzjs/file-pipeline/-/file-pipeline-0.33.1.tgz#73cf82f1337756b608d9b6e72749a4f09eca4ed8" - integrity sha512-E0OWzngW/Y2booHR6DqJNbea2FJYSJAL5KjkS8veYgr1d2NIB2M8gx29B9HdG+ki4IGA0o9S7emCeK0Hl46uog== +"@blitzjs/file-pipeline@0.34.0": + version "0.34.0" + resolved "https://registry.yarnpkg.com/@blitzjs/file-pipeline/-/file-pipeline-0.34.0.tgz#c6c6543e84aae1213529c365c0ac3fcc88e71343" + integrity sha512-nyAT9zTy4xZQlXu4MLC72lbedUVuKQ0pbY/bJeFwcZ8kKHD4tX3YZNGvW5qgBVQPrnk2uUB9uaGnkGpTLfBq7g== dependencies: - "@blitzjs/display" "0.33.1" + "@blitzjs/display" "0.34.0" chalk "^4.1.0" chokidar "3.5.1" flush-write-stream "2.0.0" @@ -1653,21 +1680,20 @@ vinyl-file "3.0.0" vinyl-fs "3.0.3" -"@blitzjs/generator@0.33.1": - version "0.33.1" - resolved "https://registry.yarnpkg.com/@blitzjs/generator/-/generator-0.33.1.tgz#49658c48bad30041d9b53ae15f0c4c56d8a67f37" - integrity sha512-RpnVdu0zzaOGX2PNX2xM3JBu0TdHjukpOPvNNv5jqXED6yUa7GF3nrnFyKbDwNfq4GmRG+yUZIPZUGXFwIvSrA== +"@blitzjs/generator@0.34.0": + version "0.34.0" + resolved "https://registry.yarnpkg.com/@blitzjs/generator/-/generator-0.34.0.tgz#315c8b79e33df1a2cac66b2942d1343cc042d767" + integrity sha512-Bg1mXOMwqbH8yG4Gi7yT9DEOp34ryPs8WaeIi8tBOWf5ULXfz9Py5DQlsMfF3Lg8+yxzWV+xttQ6axueo2wasg== dependencies: - "@babel/core" "7.13.1" + "@babel/core" "7.12.10" "@babel/plugin-transform-typescript" "7.12.1" - "@blitzjs/display" "0.33.1" + "@blitzjs/display" "0.34.0" "@types/jscodeshift" "0.7.2" chalk "^4.1.0" cross-spawn "7.0.3" diff "5.0.0" enquirer "2.3.6" fs-extra "^9.1.0" - fs-readdir-recursive "1.1.0" got "^11.8.1" jscodeshift "0.11.0" mem-fs "1.2.0" @@ -1678,16 +1704,16 @@ username "^5.1.0" vinyl "2.2.1" -"@blitzjs/installer@0.33.1": - version "0.33.1" - resolved "https://registry.yarnpkg.com/@blitzjs/installer/-/installer-0.33.1.tgz#3813ffedc9414d35e09bde3fbd2cd66c1dcb398c" - integrity sha512-15OVfrkPv8uotL1oaJdtoXQtA73VHDGE59fV2fH3hx+/U6qLsBe+i1YNz8hY5ziIW96JxzmfCAl0tq9STkPtaA== +"@blitzjs/installer@0.34.0": + version "0.34.0" + resolved "https://registry.yarnpkg.com/@blitzjs/installer/-/installer-0.34.0.tgz#7731c5040c196c0a8c0717da11ae2a8c1208b3e0" + integrity sha512-fCuLnCcR1mM+FFoVwpdSU/jNZghS/7x0nmGAa3gx/RGN9Q7/quvqY1M6iR5iXRhxYKQGTXLWOWJDcYH4wmkbKQ== dependencies: - "@babel/core" "7.13.1" + "@babel/core" "7.12.10" "@babel/plugin-transform-typescript" "7.12.1" - "@blitzjs/config" "0.33.1" - "@blitzjs/display" "0.33.1" - "@blitzjs/generator" "0.33.1" + "@blitzjs/config" "0.34.0" + "@blitzjs/display" "0.34.0" + "@blitzjs/generator" "0.34.0" "@types/jscodeshift" "0.7.2" cross-spawn "7.0.3" diff "5.0.0" @@ -1702,26 +1728,26 @@ recast "0.20.4" ts-node "^9.1.1" -"@blitzjs/repl@0.33.1": - version "0.33.1" - resolved "https://registry.yarnpkg.com/@blitzjs/repl/-/repl-0.33.1.tgz#2090cef38bced1017fece43943a193c541f029c3" - integrity sha512-Sk2Wsl08lJgpXg+nbNeVVnsjoLsMRm1n6RegbWbrScLXSzEi707BvVQvnGtgJwv4t9m+ab1av/uYPyrz8vcY8Q== +"@blitzjs/repl@0.34.0": + version "0.34.0" + resolved "https://registry.yarnpkg.com/@blitzjs/repl/-/repl-0.34.0.tgz#a9f8244958d1b4b51d234db7e19c9b214661a186" + integrity sha512-xRIfxAzLP5nP3nRHm1WlEPiCaSvYsncuG6JAS9n2SpctwUZGbCORuSTLaLtUPhbNayOm4Qc3Z2sI22fkQRnxag== dependencies: - "@blitzjs/config" "0.33.1" + "@blitzjs/config" "0.34.0" chokidar "3.5.1" globby "11.0.2" pkg-dir "^5.0.0" progress "^2.0.3" -"@blitzjs/server@0.33.1": - version "0.33.1" - resolved "https://registry.yarnpkg.com/@blitzjs/server/-/server-0.33.1.tgz#6efd8a3623afd43a55b4b46d32b15441e8575407" - integrity sha512-BpeOcFjSslskLIKoTc8242xJIsA3vpc9P6ObrHG+5kuo5VsxN1vPjoEnBOemAYRbQiJliEr5pdDE7Bk7h4TiWA== +"@blitzjs/server@0.34.0": + version "0.34.0" + resolved "https://registry.yarnpkg.com/@blitzjs/server/-/server-0.34.0.tgz#180b7fe7a7e49796165fc5bd3e51304a850f98b8" + integrity sha512-mgPgEwUDzvAL+HuyhGo0ns14dxamJxm84Fc1jswq/cXsL77BD2Ta7VH1GYGXDDp0rJMgDlc1KG3eZN4IGIJjFg== dependencies: - "@blitzjs/config" "0.33.1" - "@blitzjs/core" "0.33.1" - "@blitzjs/display" "0.33.1" - "@blitzjs/file-pipeline" "0.33.1" + "@blitzjs/config" "0.34.0" + "@blitzjs/core" "0.34.0" + "@blitzjs/display" "0.34.0" + "@blitzjs/file-pipeline" "0.34.0" cross-spawn "7.0.3" detect-port "1.3.0" expand-tilde "2.0.2" @@ -1910,15 +1936,6 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== -"@jest/console@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-24.9.0.tgz#79b1bc06fb74a8cfb01cbdedf945584b1b9707f0" - integrity sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ== - dependencies: - "@jest/source-map" "^24.9.0" - chalk "^2.0.1" - slash "^2.0.0" - "@jest/console@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/console/-/console-26.6.2.tgz#4e04bc464014358b03ab4937805ee36a0aeb98f2" @@ -1965,16 +1982,6 @@ slash "^3.0.0" strip-ansi "^6.0.0" -"@jest/environment@^24.3.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-24.9.0.tgz#21e3afa2d65c0586cbd6cbefe208bafade44ab18" - integrity sha512-5A1QluTPhvdIPFYnO3sZC3smkNeXPVELz7ikPbhUj0bQjB07EoE9qtLrem14ZUYWdVayYbsjVwIiL4WBIMV4aQ== - dependencies: - "@jest/fake-timers" "^24.9.0" - "@jest/transform" "^24.9.0" - "@jest/types" "^24.9.0" - jest-mock "^24.9.0" - "@jest/environment@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-26.6.2.tgz#ba364cc72e221e79cc8f0a99555bf5d7577cf92c" @@ -1985,15 +1992,6 @@ "@types/node" "*" jest-mock "^26.6.2" -"@jest/fake-timers@^24.3.0", "@jest/fake-timers@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-24.9.0.tgz#ba3e6bf0eecd09a636049896434d306636540c93" - integrity sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A== - dependencies: - "@jest/types" "^24.9.0" - jest-message-util "^24.9.0" - jest-mock "^24.9.0" - "@jest/fake-timers@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-26.6.2.tgz#459c329bcf70cee4af4d7e3f3e67848123535aad" @@ -2047,15 +2045,6 @@ optionalDependencies: node-notifier "^8.0.0" -"@jest/source-map@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-24.9.0.tgz#0e263a94430be4b41da683ccc1e6bffe2a191714" - integrity sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg== - dependencies: - callsites "^3.0.0" - graceful-fs "^4.1.15" - source-map "^0.6.0" - "@jest/source-map@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-26.6.2.tgz#29af5e1e2e324cafccc936f218309f54ab69d535" @@ -2065,15 +2054,6 @@ graceful-fs "^4.2.4" source-map "^0.6.0" -"@jest/test-result@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-24.9.0.tgz#11796e8aa9dbf88ea025757b3152595ad06ba0ca" - integrity sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA== - dependencies: - "@jest/console" "^24.9.0" - "@jest/types" "^24.9.0" - "@types/istanbul-lib-coverage" "^2.0.0" - "@jest/test-result@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-26.6.2.tgz#55da58b62df134576cc95476efa5f7949e3f5f18" @@ -2095,28 +2075,6 @@ jest-runner "^26.6.3" jest-runtime "^26.6.3" -"@jest/transform@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-24.9.0.tgz#4ae2768b296553fadab09e9ec119543c90b16c56" - integrity sha512-TcQUmyNRxV94S0QpMOnZl0++6RMiqpbH/ZMccFB/amku6Uwvyb1cjYX7xkp5nGNkbX4QPH/FcB6q1HBTHynLmQ== - dependencies: - "@babel/core" "^7.1.0" - "@jest/types" "^24.9.0" - babel-plugin-istanbul "^5.1.0" - chalk "^2.0.1" - convert-source-map "^1.4.0" - fast-json-stable-stringify "^2.0.0" - graceful-fs "^4.1.15" - jest-haste-map "^24.9.0" - jest-regex-util "^24.9.0" - jest-util "^24.9.0" - micromatch "^3.1.10" - pirates "^4.0.1" - realpath-native "^1.1.0" - slash "^2.0.0" - source-map "^0.6.1" - write-file-atomic "2.4.1" - "@jest/transform@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-26.6.2.tgz#5ac57c5fa1ad17b2aae83e73e45813894dcf2e4b" @@ -2138,15 +2096,6 @@ source-map "^0.6.1" write-file-atomic "^3.0.0" -"@jest/types@^24.3.0", "@jest/types@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-24.9.0.tgz#63cb26cb7500d069e5a389441a7c6ab5e909fc59" - integrity sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw== - dependencies: - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^1.1.1" - "@types/yargs" "^13.0.0" - "@jest/types@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e" @@ -2209,20 +2158,20 @@ dependencies: webpack-bundle-analyzer "4.3.0" -"@next/env@10.0.9": - version "10.0.9" - resolved "https://registry.yarnpkg.com/@next/env/-/env-10.0.9.tgz#455fd364c8a5ee012b2cd4406d5294164990706d" - integrity sha512-MERX3DY5u0Ed29eAsXeFBCZlFAGBtmjf7+Nht0hfgB25MPKKkIbC/0MRPcX/PQcAgLHsAHO6ay1u9xKzR4Vzjw== +"@next/env@10.1.3": + version "10.1.3" + resolved "https://registry.yarnpkg.com/@next/env/-/env-10.1.3.tgz#29e5d62919b4a7b1859f8d36169848dc3f5ddebe" + integrity sha512-q7z7NvmRs66lCQmVJtKjDxVtMTjSwP6ExVzaH46pbTH60MHgzEJ9H4jXrFLTihPmCIvpAv6Ai04jbS8dcg1ZMQ== -"@next/polyfill-module@10.0.9": - version "10.0.9" - resolved "https://registry.yarnpkg.com/@next/polyfill-module/-/polyfill-module-10.0.9.tgz#0c21442dd73ec31ae30ac560bc5c5fdce068a98f" - integrity sha512-kPOP6ku/e8zdrK8hwxOrjUrPLcdDEj12huLHVz+DZU+20q6VlhMOtR8aKHW1460L4LoLE/DAa7YyIuxtArhDRg== +"@next/polyfill-module@10.1.3": + version "10.1.3" + resolved "https://registry.yarnpkg.com/@next/polyfill-module/-/polyfill-module-10.1.3.tgz#beafe89bc4235d436fa0ed02c9d2a5d311fb0238" + integrity sha512-1DtUVcuoBJAn5IrxIZQjUG1KTPkiXMYloykPSkRxawimgvG9dRj2kscU+4KGNSFxHoxW9c68VRCb+7MDz5aGGw== -"@next/react-dev-overlay@10.0.9": - version "10.0.9" - resolved "https://registry.yarnpkg.com/@next/react-dev-overlay/-/react-dev-overlay-10.0.9.tgz#5162d66c05b2a0ca0d155f7e6663e8134d9d4ac9" - integrity sha512-JsSh2Y004MEuPYqBD9eTl4PVZIjSzSy2GcD7MrW/gQcExYNpeMIJAbh8/OcyO1t+OnQeIHF5s/xTMsDHBGNcew== +"@next/react-dev-overlay@10.1.3": + version "10.1.3" + resolved "https://registry.yarnpkg.com/@next/react-dev-overlay/-/react-dev-overlay-10.1.3.tgz#ee1c6033b29be9b383e061bd9705021d131ea445" + integrity sha512-vIgUah3bR9+MKzwU1Ni5ONfYM0VdI42i7jZ+Ei1c0wjwkG9anVnDqhSQ3mVg62GP2nt7ExaaFyf9THbsw5KYXg== dependencies: "@babel/code-frame" "7.12.11" anser "1.4.9" @@ -2236,10 +2185,10 @@ stacktrace-parser "0.1.10" strip-ansi "6.0.0" -"@next/react-refresh-utils@10.0.9": - version "10.0.9" - resolved "https://registry.yarnpkg.com/@next/react-refresh-utils/-/react-refresh-utils-10.0.9.tgz#cdf9e41f8854c113397853daf78469b0c8140f14" - integrity sha512-LSoKnM+fI9MHHew+mBg1w2e4/gjwPQsI+mDTzmfwdBwje+j9U2Int6XOZftMqBPXSlL04vjC9SRBkp0+3h8cNA== +"@next/react-refresh-utils@10.1.3": + version "10.1.3" + resolved "https://registry.yarnpkg.com/@next/react-refresh-utils/-/react-refresh-utils-10.1.3.tgz#65b3e1b9846c02452787fde1d54ad9c54b506dbd" + integrity sha512-P4GJZuLKfD/o42JvGZ/xP4Hxg68vd3NeZxOLqIuQKFjjaYgC2IrO+lE5PTwGmRkytjfprJC+9j7Jss/xQAS6QA== "@nodelib/fs.scandir@2.1.3": version "2.1.3" @@ -2669,20 +2618,20 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.0.0.tgz#2ff674e9611b45b528896d820d3d7a812de2f0e4" integrity sha512-FyD2meJpDPjyNQejSjvnhpgI/azsQkA4lGbuu5BQZfjvJ9cbRZXzeWL2HceCekW4lixO9JPesIIQkSoLjeJHNQ== -"@sindresorhus/slugify@1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@sindresorhus/slugify/-/slugify-1.1.0.tgz#2f195365d9b953384305b62664b44b4036c49430" - integrity sha512-ujZRbmmizX26yS/HnB3P9QNlNa4+UvHh+rIse3RbOXLp8yl6n1TxB4t7NHggtVgS8QmmOtzXo48kCxZGACpkPw== +"@sindresorhus/slugify@2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/slugify/-/slugify-2.0.0.tgz#9ccd8f9d29b8187f5b3e33e4423abae8a82ca00c" + integrity sha512-lGRwhzEHTiTlPj8HrSQpZ+YXbFW28pmO7/Xq5XREEt7Xd/JVgszec1I1XFGb2XeMYHKIY/Sc0V45DBGJbFzsoQ== dependencies: - "@sindresorhus/transliterate" "^0.1.1" - escape-string-regexp "^4.0.0" + "@sindresorhus/transliterate" "^1.0.0" + escape-string-regexp "^5.0.0" -"@sindresorhus/transliterate@^0.1.1": - version "0.1.1" - resolved "https://registry.yarnpkg.com/@sindresorhus/transliterate/-/transliterate-0.1.1.tgz#779b31244781d3c898f185b61d58c89e7c782674" - integrity sha512-QSdIQ5keUFAZ3KLbfbsntW39ox0Ym8183RqTwBq/ZEFoN3NQAtGV+qWaNdzKpIDHgj9J2CQ2iNDRVU11Zyr7MQ== +"@sindresorhus/transliterate@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/transliterate/-/transliterate-1.0.0.tgz#7efc60bd347c16f416582eb7d18fee3bbaa3379b" + integrity sha512-5429x9FXQzC8UIav+yeMVOQTL/DdJ3ANJOyuSAvwVSLmPSsw/7KWyXpncvFbJEe4h2MfvINSrjhRgDiltSjTlw== dependencies: - escape-string-regexp "^2.0.0" + escape-string-regexp "^5.0.0" lodash.deburr "^4.1.0" "@sinonjs/commons@^1.7.0": @@ -2863,7 +2812,7 @@ "@types/react" ">=16.9.0" "@types/react-test-renderer" ">=16.9.0" -"@testing-library/react@^11.2.3": +"@testing-library/react@11.2.5": version "11.2.5" resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-11.2.5.tgz#ae1c36a66c7790ddb6662c416c27863d87818eb9" integrity sha512-yEx7oIa/UWLe2F2dqK0FtMF9sJWNXD+2PPtp39BvE0Kh9MJ9Kl0HrZAgEuhUJR+Lx8Di6Xz+rKwSdEPY2UV8ZQ== @@ -2987,14 +2936,6 @@ dependencies: "@types/istanbul-lib-coverage" "*" -"@types/istanbul-reports@^1.1.1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz#e875cc689e47bce549ec81f3df5e6f6f11cfaeb2" - integrity sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw== - dependencies: - "@types/istanbul-lib-coverage" "*" - "@types/istanbul-lib-report" "*" - "@types/istanbul-reports@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz#508b13aa344fa4976234e75dddcc34925737d821" @@ -3002,7 +2943,7 @@ dependencies: "@types/istanbul-lib-report" "*" -"@types/jest@*", "@types/jest@26.x", "@types/jest@^26.0.20": +"@types/jest@*", "@types/jest@26.0.20", "@types/jest@26.x": version "26.0.20" resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.20.tgz#cd2f2702ecf69e86b586e1f5223a60e454056307" integrity sha512-9zi2Y+5USJRxd0FsahERhBwlcvFh6D2GLQnY2FH2BzK8J9s9omvNHIbvABwIluXa0fD8XVKMLTO0aOEuUfACAA== @@ -3150,11 +3091,6 @@ dependencies: "@types/node" "*" -"@types/stack-utils@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e" - integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw== - "@types/stack-utils@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.0.tgz#7036640b4e21cc2f259ae826ce843d277dad8cff" @@ -3177,13 +3113,6 @@ resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.0.tgz#dd3e6699ba3237f0348cd085e4698780204842f9" integrity sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA== -"@types/yargs@^13.0.0": - version "13.0.11" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-13.0.11.tgz#def2f0c93e4bdf2c61d7e34899b17e34be28d3b1" - integrity sha512-NRqD6T4gktUrDi1o1wLH3EKC1o2caCr7/wR87ODcbVITQF106OM3sFN92ysZ++wqelOd1CTzatnOBRDYYG6wGQ== - dependencies: - "@types/yargs-parser" "*" - "@types/yargs@^15.0.0": version "15.0.13" resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.13.tgz#34f7fec8b389d7f3c1fd08026a5763e072d3c6dc" @@ -3196,13 +3125,13 @@ resolved "https://registry.yarnpkg.com/@types/yoga-layout/-/yoga-layout-1.9.2.tgz#efaf9e991a7390dc081a0b679185979a83a9639a" integrity sha512-S9q47ByT2pPvD65IvrWp7qppVMpk9WGMbVq9wbWZOHg6tnXSD4vyhao6nOSBwwfDdV2p3Kx9evA9vI+XWTfDvw== -"@typescript-eslint/eslint-plugin@~4.14.0": - version "4.14.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.14.2.tgz#47a15803cfab89580b96933d348c2721f3d2f6fe" - integrity sha512-uMGfG7GFYK/nYutK/iqYJv6K/Xuog/vrRRZX9aEP4Zv1jsYXuvFUMDFLhUnc8WFv3D2R5QhNQL3VYKmvLS5zsQ== +"@typescript-eslint/eslint-plugin@4.17.0": + version "4.17.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.17.0.tgz#6f856eca4e6a52ce9cf127dfd349096ad936aa2d" + integrity sha512-/fKFDcoHg8oNan39IKFOb5WmV7oWhQe1K6CDaAVfJaNWEhmfqlA24g+u1lqU5bMH7zuNasfMId4LaYWC5ijRLw== dependencies: - "@typescript-eslint/experimental-utils" "4.14.2" - "@typescript-eslint/scope-manager" "4.14.2" + "@typescript-eslint/experimental-utils" "4.17.0" + "@typescript-eslint/scope-manager" "4.17.0" debug "^4.1.1" functional-red-black-tree "^1.0.1" lodash "^4.17.15" @@ -3210,61 +3139,60 @@ semver "^7.3.2" tsutils "^3.17.1" -"@typescript-eslint/experimental-utils@4.14.2": - version "4.14.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.14.2.tgz#9df35049d1d36b6cbaba534d703648b9e1f05cbb" - integrity sha512-mV9pmET4C2y2WlyHmD+Iun8SAEqkLahHGBkGqDVslHkmoj3VnxnGP4ANlwuxxfq1BsKdl/MPieDbohCEQgKrwA== +"@typescript-eslint/experimental-utils@4.17.0": + version "4.17.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.17.0.tgz#762c44aaa1a6a3c05b6d63a8648fb89b89f84c80" + integrity sha512-ZR2NIUbnIBj+LGqCFGQ9yk2EBQrpVVFOh9/Kd0Lm6gLpSAcCuLLe5lUCibKGCqyH9HPwYC0GIJce2O1i8VYmWA== dependencies: "@types/json-schema" "^7.0.3" - "@typescript-eslint/scope-manager" "4.14.2" - "@typescript-eslint/types" "4.14.2" - "@typescript-eslint/typescript-estree" "4.14.2" + "@typescript-eslint/scope-manager" "4.17.0" + "@typescript-eslint/types" "4.17.0" + "@typescript-eslint/typescript-estree" "4.17.0" eslint-scope "^5.0.0" eslint-utils "^2.0.0" -"@typescript-eslint/parser@~4.14.0": - version "4.14.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.14.2.tgz#31e216e4baab678a56e539f9db9862e2542c98d0" - integrity sha512-ipqSP6EuUsMu3E10EZIApOJgWSpcNXeKZaFeNKQyzqxnQl8eQCbV+TSNsl+s2GViX2d18m1rq3CWgnpOxDPgHg== +"@typescript-eslint/parser@4.17.0": + version "4.17.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.17.0.tgz#141b647ffc72ebebcbf9b0fe6087f65b706d3215" + integrity sha512-KYdksiZQ0N1t+6qpnl6JeK9ycCFprS9xBAiIrw4gSphqONt8wydBw4BXJi3C11ywZmyHulvMaLjWsxDjUSDwAw== dependencies: - "@typescript-eslint/scope-manager" "4.14.2" - "@typescript-eslint/types" "4.14.2" - "@typescript-eslint/typescript-estree" "4.14.2" + "@typescript-eslint/scope-manager" "4.17.0" + "@typescript-eslint/types" "4.17.0" + "@typescript-eslint/typescript-estree" "4.17.0" debug "^4.1.1" -"@typescript-eslint/scope-manager@4.14.2": - version "4.14.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.14.2.tgz#64cbc9ca64b60069aae0c060b2bf81163243b266" - integrity sha512-cuV9wMrzKm6yIuV48aTPfIeqErt5xceTheAgk70N1V4/2Ecj+fhl34iro/vIssJlb7XtzcaD07hWk7Jk0nKghg== +"@typescript-eslint/scope-manager@4.17.0": + version "4.17.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.17.0.tgz#f4edf94eff3b52a863180f7f89581bf963e3d37d" + integrity sha512-OJ+CeTliuW+UZ9qgULrnGpPQ1bhrZNFpfT/Bc0pzNeyZwMik7/ykJ0JHnQ7krHanFN9wcnPK89pwn84cRUmYjw== dependencies: - "@typescript-eslint/types" "4.14.2" - "@typescript-eslint/visitor-keys" "4.14.2" + "@typescript-eslint/types" "4.17.0" + "@typescript-eslint/visitor-keys" "4.17.0" -"@typescript-eslint/types@4.14.2": - version "4.14.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.14.2.tgz#d96da62be22dc9dc6a06647f3633815350fb3174" - integrity sha512-LltxawRW6wXy4Gck6ZKlBD05tCHQUj4KLn4iR69IyRiDHX3d3NCAhO+ix5OR2Q+q9bjCrHE/HKt+riZkd1At8Q== +"@typescript-eslint/types@4.17.0": + version "4.17.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.17.0.tgz#f57d8fc7f31b348db946498a43050083d25f40ad" + integrity sha512-RN5z8qYpJ+kXwnLlyzZkiJwfW2AY458Bf8WqllkondQIcN2ZxQowAToGSd9BlAUZDB5Ea8I6mqL2quGYCLT+2g== -"@typescript-eslint/typescript-estree@4.14.2": - version "4.14.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.14.2.tgz#9c5ebd8cae4d7b014f890acd81e8e17f309c9df9" - integrity sha512-ESiFl8afXxt1dNj8ENEZT12p+jl9PqRur+Y19m0Z/SPikGL6rqq4e7Me60SU9a2M28uz48/8yct97VQYaGl0Vg== +"@typescript-eslint/typescript-estree@4.17.0": + version "4.17.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.17.0.tgz#b835d152804f0972b80dbda92477f9070a72ded1" + integrity sha512-lRhSFIZKUEPPWpWfwuZBH9trYIEJSI0vYsrxbvVvNyIUDoKWaklOAelsSkeh3E2VBSZiNe9BZ4E5tYBZbUczVQ== dependencies: - "@typescript-eslint/types" "4.14.2" - "@typescript-eslint/visitor-keys" "4.14.2" + "@typescript-eslint/types" "4.17.0" + "@typescript-eslint/visitor-keys" "4.17.0" debug "^4.1.1" globby "^11.0.1" is-glob "^4.0.1" - lodash "^4.17.15" semver "^7.3.2" tsutils "^3.17.1" -"@typescript-eslint/visitor-keys@4.14.2": - version "4.14.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.14.2.tgz#997cbe2cb0690e1f384a833f64794e98727c70c6" - integrity sha512-KBB+xLBxnBdTENs/rUgeUKO0UkPBRs2vD09oMRRIkj5BEN8PX1ToXV532desXfpQnZsYTyLLviS7JrPhdL154w== +"@typescript-eslint/visitor-keys@4.17.0": + version "4.17.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.17.0.tgz#9c304cfd20287c14a31d573195a709111849b14d" + integrity sha512-WfuMN8mm5SSqXuAr9NM+fItJ0SVVphobWYkWOwQ1odsfC014Vdxk/92t4JwS1Q6fCA/ABfCKpa3AVtpUKTNKGQ== dependencies: - "@typescript-eslint/types" "4.14.2" + "@typescript-eslint/types" "4.17.0" eslint-visitor-keys "^2.0.0" "@visx/group@1.7.0": @@ -3301,19 +3229,11 @@ prop-types "^15.6.1" resize-observer-polyfill "1.5.1" -abab@^2.0.0, abab@^2.0.3, abab@^2.0.5: +abab@^2.0.3, abab@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== -acorn-globals@^4.3.0: - version "4.3.4" - resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.3.4.tgz#9fa1926addc11c97308c4e66d7add0d40c3272e7" - integrity sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A== - dependencies: - acorn "^6.0.1" - acorn-walk "^6.0.1" - acorn-globals@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" @@ -3336,11 +3256,6 @@ acorn-node@^1.6.1: acorn-walk "^7.0.0" xtend "^4.0.2" -acorn-walk@^6.0.1: - version "6.2.0" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.2.0.tgz#123cb8f3b84c2171f1f7fb252615b1c78a6b1a8c" - integrity sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA== - acorn-walk@^7.0.0, acorn-walk@^7.1.1: version "7.2.0" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" @@ -3351,11 +3266,6 @@ acorn-walk@^8.0.0: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.0.2.tgz#d4632bfc63fd93d0f15fd05ea0e984ffd3f5a8c3" integrity sha512-+bpA9MJsHdZ4bgfDcpk0ozQyhhVct7rzOmO0s1IIr0AGGgKBljss8n2zp11rRP2wid5VGeh04CgeKzgat5/25A== -acorn@^6.0.1, acorn@^6.0.4: - version "6.4.2" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" - integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== - acorn@^7.0.0, acorn@^7.1.1, acorn@^7.4.0: version "7.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" @@ -3620,10 +3530,10 @@ array-differ@^3.0.0: resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-3.0.0.tgz#3cbb3d0f316810eafcc47624734237d6aee4ae6b" integrity sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg== -array-equal@^1.0.0: +array-filter@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93" - integrity sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM= + resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-1.0.0.tgz#baf79e62e6ef4c2a4c0b831232daffec251f9d83" + integrity sha1-uveeYubvTCpMC4MSMtr/7CUfnYM= array-includes@^3.1.1: version "3.1.1" @@ -3698,6 +3608,16 @@ assert-plus@1.0.0, assert-plus@^1.0.0: resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= +assert@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/assert/-/assert-2.0.0.tgz#95fc1c616d48713510680f2eaf2d10dd22e02d32" + integrity sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A== + dependencies: + es6-object-assign "^1.1.0" + is-nan "^1.2.1" + object-is "^1.0.1" + util "^0.12.0" + assert@^1.1.1: version "1.5.0" resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz#55c109aaf6e0aefdb3dc4b71240c70bf574b18eb" @@ -3743,11 +3663,6 @@ astral-regex@^2.0.0: resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== -async-limiter@~1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" - integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== - async@0.9.x: version "0.9.2" resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d" @@ -3795,6 +3710,13 @@ autoprefixer@10.2.5: normalize-range "^0.1.2" postcss-value-parser "^4.1.0" +available-typed-arrays@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz#6b098ca9d8039079ee3f77f7b783c4480ba513f5" + integrity sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ== + dependencies: + array-filter "^1.0.0" + aws-sign2@~0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" @@ -3875,16 +3797,6 @@ babel-plugin-extract-import-names@1.6.22: dependencies: "@babel/helper-plugin-utils" "7.10.4" -babel-plugin-istanbul@^5.1.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-5.2.0.tgz#df4ade83d897a92df069c4d9a25cf2671293c854" - integrity sha512-5LphC0USA8t4i1zCtjbbNb6jJj/9+X6P37Qfirc/70EQ34xKlMW+a1RHGwxGI+SwWpNwZ27HqvzAobeqaXwiZw== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - find-up "^3.0.0" - istanbul-lib-instrument "^3.3.0" - test-exclude "^5.2.3" - babel-plugin-istanbul@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz#e159ccdc9af95e0b570c75b4573b7c34d671d765" @@ -4038,6 +3950,18 @@ before-after-hook@^2.1.0: resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.1.0.tgz#b6c03487f44e24200dd30ca5e6a1979c5d2fb635" integrity sha512-IWIbu7pMqyw3EAJHzzHbWa85b6oud/yfKYg5rqB5hNE8CeMi3nX+2C2sj0HswfblST86hpVEOAb9x34NZd6P7A== +better-path-resolve@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/better-path-resolve/-/better-path-resolve-1.0.0.tgz#13a35a1104cdd48a7b74bf8758f96a1ee613f99d" + integrity sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g== + dependencies: + is-windows "^1.0.0" + +big-integer@^1.6.16: + version "1.6.48" + resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.48.tgz#8fd88bd1632cba4a1c8c3e3d7159f08bb95b4b9e" + integrity sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w== + big.js@^5.2.2: version "5.2.2" resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" @@ -4048,13 +3972,6 @@ binary-extensions@^2.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.1.0.tgz#30fa40c9e7fe07dbc895678cd287024dea241dd9" integrity sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ== -bindings@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" - integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== - dependencies: - file-uri-to-path "1.0.0" - bl@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/bl/-/bl-4.0.3.tgz#12d6287adc29080e22a705e5764b2a9522cdc489" @@ -4064,33 +3981,33 @@ bl@^4.0.3: inherits "^2.0.4" readable-stream "^3.4.0" -blitz@0.33.1: - version "0.33.1" - resolved "https://registry.yarnpkg.com/blitz/-/blitz-0.33.1.tgz#4a4cccb9bd1e6633fd6e17b3258656289f651a6e" - integrity sha512-1SfQmtWXRGtu63rqK3Rb3B1V6WdOiBlF2ruZX3zRjiP0FFC8wuIoAX5qiiVEKWO6te3b6XHsxaiwW0Zcn7Q7JA== - dependencies: - "@blitzjs/babel-preset" "0.33.1" - "@blitzjs/cli" "0.33.1" - "@blitzjs/config" "0.33.1" - "@blitzjs/core" "0.33.1" - "@blitzjs/display" "0.33.1" - "@blitzjs/generator" "0.33.1" - "@blitzjs/installer" "0.33.1" - "@blitzjs/server" "0.33.1" +blitz@0.34.0: + version "0.34.0" + resolved "https://registry.yarnpkg.com/blitz/-/blitz-0.34.0.tgz#f5672c4f86fb5cac517687221220ddd0b6938372" + integrity sha512-+rQnOSfCCwuRxRALwi/tXpTUYXZvwW2J0nxhW3y1WvxZspBGSC5DKo+O5FBM/JgsqwrbQaBQTZUzYPx3Llvi4Q== + dependencies: + "@blitzjs/babel-preset" "0.34.0" + "@blitzjs/cli" "0.34.0" + "@blitzjs/config" "0.34.0" + "@blitzjs/core" "0.34.0" + "@blitzjs/display" "0.34.0" + "@blitzjs/generator" "0.34.0" + "@blitzjs/installer" "0.34.0" + "@blitzjs/server" "0.34.0" "@testing-library/jest-dom" "5.11.9" - "@testing-library/react" "^11.2.3" + "@testing-library/react" "11.2.5" "@testing-library/react-hooks" "^4.0.1" - "@types/jest" "^26.0.20" + "@types/jest" "26.0.20" chalk "^4.1.0" envinfo "^7.7.3" - eslint-config-blitz "0.33.1" + eslint-config-blitz "0.34.0" jest "^26.6.3" - jest-environment-jsdom-fourteen "^1.0.1" jest-watch-typeahead "^0.6.1" os-name "^4.0.0" pkg-dir "^5.0.0" react-test-renderer "17.0.1" resolve-from "^5.0.0" + symlink-dir "4.1.0" ts-jest "26.5.0" bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.9: @@ -4153,6 +4070,19 @@ braces@^3.0.1, braces@~3.0.2: dependencies: fill-range "^7.0.1" +broadcast-channel@^3.4.1: + version "3.5.3" + resolved "https://registry.yarnpkg.com/broadcast-channel/-/broadcast-channel-3.5.3.tgz#c75c39d923ae8af6284a893bfdc8bd3996d2dd2d" + integrity sha512-OLOXfwReZa2AAAh9yOUyiALB3YxBe0QpThwwuyRHLgpl8bSznSDmV6Mz7LeBJg1VZsMcDcNMy7B53w12qHrIhQ== + dependencies: + "@babel/runtime" "^7.7.2" + detect-node "^2.0.4" + js-sha3 "0.8.0" + microseconds "0.2.0" + nano-time "1.0.0" + rimraf "3.0.2" + unload "2.2.0" + brorand@^1.0.1, brorand@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" @@ -4217,7 +4147,7 @@ browserify-sign@^4.0.0: readable-stream "^3.6.0" safe-buffer "^5.2.0" -browserify-zlib@^0.2.0: +browserify-zlib@0.2.0, browserify-zlib@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f" integrity sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA== @@ -4480,7 +4410,7 @@ ccount@^1.0.0, ccount@^1.0.3: resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.1.0.tgz#246687debb6014735131be8abab2d93898f8d043" integrity sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg== -chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.4.1, chalk@^2.4.2: +chalk@2.4.2, chalk@^2.0.0, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -4952,7 +4882,7 @@ console-table-printer@^2.7.5: dependencies: simple-wcswidth "^1.0.0" -constants-browserify@^1.0.0: +constants-browserify@1.0.0, constants-browserify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U= @@ -5217,22 +5147,15 @@ csso@^4.0.2: dependencies: css-tree "^1.0.0" -cssom@0.3.x, cssom@^0.3.4, cssom@~0.3.6: - version "0.3.8" - resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" - integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== - cssom@^0.4.4: version "0.4.4" resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw== -cssstyle@^1.1.1: - version "1.4.0" - resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-1.4.0.tgz#9d31328229d3c565c61e586b02041a28fccdccf1" - integrity sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA== - dependencies: - cssom "0.3.x" +cssom@~0.3.6: + version "0.3.8" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" + integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== cssstyle@^2.3.0: version "2.3.0" @@ -5278,15 +5201,6 @@ data-uri-to-buffer@3.0.1: resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz#594b8973938c5bc2c33046535785341abc4f3636" integrity sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og== -data-urls@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-1.1.0.tgz#15ee0582baa5e22bb59c77140da8f9c76963bbfe" - integrity sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ== - dependencies: - abab "^2.0.0" - whatwg-mimetype "^2.2.0" - whatwg-url "^7.0.0" - data-urls@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" @@ -5498,6 +5412,11 @@ detect-newline@^3.0.0: resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== +detect-node@^2.0.4: + version "2.0.5" + resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.5.tgz#9d270aa7eaa5af0b72c4c9d9b814e7f4ce738b79" + integrity sha512-qi86tE6hRcFHy8jI1m2VG+LaPUR1LhqDa5G8tVjuUXmOrpuAgqsA1pN0+ldgr3aKUH+QLI9hCY/OcRYisERejw== + detect-port@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/detect-port/-/detect-port-1.3.0.tgz#d9c40e9accadd4df5cac6a782aefd014d573d1f1" @@ -5599,6 +5518,11 @@ dom-serializer@0: domelementtype "^2.0.1" entities "^2.0.0" +domain-browser@4.19.0: + version "4.19.0" + resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-4.19.0.tgz#1093e17c0a17dbd521182fe90d49ac1370054af1" + integrity sha512-fRA+BaAWOR/yr/t7T9E9GJztHPeFjj8U35ajyAjCDtAAnTn1Rc1f6W6VGPJrO1tkQv9zWu+JRof7z6oQtiYVFQ== + domain-browser@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" @@ -5614,13 +5538,6 @@ domelementtype@^2.0.1: resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.0.2.tgz#f3b6e549201e46f588b59463dd77187131fe6971" integrity sha512-wFwTwCVebUrMgGeAwRL/NhZtHAUyT9n9yg4IMDwf10+6iCMxSkVq9MGCVEH+QZWo1nNidy8kNvwmv4zWHDTqvA== -domexception@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90" - integrity sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug== - dependencies: - webidl-conversions "^4.0.2" - domexception@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" @@ -5770,6 +5687,13 @@ emojis-list@^3.0.0: resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== +encoding@0.1.13: + version "0.1.13" + resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" + integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== + dependencies: + iconv-lite "^0.6.2" + end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" @@ -5872,6 +5796,11 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" +es6-object-assign@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c" + integrity sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw= + escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" @@ -5882,7 +5811,7 @@ escape-goat@^2.0.0: resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675" integrity sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q== -escape-string-regexp@4.0.0, escape-string-regexp@^4.0.0: +escape-string-regexp@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== @@ -5897,17 +5826,10 @@ escape-string-regexp@^2.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== -escodegen@^1.11.0: - version "1.14.3" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503" - integrity sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw== - dependencies: - esprima "^4.0.1" - estraverse "^4.2.0" - esutils "^2.0.2" - optionator "^0.8.1" - optionalDependencies: - source-map "~0.6.1" +escape-string-regexp@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz#4683126b500b61762f2dbebace1806e8be31b1c8" + integrity sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw== escodegen@^2.0.0: version "2.0.0" @@ -5921,13 +5843,13 @@ escodegen@^2.0.0: optionalDependencies: source-map "~0.6.1" -eslint-config-blitz@0.33.1: - version "0.33.1" - resolved "https://registry.yarnpkg.com/eslint-config-blitz/-/eslint-config-blitz-0.33.1.tgz#122bd0606c2f62cbe1d30cf9b926bcbcdd760e57" - integrity sha512-ObSP361SlIa9EEoYZWkj21M6/Qu+dwEXtJrd0BH8OrWDGJ7kgFIwI/zKrwTAirbiOZFQGKRQYedXo91YtNDSlw== +eslint-config-blitz@0.34.0: + version "0.34.0" + resolved "https://registry.yarnpkg.com/eslint-config-blitz/-/eslint-config-blitz-0.34.0.tgz#8aa8c45b0ff25db80a8754cd2f6f8c57f708a452" + integrity sha512-Hz8F2ATZ3wce41iEZSVfk2PjBdFHO7qlnH34gBdIPb4CXPelXKHXIk5kUS6ne55qeWMlEm8xEzRR7zAZnDlW9A== dependencies: - "@typescript-eslint/eslint-plugin" "~4.14.0" - "@typescript-eslint/parser" "~4.14.0" + "@typescript-eslint/eslint-plugin" "4.17.0" + "@typescript-eslint/parser" "4.17.0" babel-eslint "~10.1.0" eslint-config-react-app "~6.0.0" eslint-plugin-cypress "~2.11.2" @@ -6139,7 +6061,7 @@ esrecurse@^4.3.0: dependencies: estraverse "^5.2.0" -estraverse@^4.1.1, estraverse@^4.2.0: +estraverse@^4.1.1: version "4.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== @@ -6421,11 +6343,6 @@ file-loader@6.2.0: loader-utils "^2.0.0" schema-utils "^3.0.0" -file-uri-to-path@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" - integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== - filelist@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.2.tgz#80202f21462d4d1c2e214119b1807c1bc0380e5b" @@ -6450,15 +6367,6 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" -find-cache-dir@3.3.1, find-cache-dir@^3.3.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.1.tgz#89b33fad4a4670daa94f855f7fbe31d6d84fe880" - integrity sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ== - dependencies: - commondir "^1.0.1" - make-dir "^3.0.2" - pkg-dir "^4.1.0" - find-cache-dir@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" @@ -6468,6 +6376,15 @@ find-cache-dir@^2.0.0: make-dir "^2.0.0" pkg-dir "^3.0.0" +find-cache-dir@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.1.tgz#89b33fad4a4670daa94f855f7fbe31d6d84fe880" + integrity sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ== + dependencies: + commondir "^1.0.1" + make-dir "^3.0.2" + pkg-dir "^4.1.0" + find-up@5.0.0, find-up@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" @@ -6544,18 +6461,16 @@ focus-visible@5.2.0: resolved "https://registry.yarnpkg.com/focus-visible/-/focus-visible-5.2.0.tgz#3a9e41fccf587bd25dcc2ef045508284f0a4d6b3" integrity sha512-Rwix9pBtC1Nuy5wysTmKy+UjbDJpIfg8eHjw0rjZ1mX4GNLz1Bmd16uDpI3Gk1i70Fgcs8Csg2lPm8HULFg9DQ== -for-each@^0.3.3: - version "0.3.3" - resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" - integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== - dependencies: - is-callable "^1.1.3" - for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= +foreach@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" + integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k= + forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" @@ -6592,10 +6507,10 @@ fragment-cache@^0.2.1: dependencies: map-cache "^0.2.2" -framer-motion@4.1.3: - version "4.1.3" - resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-4.1.3.tgz#fdde56885910f516233233073f8f0ed6aab6492e" - integrity sha512-WJ3iZiMgzR3cf/W5/o5OwGyNeXvjuIriItPnDtqbAGgd8rCq5ysAW3RS4cC2i2wVA9EfrlxWmosTt+lKhaVI3Q== +framer-motion@4.1.5: + version "4.1.5" + resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-4.1.5.tgz#451ac3a41c682190bf247d5e209e24572e45cfc3" + integrity sha512-ExZ/BGKecRDs91W9ZebbCW5HgO8PaVT5V2ZUs28/jqLyef7VrTho0J5BRH/oAvwc9Qdnl0nRS/YRJWNOCt/PYQ== dependencies: framesync "5.3.0" hey-listen "^1.0.8" @@ -6673,24 +6588,11 @@ fs-mkdirp-stream@^1.0.0: graceful-fs "^4.1.11" through2 "^2.0.3" -fs-readdir-recursive@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz#e32fc030a2ccee44a6b5371308da54be0b397d27" - integrity sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA== - fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= -fsevents@^1.2.7: - version "1.2.13" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.13.tgz#f325cb0455592428bcf11b383370ef70e3bfcc38" - integrity sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw== - dependencies: - bindings "^1.5.0" - nan "^2.12.1" - fsevents@^2.1.2, fsevents@~2.3.1: version "2.3.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" @@ -7313,13 +7215,6 @@ hosted-git-info@^2.1.4: resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg== -html-encoding-sniffer@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz#e70d84b94da53aa375e11fe3a351be6642ca46f8" - integrity sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw== - dependencies: - whatwg-encoding "^1.0.1" - html-encoding-sniffer@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3" @@ -7389,7 +7284,7 @@ http2-wrapper@^1.0.0-beta.5.2: quick-lru "^5.1.1" resolve-alpn "^1.0.0" -https-browserify@^1.0.0: +https-browserify@1.0.0, https-browserify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= @@ -7429,6 +7324,13 @@ iconv-lite@0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" +iconv-lite@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.2.tgz#ce13d1875b0c3a674bd6a04b7f76b01b1b6ded01" + integrity sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + ieee754@^1.1.13, ieee754@^1.1.4: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" @@ -7587,13 +7489,6 @@ internal-slot@^1.0.2: has "^1.0.3" side-channel "^1.0.2" -invariant@^2.2.4: - version "2.2.4" - resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" - integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== - dependencies: - loose-envify "^1.0.0" - is-absolute@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-absolute/-/is-absolute-1.0.0.tgz#395e1ae84b11f26ad1795e73c17378e48a301576" @@ -7634,6 +7529,13 @@ is-alphanumerical@^1.0.0: is-alphabetical "^1.0.0" is-decimal "^1.0.0" +is-arguments@^1.0.4: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.0.tgz#62353031dfbee07ceb34656a6bde59efecae8dd9" + integrity sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg== + dependencies: + call-bind "^1.0.0" + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" @@ -7673,16 +7575,16 @@ is-buffer@^2.0.0: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== -is-callable@^1.1.3, is-callable@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.3.tgz#8b1e0500b73a1d76c70487636f368e519de8db8e" - integrity sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ== - is-callable@^1.1.4, is-callable@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.2.tgz#c7c6715cd22d4ddb48d3e19970223aceabb080d9" integrity sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA== +is-callable@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.3.tgz#8b1e0500b73a1d76c70487636f368e519de8db8e" + integrity sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ== + is-ci@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" @@ -7805,6 +7707,11 @@ is-generator-fn@^2.0.0: resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== +is-generator-function@^1.0.7: + version "1.0.8" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.8.tgz#dfb5c2b120e02b0a8d9d2c6806cd5621aa922f7b" + integrity sha512-2Omr/twNtufVZFr1GhxjOMFPAj2sjc/dKaIqBhvo4qciXfJmITGH6ZGd8eZYNHza8t1y0e01AuqRhJwfWp26WQ== + is-glob@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" @@ -7844,6 +7751,14 @@ is-interactive@^1.0.0: resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== +is-nan@^1.2.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.3.2.tgz#043a54adea31748b55b6cd4e09aadafa69bd9e1d" + integrity sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + is-negated-glob@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-negated-glob/-/is-negated-glob-1.0.0.tgz#6910bca5da8c95e784b5751b976cf5a10fee36d2" @@ -7977,6 +7892,17 @@ is-symbol@^1.0.2, is-symbol@^1.0.3: dependencies: has-symbols "^1.0.1" +is-typed-array@^1.1.3: + version "1.1.5" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.5.tgz#f32e6e096455e329eb7b423862456aa213f0eb4e" + integrity sha512-S+GRDgJlR3PyEbsX/Fobd9cqpZBuvUS+8asRqYDMLCb2qMzt1oz5m5oxQCxOgUDxiWsOVNi4yaF+/uvdlHlYug== + dependencies: + available-typed-arrays "^1.0.2" + call-bind "^1.0.2" + es-abstract "^1.18.0-next.2" + foreach "^2.0.5" + has-symbols "^1.0.1" + is-typedarray@^1.0.0, is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" @@ -8004,7 +7930,7 @@ is-whitespace-character@^1.0.0: resolved "https://registry.yarnpkg.com/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz#0858edd94a95594c7c9dd0b5c174ec6e45ee4aa7" integrity sha512-SDweEzfIZM0SJV0EUga669UTKlmL0Pq8Lno0QDQsPnvECB3IM2aP0gdx5TrU0A01MAPfViaZiI2V1QMZLaKK5w== -is-windows@^1.0.1, is-windows@^1.0.2: +is-windows@^1.0.0, is-windows@^1.0.1, is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== @@ -8068,29 +7994,11 @@ isstream@~0.1.2: resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= -istanbul-lib-coverage@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz#675f0ab69503fad4b1d849f736baaca803344f49" - integrity sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA== - istanbul-lib-coverage@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz#f5944a37c70b550b02a78a5c3b2055b280cec8ec" integrity sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg== -istanbul-lib-instrument@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz#a5f63d91f0bbc0c3e479ef4c5de027335ec6d630" - integrity sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA== - dependencies: - "@babel/generator" "^7.4.0" - "@babel/parser" "^7.4.3" - "@babel/template" "^7.4.0" - "@babel/traverse" "^7.4.3" - "@babel/types" "^7.4.0" - istanbul-lib-coverage "^2.0.5" - semver "^6.0.0" - istanbul-lib-instrument@^4.0.0, istanbul-lib-instrument@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d" @@ -8217,18 +8125,6 @@ jest-each@^26.6.2: jest-util "^26.6.2" pretty-format "^26.6.2" -jest-environment-jsdom-fourteen@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom-fourteen/-/jest-environment-jsdom-fourteen-1.0.1.tgz#4cd0042f58b4ab666950d96532ecb2fc188f96fb" - integrity sha512-DojMX1sY+at5Ep+O9yME34CdidZnO3/zfPh8UW+918C5fIZET5vCjfkegixmsi7AtdYfkr4bPlIzmWnlvQkP7Q== - dependencies: - "@jest/environment" "^24.3.0" - "@jest/fake-timers" "^24.3.0" - "@jest/types" "^24.3.0" - jest-mock "^24.0.0" - jest-util "^24.0.0" - jsdom "^14.1.0" - jest-environment-jsdom@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-26.6.2.tgz#78d09fe9cf019a357009b9b7e1f101d23bd1da3e" @@ -8259,25 +8155,6 @@ jest-get-type@^26.3.0: resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" integrity sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig== -jest-haste-map@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-24.9.0.tgz#b38a5d64274934e21fa417ae9a9fbeb77ceaac7d" - integrity sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ== - dependencies: - "@jest/types" "^24.9.0" - anymatch "^2.0.0" - fb-watchman "^2.0.0" - graceful-fs "^4.1.15" - invariant "^2.2.4" - jest-serializer "^24.9.0" - jest-util "^24.9.0" - jest-worker "^24.9.0" - micromatch "^3.1.10" - sane "^4.0.3" - walker "^1.0.7" - optionalDependencies: - fsevents "^1.2.7" - jest-haste-map@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-26.6.2.tgz#dd7e60fe7dc0e9f911a23d79c5ff7fb5c2cafeaa" @@ -8341,20 +8218,6 @@ jest-matcher-utils@^26.6.2: jest-get-type "^26.3.0" pretty-format "^26.6.2" -jest-message-util@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-24.9.0.tgz#527f54a1e380f5e202a8d1149b0ec872f43119e3" - integrity sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw== - dependencies: - "@babel/code-frame" "^7.0.0" - "@jest/test-result" "^24.9.0" - "@jest/types" "^24.9.0" - "@types/stack-utils" "^1.0.1" - chalk "^2.0.1" - micromatch "^3.1.10" - slash "^2.0.0" - stack-utils "^1.0.1" - jest-message-util@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-26.6.2.tgz#58173744ad6fc0506b5d21150b9be56ef001ca07" @@ -8370,13 +8233,6 @@ jest-message-util@^26.6.2: slash "^3.0.0" stack-utils "^2.0.2" -jest-mock@^24.0.0, jest-mock@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-24.9.0.tgz#c22835541ee379b908673ad51087a2185c13f1c6" - integrity sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w== - dependencies: - "@jest/types" "^24.9.0" - jest-mock@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-26.6.2.tgz#d6cb712b041ed47fe0d9b6fc3474bc6543feb302" @@ -8390,11 +8246,6 @@ jest-pnp-resolver@^1.2.2: resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== -jest-regex-util@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-24.9.0.tgz#c13fb3380bde22bf6575432c493ea8fe37965636" - integrity sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA== - jest-regex-util@^26.0.0: version "26.0.0" resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-26.0.0.tgz#d25e7184b36e39fd466c3bc41be0971e821fee28" @@ -8482,11 +8333,6 @@ jest-runtime@^26.6.3: strip-bom "^4.0.0" yargs "^15.4.1" -jest-serializer@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-24.9.0.tgz#e6d7d7ef96d31e8b9079a714754c5d5c58288e73" - integrity sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ== - jest-serializer@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-26.6.2.tgz#d139aafd46957d3a448f3a6cdabe2919ba0742d1" @@ -8517,24 +8363,6 @@ jest-snapshot@^26.6.2: pretty-format "^26.6.2" semver "^7.3.2" -jest-util@^24.0.0, jest-util@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-24.9.0.tgz#7396814e48536d2e85a37de3e4c431d7cb140162" - integrity sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg== - dependencies: - "@jest/console" "^24.9.0" - "@jest/fake-timers" "^24.9.0" - "@jest/source-map" "^24.9.0" - "@jest/test-result" "^24.9.0" - "@jest/types" "^24.9.0" - callsites "^3.0.0" - chalk "^2.0.1" - graceful-fs "^4.1.15" - is-ci "^2.0.0" - mkdirp "^0.5.1" - slash "^2.0.0" - source-map "^0.6.0" - jest-util@^26.1.0, jest-util@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.6.2.tgz#907535dbe4d5a6cb4c47ac9b926f6af29576cbc1" @@ -8585,13 +8413,14 @@ jest-watcher@^26.3.0, jest-watcher@^26.6.2: jest-util "^26.6.2" string-length "^4.0.1" -jest-worker@24.9.0, jest-worker@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-24.9.0.tgz#5dbfdb5b2d322e98567898238a9697bcce67b3e5" - integrity sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw== +jest-worker@27.0.0-next.5: + version "27.0.0-next.5" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.0.0-next.5.tgz#5985ee29b12a4e191f4aae4bb73b97971d86ec28" + integrity sha512-mk0umAQ5lT+CaOJ+Qp01N6kz48sJG2kr2n1rX0koqKf6FIygQV0qLOdN9SCYID4IVeSigDOcPeGLozdMLYfb5g== dependencies: + "@types/node" "*" merge-stream "^2.0.0" - supports-color "^6.1.0" + supports-color "^8.0.0" jest-worker@^26.6.2: version "26.6.2" @@ -8611,6 +8440,11 @@ jest@^26.6.3: import-local "^3.0.2" jest-cli "^26.6.3" +js-sha3@0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" + integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== + "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -8662,38 +8496,6 @@ jscodeshift@0.11.0: temp "^0.8.1" write-file-atomic "^2.3.0" -jsdom@^14.1.0: - version "14.1.0" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-14.1.0.tgz#916463b6094956b0a6c1782c94e380cd30e1981b" - integrity sha512-O901mfJSuTdwU2w3Sn+74T+RnDVP+FuV5fH8tcPWyqrseRAb0s5xOtPgCFiPOtLcyK7CLIJwPyD83ZqQWvA5ng== - dependencies: - abab "^2.0.0" - acorn "^6.0.4" - acorn-globals "^4.3.0" - array-equal "^1.0.0" - cssom "^0.3.4" - cssstyle "^1.1.1" - data-urls "^1.1.0" - domexception "^1.0.1" - escodegen "^1.11.0" - html-encoding-sniffer "^1.0.2" - nwsapi "^2.1.3" - parse5 "5.1.0" - pn "^1.1.0" - request "^2.88.0" - request-promise-native "^1.0.5" - saxes "^3.1.9" - symbol-tree "^3.2.2" - tough-cookie "^2.5.0" - w3c-hr-time "^1.0.1" - w3c-xmlserializer "^1.1.2" - webidl-conversions "^4.0.2" - whatwg-encoding "^1.0.5" - whatwg-mimetype "^2.3.0" - whatwg-url "^7.0.0" - ws "^6.1.2" - xml-name-validator "^3.0.0" - jsdom@^16.4.0: version "16.5.1" resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.5.1.tgz#4ced6bbd7b77d67fb980e64d9e3e6fb900f97dd6" @@ -8746,11 +8548,6 @@ json-buffer@3.0.1: resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== -json-parse-better-errors@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" - integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== - json-parse-even-better-errors@^2.3.0: version "2.3.1" resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" @@ -9050,16 +8847,6 @@ load-json-file@^2.0.0: pify "^2.0.0" strip-bom "^3.0.0" -load-json-file@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" - integrity sha1-L19Fq5HjMhYjT9U62rZo607AmTs= - dependencies: - graceful-fs "^4.1.2" - parse-json "^4.0.0" - pify "^3.0.0" - strip-bom "^3.0.0" - load-plugin@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/load-plugin/-/load-plugin-3.0.0.tgz#8f3ce57cf4e5111639911012487bc1c2ba3d0e6c" @@ -9278,7 +9065,7 @@ longest-streak@^2.0.1: resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-2.0.4.tgz#b8599957da5b5dab64dee3fe316fa774597d90e4" integrity sha512-vM6rUVCVUJJt33bnmHiZEvr7wPT78ztX7rojL+LW51bHtLh6HTjx84LA5W4+oa6aKEJA7jJu5LR6vQRBpA5DVg== -loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: +loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== @@ -9385,6 +9172,14 @@ markdown-table@^2.0.0: dependencies: repeat-string "^1.0.0" +match-sorter@^6.0.2: + version "6.3.0" + resolved "https://registry.yarnpkg.com/match-sorter/-/match-sorter-6.3.0.tgz#454a1b31ed218cddbce6231a0ecb5fdc549fed01" + integrity sha512-efYOf/wUpNb8FgNY+cOD2EIJI1S5I7YPKsw0LBp7wqPh5pmMS6i/wr3ZWwfwrAw1NvqTA2KUReVRWDX84lUcOQ== + dependencies: + "@babel/runtime" "^7.12.5" + remove-accents "0.4.2" + md5.js@^1.3.4: version "1.3.5" resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" @@ -9551,6 +9346,11 @@ micromatch@^3.1.10, micromatch@^3.1.4: snapdragon "^0.8.1" to-regex "^3.0.2" +microseconds@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/microseconds/-/microseconds-0.2.0.tgz#233b25f50c62a65d861f978a4a4f8ec18797dc39" + integrity sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA== + miller-rabin@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" @@ -9655,7 +9455,7 @@ mkdirp@1.x, mkdirp@^1.0.3, mkdirp@^1.0.4: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -mkdirp@^0.5.1, mkdirp@~0.5.1: +mkdirp@~0.5.1: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== @@ -9714,10 +9514,12 @@ multimatch@^5.0.0: arrify "^2.0.1" minimatch "^3.0.4" -nan@^2.12.1: - version "2.14.2" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19" - integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ== +nano-time@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/nano-time/-/nano-time-1.0.0.tgz#b0554f69ad89e22d0907f7a12b0993a5d96137ef" + integrity sha1-sFVPaa2J4i0JB/ehKwmTpdlhN+8= + dependencies: + big-integer "^1.6.16" nanoassert@^1.0.0: version "1.1.0" @@ -9788,46 +9590,58 @@ next-themes@0.0.14: resolved "https://registry.yarnpkg.com/next-themes/-/next-themes-0.0.14.tgz#2b9861990bc453149e23d8e6ef1a25a119e36675" integrity sha512-x09OaM+wg3SIlEjOv8B21aw/E36jxTtfW3Dm/DPwMsSMluGt7twe1LigA6nc+mXP1u0qu9MxBaIrPPH6UTiKnA== -next@10.0.9: - version "10.0.9" - resolved "https://registry.yarnpkg.com/next/-/next-10.0.9.tgz#ad5d8e0368fee8363cdfd64d22dfbf71f683ae66" - integrity sha512-HyoVjYydcM6LaFAUOHSxVQCcKOsIimVO/IKXCuWUu1rr6DDgXbWNg/8ckH084qD46MOYlLzjViiZ3KCmNQL4Cw== +"next@npm:@blitzjs/next@0.34.0": + version "0.34.0" + resolved "https://registry.yarnpkg.com/@blitzjs/next/-/next-0.34.0.tgz#0094c8eedf22a0c0e8c65dafd25cced440a42732" + integrity sha512-rAqGufaov9OooexcNjcWN/zU51zdXEpthTlPATT+gVKYvA+ZJ/+AgT8YGZG4gSLN9VIY1wciiZTSgKhZrWM1wg== dependencies: "@babel/runtime" "7.12.5" "@hapi/accept" "5.0.1" - "@next/env" "10.0.9" - "@next/polyfill-module" "10.0.9" - "@next/react-dev-overlay" "10.0.9" - "@next/react-refresh-utils" "10.0.9" + "@next/env" "10.1.3" + "@next/polyfill-module" "10.1.3" + "@next/react-dev-overlay" "10.1.3" + "@next/react-refresh-utils" "10.1.3" "@opentelemetry/api" "0.14.0" + assert "2.0.0" ast-types "0.13.2" + browserify-zlib "0.2.0" browserslist "4.16.1" buffer "5.6.0" caniuse-lite "^1.0.30001179" chalk "2.4.2" chokidar "3.5.1" + constants-browserify "1.0.0" crypto-browserify "3.12.0" cssnano-simple "1.2.2" + domain-browser "4.19.0" + encoding "0.1.13" etag "1.8.1" - find-cache-dir "3.3.1" get-orientation "1.1.2" - jest-worker "24.9.0" + https-browserify "1.0.0" + jest-worker "27.0.0-next.5" native-url "0.3.4" node-fetch "2.6.1" node-html-parser "1.4.9" node-libs-browser "^2.2.1" + os-browserify "0.3.0" p-limit "3.1.0" path-browserify "1.0.1" pnp-webpack-plugin "1.6.4" postcss "8.1.7" process "0.11.10" prop-types "15.7.2" + querystring-es3 "0.2.1" raw-body "2.4.1" react-is "16.13.1" react-refresh "0.8.3" stream-browserify "3.0.0" + stream-http "3.1.1" + string_decoder "1.3.0" styled-jsx "3.3.2" + timers-browserify "2.0.12" + tty-browserify "0.0.1" use-subscription "1.5.1" + util "0.12.3" vm-browserify "1.1.2" watchpack "2.1.1" @@ -10045,7 +9859,7 @@ null-loader@4.0.1: loader-utils "^2.0.0" schema-utils "^3.0.0" -nwsapi@^2.1.3, nwsapi@^2.2.0: +nwsapi@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== @@ -10084,6 +9898,14 @@ object-inspect@^1.9.0: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.9.0.tgz#c90521d74e1127b67266ded3394ad6116986533a" integrity sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw== +object-is@^1.0.1: + version "1.1.5" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac" + integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + object-keys@^1.0.12, object-keys@^1.0.9, object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" @@ -10143,15 +9965,6 @@ object.getownpropertydescriptors@^2.1.0: define-properties "^1.1.3" es-abstract "^1.17.0-next.1" -object.getownpropertydescriptors@^2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.2.tgz#1bd63aeacf0d5d2d2f31b5e393b03a7c601a23f7" - integrity sha512-WtxeKSzfBjlzL+F9b7M7hewDzMwy+C8NRssHd1YrNlzHzIDrXcXiNOMrezdAEM4UXixgV+vvnyBeN7Rygl2ttQ== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.18.0-next.2" - object.pick@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" @@ -10238,7 +10051,7 @@ ordered-read-streams@^1.0.0: dependencies: readable-stream "^2.0.1" -os-browserify@^0.3.0: +os-browserify@0.3.0, os-browserify@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= @@ -10467,14 +10280,6 @@ parse-json@^2.2.0: dependencies: error-ex "^1.2.0" -parse-json@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" - integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= - dependencies: - error-ex "^1.3.1" - json-parse-better-errors "^1.0.1" - parse-json@^5.0.0: version "5.1.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.1.0.tgz#f96088cdf24a8faa9aea9a009f2d9d942c999646" @@ -10499,11 +10304,6 @@ parse-passwd@^1.0.0: resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" integrity sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY= -parse5@5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.0.tgz#c59341c9723f414c452975564c7c00a68d58acd2" - integrity sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ== - parse5@6.0.1, parse5@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" @@ -10597,13 +10397,6 @@ path-type@^2.0.0: dependencies: pify "^2.0.0" -path-type@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" - integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg== - dependencies: - pify "^3.0.0" - path-type@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" @@ -10647,11 +10440,6 @@ pify@^2.0.0, pify@^2.3.0: resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= -pify@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" - integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= - pify@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" @@ -10709,11 +10497,6 @@ pluralize@^8.0.0: resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1" integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA== -pn@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb" - integrity sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA== - pnp-webpack-plugin@1.6.4: version "1.6.4" resolved "https://registry.yarnpkg.com/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz#c9711ac4dc48a685dabafc86f8b6dd9f8df84149" @@ -11045,7 +10828,7 @@ qs@~6.5.2: resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== -querystring-es3@^0.2.0: +querystring-es3@0.2.1, querystring-es3@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" integrity sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM= @@ -11165,12 +10948,14 @@ react-player@2.9.0: prop-types "^15.7.2" react-fast-compare "^3.0.1" -react-query@2.5.12: - version "2.5.12" - resolved "https://registry.yarnpkg.com/react-query/-/react-query-2.5.12.tgz#f48be402886b9d95f892d836c2476ee3fdd3e479" - integrity sha512-ni2Hrz8tU/WCc5EQj+QB0jzSgxKdVL3tznFvTrXDmGjTxFj9hg1vXoxEPAn8d4pXy+lgPAflcjxht3IakUgHzg== +react-query@3.13.9: + version "3.13.9" + resolved "https://registry.yarnpkg.com/react-query/-/react-query-3.13.9.tgz#52f3e29176eb115248b4c6e3b5c0bea7b60322d4" + integrity sha512-gPJ2ekm2NA911C9Fc18h///WcE7b4edf5K1JGtyi8kTSv0q9FyM2V+ttTbBAKafTMbbYh4Xg2wqXlR+/cIUZPw== dependencies: - ts-toolbelt "^6.9.4" + "@babel/runtime" "^7.5.5" + broadcast-channel "^3.4.1" + match-sorter "^6.0.2" react-reconciler@^0.24.0: version "0.24.0" @@ -11244,14 +11029,6 @@ read-pkg-up@^2.0.0: find-up "^2.0.0" read-pkg "^2.0.0" -read-pkg-up@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-4.0.0.tgz#1b221c6088ba7799601c808f91161c66e58f8978" - integrity sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA== - dependencies: - find-up "^3.0.0" - read-pkg "^3.0.0" - read-pkg-up@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" @@ -11270,15 +11047,6 @@ read-pkg@^2.0.0: normalize-package-data "^2.3.2" path-type "^2.0.0" -read-pkg@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" - integrity sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k= - dependencies: - load-json-file "^4.0.0" - normalize-package-data "^2.3.2" - path-type "^3.0.0" - read-pkg@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" @@ -11345,13 +11113,6 @@ readline@1.3.0: resolved "https://registry.yarnpkg.com/readline/-/readline-1.3.0.tgz#c580d77ef2cfc8752b132498060dc9793a7ac01c" integrity sha1-xYDXfvLPyHUrEySYBg3JeTp6wBw= -realpath-native@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.1.0.tgz#2003294fea23fb0672f2476ebe22fcf498a2d65c" - integrity sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA== - dependencies: - util.promisify "^1.0.0" - recast@0.17.2: version "0.17.2" resolved "https://registry.yarnpkg.com/recast/-/recast-0.17.2.tgz#f18f18cf20bf3fad4522621a7f9c2ada37276814" @@ -11612,6 +11373,11 @@ remark-stringify@^8.1.0: unherit "^1.0.4" xtend "^4.0.1" +remove-accents@0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/remove-accents/-/remove-accents-0.4.2.tgz#0a43d3aaae1e80db919e07ae254b285d9e1c7bb5" + integrity sha1-CkPTqq4egNuRngeuJUsoXZ4ce7U= + remove-bom-buffer@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz#c2bf1e377520d324f623892e33c10cac2c252b53" @@ -11634,6 +11400,13 @@ remove-trailing-separator@^1.0.1: resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= +rename-overwrite@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/rename-overwrite/-/rename-overwrite-3.1.2.tgz#a176b9b64081aca8a7396d9f442e69eaa0caa30a" + integrity sha512-hRjXzyL+g9uBmRDpX8m/00zwtso+e3XQsOgsCJGGJOQm+paoNZCKBS8Hm9x4WFfPCv2W0Ql5HOFqHlPiAO/UDw== + dependencies: + rimraf "^3.0.2" + repeat-element@^1.1.2: version "1.1.3" resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" @@ -11661,7 +11434,7 @@ request-promise-core@1.1.4: dependencies: lodash "^4.17.19" -request-promise-native@^1.0.5, request-promise-native@^1.0.9: +request-promise-native@^1.0.9: version "1.0.9" resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.9.tgz#e407120526a5efdc9a39b28a5679bf47b9d9dc28" integrity sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g== @@ -11670,7 +11443,7 @@ request-promise-native@^1.0.5, request-promise-native@^1.0.9: stealthy-require "^1.1.1" tough-cookie "^2.3.3" -request@^2.88.0, request@^2.88.2: +request@^2.88.2: version "2.88.2" resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== @@ -11849,7 +11622,7 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -rimraf@^3.0.0, rimraf@^3.0.2: +rimraf@3.0.2, rimraf@^3.0.0, rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== @@ -11905,7 +11678,7 @@ safe-regex@^1.1.0: dependencies: ret "~0.1.10" -"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== @@ -11930,13 +11703,6 @@ sax@~1.2.4: resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== -saxes@^3.1.9: - version "3.1.11" - resolved "https://registry.yarnpkg.com/saxes/-/saxes-3.1.11.tgz#d59d1fd332ec92ad98a2e0b2ee644702384b1c5b" - integrity sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g== - dependencies: - xmlchars "^2.1.1" - saxes@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" @@ -12155,11 +11921,6 @@ sisteransi@^1.0.5: resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== -slash@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" - integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== - slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" @@ -12367,13 +12128,6 @@ stable@^0.1.8: resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== -stack-utils@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.4.tgz#4b600971dcfc6aed0cbdf2a8268177cc916c87c8" - integrity sha512-IPDJfugEGbfizBwBZRZ3xpccMdRyP5lqsBWXGQWimVjua/ccLCeMOAVjlc1R7LxFjo5sEDhyNIXd8mo/AiDS9w== - dependencies: - escape-string-regexp "^2.0.0" - stack-utils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.3.tgz#cd5f030126ff116b78ccb3c027fe302713b61277" @@ -12434,6 +12188,16 @@ stream-combiner@~0.0.4: dependencies: duplexer "~0.1.1" +stream-http@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-3.1.1.tgz#0370a8017cf8d050b9a8554afe608f043eaff564" + integrity sha512-S7OqaYu0EkFpgeGFb/NPOoPLxFko7TPqtEeFg5DXPB4v/KETHG0Ln6fRFrNezoelpaDKmycEmmZ81cC9DAwgYg== + dependencies: + builtin-status-codes "^3.0.0" + inherits "^2.0.4" + readable-stream "^3.6.0" + xtend "^4.0.2" + stream-http@^2.7.2: version "2.8.3" resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc" @@ -12558,7 +12322,7 @@ string.prototype.trimstart@^1.0.4: call-bind "^1.0.2" define-properties "^1.1.3" -string_decoder@^1.0.0, string_decoder@^1.1.1: +string_decoder@1.3.0, string_decoder@^1.0.0, string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== @@ -12752,6 +12516,13 @@ supports-color@^7.0.0, supports-color@^7.1.0: dependencies: has-flag "^4.0.0" +supports-color@^8.0.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + supports-hyperlinks@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-1.0.1.tgz#71daedf36cc1060ac5100c351bb3da48c29c0ef7" @@ -12792,11 +12563,20 @@ svgo@^1.2.2: unquote "~1.1.1" util.promisify "~1.0.0" -symbol-tree@^3.2.2, symbol-tree@^3.2.4: +symbol-tree@^3.2.4: version "3.2.4" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== +symlink-dir@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/symlink-dir/-/symlink-dir-4.1.0.tgz#655bc0573f6a359fe4f07265dfb8c2ddb98d4fa7" + integrity sha512-yT256U0E+AcFxbSUc0suJXTdOjVbVk5MmBLzqD+unQRwQbcMhTvTZnG/3UZZknemOK8+nQz2lTBZs0+uYS2apg== + dependencies: + better-path-resolve "^1.0.0" + graceful-fs "^4.1.11" + rename-overwrite "^3.0.0" + table@^6.0.4: version "6.0.7" resolved "https://registry.yarnpkg.com/table/-/table-6.0.7.tgz#e45897ffbcc1bcf9e8a87bf420f2c9e5a7a52a34" @@ -12937,16 +12717,6 @@ ternary-stream@^3.0.0: merge-stream "^2.0.0" through2 "^3.0.1" -test-exclude@^5.2.3: - version "5.2.3" - resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-5.2.3.tgz#c3d3e1e311eb7ee405e092dac10aefd09091eac0" - integrity sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g== - dependencies: - glob "^7.1.3" - minimatch "^3.0.4" - read-pkg-up "^4.0.0" - require-main-filename "^2.0.0" - test-exclude@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" @@ -13018,7 +12788,7 @@ through@2, through@^2.3.8, through@~2.3, through@~2.3.1: resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= -timers-browserify@^2.0.4: +timers-browserify@2.0.12, timers-browserify@^2.0.4: version "2.0.12" resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.12.tgz#44a45c11fbf407f34f97bccd1577c652361b00ee" integrity sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ== @@ -13127,7 +12897,7 @@ totalist@^1.0.0: resolved "https://registry.yarnpkg.com/totalist/-/totalist-1.1.0.tgz#a4d65a3e546517701e3e5c37a47a70ac97fe56df" integrity sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g== -tough-cookie@^2.3.3, tough-cookie@^2.5.0, tough-cookie@~2.5.0: +tough-cookie@^2.3.3, tough-cookie@~2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== @@ -13217,11 +12987,6 @@ ts-pnp@^1.1.6: resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.2.0.tgz#a500ad084b0798f1c3071af391e65912c86bca92" integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw== -ts-toolbelt@^6.9.4: - version "6.15.5" - resolved "https://registry.yarnpkg.com/ts-toolbelt/-/ts-toolbelt-6.15.5.tgz#cb3b43ed725cb63644782c64fbcad7d8f28c0a83" - integrity sha512-FZIXf1ksVyLcfr7M317jbB67XFJhOO1YqdTcuGaq9q5jLUoTikukZ+98TPjKiP2jC5CgmYdWWYs0s2nLSU0/1A== - tsconfig-paths@3.9.0, tsconfig-paths@^3.9.0: version "3.9.0" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz#098547a6c4448807e8fcb8eae081064ee9a3c90b" @@ -13271,6 +13036,11 @@ tty-browserify@0.0.0: resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" integrity sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY= +tty-browserify@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.1.tgz#3f05251ee17904dfd0677546670db9651682b811" + integrity sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw== + tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" @@ -13604,6 +13374,14 @@ universalify@^2.0.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== +unload@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/unload/-/unload-2.2.0.tgz#ccc88fdcad345faa06a92039ec0f80b488880ef7" + integrity sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA== + dependencies: + "@babel/runtime" "^7.6.2" + detect-node "^2.0.4" + unpipe@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" @@ -13701,17 +13479,6 @@ util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= -util.promisify@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.1.1.tgz#77832f57ced2c9478174149cae9b96e9918cd54b" - integrity sha512-/s3UsZUrIfa6xDhr7zZhnE9SLQ5RIXyYfiVnMMyMDzOc8WhWN4Nbh36H842OyurKbCDAesZOJaVyvmSl6fhGQw== - dependencies: - call-bind "^1.0.0" - define-properties "^1.1.3" - for-each "^0.3.3" - has-symbols "^1.0.1" - object.getownpropertydescriptors "^2.1.1" - util.promisify@~1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.1.tgz#6baf7774b80eeb0f7520d8b81d07982a59abbaee" @@ -13729,6 +13496,18 @@ util@0.10.3: dependencies: inherits "2.0.1" +util@0.12.3, util@^0.12.0: + version "0.12.3" + resolved "https://registry.yarnpkg.com/util/-/util-0.12.3.tgz#971bb0292d2cc0c892dab7c6a5d37c2bec707888" + integrity sha512-I8XkoQwE+fPQEhy9v012V+TSdH2kp9ts29i20TaaDUXsg7x/onePbhFJUExBfv/2ay1ZOp/Vsm3nDlmnFGSAog== + dependencies: + inherits "^2.0.3" + is-arguments "^1.0.4" + is-generator-function "^1.0.7" + is-typed-array "^1.1.3" + safe-buffer "^5.1.2" + which-typed-array "^1.1.2" + util@^0.11.0: version "0.11.1" resolved "https://registry.yarnpkg.com/util/-/util-0.11.1.tgz#3236733720ec64bb27f6e26f421aaa2e1b588d61" @@ -13899,22 +13678,13 @@ vm-browserify@1.1.2, vm-browserify@^1.0.1: resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== -w3c-hr-time@^1.0.1, w3c-hr-time@^1.0.2: +w3c-hr-time@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== dependencies: browser-process-hrtime "^1.0.0" -w3c-xmlserializer@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz#30485ca7d70a6fd052420a3d12fd90e6339ce794" - integrity sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg== - dependencies: - domexception "^1.0.1" - webidl-conversions "^4.0.2" - xml-name-validator "^3.0.0" - w3c-xmlserializer@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz#3e7104a05b75146cc60f564380b7f683acf1020a" @@ -13979,14 +13749,14 @@ webpack-bundle-analyzer@4.3.0: sirv "^1.0.7" ws "^7.3.1" -whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.5: +whatwg-encoding@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== dependencies: iconv-lite "0.4.24" -whatwg-mimetype@^2.2.0, whatwg-mimetype@^2.3.0: +whatwg-mimetype@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== @@ -14025,6 +13795,19 @@ which-module@^2.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= +which-typed-array@^1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.4.tgz#8fcb7d3ee5adf2d771066fba7cf37e32fe8711ff" + integrity sha512-49E0SpUe90cjpoc7BOJwyPHRqSAd12c10Qm2amdEZrJPCY2NDxaW01zHITrem+rnETY3dwrbH3UUrUwagfCYDA== + dependencies: + available-typed-arrays "^1.0.2" + call-bind "^1.0.0" + es-abstract "^1.18.0-next.1" + foreach "^2.0.5" + function-bind "^1.1.1" + has-symbols "^1.0.1" + is-typed-array "^1.1.3" + which@^1.2.10, which@^1.2.9: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" @@ -14090,15 +13873,6 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= -write-file-atomic@2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.1.tgz#d0b05463c188ae804396fd5ab2a370062af87529" - integrity sha512-TGHFeZEZMnv+gBFRfjAcxL5bPHrsGKtnb4qsFAws7/vlh+QfwAaySIw4AXP9ZskTTh5GWu3FLuJhsWVdiJPGvg== - dependencies: - graceful-fs "^4.1.11" - imurmurhash "^0.1.4" - signal-exit "^3.0.2" - write-file-atomic@^2.3.0: version "2.4.3" resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.3.tgz#1fd2e9ae1df3e75b8d8c367443c692d4ca81f481" @@ -14118,13 +13892,6 @@ write-file-atomic@^3.0.0: signal-exit "^3.0.2" typedarray-to-buffer "^3.1.5" -ws@^6.1.2: - version "6.2.1" - resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb" - integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA== - dependencies: - async-limiter "~1.0.0" - ws@^7, ws@^7.2.5: version "7.4.0" resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.0.tgz#a5dd76a24197940d4a8bb9e0e152bb4503764da7" @@ -14150,7 +13917,7 @@ xml-name-validator@^3.0.0: resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== -xmlchars@^2.1.1, xmlchars@^2.2.0: +xmlchars@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== From f6ce373a6d70c043dc0893598a5430c82e76a995 Mon Sep 17 00:00:00 2001 From: Roshan Manuel <31125563+Roesh@users.noreply.github.com> Date: Mon, 19 Apr 2021 18:13:35 -0400 Subject: [PATCH 18/67] Remove Anonymous Functions Exports (#451) * typo fix Small typo fix: you'll need to do the chech => you'll need to do the check * Update session.create > session.$create * Update api routes doc examples - no anon exports * Edit preview mode doc examples - no anon exports * Change export name to handler * Change export name to handler --- app/pages/docs/api-routes.mdx | 15 ++++++++++----- app/pages/docs/preview-mode.mdx | 6 ++++-- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/app/pages/docs/api-routes.mdx b/app/pages/docs/api-routes.mdx index d141d7c..a31ee4f 100644 --- a/app/pages/docs/api-routes.mdx +++ b/app/pages/docs/api-routes.mdx @@ -19,11 +19,12 @@ For example, the following API route `app/api/hello.js` handles a `json` response: ```ts -export default (req: BlitzApiRequest, res: BlitzApiResponse) => { +const handler = (req: BlitzApiRequest, res: BlitzApiResponse) => { res.statusCode = 200 res.setHeader("Content-Type", "application/json") res.end(JSON.stringify({name: "John Doe"})) } +export default handler ``` For an API route to work, you need to export as default a function (a.k.a @@ -40,13 +41,14 @@ To handle different HTTP methods in an API route, you can use `req.method` in your request handler, like so: ```ts -export default (req: BlitzApiRequest, res: BlitzApiResponse) => { +const handler = (req: BlitzApiRequest, res: BlitzApiResponse) => { if (req.method === "POST") { // Process a POST request } else { // Handle any other HTTP method } } +export default handler ``` To fetch API endpoints, take a look into any of the examples at the start @@ -105,13 +107,14 @@ Dynamic API routes follow the same file naming rules used for `pages`. For example, the API route `app/api/post/[pid].js` has the following code: ```ts -export default (req: BlitzApiRequest, res: BlitzApiResponse) => { +const handler = (req: BlitzApiRequest, res: BlitzApiResponse) => { const { query: {pid}, } = req res.end(`Post: ${pid}`) } +export default handler ``` Now, a request to `/api/post/abc` will respond with the text: `Post: abc`. @@ -163,13 +166,14 @@ parameters will be added to the array, like so: An API route for `app/api/post/[...slug].js` could look like this: ```ts -export default (req: BlitzApiRequest, res: BlitzApiResponse) => { +const handler = (req: BlitzApiRequest, res: BlitzApiResponse) => { const { query: {slug}, } = req res.end(`Post: ${slug.join(", ")}`) } +export default handler ``` Now, a request to `/api/post/a/b/c` will respond with the text: @@ -209,9 +213,10 @@ the developer experience and increase the speed of creating new API endpoints, take a look at the following example: ```ts -export default (req: BlitzApiRequest, res: BlitzApiResponse) => { +const handler = (req: BlitzApiRequest, res: BlitzApiResponse) => { res.status(200).json({name: "Blitz.js"}) } +export default handler ``` The included helpers are: diff --git a/app/pages/docs/preview-mode.mdx b/app/pages/docs/preview-mode.mdx index 9ec3967..d235e6d 100644 --- a/app/pages/docs/preview-mode.mdx +++ b/app/pages/docs/preview-mode.mdx @@ -32,11 +32,12 @@ can be used by `getStaticProps` (more on this later). For now, we’ll use `{}`. ```js -export default (req, res) => { +const handler = (req, res) => { // ... res.setPreviewData({}) // ... } +export default handler ``` `res.setPreviewData` sets some **cookies** on the browser which turns on @@ -51,10 +52,11 @@ accessing it from your browser manually: // A simple example for testing it manually from your browser. // If this is located at app/api/preview.js, then // open /api/preview from your browser. -export default (req, res) => { +const handler = (req, res) => { res.setPreviewData({}) res.end("Preview mode enabled") } +export default handler ``` If you use your browser’s developer tools, you’ll notice that the From 4dbbdab2a89f86c4647ce1254520baadfc9cd42b Mon Sep 17 00:00:00 2001 From: JuanM04 <me@juanm04.com> Date: Mon, 19 Apr 2021 19:29:29 -0300 Subject: [PATCH 19/67] Added BlitzApiHandler --- app/pages/docs/api-routes.mdx | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/app/pages/docs/api-routes.mdx b/app/pages/docs/api-routes.mdx index a31ee4f..76a1d75 100644 --- a/app/pages/docs/api-routes.mdx +++ b/app/pages/docs/api-routes.mdx @@ -37,11 +37,23 @@ For an API route to work, you need to export as default a function (a.k.a [http.ServerResponse](https://nodejs.org/api/http.html#http_class_http_serverresponse), plus some helper functions you can see [here](#response-helpers) +If you want to avoid writting the types `BlitzApiRequest` and +`BlitzApiResponse` everywhere, you can use `BlitzApiHandler`: + +```ts +const handler: BlitzApiHandler = (req, res) => { + res.statusCode = 200 + res.setHeader("Content-Type", "application/json") + res.end(JSON.stringify({name: "John Doe"})) +} +export default handler +``` + To handle different HTTP methods in an API route, you can use `req.method` in your request handler, like so: ```ts -const handler = (req: BlitzApiRequest, res: BlitzApiResponse) => { +const handler: BlitzApiHandler = (req, res) => { if (req.method === "POST") { // Process a POST request } else { @@ -107,7 +119,7 @@ Dynamic API routes follow the same file naming rules used for `pages`. For example, the API route `app/api/post/[pid].js` has the following code: ```ts -const handler = (req: BlitzApiRequest, res: BlitzApiResponse) => { +const handler: BlitzApiHandler = (req, res) => { const { query: {pid}, } = req @@ -166,7 +178,7 @@ parameters will be added to the array, like so: An API route for `app/api/post/[...slug].js` could look like this: ```ts -const handler = (req: BlitzApiRequest, res: BlitzApiResponse) => { +const handler: BlitzApiHandler = (req, res) => { const { query: {slug}, } = req @@ -213,7 +225,7 @@ the developer experience and increase the speed of creating new API endpoints, take a look at the following example: ```ts -const handler = (req: BlitzApiRequest, res: BlitzApiResponse) => { +const handler: BlitzApiHandler = (req, res) => { res.status(200).json({name: "Blitz.js"}) } export default handler From 4b0a1d0ee6d6eae06e8572bf0aa078dda0bf9830 Mon Sep 17 00:00:00 2001 From: Brandon Bayer <b@bayer.ws> Date: Tue, 20 Apr 2021 10:14:25 -0400 Subject: [PATCH 20/67] Update contributing.mdx --- app/pages/docs/contributing.mdx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/pages/docs/contributing.mdx b/app/pages/docs/contributing.mdx index 8b2645b..be17378 100644 --- a/app/pages/docs/contributing.mdx +++ b/app/pages/docs/contributing.mdx @@ -147,8 +147,16 @@ Most Blitz packages in `packages/` have jest unit tests. #### Integration Tests + Blitz integration tests are inside the root `test/` folder. +Make sure you have `chromedriver` installed for your Chrome version. You can install it with + +- `brew install --cask chromedriver` on Mac OS X +- `chocolatey install chromedriver` on Windows +- Or manually download the version that matches your installed chrome version (if there's no match, download a version under it, but not above) from the [chromedriver repo](https://chromedriver.storage.googleapis.com/index.html) and add the binary to `<blitz-repo>/node_modules/.bin` + + You can run all integration tests by running `yarn test:integration` from the repo root. Or you can run `yarn testheadles` to run them in headless mode (so it doesn't open a web browser window). From c7c36dd56d4cf80f018ab63977ee1f476be1aeec Mon Sep 17 00:00:00 2001 From: Brandon Bayer <b@bayer.ws> Date: Wed, 21 Apr 2021 11:08:39 -0400 Subject: [PATCH 21/67] add RIT as sponsor --- app/core/components/SponsorPack.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/core/components/SponsorPack.js b/app/core/components/SponsorPack.js index c68d74c..9fe62de 100644 --- a/app/core/components/SponsorPack.js +++ b/app/core/components/SponsorPack.js @@ -20,6 +20,14 @@ const sponsors = [ tier: 3, cost: 250, }, + { + name: "RIT", + href: + "https://rit-inc.co.jp/?utm_source=BlitzJS&utm_medium=sponsorship&utm_campaign=BlitzJS_Sponsorship_2021", + imageUrl: "https://raw.githubusercontent.com/blitz-js/blitz/canary/assets/rit_logo.png", + tier: 3, + cost: 250, + }, { name: "Andreas", href: "https://andreas.fyi/", From 287b7ba2943f570971a480d3c59b8de3edc65d90 Mon Sep 17 00:00:00 2001 From: Antony <tony.kamp@web.de> Date: Wed, 21 Apr 2021 17:49:25 +0200 Subject: [PATCH 22/67] Added description to `useInfiniteQuery` and `usePaginatedQuery` (#453) --- app/pages/docs/use-infinite-query.mdx | 6 ++++++ app/pages/docs/use-paginated-query.mdx | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/app/pages/docs/use-infinite-query.mdx b/app/pages/docs/use-infinite-query.mdx index 4dd35b3..6efad17 100644 --- a/app/pages/docs/use-infinite-query.mdx +++ b/app/pages/docs/use-infinite-query.mdx @@ -3,6 +3,12 @@ title: useInfiniteQuery sidebar_label: useInfiniteQuery --- +Use `useInfiniteQuery` instead of `useQuery`, if you want to show large +data gradually more. For instance, you have many numbered pages, and you +want to show the first ten pages initially. After clicking on +`Show more pages`, the user should see three pages more than before. For +this case, you can `useInfiniteQuery` as shown in the following example. + ### Example {#example} ```ts diff --git a/app/pages/docs/use-paginated-query.mdx b/app/pages/docs/use-paginated-query.mdx index 7ea51f8..c721bc8 100644 --- a/app/pages/docs/use-paginated-query.mdx +++ b/app/pages/docs/use-paginated-query.mdx @@ -3,6 +3,13 @@ title: usePaginatedQuery sidebar_label: usePaginatedQuery --- +Use `usePaginatedQuery` instead of `useQuery`, if you want to divide large +data into smaller contiguous intervals of data. For instance, you have +many numbered pages, and you want to show the first three pages initially. +After clicking on `Show next pages`, the user should see the following +three pages only. For this case, you can `usePaginatedQuery` as shown in +the following example. + ### Example {#example} ```ts From 2f576f62e4a42d01af2f4f969ab46c010343b13f Mon Sep 17 00:00:00 2001 From: Brandon Bayer <b@bayer.ws> Date: Wed, 21 Apr 2021 15:44:27 -0400 Subject: [PATCH 23/67] update docs for server.ts --- app/pages/docs/custom-server.mdx | 55 +++++++++++++++++--------------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/app/pages/docs/custom-server.mdx b/app/pages/docs/custom-server.mdx index 77a8e1a..aaf3f14 100644 --- a/app/pages/docs/custom-server.mdx +++ b/app/pages/docs/custom-server.mdx @@ -8,7 +8,8 @@ some other direct control over the web server itself. ### How {#how} -1. Add a `server.js` file in your project root. See below for an example. +1. Add a `server.ts` or `server.js` file in your project root. See below + for an example. 2. Now `blitz dev` and `blitz start` will automatically detect and use your custom server. @@ -16,12 +17,14 @@ some other direct control over the web server itself. Here's an example custom server: -```js -// server.js -const {createServer} = require("http") -const {parse} = require("url") -const blitz = require("blitz/custom-server") +```ts +// server.ts +import blitz from "blitz/custom-server" +import {createServer} from "http" +import {parse} from "url" +import {log} from "@blitzjs/display" +const {PORT = "3000"} = process.env const dev = process.env.NODE_ENV !== "production" const app = blitz({dev}) const handle = app.getRequestHandler() @@ -30,34 +33,36 @@ app.prepare().then(() => { createServer((req, res) => { // Be sure to pass `true` as the second argument to `url.parse`. // This tells it to parse the query portion of the URL. - const parsedUrl = parse(req.url, true) - const {pathname, query} = parsedUrl + const parsedUrl = parse(req.url!, true) + const {pathname} = parsedUrl + + if (pathname === "/hello") { + res.writeHead(200).end("world") + return + } - if (pathname === "/a") { + if (pathname === "/hello") { + res.writeHead(200).end("world") + return + } else if (pathname === "/a") { app.render(req, res, "/a", query) } else if (pathname === "/b") { app.render(req, res, "/b", query) } else { handle(req, res, parsedUrl) } - }).listen(3000, (err) => { - if (err) throw err - console.log("> Ready on http://localhost:3000") + + handle(req, res, parsedUrl) + }).listen(PORT, () => { + log.success(`Ready on http://localhost:${PORT}`) }) }) ``` -> `server.js` doesn't go through babel or webpack. Make sure the syntax -> and sources this file requires are compatible with the current node -> version you are running. - --- -The custom server uses the following import to connect the server with the -Blitz application: - -The above `blitz` import is a function that receives an object with the -following options: +The above `blitz/custom-server` import is a function that receives an +object with the following options: - `dev`: `Boolean` - Whether or not to launch Blitz in dev mode. Defaults to `false` @@ -72,10 +77,10 @@ required. ## Disabling file-system routing {#disabling-file-system-routing} -By default, `blitz` will serve each file in the `pages` folder under a -pathname matching the filename. If your project uses a custom server, this -behavior may result in the same content being served from multiple paths, -which can present problems with SEO and UX. +By default, `blitz` will serve each file `pages` folders under a pathname +matching the filename. If your project uses a custom server, this behavior +may result in the same content being served from multiple paths, which can +present problems with SEO and UX. To disable this behavior and prevent routing based on files in `pages`, open `blitz.config.js` and disable the `useFileSystemPublicRoutes` config: From 00be1036eecfa0167a19a0253f361334b5727a27 Mon Sep 17 00:00:00 2001 From: Brandon Bayer <b@bayer.ws> Date: Thu, 22 Apr 2021 12:18:57 -0400 Subject: [PATCH 24/67] Update get-started.mdx --- app/pages/docs/get-started.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/pages/docs/get-started.mdx b/app/pages/docs/get-started.mdx index 92684ce..dffe27a 100644 --- a/app/pages/docs/get-started.mdx +++ b/app/pages/docs/get-started.mdx @@ -9,7 +9,7 @@ You need Node.js 12 or newer ### Install Blitz {#install-blitz} -Run `npm install -g blitz` +Run `yarn global add blitz` or `npm install -g blitz --legacy-peer-deps` (legacy-peer-deps is needed because npm totally change behavior of peer deps and some dependencies haven't caught up) ### Create a New App {#create-a-new-app} From 3f003d29d83aefc97d4b866f9acce50681c29eae Mon Sep 17 00:00:00 2001 From: JuanM04 <me@juanm04.com> Date: Sun, 25 Apr 2021 10:11:07 -0300 Subject: [PATCH 25/67] Formatted docs --- app/pages/docs/contributing.mdx | 11 ++++--- app/pages/docs/error-pages.mdx | 57 ++++++++++++++++++++++++--------- app/pages/docs/get-started.mdx | 4 ++- app/pages/docs/query-usage.mdx | 8 ++--- app/pages/docs/why-blitz.mdx | 2 +- 5 files changed, 57 insertions(+), 25 deletions(-) diff --git a/app/pages/docs/contributing.mdx b/app/pages/docs/contributing.mdx index be17378..ae928c2 100644 --- a/app/pages/docs/contributing.mdx +++ b/app/pages/docs/contributing.mdx @@ -147,15 +147,18 @@ Most Blitz packages in `packages/` have jest unit tests. #### Integration Tests - Blitz integration tests are inside the root `test/` folder. -Make sure you have `chromedriver` installed for your Chrome version. You can install it with +Make sure you have `chromedriver` installed for your Chrome version. You +can install it with - `brew install --cask chromedriver` on Mac OS X - `chocolatey install chromedriver` on Windows -- Or manually download the version that matches your installed chrome version (if there's no match, download a version under it, but not above) from the [chromedriver repo](https://chromedriver.storage.googleapis.com/index.html) and add the binary to `<blitz-repo>/node_modules/.bin` - +- Or manually download the version that matches your installed chrome + version (if there's no match, download a version under it, but not + above) from the + [chromedriver repo](https://chromedriver.storage.googleapis.com/index.html) + and add the binary to `<blitz-repo>/node_modules/.bin` You can run all integration tests by running `yarn test:integration` from the repo root. Or you can run `yarn testheadles` to run them in headless diff --git a/app/pages/docs/error-pages.mdx b/app/pages/docs/error-pages.mdx index 7945fbc..15d3d81 100644 --- a/app/pages/docs/error-pages.mdx +++ b/app/pages/docs/error-pages.mdx @@ -58,27 +58,31 @@ export default Error ## Customize the Error Boundary Fallback Component {#custom-error-boundary-component} -The default `app/pages/_app.tsx` configures an error boundary which catches errors that happen during render, on the client side. This will also catch errors within `useQuery` or `useMutation` because they happen in suspense. +The default `app/pages/_app.tsx` configures an error boundary which +catches errors that happen during render, on the client side. This will +also catch errors within `useQuery` or `useMutation` because they happen +in suspense. In essence, the default configuration is: ```tsx -export default function App({ Component, pageProps }: AppProps) { +export default function App({Component, pageProps}: AppProps) { return ( - <ErrorBoundary - FallbackComponent={RootErrorFallback} - {...etc} - > + <ErrorBoundary FallbackComponent={RootErrorFallback} {...etc}> {getLayout(<Component {...pageProps} />)} </ErrorBoundary> ) } ``` -The `RootErrorFallback` is rendered when a error happens within render, or a suspended mutation or query. By default: +The `RootErrorFallback` is rendered when a error happens within render, or +a suspended mutation or query. By default: ```tsx -function RootErrorFallback({ error, resetErrorBoundary }: ErrorFallbackProps) { +function RootErrorFallback({ + error, + resetErrorBoundary, +}: ErrorFallbackProps) { if (error instanceof AuthenticationError) { return <LoginForm onSuccess={resetErrorBoundary} /> } else if (error instanceof AuthorizationError) { @@ -90,7 +94,10 @@ function RootErrorFallback({ error, resetErrorBoundary }: ErrorFallbackProps) { ) } else { return ( - <ErrorComponent statusCode={error.statusCode || 400} title={error.message || error.name} /> + <ErrorComponent + statusCode={error.statusCode || 400} + title={error.message || error.name} + /> ) } } @@ -98,27 +105,47 @@ function RootErrorFallback({ error, resetErrorBoundary }: ErrorFallbackProps) { ### Authentication Error Component (401) {#custom-authentication-error-component} -If your query, mutation, or render function `throw new AuthenticationError()` when the user is not authenticated, you can detect that within the error fallback: +If your query, mutation, or render function +`throw new AuthenticationError()` when the user is not authenticated, you +can detect that within the error fallback: ```tsx -function RootErrorFallback({ error, resetErrorBoundary }: ErrorFallbackProps) { +function RootErrorFallback({ + error, + resetErrorBoundary, +}: ErrorFallbackProps) { if (error instanceof AuthenticationError) { return <MyCustomError error={error} onRetry={resetErrorBoundary} /> } - return <ErrorComponent statusCode={error.statusCode || 400} title={error.message || error.name} /> + return ( + <ErrorComponent + statusCode={error.statusCode || 400} + title={error.message || error.name} + /> + ) } ``` ### Authorization Error Component (403) {#custom-authorization-error-component} -Your query, mutation, or render function may call `throw new AuthorizationError()` when a user is authenticated but not authorized to do something. The error fallback may handle that: +Your query, mutation, or render function may call +`throw new AuthorizationError()` when a user is authenticated but not +authorized to do something. The error fallback may handle that: ```tsx -function RootErrorFallback({ error, resetErrorBoundary }: ErrorFallbackProps) { +function RootErrorFallback({ + error, + resetErrorBoundary, +}: ErrorFallbackProps) { if (error instanceof AuthorizationError) { return <MyCustomError error={error} onRetry={resetErrorBoundary} /> } - return <ErrorComponent statusCode={error.statusCode || 400} title={error.message || error.name} /> + return ( + <ErrorComponent + statusCode={error.statusCode || 400} + title={error.message || error.name} + /> + ) } ``` diff --git a/app/pages/docs/get-started.mdx b/app/pages/docs/get-started.mdx index dffe27a..9fbd16b 100644 --- a/app/pages/docs/get-started.mdx +++ b/app/pages/docs/get-started.mdx @@ -9,7 +9,9 @@ You need Node.js 12 or newer ### Install Blitz {#install-blitz} -Run `yarn global add blitz` or `npm install -g blitz --legacy-peer-deps` (legacy-peer-deps is needed because npm totally change behavior of peer deps and some dependencies haven't caught up) +Run `yarn global add blitz` or `npm install -g blitz --legacy-peer-deps` +(legacy-peer-deps is needed because npm totally change behavior of peer +deps and some dependencies haven't caught up) ### Create a New App {#create-a-new-app} diff --git a/app/pages/docs/query-usage.mdx b/app/pages/docs/query-usage.mdx index a65d404..87463b1 100644 --- a/app/pages/docs/query-usage.mdx +++ b/app/pages/docs/query-usage.mdx @@ -159,10 +159,10 @@ export default ProjectPage To ensure data is not shared between users and requests, a new query client is created for each page request. You can prefetch your data by -invoking the `prefetchQuery` method on the newly-created client, passing in -the query key and resolver along with any relevant input arguments. Once -the data has been fetched, dehydrate the queries to the query client using -the `dehydrate` method and pass the result to the page via the +invoking the `prefetchQuery` method on the newly-created client, passing +in the query key and resolver along with any relevant input arguments. +Once the data has been fetched, dehydrate the queries to the query client +using the `dehydrate` method and pass the result to the page via the `dehydratedState` prop. ## On the Server {#on-the-server} diff --git a/app/pages/docs/why-blitz.mdx b/app/pages/docs/why-blitz.mdx index a64dd39..44fbd04 100644 --- a/app/pages/docs/why-blitz.mdx +++ b/app/pages/docs/why-blitz.mdx @@ -124,7 +124,7 @@ Examples: - `app/admin/pages/` could contain all pages related to the backend admin section -### 9. Route Manifest {#9-relaxed-restrictions} +### 9. Route Manifest {#9-route-manifest} Next.js requires you to manually type out page locations. Blitz comes with a [Route Manifest](./route-manifest), so you can do: From 1bb9378131611b7ef745abe9083d993af7b76b45 Mon Sep 17 00:00:00 2001 From: JuanM04 <me@juanm04.com> Date: Sun, 25 Apr 2021 10:14:17 -0300 Subject: [PATCH 26/67] Updated dependencies --- package.json | 14 +-- yarn.lock | 254 +++++++++++++++++++++++++-------------------------- 2 files changed, 134 insertions(+), 134 deletions(-) diff --git a/package.json b/package.json index 3742b12..4412c50 100644 --- a/package.json +++ b/package.json @@ -18,18 +18,18 @@ }, "dependencies": { "@docsearch/react": "1.0.0-alpha.28", - "@octokit/rest": "18.5.2", + "@octokit/rest": "18.5.3", "@reach/rect": "0.15.0", "@sindresorhus/slugify": "2.0.0", "@visx/hierarchy": "1.7.0", "@visx/responsive": "1.7.0", - "blitz": "0.34.0", + "blitz": "0.34.3", "clsx": "1.1.1", "dlv": "1.1.3", "fathom-client": "3.0.0", "focus-visible": "5.2.0", - "framer-motion": "4.1.5", - "gray-matter": "4.0.2", + "framer-motion": "4.1.9", + "gray-matter": "4.0.3", "just-group-by": "1.0.0", "next-themes": "0.0.14", "prismjs": "1.23.0", @@ -54,20 +54,20 @@ "alex": "9.1.0", "autoprefixer": "10.2.5", "babel-plugin-preval": "5.0.0", - "eslint": "7.24.0", + "eslint": "7.25.0", "eslint-plugin-simple-import-sort": "7.0.0", "file-loader": "6.2.0", "glob": "7.1.6", "husky": "6.0.0", "lint-staged": "10.5.4", "minimatch": "3.0.4", - "postcss": "8.2.10", + "postcss": "8.2.12", "postcss-nested": "5.0.5", "prettier": "2.2.1", "pretty-quick": "3.1.0", "remark-admonitions": "1.2.1", "simple-functional-loader": "1.2.1", - "tailwindcss": "2.1.1" + "tailwindcss": "2.1.2" }, "private": true } diff --git a/yarn.lock b/yarn.lock index 744f9aa..87fc9dd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1566,21 +1566,21 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@blitzjs/babel-preset@0.34.0": - version "0.34.0" - resolved "https://registry.yarnpkg.com/@blitzjs/babel-preset/-/babel-preset-0.34.0.tgz#728f99aa2f85860f5d06fa2a6ab55727b99ca591" - integrity sha512-oFVh7oXb3qh8IAlNLjSS/NeXSnAZYDrXDjRz5C4t6wTIzGkGhbVFBXKX9Jb6mrGJCbWeNo667rmCRZhQB0NxNg== +"@blitzjs/babel-preset@0.34.3": + version "0.34.3" + resolved "https://registry.yarnpkg.com/@blitzjs/babel-preset/-/babel-preset-0.34.3.tgz#c3756c6bdeca540072ed3472ed1bb148834896af" + integrity sha512-A5kqm69btVWXQaeqA2qySRkSeKUw/SrW+iF5VE6hFDz/euINMKrRQiKxS3on3lotb1LBUP3FTF2iHHN6PSmq0g== dependencies: "@babel/helper-module-imports" "^7.0.0" babel-plugin-superjson-next "0.2.2" -"@blitzjs/cli@0.34.0": - version "0.34.0" - resolved "https://registry.yarnpkg.com/@blitzjs/cli/-/cli-0.34.0.tgz#f81e7d9bf0b8bf1d88d1cdeeb427462399a57c39" - integrity sha512-Qtq5Cyy8QcX0KCzCALaBIloRl00Lp/aaw4hPm5JF+dpzyqhcnmdhq4OonwU6ebx7IYg+Bg2OIPButGBKjHHb9Q== +"@blitzjs/cli@0.34.3": + version "0.34.3" + resolved "https://registry.yarnpkg.com/@blitzjs/cli/-/cli-0.34.3.tgz#4a40bdf0f955a261f85728e03f7f57d719df10ca" + integrity sha512-ZRS7sNPlZ8A1RqtAdgG9uhsstad28XZYCQum76tkPuh++T8qvMKJCyzGdRjX2ScyoD7Ny9HhujokxPdTVbq6Pg== dependencies: - "@blitzjs/display" "0.34.0" - "@blitzjs/repl" "0.34.0" + "@blitzjs/display" "0.34.3" + "@blitzjs/repl" "0.34.3" "@oclif/command" "1.8.0" "@oclif/config" "1.17.0" "@oclif/plugin-autocomplete" "0.3.0" @@ -1609,21 +1609,21 @@ tsconfig-paths "3.9.0" v8-compile-cache "2.2.0" -"@blitzjs/config@0.34.0": - version "0.34.0" - resolved "https://registry.yarnpkg.com/@blitzjs/config/-/config-0.34.0.tgz#72b1f48e3d1b036571635ee84f82f4dbd3afa957" - integrity sha512-7F1zyCeVQA6yr0GVbCK8/NCDCsddmpR8JKWK1SPperbmddVSPUbeHGdYGlF0xzGZdaBEU+uhofmYSRR/KchbQA== +"@blitzjs/config@0.34.3": + version "0.34.3" + resolved "https://registry.yarnpkg.com/@blitzjs/config/-/config-0.34.3.tgz#83604d63e9722a80ba3fa324e99dba359e6b658b" + integrity sha512-3MAmHK6Kh7XJIT6mCETUvRFalqJLkk1wCYBuFXLtzqpwry/bSgPHBzAlpzccgSys5zpjBvbftns2X3i1L9X2gQ== dependencies: fs-extra "^9.1.0" pkg-dir "^5.0.0" -"@blitzjs/core@0.34.0": - version "0.34.0" - resolved "https://registry.yarnpkg.com/@blitzjs/core/-/core-0.34.0.tgz#34f639d87f9db673c793df625d36184df8df4932" - integrity sha512-jt92Ui2vPWl/AGmIbPa91A7ULeLVrAntlDnSiqFkrMgd0LwmUOvAP4BAt4EXujiFei7emht49sAt5j2/Z3SyCA== +"@blitzjs/core@0.34.3": + version "0.34.3" + resolved "https://registry.yarnpkg.com/@blitzjs/core/-/core-0.34.3.tgz#8078ec3f9856450dd303a828782b7742bde93323" + integrity sha512-moheoboOMVxR5YGEpexjt5HxO1NyoY984uvSsqiEmQkoWz6Fk/HvU2tfwlj+ybNOkBr2XNU6pAzxbMVZflHdzw== dependencies: - "@blitzjs/config" "0.34.0" - "@blitzjs/display" "0.34.0" + "@blitzjs/config" "0.34.3" + "@blitzjs/display" "0.34.3" "@types/secure-password" "3.1.0" b64-lite "^1.4.0" bad-behavior "^1.0.1" @@ -1635,7 +1635,7 @@ jsonwebtoken "8.5.1" lodash.frompairs "4.0.1" nanoid "^3.1.20" - next "npm:@blitzjs/next@0.34.0" + next "npm:@blitzjs/next@10.1.3-0.34.3" npm-which "^3.0.1" null-loader "4.0.1" passport "0.4.1" @@ -1643,25 +1643,25 @@ secure-password "4.0.0" superjson "1.7.2" -"@blitzjs/display@0.34.0": - version "0.34.0" - resolved "https://registry.yarnpkg.com/@blitzjs/display/-/display-0.34.0.tgz#f46ed3d3774d0170eb52af71d4c89da09cac9e8f" - integrity sha512-+tHVyEe5umCnwlRoPqzryfZLuDqSORUXNiXP8z3Ufrpjo6hr4Yl0DxGbtcCz4TgBx91OvbVyDI68bkHFePj0Iw== +"@blitzjs/display@0.34.3": + version "0.34.3" + resolved "https://registry.yarnpkg.com/@blitzjs/display/-/display-0.34.3.tgz#9d17d16658c3d5576509356bccc33351e581ec4b" + integrity sha512-PvZphihfJKFmr5aVLQZgq/MEYDJFb2I8NmwNwNE8U+M4asctZACbiXeuRnjqN3JUultO8bk+ZyLos4vUr6nDcw== dependencies: - "@blitzjs/config" "0.34.0" - "@blitzjs/display" "0.34.0" + "@blitzjs/config" "0.34.3" + "@blitzjs/display" "0.34.3" chalk "^4.1.0" console-table-printer "^2.7.5" ora "^5.3.0" readline "1.3.0" tslog "^3.1.1" -"@blitzjs/file-pipeline@0.34.0": - version "0.34.0" - resolved "https://registry.yarnpkg.com/@blitzjs/file-pipeline/-/file-pipeline-0.34.0.tgz#c6c6543e84aae1213529c365c0ac3fcc88e71343" - integrity sha512-nyAT9zTy4xZQlXu4MLC72lbedUVuKQ0pbY/bJeFwcZ8kKHD4tX3YZNGvW5qgBVQPrnk2uUB9uaGnkGpTLfBq7g== +"@blitzjs/file-pipeline@0.34.3": + version "0.34.3" + resolved "https://registry.yarnpkg.com/@blitzjs/file-pipeline/-/file-pipeline-0.34.3.tgz#10c40e8906683fc5e936b27f04b981f7ad4c5cf7" + integrity sha512-AeSAypDniFuXC9xUbz07daFAbQWBIUzlz3M6tCWA2lWFBegPDciC/3xKeP/LCHjKqSVpA5tQcR2qVsoxM0R1Qg== dependencies: - "@blitzjs/display" "0.34.0" + "@blitzjs/display" "0.34.3" chalk "^4.1.0" chokidar "3.5.1" flush-write-stream "2.0.0" @@ -1680,14 +1680,14 @@ vinyl-file "3.0.0" vinyl-fs "3.0.3" -"@blitzjs/generator@0.34.0": - version "0.34.0" - resolved "https://registry.yarnpkg.com/@blitzjs/generator/-/generator-0.34.0.tgz#315c8b79e33df1a2cac66b2942d1343cc042d767" - integrity sha512-Bg1mXOMwqbH8yG4Gi7yT9DEOp34ryPs8WaeIi8tBOWf5ULXfz9Py5DQlsMfF3Lg8+yxzWV+xttQ6axueo2wasg== +"@blitzjs/generator@0.34.3": + version "0.34.3" + resolved "https://registry.yarnpkg.com/@blitzjs/generator/-/generator-0.34.3.tgz#0b75758bc5b8e319853f548a0df161621c418107" + integrity sha512-KlsroS8Tx0Dq1MkcfT3qBux90CgKFp2Gs4AHWy1ZYV/9Fiu3lw/d53sY1msEEB0ggIhXjlb+AFgE5FqtBG5+FA== dependencies: "@babel/core" "7.12.10" "@babel/plugin-transform-typescript" "7.12.1" - "@blitzjs/display" "0.34.0" + "@blitzjs/display" "0.34.3" "@types/jscodeshift" "0.7.2" chalk "^4.1.0" cross-spawn "7.0.3" @@ -1704,16 +1704,16 @@ username "^5.1.0" vinyl "2.2.1" -"@blitzjs/installer@0.34.0": - version "0.34.0" - resolved "https://registry.yarnpkg.com/@blitzjs/installer/-/installer-0.34.0.tgz#7731c5040c196c0a8c0717da11ae2a8c1208b3e0" - integrity sha512-fCuLnCcR1mM+FFoVwpdSU/jNZghS/7x0nmGAa3gx/RGN9Q7/quvqY1M6iR5iXRhxYKQGTXLWOWJDcYH4wmkbKQ== +"@blitzjs/installer@0.34.3": + version "0.34.3" + resolved "https://registry.yarnpkg.com/@blitzjs/installer/-/installer-0.34.3.tgz#51f33c855391c00fb1a5f417fb4818a4e3c00249" + integrity sha512-y7Od0YCj+ltwegNB0gmtjnLJk5e6uiqfHp8ZmpK/mnETIJXfXab/xXSJBhvyKcEvh0oO9GQC7v7ivGGouBL57g== dependencies: "@babel/core" "7.12.10" "@babel/plugin-transform-typescript" "7.12.1" - "@blitzjs/config" "0.34.0" - "@blitzjs/display" "0.34.0" - "@blitzjs/generator" "0.34.0" + "@blitzjs/config" "0.34.3" + "@blitzjs/display" "0.34.3" + "@blitzjs/generator" "0.34.3" "@types/jscodeshift" "0.7.2" cross-spawn "7.0.3" diff "5.0.0" @@ -1728,26 +1728,26 @@ recast "0.20.4" ts-node "^9.1.1" -"@blitzjs/repl@0.34.0": - version "0.34.0" - resolved "https://registry.yarnpkg.com/@blitzjs/repl/-/repl-0.34.0.tgz#a9f8244958d1b4b51d234db7e19c9b214661a186" - integrity sha512-xRIfxAzLP5nP3nRHm1WlEPiCaSvYsncuG6JAS9n2SpctwUZGbCORuSTLaLtUPhbNayOm4Qc3Z2sI22fkQRnxag== +"@blitzjs/repl@0.34.3": + version "0.34.3" + resolved "https://registry.yarnpkg.com/@blitzjs/repl/-/repl-0.34.3.tgz#403469358ec162077ab4dde91cf3be93bde060e2" + integrity sha512-XHj9DYPqc8KY+zEPcoTL9Nc4vups7Vj4Fz5v82o7i6SPXGuzuCsOJu528HwtX8Y5fO5EHJFNAp8bhr8cDML20g== dependencies: - "@blitzjs/config" "0.34.0" + "@blitzjs/config" "0.34.3" chokidar "3.5.1" globby "11.0.2" pkg-dir "^5.0.0" progress "^2.0.3" -"@blitzjs/server@0.34.0": - version "0.34.0" - resolved "https://registry.yarnpkg.com/@blitzjs/server/-/server-0.34.0.tgz#180b7fe7a7e49796165fc5bd3e51304a850f98b8" - integrity sha512-mgPgEwUDzvAL+HuyhGo0ns14dxamJxm84Fc1jswq/cXsL77BD2Ta7VH1GYGXDDp0rJMgDlc1KG3eZN4IGIJjFg== +"@blitzjs/server@0.34.3": + version "0.34.3" + resolved "https://registry.yarnpkg.com/@blitzjs/server/-/server-0.34.3.tgz#9e682b0b1ee6cf144a986bc4e1f268e0d248af56" + integrity sha512-FVmKHH9sawM/xTRtboUKRMehxeChTosD6Eyj/XxrvP+GAirgCKPLefttyjvwzJdw+y7h+RWHH5iZpxdQDXppDg== dependencies: - "@blitzjs/config" "0.34.0" - "@blitzjs/core" "0.34.0" - "@blitzjs/display" "0.34.0" - "@blitzjs/file-pipeline" "0.34.0" + "@blitzjs/config" "0.34.3" + "@blitzjs/core" "0.34.3" + "@blitzjs/display" "0.34.3" + "@blitzjs/file-pipeline" "0.34.3" cross-spawn "7.0.3" detect-port "1.3.0" expand-tilde "2.0.2" @@ -2374,10 +2374,10 @@ resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-2.0.1.tgz#7453d8281ce66b8ed1607f7ac7d751c3baffd2cc" integrity sha512-9AuC04PUnZrjoLiw3uPtwGh9FE4Q3rTqs51oNlQ0rkwgE8ftYsOC+lsrQyvCvWm85smBbSc0FNRKKumvGyb44Q== -"@octokit/openapi-types@^6.0.0": - version "6.0.0" - resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-6.0.0.tgz#7da8d7d5a72d3282c1a3ff9f951c8133a707480d" - integrity sha512-CnDdK7ivHkBtJYzWzZm7gEkanA7gKH6a09Eguz7flHw//GacPJLmkHA3f3N++MJmlxD1Fl+mB7B32EEpSCwztQ== +"@octokit/openapi-types@^6.1.1": + version "6.1.1" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-6.1.1.tgz#27f9386fcbcb9846b27b1bc8a41ba6f313c922a6" + integrity sha512-ICBhnEb+ahi/TTdNuYb/kTyKVBgAM0VD4k6JPzlhJyzt3Z+Tq/bynwCD+gpkJP7AEcNnzC8YO5R39trmzEo2UA== "@octokit/plugin-paginate-rest@^2.6.2": version "2.6.2" @@ -2391,12 +2391,12 @@ resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.2.tgz#394d59ec734cd2f122431fbaf05099861ece3c44" integrity sha512-oTJSNAmBqyDR41uSMunLQKMX0jmEXbwD1fpz8FG27lScV3RhtGfBa1/BBLym+PxcC16IBlF7KH9vP1BUYxA+Eg== -"@octokit/plugin-rest-endpoint-methods@5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.0.0.tgz#cf2cdeb24ea829c31688216a5b165010b61f9a98" - integrity sha512-Jc7CLNUueIshXT+HWt6T+M0sySPjF32mSFQAK7UfAg8qGeRI6OM1GSBxDLwbXjkqy2NVdnqCedJcP1nC785JYg== +"@octokit/plugin-rest-endpoint-methods@5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.0.1.tgz#631b8d4edc6798b03489911252a25f2a4e58c594" + integrity sha512-vvWbPtPqLyIzJ7A4IPdTl+8IeuKAwMJ4LjvmqWOOdfSuqWQYZXq2CEd0hsnkidff2YfKlguzujHs/reBdAx8Sg== dependencies: - "@octokit/types" "^6.13.0" + "@octokit/types" "^6.13.1" deprecation "^2.3.1" "@octokit/request-error@^2.0.0": @@ -2422,15 +2422,15 @@ once "^1.4.0" universal-user-agent "^6.0.0" -"@octokit/rest@18.5.2": - version "18.5.2" - resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.5.2.tgz#0369e554b7076e3749005147be94c661c7a5a74b" - integrity sha512-Kz03XYfKS0yYdi61BkL9/aJ0pP2A/WK5vF/syhu9/kY30J8He3P68hv9GRpn8bULFx2K0A9MEErn4v3QEdbZcw== +"@octokit/rest@18.5.3": + version "18.5.3" + resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.5.3.tgz#6a2e6006a87ebbc34079c419258dd29ec9ff659d" + integrity sha512-KPAsUCr1DOdLVbZJgGNuE/QVLWEaVBpFQwDAz/2Cnya6uW2wJ/P5RVGk0itx7yyN1aGa8uXm2pri4umEqG1JBA== dependencies: "@octokit/core" "^3.2.3" "@octokit/plugin-paginate-rest" "^2.6.2" "@octokit/plugin-request-log" "^1.0.2" - "@octokit/plugin-rest-endpoint-methods" "5.0.0" + "@octokit/plugin-rest-endpoint-methods" "5.0.1" "@octokit/types@^6.0.0", "@octokit/types@^6.0.1", "@octokit/types@^6.0.3": version "6.1.2" @@ -2440,12 +2440,12 @@ "@octokit/openapi-types" "^2.0.1" "@types/node" ">= 8" -"@octokit/types@^6.13.0": - version "6.13.0" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.13.0.tgz#779e5b7566c8dde68f2f6273861dd2f0409480d0" - integrity sha512-W2J9qlVIU11jMwKHUp5/rbVUeErqelCsO5vW5PKNb7wAXQVUz87Rc+imjlEvpvbH8yUb+KHmv8NEjVZdsdpyxA== +"@octokit/types@^6.13.1": + version "6.13.2" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.13.2.tgz#e3423dc733567ac4836e116b34d154a8e9cbbf3c" + integrity sha512-jN5LImYHvv7W6SZargq1UMJ3EiaqIz5qkpfsv4GAb4b16SGqctxtOU2TQAZxvsKHkOw2A4zxdsi5wR9en1/ezQ== dependencies: - "@octokit/openapi-types" "^6.0.0" + "@octokit/openapi-types" "^6.1.1" "@opentelemetry/api@0.14.0": version "0.14.0" @@ -3981,26 +3981,26 @@ bl@^4.0.3: inherits "^2.0.4" readable-stream "^3.4.0" -blitz@0.34.0: - version "0.34.0" - resolved "https://registry.yarnpkg.com/blitz/-/blitz-0.34.0.tgz#f5672c4f86fb5cac517687221220ddd0b6938372" - integrity sha512-+rQnOSfCCwuRxRALwi/tXpTUYXZvwW2J0nxhW3y1WvxZspBGSC5DKo+O5FBM/JgsqwrbQaBQTZUzYPx3Llvi4Q== - dependencies: - "@blitzjs/babel-preset" "0.34.0" - "@blitzjs/cli" "0.34.0" - "@blitzjs/config" "0.34.0" - "@blitzjs/core" "0.34.0" - "@blitzjs/display" "0.34.0" - "@blitzjs/generator" "0.34.0" - "@blitzjs/installer" "0.34.0" - "@blitzjs/server" "0.34.0" +blitz@0.34.3: + version "0.34.3" + resolved "https://registry.yarnpkg.com/blitz/-/blitz-0.34.3.tgz#27f84c53c4ec3b01a428b1b87e9823013ad95ae1" + integrity sha512-C0zNSrdAO+9L0yfX3bRU9T8orfhSQCdbjy46XW4GKNKotU7qHu0rpafJZX9hYf/0MEHmKXc+7yxByJpoH2+DiQ== + dependencies: + "@blitzjs/babel-preset" "0.34.3" + "@blitzjs/cli" "0.34.3" + "@blitzjs/config" "0.34.3" + "@blitzjs/core" "0.34.3" + "@blitzjs/display" "0.34.3" + "@blitzjs/generator" "0.34.3" + "@blitzjs/installer" "0.34.3" + "@blitzjs/server" "0.34.3" "@testing-library/jest-dom" "5.11.9" "@testing-library/react" "11.2.5" "@testing-library/react-hooks" "^4.0.1" "@types/jest" "26.0.20" chalk "^4.1.0" envinfo "^7.7.3" - eslint-config-blitz "0.34.0" + eslint-config-blitz "0.34.3" jest "^26.6.3" jest-watch-typeahead "^0.6.1" os-name "^4.0.0" @@ -5843,10 +5843,10 @@ escodegen@^2.0.0: optionalDependencies: source-map "~0.6.1" -eslint-config-blitz@0.34.0: - version "0.34.0" - resolved "https://registry.yarnpkg.com/eslint-config-blitz/-/eslint-config-blitz-0.34.0.tgz#8aa8c45b0ff25db80a8754cd2f6f8c57f708a452" - integrity sha512-Hz8F2ATZ3wce41iEZSVfk2PjBdFHO7qlnH34gBdIPb4CXPelXKHXIk5kUS6ne55qeWMlEm8xEzRR7zAZnDlW9A== +eslint-config-blitz@0.34.3: + version "0.34.3" + resolved "https://registry.yarnpkg.com/eslint-config-blitz/-/eslint-config-blitz-0.34.3.tgz#337a41187333944d2e03ed320a985d4dd8194e07" + integrity sha512-sYR8IcJSG4sag3FrlRRNtRPpNDxY5qSq1oiPjsCgN3rdbx7OcvuBMUsqSVXtoNh4PEgrltOXJVBcrwX7cVmwhA== dependencies: "@typescript-eslint/eslint-plugin" "4.17.0" "@typescript-eslint/parser" "4.17.0" @@ -5985,10 +5985,10 @@ eslint-visitor-keys@^2.0.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8" integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ== -eslint@7.24.0: - version "7.24.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.24.0.tgz#2e44fa62d93892bfdb100521f17345ba54b8513a" - integrity sha512-k9gaHeHiFmGCDQ2rEfvULlSLruz6tgfA8DEn+rY9/oYPFFTlz55mM/Q/Rij1b2Y42jwZiK3lXvNTw6w6TXzcKQ== +eslint@7.25.0: + version "7.25.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.25.0.tgz#1309e4404d94e676e3e831b3a3ad2b050031eb67" + integrity sha512-TVpSovpvCNpLURIScDRB6g5CYu/ZFq9GfX2hLNIV4dSBKxIWojeDODvYl3t0k0VtMxYeR8OXPCFE5+oHMlGfhw== dependencies: "@babel/code-frame" "7.12.11" "@eslint/eslintrc" "^0.4.0" @@ -6507,10 +6507,10 @@ fragment-cache@^0.2.1: dependencies: map-cache "^0.2.2" -framer-motion@4.1.5: - version "4.1.5" - resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-4.1.5.tgz#451ac3a41c682190bf247d5e209e24572e45cfc3" - integrity sha512-ExZ/BGKecRDs91W9ZebbCW5HgO8PaVT5V2ZUs28/jqLyef7VrTho0J5BRH/oAvwc9Qdnl0nRS/YRJWNOCt/PYQ== +framer-motion@4.1.9: + version "4.1.9" + resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-4.1.9.tgz#e4b237cb4cedb26002fb61fdbb64149a7cb3c422" + integrity sha512-s5vvzxlHk8sxvvFOr/jA7pON7elI4bvsqjmgeyvLZj0z58yzC94AGx09uB+wuzuWpKOoFwjgiD+ClcyMQ6WC9A== dependencies: framesync "5.3.0" hey-listen "^1.0.8" @@ -6868,12 +6868,12 @@ graceful-fs@^4.0.0, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2 resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== -gray-matter@4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/gray-matter/-/gray-matter-4.0.2.tgz#9aa379e3acaf421193fce7d2a28cebd4518ac454" - integrity sha512-7hB/+LxrOjq/dd8APlK0r24uL/67w7SkYnfwhNFwg/VDIGWGmduTDYf3WNstLW2fbbmRwrDGCVSJ2isuf2+4Hw== +gray-matter@4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/gray-matter/-/gray-matter-4.0.3.tgz#e893c064825de73ea1f5f7d88c7a9f7274288798" + integrity sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q== dependencies: - js-yaml "^3.11.0" + js-yaml "^3.13.1" kind-of "^6.0.2" section-matter "^1.0.0" strip-bom-string "^1.0.0" @@ -8450,14 +8450,6 @@ js-sha3@0.8.0: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-yaml@^3.11.0, js-yaml@^3.6.1: - version "3.14.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" - integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - js-yaml@^3.13.1: version "3.14.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" @@ -8466,6 +8458,14 @@ js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" +js-yaml@^3.6.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" @@ -9590,10 +9590,10 @@ next-themes@0.0.14: resolved "https://registry.yarnpkg.com/next-themes/-/next-themes-0.0.14.tgz#2b9861990bc453149e23d8e6ef1a25a119e36675" integrity sha512-x09OaM+wg3SIlEjOv8B21aw/E36jxTtfW3Dm/DPwMsSMluGt7twe1LigA6nc+mXP1u0qu9MxBaIrPPH6UTiKnA== -"next@npm:@blitzjs/next@0.34.0": - version "0.34.0" - resolved "https://registry.yarnpkg.com/@blitzjs/next/-/next-0.34.0.tgz#0094c8eedf22a0c0e8c65dafd25cced440a42732" - integrity sha512-rAqGufaov9OooexcNjcWN/zU51zdXEpthTlPATT+gVKYvA+ZJ/+AgT8YGZG4gSLN9VIY1wciiZTSgKhZrWM1wg== +"next@npm:@blitzjs/next@10.1.3-0.34.3": + version "10.1.3-0.34.3" + resolved "https://registry.yarnpkg.com/@blitzjs/next/-/next-10.1.3-0.34.3.tgz#6ae0a769b3740267c99b9ead7caeef02c210b328" + integrity sha512-imoVqfPnxgQ5aJ1v1PIfIlDRK8bKL6OhiHUJ3+WGtTaJDfyvXQEIa1M4MrZsEua92jV5/VUbRICp7erJRwGqrw== dependencies: "@babel/runtime" "7.12.5" "@hapi/accept" "5.0.1" @@ -10574,10 +10574,10 @@ postcss@8.1.7, postcss@^8.1.6: nanoid "^3.1.16" source-map "^0.6.1" -postcss@8.2.10: - version "8.2.10" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.10.tgz#ca7a042aa8aff494b334d0ff3e9e77079f6f702b" - integrity sha512-b/h7CPV7QEdrqIxtAf2j31U5ef05uBDuvoXv6L51Q4rcS1jdlXAVKJv+atCFdUXYl9dyTHGyoMzIepwowRJjFw== +postcss@8.2.12: + version "8.2.12" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.12.tgz#81248a1a87e0f575cc594a99a08207fd1c4addc4" + integrity sha512-BJnGT5+0q2tzvs6oQfnY2NpEJ7rIXNfBnZtQOKCIsweeWXBXeDd5k31UgTdS3d/c02ouspufn37mTaHWkJyzMQ== dependencies: colorette "^1.2.2" nanoid "^3.1.22" @@ -12587,10 +12587,10 @@ table@^6.0.4: slice-ansi "^4.0.0" string-width "^4.2.0" -tailwindcss@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-2.1.1.tgz#642f6038c9283a8e1454da34585b8b7c1a1e8877" - integrity sha512-zZ6axGqpSZOCBS7wITm/WNHkBzDt5CIZlDlx0eCVldwTxFPELCVGbgh7Xpb3/kZp3cUxOmK7bZUjqhuMrbN6xQ== +tailwindcss@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-2.1.2.tgz#29402bf73a445faedd03df6d3b177e7b52b7c4a1" + integrity sha512-T5t+wwd+/hsOyRw2HJuFuv0LTUm3MUdHm2DJ94GPVgzqwPPFa9XxX0KlwLWupUuiOUj6uiKURCzYPHFcuPch/w== dependencies: "@fullhuman/postcss-purgecss" "^3.1.3" bytes "^3.0.0" From 5ad43493cd4a6f6c5161e0c988f02cf1129664b1 Mon Sep 17 00:00:00 2001 From: mlabate <labate.mario@gmail.com> Date: Sun, 25 Apr 2021 20:00:20 +0200 Subject: [PATCH 27/67] Fix typo tutorial.mdx (#458) Typo removed --- app/pages/docs/tutorial.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/pages/docs/tutorial.mdx b/app/pages/docs/tutorial.mdx index 94e3792..d299d78 100644 --- a/app/pages/docs/tutorial.mdx +++ b/app/pages/docs/tutorial.mdx @@ -828,7 +828,7 @@ this! #### Now it’s time to add voting! First we need to open `app/choices/mutations/updateChoice.ts`, update the -zod schema, and add add a vote increment. +zod schema, and add a vote increment. ```diff const UpdateChoice = z From 2c987bbf1570847bc4688923d4f8c26e067eba82 Mon Sep 17 00:00:00 2001 From: JuanM04 <me@juanm04.com> Date: Tue, 27 Apr 2021 17:57:35 -0300 Subject: [PATCH 28/67] Admonitions --> Cards (#456) --- README.md | 21 +++++++ app/core/components/docs/Card.js | 44 ++++++++++++++ app/core/components/docs/Card.module.css | 30 ++++++++++ app/core/styles/utilities.css | 55 ------------------ app/pages/docs/api-routes.mdx | 12 ++-- app/pages/docs/cli-db.mdx | 14 ++--- app/pages/docs/cli-start.mdx | 8 +-- app/pages/docs/contributing.mdx | 20 +++---- app/pages/docs/database-overview.mdx | 8 +-- app/pages/docs/deploy-heroku.mdx | 6 +- app/pages/docs/deploy-vercel.mdx | 8 +-- app/pages/docs/get-static-props.mdx | 8 +-- app/pages/docs/image-optimization.mdx | 12 ++-- app/pages/docs/mutation-usage.mdx | 16 ++--- app/pages/docs/query-usage.mdx | 16 ++--- app/pages/docs/resolver-server-utilities.mdx | 12 ++-- app/pages/docs/rpc-specification.mdx | 12 ++-- app/pages/docs/static-html-export.mdx | 8 +-- app/pages/docs/templates.mdx | 8 +-- app/pages/docs/tutorial.mdx | 14 ++--- blitz.config.js | 29 ++-------- package.json | 1 - tailwind.config.js | 6 -- yarn.lock | 61 +------------------- 24 files changed, 193 insertions(+), 236 deletions(-) create mode 100644 app/core/components/docs/Card.js create mode 100644 app/core/components/docs/Card.module.css diff --git a/README.md b/README.md index ee04909..f7d345a 100644 --- a/README.md +++ b/README.md @@ -70,3 +70,24 @@ Example: ``` If you aren't totally sure how the slug should look like, or just want to automate the process, run `yarn english-slugify` + +## Cards + +You can use cards, like the one at the start of [_API Routes_](https://blitzjs.com/docs/api-routes) like this: + +```md +<Card type="info"> + +Unlike Next.js, your `api/` folder should be a sibling of `pages/` instead +of being nested inside. But `pages/api` is still supported for +compatibility with Next.js. + +</Card> +``` + +Properties: + +- `type`: `'caution' | 'info' | 'note'` +- `title`: `string` (optional) + +**Remember to leave an empty line** between the component tag and the content as shown in the example above. diff --git a/app/core/components/docs/Card.js b/app/core/components/docs/Card.js new file mode 100644 index 0000000..c09bc6a --- /dev/null +++ b/app/core/components/docs/Card.js @@ -0,0 +1,44 @@ +import clsx from "clsx" + +import styles from "./Card.module.css" + +/** + * @param {{type: 'caution' | 'info' | 'note', title: string, children: any}} + * @returns + */ +export function Card({type, title, children}) { + const defaultTitle = type[0].toUpperCase() + type.substr(1) + + return ( + <div + className={clsx( + styles.container, + type === "caution" + ? "bg-[#fdea69]" + : type === "info" + ? "bg-[#69c6fd]" + : type === "note" + ? "bg-blue-primary" + : undefined, + )} + > + <h5 className={styles.heading}> + <span className={styles.icon}> + <InfoIcon /> + </span> + {title || defaultTitle} + </h5> + <div className={styles.content}>{children}</div> + </div> + ) +} + +const InfoIcon = () => ( + <svg width={15} height={15} viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"> + <circle cx={7.5} cy={7.5} r={6.75} stroke="black" strokeWidth={1.5} strokeLinecap="round" /> + <path + d="M6.81226 4.27344H8.18774V5.91699L7.83179 8.94043H7.177L6.81226 5.91699V4.27344ZM6.84302 9.45898H8.15259V10.729H6.84302V9.45898Z" + fill="black" + /> + </svg> +) diff --git a/app/core/components/docs/Card.module.css b/app/core/components/docs/Card.module.css new file mode 100644 index 0000000..61bd0ff --- /dev/null +++ b/app/core/components/docs/Card.module.css @@ -0,0 +1,30 @@ +.container { + @apply p-5 mb-4 rounded-xl; + + a { + /* Remember to change this if you update the a (anchor, link) styles */ + @apply text-purple-light font-medium no-underline hover:underline; + } +} + +.heading { + @apply mt-0 mb-4 capitalize font-bold flex items-center text-black; +} + +.icon { + @apply inline-block align-middle mr-2; + + svg { + @apply block w-4 h-4 stroke-0; + } +} + +.content { + * { + @apply text-black; + } + + strong { + color: inherit !important; + } +} diff --git a/app/core/styles/utilities.css b/app/core/styles/utilities.css index f7961a9..43541d0 100644 --- a/app/core/styles/utilities.css +++ b/app/core/styles/utilities.css @@ -130,61 +130,6 @@ body.cursor-grabbing * { background-repeat: no-repeat; } -/* Like doing `!important`, but worse */ -.admonition.admonition.admonition { - @apply px-5 pt-5 pb-px rounded-xl text-black; - - a { - /* Remember to change this if you update the a (anchor, link) styles */ - @apply text-purple-light font-medium no-underline hover:underline; - } -} - -.admonition-content > *:first-child { - @apply mt-2; -} - -.admonition.admonition-caution { - @apply bg-supplementary-yellow; -} - -.admonition.admonition-info { - @apply bg-supplementary-blue; -} - -.admonition.admonition-note { - @apply bg-blue-primary; -} - -.admonition .admonition-heading { - @apply capitalize font-bold; -} - -.admonition .admonition-heading > * { - @apply flex items-center; - margin-top: 0; -} - -.admonition .admonition-icon { - @apply mr-2; -} - -.admonition .admonition-icon { - @apply mr-2; -} - -.dark .admonition .admonition-heading h5 { - @apply text-black; -} - -.dark .admonition .admonition-content.admonition-content { - @apply text-black; -} - -.dark .admonition .admonition-content.admonition-content strong { - color: inherit; -} - .topic-select .topic-select__placeholder, .topic-select .topic-select__menu-list, .topic-select .topic-select__single-value { diff --git a/app/pages/docs/api-routes.mdx b/app/pages/docs/api-routes.mdx index 76a1d75..83573a2 100644 --- a/app/pages/docs/api-routes.mdx +++ b/app/pages/docs/api-routes.mdx @@ -3,13 +3,13 @@ title: API Routes sidebar_label: API Routes --- -<!-- prettier-ignore-start --> -:::info -Unlike Next.js, your `api/` folder should be a sibling of `pages/` -instead of being nested inside. But `pages/api` is still supported for +<Card type="info"> + +Unlike Next.js, your `api/` folder should be a sibling of `pages/` instead +of being nested inside. But `pages/api` is still supported for compatibility with Next.js. -::: -<!-- prettier-ignore-end --> + +</Card> Any file inside an `api/` folder are accessible at a URL corresponding to its path inside `api/`. So `app/projects/api/webhook.ts` will be at diff --git a/app/pages/docs/cli-db.mdx b/app/pages/docs/cli-db.mdx index 643c3c6..b2cb5d8 100644 --- a/app/pages/docs/cli-db.mdx +++ b/app/pages/docs/cli-db.mdx @@ -3,13 +3,13 @@ title: blitz db sidebar_label: blitz db --- -<!-- prettier-ignore-start --> -:::caution -This command is now deprecated in favor of using the -`blitz prisma` command. The `blitz db seed` command still works until -Prisma finishes adding their native seeding functionatily. -::: -<!-- prettier-ignore-end --> +<Card type="caution"> + +This command is now deprecated in favor of using the `blitz prisma` +command. The `blitz db seed` command still works until Prisma finishes +adding their native seeding functionatily. + +</Card> ### `blitz db seed` {#blitz-db-seed} diff --git a/app/pages/docs/cli-start.mdx b/app/pages/docs/cli-start.mdx index 4a050b6..6010aae 100644 --- a/app/pages/docs/cli-start.mdx +++ b/app/pages/docs/cli-start.mdx @@ -17,11 +17,11 @@ Starts the Blitz production server. #### Examples -<!-- prettier-ignore-start --> -:::note +<Card type="note"> + Make sure to run `blitz build` before running `blitz start` -::: -<!-- prettier-ignore-end --> + +</Card> ```bash blitz start diff --git a/app/pages/docs/contributing.mdx b/app/pages/docs/contributing.mdx index ae928c2..b85185c 100644 --- a/app/pages/docs/contributing.mdx +++ b/app/pages/docs/contributing.mdx @@ -204,16 +204,16 @@ If they do, then locally run the one that fails and fix the issue. ### Testing Development Version of Blitz Inside and App {#testing-development-version-of-blitz} -<!-- prettier-ignore-start --> -:::info -Currently, to test the local dev version of Blitz, you can -**only** test an app inside the `blitz/examples/` folder. In there, the -blitz dependency will automatically use the local dev version. We mainly -use the `auth` and `store` example apps. We use them for development -testing and for blitz integration tests. You must also make sure you are -running `yarn dev` in the `blitz` folder at the same time. -::: -<!-- prettier-ignore-end --> +<Card type="info"> + +Currently, to test the local dev version of Blitz, you can **only** test +an app inside the `blitz/examples/` folder. In there, the blitz dependency +will automatically use the local dev version. We mainly use the `auth` and +`store` example apps. We use them for development testing and for blitz +integration tests. You must also make sure you are running `yarn dev` in +the `blitz` folder at the same time. + +</Card> For using in apps outside the repo, [yalc](https://github.com/whitecolor/yalc) should work, but hasn't been diff --git a/app/pages/docs/database-overview.mdx b/app/pages/docs/database-overview.mdx index 5ef504c..b5f670b 100644 --- a/app/pages/docs/database-overview.mdx +++ b/app/pages/docs/database-overview.mdx @@ -3,13 +3,13 @@ title: Database Overview sidebar_label: Overview --- -<!-- prettier-ignore-start --> -:::info +<Card type="info"> + If you are totally new to Databases, check out [Prisma's Data Guide](https://www.prisma.io/dataguide/) which covers the very large majority of everything you might want to know. -::: -<!-- prettier-ignore-end --> + +</Card> By default, Blitz uses Prisma 2 which is a strongly typed database client. diff --git a/app/pages/docs/deploy-heroku.mdx b/app/pages/docs/deploy-heroku.mdx index db3ac67..8375a24 100644 --- a/app/pages/docs/deploy-heroku.mdx +++ b/app/pages/docs/deploy-heroku.mdx @@ -3,8 +3,7 @@ title: Deploy to a Server on Heroku sidebar_label: To Heroku --- -<!-- prettier-ignore-start --> -:::info +<Card type="info"> This guide assumes the following: @@ -16,8 +15,7 @@ This guide assumes the following: 4. That deployments will be made with git and the [Heroku CLI](https://devcenter.heroku.com/articles/heroku-cli). -::: -<!-- prettier-ignore-end --> +</Card> **In Heroku:** diff --git a/app/pages/docs/deploy-vercel.mdx b/app/pages/docs/deploy-vercel.mdx index 77348ae..0c71e29 100644 --- a/app/pages/docs/deploy-vercel.mdx +++ b/app/pages/docs/deploy-vercel.mdx @@ -3,15 +3,15 @@ title: Deploy Serverless to Vercel sidebar_label: To Vercel --- -<!-- prettier-ignore-start --> -:::caution +<Card type="caution"> + We do not recommend deploying Blitz to Vercel. Vercel is a platform optimized for frontend deployments, not fullstack. While you can deploy to Vercel, most people run into too many issues and give up. Direct SQL database connections are one of the biggest issues. You have to pay for a very expensive database to support any amount of scale. -::: -<!-- prettier-ignore-end --> + +</Card> ## Serverless Peculiarities for SQL Databases {#peculiarities} diff --git a/app/pages/docs/get-static-props.mdx b/app/pages/docs/get-static-props.mdx index 9f4f34b..830227b 100644 --- a/app/pages/docs/get-static-props.mdx +++ b/app/pages/docs/get-static-props.mdx @@ -3,8 +3,8 @@ title: getStaticProps API sidebar_label: getStaticProps API --- -<!-- prettier-ignore-start --> -:::info +<Card type="info"> + `getStaticProps` should only be used for pages that do not require authentication. The statically generated pages will be publicly available via your server and via a CDN if you have one. Therefore they cannot @@ -13,8 +13,8 @@ contain any personal or sensitive data. Also, this is why there is no `req` and `res` objects for `getStaticProps`, because the pages are generated at build time, not at runtime. And at build time, there is no user http request to handle. -::: -<!-- prettier-ignore-end --> + +</Card> If you export an `async` function called `getStaticProps` from a page, Blitz will pre-render this page at build time using the props returned by diff --git a/app/pages/docs/image-optimization.mdx b/app/pages/docs/image-optimization.mdx index 288e299..a5ccc58 100644 --- a/app/pages/docs/image-optimization.mdx +++ b/app/pages/docs/image-optimization.mdx @@ -330,15 +330,15 @@ The image position when using `layout="fill"`. #### loading -<!-- prettier-ignore-start --> -:::caution -This property is only meant for advanced usage. Switching an image to -load with `eager` will normally **hurt performance**. +<Card type="caution"> + +This property is only meant for advanced usage. Switching an image to load +with `eager` will normally **hurt performance**. We recommend using the `priority` property instead, which properly loads the image eagerly for nearly all use cases. -::: -<!-- prettier-ignore-end --> + +</Card> The loading behavior of the image. Defaults to `lazy`. diff --git a/app/pages/docs/mutation-usage.mdx b/app/pages/docs/mutation-usage.mdx index 855f3ae..f1a21c6 100644 --- a/app/pages/docs/mutation-usage.mdx +++ b/app/pages/docs/mutation-usage.mdx @@ -40,14 +40,14 @@ function (props) { For complete details, see the [`useMutation` documentation](./use-mutation). -<!-- prettier-ignore-start --> -:::info 🤔 -You may be wondering how that can work since it's importing -server code into your component: At build time, the direct function import -is swapped out with a network call. So the query function code is never -included in your client code. -::: -<!-- prettier-ignore-end --> +<Card type="info" title="🤔"> + +You may be wondering how that can work since it's importing server code +into your component: At build time, the direct function import is swapped +out with a network call. So the query function code is never included in +your client code. + +</Card> ## Error Handling {#error-handling} diff --git a/app/pages/docs/query-usage.mdx b/app/pages/docs/query-usage.mdx index 87463b1..6bb024b 100644 --- a/app/pages/docs/query-usage.mdx +++ b/app/pages/docs/query-usage.mdx @@ -31,14 +31,14 @@ function App() { For complete details, see the [`useQuery` documentation](./use-query). -<!-- prettier-ignore-start --> -:::info 🤔 -You may be wondering how that can work since it's importing -server code into your component: At build time, the direct function import -is swapped out with a network call. So the query function code is never -included in your client code. -::: -<!-- prettier-ignore-end --> +<Card type="info" title="🤔"> + +You may be wondering how that can work since it's importing server code +into your component: At build time, the direct function import is swapped +out with a network call. So the query function code is never included in +your client code. + +</Card> ### Defaults to Keep in Mind {#defaults-to-keep-in-mind} diff --git a/app/pages/docs/resolver-server-utilities.mdx b/app/pages/docs/resolver-server-utilities.mdx index 90c17b4..f73835e 100644 --- a/app/pages/docs/resolver-server-utilities.mdx +++ b/app/pages/docs/resolver-server-utilities.mdx @@ -78,15 +78,15 @@ export default resolver.pipe( ) ``` -<!-- prettier-ignore-start --> -:::info -The input type of the entire composed resolver function is -determined by the input type of the **first** argument to pipe. +<Card type="info"> + +The input type of the entire composed resolver function is determined by +the input type of the **first** argument to pipe. This means you will almost always want `resolver.zod()` to be the first in the pipe. -::: -<!-- prettier-ignore-end --> + +</Card> ### API {#api} diff --git a/app/pages/docs/rpc-specification.mdx b/app/pages/docs/rpc-specification.mdx index d453efa..1aced09 100644 --- a/app/pages/docs/rpc-specification.mdx +++ b/app/pages/docs/rpc-specification.mdx @@ -4,18 +4,18 @@ title: RPC Specification sidebar_label: RPC Specification --- -<!-- prettier-ignore-start --> -:::caution -This is documentation for using Blitz queries and mutations in -a non-Blitz app. For example, from a mobile application or some other app. +<Card type="caution"> + +This is documentation for using Blitz queries and mutations in a non-Blitz +app. For example, from a mobile application or some other app. **For using queries and mutations in your app, refer to [Using Queries](./query-usage) and [Using Mutations](./mutation-usage).** **If you want to access your app e.g. as an external API, take a look at [API Routes](./api-routes) instead.** -::: -<!-- prettier-ignore-end --> + +</Card> ## URL {#url} diff --git a/app/pages/docs/static-html-export.mdx b/app/pages/docs/static-html-export.mdx index efd2c9d..ce2ca2d 100644 --- a/app/pages/docs/static-html-export.mdx +++ b/app/pages/docs/static-html-export.mdx @@ -20,14 +20,14 @@ route. server-side or incremental data requirements (though statically-rendered pages can still fetch data on the client side normally). -<!-- prettier-ignore-start --> -:::info +<Card type="info"> + If you're looking to make a hybrid site where only some pages are prerendered to static HTML, Blitz already does that automatically for you! If your page doesn't use `getServerSideProps`, then it will be a static HTML page. -::: -<!-- prettier-ignore-end --> + +</Card> `blitz export` also causes features like [Incremental Static Generation and Regeneration](get-static-props#incremental-static-regeneration) diff --git a/app/pages/docs/templates.mdx b/app/pages/docs/templates.mdx index ad7064e..1e46303 100644 --- a/app/pages/docs/templates.mdx +++ b/app/pages/docs/templates.mdx @@ -43,14 +43,14 @@ If you access a variable that _isn't_ a template value it is ignored, allowing functionality like dead code elimination via `process.env.NODE_ENV` into template to work properly. -:::info +<Card type="info"> The conditions are not dynamically evaluated, they're only checked for truthiness. This means that for dynamic evaluation you'll need to do the check ahead of time and pass it into the template as context, a check like `if (process.env.componentName === 'SomeComp')` will not work properly. -::: +</Card> ```typescript // Input @@ -123,13 +123,13 @@ prop on the `if` statement is read out of the template context. If its value is truthy the first child of `<if />` will be rendered, otherwise the contents of the `<else />` element will be rendered. -:::info +<Card type="info"> Both `<if />` and `<else />` only accept a **single child component.** You will see unexpected behavior and errors if you try to provide a fragment or multiple elements. -::: +</Card> ```tsx // input diff --git a/app/pages/docs/tutorial.mdx b/app/pages/docs/tutorial.mdx index d299d78..4da4539 100644 --- a/app/pages/docs/tutorial.mdx +++ b/app/pages/docs/tutorial.mdx @@ -401,13 +401,13 @@ undefined ## Update generated code for our model attributes {#update-generated-code-for-our-model-attributes} -<!-- prettier-ignore-start --> -:::info -Before running the app again, we need to customize some of the -code that has been generated. Ultimately, these fixes will not be needed - -but for now, we need to work around a couple outstanding issues. -::: -<!-- prettier-ignore-end --> +<Card type="info"> + +Before running the app again, we need to customize some of the code that +has been generated. Ultimately, these fixes will not be needed - but for +now, we need to work around a couple outstanding issues. + +</Card> The generated page content does not currently use the actual model attributes you defined during generation. It will soon, but in the diff --git a/blitz.config.js b/blitz.config.js index 105ea25..eb06b31 100644 --- a/blitz.config.js +++ b/blitz.config.js @@ -11,7 +11,6 @@ const minimatch = require("minimatch") const withBundleAnalyzer = require("@next/bundle-analyzer")({ enabled: process.env.ANALYZE === "true", }) -const admonitions = require("remark-admonitions") const fallbackDefaultExports = { // Have to use compiled locations @@ -90,29 +89,7 @@ module.exports = withBundleAnalyzer({ { loader: "@mdx-js/loader", options: { - remarkPlugins: [ - withProse, - withTableOfContents, - withSyntaxHighlighting, - withBlitzLinks, - [ - admonitions, - { - customTypes: { - caution: { - keyword: "caution", - svg: - '<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="7.5" cy="7.5" r="6.75" stroke="black" stroke-width="1.5" stroke-linecap="round"/><path d="M6.81226 4.27344H8.18774V5.91699L7.83179 8.94043H7.177L6.81226 5.91699V4.27344ZM6.84302 9.45898H8.15259V10.729H6.84302V9.45898Z" fill="black"/></svg>', - }, - info: { - keyword: "info", - svg: - '<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="7.5" cy="7.5" r="6.75" stroke="black" stroke-width="1.5" stroke-linecap="round"/><path d="M6.81226 4.27344H8.18774V5.91699L7.83179 8.94043H7.177L6.81226 5.91699V4.27344ZM6.84302 9.45898H8.15259V10.729H6.84302V9.45898Z" fill="black"/></svg>', - }, - }, - }, - ], - ], + remarkPlugins: [withProse, withTableOfContents, withSyntaxHighlighting, withBlitzLinks], }, }, createLoader(function (source) { @@ -141,6 +118,10 @@ module.exports = withBundleAnalyzer({ } } + if (/^<\/Card>$/m.test(source)) { + extra.push(`import { Card } from '@/components/docs/Card'`) + } + return [ ...(typeof fields === "undefined" ? extra : []), typeof fields === "undefined" ? body : "", diff --git a/package.json b/package.json index 4412c50..a26a4dc 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,6 @@ "postcss-nested": "5.0.5", "prettier": "2.2.1", "pretty-quick": "3.1.0", - "remark-admonitions": "1.2.1", "simple-functional-loader": "1.2.1", "tailwindcss": "2.1.2" }, diff --git a/tailwind.config.js b/tailwind.config.js index d0de892..e97669d 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -79,12 +79,6 @@ module.exports = { string: "#A2F679", highlight: "rgba(134, 239, 172, 0.25)", }, - - supplementary: { - yellow: "#FDEA69", - blue: "#69C6FD", - red: "#FF003D", - }, }, fontSize: { xxs: "0.75rem", // 12px diff --git a/yarn.lock b/yarn.lock index 87fc9dd..9146fdd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4405,7 +4405,7 @@ caseless@~0.12.0: resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= -ccount@^1.0.0, ccount@^1.0.3: +ccount@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.1.0.tgz#246687debb6014735131be8abab2d93898f8d043" integrity sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg== @@ -7049,17 +7049,6 @@ hast-util-embedded@^1.0.0: dependencies: hast-util-is-element "^1.1.0" -hast-util-from-parse5@^5.0.0: - version "5.0.3" - resolved "https://registry.yarnpkg.com/hast-util-from-parse5/-/hast-util-from-parse5-5.0.3.tgz#3089dc0ee2ccf6ec8bc416919b51a54a589e097c" - integrity sha512-gOc8UB99F6eWVWFtM9jUikjN7QkWxB3nY0df5Z0Zq1/Nkwl5V4hAAsl0tmwlgWl/1shlTF8DnNYLO8X6wRV9pA== - dependencies: - ccount "^1.0.3" - hastscript "^5.0.0" - property-information "^5.0.0" - web-namespaces "^1.1.2" - xtend "^4.0.1" - hast-util-from-parse5@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/hast-util-from-parse5/-/hast-util-from-parse5-6.0.1.tgz#554e34abdeea25ac76f5bd950a1f0180e0b3bc2a" @@ -7156,16 +7145,6 @@ hast-util-whitespace@^1.0.0: resolved "https://registry.yarnpkg.com/hast-util-whitespace/-/hast-util-whitespace-1.0.4.tgz#e4fe77c4a9ae1cb2e6c25e02df0043d0164f6e41" integrity sha512-I5GTdSfhYfAPNztx2xJRQpG8cuDSNt599/7YUn7Gx/WxNMsG+a835k97TDkFgk123cwjfwINaZknkKkphx/f2A== -hastscript@^5.0.0: - version "5.1.2" - resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-5.1.2.tgz#bde2c2e56d04c62dd24e8c5df288d050a355fb8a" - integrity sha512-WlztFuK+Lrvi3EggsqOkQ52rKbxkXL3RwB6t5lwoa8QLMemoWfBuL43eDrwOamJyR7uKQKdmKYaBH1NZBiIRrQ== - dependencies: - comma-separated-tokens "^1.0.0" - hast-util-parse-selector "^2.0.0" - property-information "^5.0.0" - space-separated-tokens "^1.0.0" - hastscript@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-6.0.0.tgz#e8768d7eac56c3fdeac8a92830d58e811e5bf640" @@ -10309,11 +10288,6 @@ parse5@6.0.1, parse5@^6.0.0: resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== -parse5@^5.0.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178" - integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug== - pascalcase@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" @@ -11239,15 +11213,6 @@ regjsparser@^0.6.4: dependencies: jsesc "~0.5.0" -rehype-parse@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/rehype-parse/-/rehype-parse-6.0.2.tgz#aeb3fdd68085f9f796f1d3137ae2b85a98406964" - integrity sha512-0S3CpvpTAgGmnz8kiCyFLGuW5yA4OQhyNTm/nwPopZ7+PI11WnGl1TTWTGv/2hPEe/g2jRLlhVVSsoDH8waRug== - dependencies: - hast-util-from-parse5 "^5.0.0" - parse5 "^5.0.0" - xtend "^4.0.0" - rehype-parse@^7.0.0: version "7.0.1" resolved "https://registry.yarnpkg.com/rehype-parse/-/rehype-parse-7.0.1.tgz#58900f6702b56767814afc2a9efa2d42b1c90c57" @@ -11263,15 +11228,6 @@ rehype-retext@^2.0.1: dependencies: hast-util-to-nlcst "^1.0.0" -remark-admonitions@1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/remark-admonitions/-/remark-admonitions-1.2.1.tgz#87caa1a442aa7b4c0cafa04798ed58a342307870" - integrity sha512-Ji6p68VDvD+H1oS95Fdx9Ar5WA2wcDA4kwrrhVU7fGctC6+d3uiMICu7w7/2Xld+lnU7/gi+432+rRbup5S8ow== - dependencies: - rehype-parse "^6.0.2" - unified "^8.4.2" - unist-util-visit "^2.0.1" - remark-footnotes@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/remark-footnotes/-/remark-footnotes-2.0.0.tgz#9001c4c2ffebba55695d2dd80ffb8b82f7e6303f" @@ -13241,17 +13197,6 @@ unified@9.2.0, unified@^9.0.0: trough "^1.0.0" vfile "^4.0.0" -unified@^8.4.2: - version "8.4.2" - resolved "https://registry.yarnpkg.com/unified/-/unified-8.4.2.tgz#13ad58b4a437faa2751a4a4c6a16f680c500fff1" - integrity sha512-JCrmN13jI4+h9UAyKEoGcDZV+i1E7BLFuG7OsaDvTXI5P0qhHX+vZO/kOhz9jn8HGENDKbwSeB0nVOg4gVStGA== - dependencies: - bail "^1.0.0" - extend "^3.0.0" - is-plain-obj "^2.0.0" - trough "^1.0.0" - vfile "^4.0.0" - union-value@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" @@ -13350,7 +13295,7 @@ unist-util-visit-parents@^3.0.0: "@types/unist" "^2.0.0" unist-util-is "^4.0.0" -unist-util-visit@2.0.3, unist-util-visit@^2.0.0, unist-util-visit@^2.0.1: +unist-util-visit@2.0.3, unist-util-visit@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-2.0.3.tgz#c3703893146df47203bb8a9795af47d7b971208c" integrity sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q== @@ -13714,7 +13659,7 @@ wcwidth@^1.0.1: dependencies: defaults "^1.0.3" -web-namespaces@^1.0.0, web-namespaces@^1.1.2: +web-namespaces@^1.0.0: version "1.1.4" resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-1.1.4.tgz#bc98a3de60dadd7faefc403d1076d529f5e030ec" integrity sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw== From a67a535d5c192c1b0272dccf5991ddf2312236b9 Mon Sep 17 00:00:00 2001 From: Tom MacWright <tmcw@users.noreply.github.com> Date: Tue, 27 Apr 2021 13:58:15 -0700 Subject: [PATCH 29/67] Recommend customTsParser for building a new recipe (#455) --- app/pages/docs/writing-recipes.mdx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/pages/docs/writing-recipes.mdx b/app/pages/docs/writing-recipes.mdx index 301ae6a..2dbb341 100644 --- a/app/pages/docs/writing-recipes.mdx +++ b/app/pages/docs/writing-recipes.mdx @@ -187,11 +187,15 @@ testing, the tests are quick to write: ```typescript import j from "jscodeshift" +import { customTsParser } from "@blitzjs/installer"; + const sampleFile = `export default function Comp() { return <div>hello!</div>; })` -expect(addImport(j(sampleFile), newImport).toSource()).toMatchSnapshot() +expect(addImport(j(sampleFile, { + parser: customTsParser +}), newImport).toSource()).toMatchSnapshot() ``` #### Modifying Non-JS files From d21543d3654b286e15f95bf80c58ac03fff73ecf Mon Sep 17 00:00:00 2001 From: Muhammad Ubaid Raza <mubaidr@gmail.com> Date: Wed, 28 Apr 2021 01:58:57 +0500 Subject: [PATCH 30/67] add info for proxy-support https://github.com/blitz-js/blitz/pull/2264 (#454) Co-authored-by: Brandon Bayer <b@bayer.ws> --- app/pages/docs/blitz-config.mdx | 19 +++++++++++++++++++ app/pages/docs/get-started.mdx | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/app/pages/docs/blitz-config.mdx b/app/pages/docs/blitz-config.mdx index f389057..bf6d511 100644 --- a/app/pages/docs/blitz-config.mdx +++ b/app/pages/docs/blitz-config.mdx @@ -129,6 +129,8 @@ module.exports = { ## CLI {#cli} +### Clear Console On Blitz Dev {#clear-console-on-blitz-dev} + When you run `blitz dev`, the console gets cleared. You can disable it by setting the `cli.clearConsoleOnBlitzDev` to `false`: @@ -140,6 +142,23 @@ module.exports = { } ``` +### Proxy support {#proxy-support} + +Proxy support is enabled automatically for recipe install if either `http_proxy` or `https_proxy` +environment variable is present. You can also set proxy using: + +```js +module.exports = { + cli: { + httpProxy: "http://127.0.0.1:8080", + httpsProxy: "http://127.0.0.1:8080", + noProxy: "localhost", + }, +} +``` + +Please note that proxy configuration in `blitz.config.js` will override environment proxy configuration. + ## CDN Support with Asset Prefix {#cdn-support-with-asset-prefix} To set up a [CDN](https://en.wikipedia.org/wiki/Content_delivery_network), diff --git a/app/pages/docs/get-started.mdx b/app/pages/docs/get-started.mdx index 92684ce..dffe27a 100644 --- a/app/pages/docs/get-started.mdx +++ b/app/pages/docs/get-started.mdx @@ -9,7 +9,7 @@ You need Node.js 12 or newer ### Install Blitz {#install-blitz} -Run `npm install -g blitz` +Run `yarn global add blitz` or `npm install -g blitz --legacy-peer-deps` (legacy-peer-deps is needed because npm totally change behavior of peer deps and some dependencies haven't caught up) ### Create a New App {#create-a-new-app} From 8fdf72128e8d17b8b0b4620e4140f506d0662cef Mon Sep 17 00:00:00 2001 From: Jeremy Liberman <jeremy@jeremyliberman.com> Date: Tue, 27 Apr 2021 15:59:49 -0500 Subject: [PATCH 31/67] Documentation for new schema.prisma transform utilities (#457) Co-authored-by: Brandon Bayer <b@bayer.ws> --- app/pages/docs/custom-server.mdx | 55 +++++----- app/pages/docs/writing-recipes.mdx | 163 +++++++++++++++++++++++++++++ 2 files changed, 193 insertions(+), 25 deletions(-) diff --git a/app/pages/docs/custom-server.mdx b/app/pages/docs/custom-server.mdx index 77a8e1a..aaf3f14 100644 --- a/app/pages/docs/custom-server.mdx +++ b/app/pages/docs/custom-server.mdx @@ -8,7 +8,8 @@ some other direct control over the web server itself. ### How {#how} -1. Add a `server.js` file in your project root. See below for an example. +1. Add a `server.ts` or `server.js` file in your project root. See below + for an example. 2. Now `blitz dev` and `blitz start` will automatically detect and use your custom server. @@ -16,12 +17,14 @@ some other direct control over the web server itself. Here's an example custom server: -```js -// server.js -const {createServer} = require("http") -const {parse} = require("url") -const blitz = require("blitz/custom-server") +```ts +// server.ts +import blitz from "blitz/custom-server" +import {createServer} from "http" +import {parse} from "url" +import {log} from "@blitzjs/display" +const {PORT = "3000"} = process.env const dev = process.env.NODE_ENV !== "production" const app = blitz({dev}) const handle = app.getRequestHandler() @@ -30,34 +33,36 @@ app.prepare().then(() => { createServer((req, res) => { // Be sure to pass `true` as the second argument to `url.parse`. // This tells it to parse the query portion of the URL. - const parsedUrl = parse(req.url, true) - const {pathname, query} = parsedUrl + const parsedUrl = parse(req.url!, true) + const {pathname} = parsedUrl + + if (pathname === "/hello") { + res.writeHead(200).end("world") + return + } - if (pathname === "/a") { + if (pathname === "/hello") { + res.writeHead(200).end("world") + return + } else if (pathname === "/a") { app.render(req, res, "/a", query) } else if (pathname === "/b") { app.render(req, res, "/b", query) } else { handle(req, res, parsedUrl) } - }).listen(3000, (err) => { - if (err) throw err - console.log("> Ready on http://localhost:3000") + + handle(req, res, parsedUrl) + }).listen(PORT, () => { + log.success(`Ready on http://localhost:${PORT}`) }) }) ``` -> `server.js` doesn't go through babel or webpack. Make sure the syntax -> and sources this file requires are compatible with the current node -> version you are running. - --- -The custom server uses the following import to connect the server with the -Blitz application: - -The above `blitz` import is a function that receives an object with the -following options: +The above `blitz/custom-server` import is a function that receives an +object with the following options: - `dev`: `Boolean` - Whether or not to launch Blitz in dev mode. Defaults to `false` @@ -72,10 +77,10 @@ required. ## Disabling file-system routing {#disabling-file-system-routing} -By default, `blitz` will serve each file in the `pages` folder under a -pathname matching the filename. If your project uses a custom server, this -behavior may result in the same content being served from multiple paths, -which can present problems with SEO and UX. +By default, `blitz` will serve each file `pages` folders under a pathname +matching the filename. If your project uses a custom server, this behavior +may result in the same content being served from multiple paths, which can +present problems with SEO and UX. To disable this behavior and prevent routing based on files in `pages`, open `blitz.config.js` and disable the `useFileSystemPublicRoutes` config: diff --git a/app/pages/docs/writing-recipes.mdx b/app/pages/docs/writing-recipes.mdx index 2dbb341..d0e654e 100644 --- a/app/pages/docs/writing-recipes.mdx +++ b/app/pages/docs/writing-recipes.mdx @@ -216,6 +216,169 @@ builder This step would append "Paul Plain was here!" to the user's README.md +#### Modifying Prisma schemas + +A lot of recipes may need to add or modify models in the schema.prisma +file in order to apply the recipe's effects. You can attempt to manipulate +the schema using plain string transformations in `transformPlain`, but a +lot of recipes may require the ability to query something about the schema +first. For example, to determine the model on the other end of a +[Relation](https://www.prisma.io/docs/concepts/components/prisma-schema/relations/), +or to detect if a field already exists. + +For your convenience, there are several pre-written utilities for common +schema modifications: + +##### Create an Enum + +```ts +singleFileSearch: paths.prismaSchema(), +transformPlain(source: string) { + // Create the enum Role with two values, USER and ADMIN + return addPrismaEnum(source, { + type: "enum", + name: "Role", + enumerators: [ + {type: "enumerator", name: "USER"}, + {type: "enumerator", name: "ADMIN"}, + ], + }) +} +``` + +##### Add a Field to a Model + +```ts +singleFileSearch: paths.prismaSchema(), +transformPlain(source: string) { + // Add a field "name String @unique" to the "Project" model + return addPrismaField(source, "Project", { + type: "field", + name: "name", + fieldType: "String", + optional: false, + attributes: [{type: "attribute", kind: "field", name: "unique"}], + }) +} +``` + +##### Create a Generator + +```ts +singleFileSearch: paths.prismaSchema(), +transformPlain(source: string) { + // Create a prisma generator for nexus-prisma + return addPrismaGenerator(source, { + type: "generator", + name: "nexusPrisma", + assignments: [ + {type: "assignment", key: "provider", value: '"nexus-prisma"'}, + ], + }) +} +``` + +##### Create a Model + +```ts +singleFileSearch: paths.prismaSchema(), +transformPlain(source: string) { + // Create a prisma model called Project + return addPrismaModel(source, { + type: "model", + name: "Project", + properties: [{type: "field", name: "name", fieldType: "String"}], + }) +} +``` + +##### Add an Attribute such as an Index to a Model + +```ts +singleFileSearch: paths.prismaSchema(), +transformPlain(source: string) { + // Creates an index attribute on the Project model "@@index([name])" + return addPrismaModelAttribute(source, "Project", { + type: "attribute", + kind: "model", + name: "index", + args: [ + {type: "attributeArgument", value: {type: "array", args: ["name"]}}, + ], + }) +} +``` + +##### Set the schema's Datasource + +Since a prisma schema can only have one data source, there is no "add" +utility, this will replace the schema's current data source. + +```ts +singleFileSearch: paths.prismaSchema(), +transformPlain(source: string) { + // Set the datasource to postgresql + return setPrismaDataSource(source, { + type: "datasource", + name: "db", + assignments: [ + {type: "assignment", key: "provider", value: '"postgresql"'}, + { + type: "assignment", + key: "url", + value: {type: "function", name: "env", params: ['"DATABASE_URL"']}, + }, + ], + }) +} +``` + +##### Custom Schema Transformations with produceSchema + +If the provided helpers aren't flexible enough for your recipe, you can +use the `produceSchema` utility function to parse the prisma schema file +and apply custom transformations. It will convert the schema into a JSON +object format that you can modify using JavaScript, then print the schema +(with your changes applied) back out to a string. All of the above helpers +are implemented using `produceSchema`. + +```ts +builder.addTransformFilesStep({ + ..., + singleFileSearch: paths.prismaSchema(), + transformPlain(source: string) { + return produceSchema(source, (schema) => { + // find a model named "User" + const model = schema.list.find(function(item): item is Model { + return item.type === "model" && item.name === "User" + }) as Model + if (!model) return + + // find a field on the "User" model named "email" + const field = model.properties.find(function (property): property is Field { + return property.type === "attribute" && property.name === "email" + }) + if (!field) return + + // add the "@unique" attribute to "email" + field.attributes?.push({ + type: "attribute", + kind: "field", + name: "unique" + }) + }) + } +}) +``` + +It is a best practice for schema transformations to be idempotent, meaning +that the function should not attempt to make a change to the schema if +that change already has been made. For instance, do not add a field to a +model if that field already exists. + +To see how the schema file is parsed, +[click here](https://github.com/MrLeebo/prisma-ast). + ### Publishing {#publishing} That's all you need to build a recipe! At this point, you can commit and From a52e32decbd679d6b1ffdb0ddd14ca8f6c3c21bb Mon Sep 17 00:00:00 2001 From: Antony <tony.kamp@web.de> Date: Tue, 27 Apr 2021 23:00:41 +0200 Subject: [PATCH 32/67] Extend "run postgres via docker" section (#460) --- app/pages/docs/postgres.mdx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/pages/docs/postgres.mdx b/app/pages/docs/postgres.mdx index 5b509b8..3f2e54c 100644 --- a/app/pages/docs/postgres.mdx +++ b/app/pages/docs/postgres.mdx @@ -68,7 +68,8 @@ POSTGRES_PASSWORD=your_password POSTGRES_DB=your_database_name ``` -Given these values your `DATABASE_URL` should look like this +Given these values, update `DATABASE_URL` in `.env.local` to something +similar like `postgresql://your_user:your_password@localhost:5432/your_database_name` 3. Modify your `package.json` to start the database before Blitz @@ -80,5 +81,6 @@ Given these values your `DATABASE_URL` should look like this } ``` -4. Run `blitz prisma migrate dev` to get your new database to the latest - version of your migrations +4. Start your new database and get it to the latest version of your + migrations by running `docker-compose up -d` and + `blitz prisma migrate dev`. From 63d81edb440b8a4fe06b5875d3ca219f6625f27f Mon Sep 17 00:00:00 2001 From: Brandon Bayer <b@bayer.ws> Date: Wed, 28 Apr 2021 17:25:22 -0400 Subject: [PATCH 33/67] add multitenancy doc page --- app/core/navs/documentation.json | 2 +- app/pages/docs/multitenancy.mdx | 360 +++++++++++++++++++++++++++++++ 2 files changed, 361 insertions(+), 1 deletion(-) create mode 100644 app/pages/docs/multitenancy.mdx diff --git a/app/core/navs/documentation.json b/app/core/navs/documentation.json index 2006562..0979214 100644 --- a/app/core/navs/documentation.json +++ b/app/core/navs/documentation.json @@ -126,7 +126,7 @@ "iconPath": "/img/mutations.svg", "iconDarkPath": "/img/mutations-white.svg" }, - "pages": ["background-processing-with-quirrel", "middleware", "custom-server"] + "pages": ["multitenancy", "background-processing-with-quirrel", "middleware", "custom-server"] }, { "title": { diff --git a/app/pages/docs/multitenancy.mdx b/app/pages/docs/multitenancy.mdx new file mode 100644 index 0000000..d716ead --- /dev/null +++ b/app/pages/docs/multitenancy.mdx @@ -0,0 +1,360 @@ +--- +title: Multitenancy +sidebar_label: Multitenancy +--- + +Multitenancy is a software architecture where a single app can server +multiple different users or organizations whose data is kept private from +the other users of the system. One way to do this is have a totally +separate database for each user, but that has a high operations overhead. +The most common way is to store all user data in a single database and use +foreign keys to keep data private. + +This guide shows you a good way to implement a multitenant Blitz app. + +## Data Model {#data-model} + +We recommend implementing the data model +[as described by Andrew Culver of Bullet Train](https://blog.bullettrain.co/teams-should-be-an-mvp-feature/). + +- The `Organization` is the "God" model which owns everything for an + account +- An `Organization` **has many** `User`s **through** `Membership` +- Every other model in the system has an `organizationId` to indicate who + owns it. +- A `User` can have access to multiple `Organization`s +- When assigning an entity to a user, like a task, assign the task to the + user's `Membership` instead of directly to the user. See the Bullet + Train blog post linked above for more explanation on this. + +The prisma schema looks like this: + +```prisma +model Organization { + id Int @id @default(autoincrement()) + name String + role GlobalRole + + membership Membership[] +} + +// The owners of the SaaS (you) can have a SUPERADMIN role to access all data +enum GlobalRole { + SUPERADMIN + CUSTOMER +} + +model Membership { + id Int @id @default(autoincrement()) + role MembershipRole + + organization Organization @relation(fields: [organizationId], references: [id]) + organizationId Int + + user User? @relation(fields: [userId], references: [id]) + userId Int? + + // When the user joins, we will clear out the name and email and set the user. + invitedName String? + invitedEmail String? + + @@unique([organizationId, invitedEmail]) +} + +enum MembershipRole { + OWNER + ADMIN + USER +} + +model User { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + name String? + email String @unique + + memberships Membership[] +} +``` + +Then you will need to update your `signup` mutation to also create an +organization and membership at the same time as you create the user. Like +this: + +```ts +const user = await db.user.create({ + data: { + // ... + memberships: { + create: { + role: "OWNER", + organization: { + create: { + name: organizationName, + }, + }, + }, + }, + }, +}) +``` + +## User Sessions {#sessions} + +The above data model allows a single user to be in multiple organizations. +So how can we track which organization a user is currently accessing or +modifying? + +The best way is to only let a user access one organization at a time. And +then provide a menu in the UI that let's them switch which organization +they are accessing. + +To do that, first add an `orgId` field to the +[session `PublicData`](./session-management#change-session-public-data-of-current-user). + +Update `types.ts` like this: + +```diff ++import { GlobalRole, MembershipRole, Organization } from "db" + +declare module "blitz" { + export interface Ctx extends DefaultCtx { + session: SessionContext + } + export interface Session { + isAuthorized: SimpleRolesIsAuthorized<Role> + PublicData: { + userId: User["id"] ++ roles: Array<MembershipRole & GlobalRole> ++ orgId: Organization["id"] + } + } +} +``` + +and then update all places where you call `ctx.session.$create()`. You +will need to add `orgId` and update `roles`. + +It will look something like this: + +```ts +await session.$create({ + userId: user.id, + roles: [user.role, user.memberships[0].role], + orgId: user.memberships[0].organizationId, +}) +``` + +Then you can use `ctx.session.orgId` in queries and mutations to filter +your queries based on the current organization. + +## Queries {#queries} + +You must filter all your queries by `organizationId` to ensure one user +cannot see another user's private data. + +```ts +import db from "db" + +// If you accept only the `id` as input +const project = await db.project.findFirst({ + where: { + id: input.id, + organizationId: ctx.session.orgId, + }, +}) + +// If you accept `where` as input +const projects = await db.project.findMany({ + where: { + ...input.where, + organizationId: ctx.session.orgId, + }, +}) +``` + +## Mutations {#mutations} + +You must also filter mutations by `organizationId` to ensure another +user's data can't be updated. And when creating new entities, ensure you +set `organizationId` to the current organization (`ctx.session.orgId`). + +Here's an example where a mutation accepts `id` that could be the id of an +entity belonging to a different organization. You could first make a +`db.project.findFirst()` query for that id and then manually verify that +`organizationId` is correct. But the easier way shown here is by adding +`organizationId` to the `db.update` `where` input. This update call will +fail if the organizationId doesn't match. + +```ts +import {resolver} from "blitz" +import db from "db" +import * as z from "zod" + +const UpdateProject = z + .object({ + id: z.number(), + name: z.string(), + }) + .nonstrict() + +export default resolver.pipe( + resolver.zod(UpdateProject), + resolver.authorize(), + async ({id, ...data}, ctx) => { + const project = await db.project.update({ + where: { + id, + // Filter by organizationId + organizationId: ctx.session.orgId, + }, + data, + }) + + return project + }, +) +``` + +## Advanced Authorization {#authorization} + +You can do more advanced things like calling `ctx.session.$authorize()` +inside an if/else + +```ts +import {resolver} from "blitz" +import db, {GlobalRole, MembershipRole} from "db" +import * as z from "zod" + +const UpdateProject = z + .object({ + id: z.number(), + organizationId: z.number(), + name: z.string(), + }) + .nonstrict() + +export default resolver.pipe( + resolver.zod(UpdateProject), + // Ensure all users are logged in + resolver.authorize(), + async ({id, organizationId, ...data}, ctx) => { + // if organizationId doesn't match current organization + if (organizationId !== ctx.session.orgId) { + // Require SUPERADMIN role + ctx.session.$authorize(GlobalRole.SUPERADMIN) + } else if (!ctx.session.accessibleProjects.includes(id)) { + // If user doesn't have specific access to this project, + // require them to be a project manager + ctx.session.$authorize(MembershipRole.PROJECT_MANAGER) + } + + const project = await db.project.update({ + where: { + id, + organizationId, + }, + data, + }) + + return project + }, +) +``` + +#### Utilities + +Here's some advanced utilities that allow you to do authorization in a way +that allows SUPERADMINs to access all organizations but only permits +regular users to access the organization they are currently logged in to. + +```ts +// app/orders/queries/getOrder.ts +import {NotFoundError, resolver} from "blitz" +import db from "db" +import * as z from "zod" +import { + enforceAdminOrProctorIfNotCurrentOrganization, + setDefaultOrganizationId, +} from "app/core/utils" + +const GetOrder = z.object({ + id: z.number(), + organizationId: z.number().optional(), +}) + +export default resolver.pipe( + resolver.zod(GetOrder), + // Ensure user is logged in + resolver.authorize(), //highlight-line + // Set input.organizationId to the current organization if one is not set + // This allows SUPERADMINs to pass in a specific organizationId + setDefaultOrganizationId, //highlight-line + // But now we need to enforce input.organizationId matches + // session.orgId unless user is a SUPERADMIN + enforceSuperAdminIfNotCurrentOrganization, //highlight-line + async ({id, organizationId}) => { + const order = await db.getOrder({ + where: { + id, + // Now we can safely use organizationId to filter queries + organizationId, + }, + }) + if (!order) throw new NotFoundError() + return order + }, +) +``` + +```ts +// app/core/utils.ts +import {Ctx} from "blitz" +import {Prisma, GloblRole} from "db" + +export default function assert( + condition: any, + message: string, +): asserts condition { + if (!condition) throw new Error(message) +} + +export const setDefaultOrganizationId = <T extends Record<any, any>>( + input: T, + {session}: Ctx, +): T & {organizationId: Prisma.IntNullableFilter | number} => { + assert( + session.orgId, + "Missing session.orgId in setDefaultOrganizationId", + ) + if (input.organizationId) { + // Pass through the input + return input as T & {organizationId: number} + } else if (session.roles?.includes(GloblRole.SUPERADMIN)) { + // Allow viewing any organization + return {...input, organizationId: {not: 0}} + } else { + // Set organizationId to session.orgId + return {...input, organizationId: session.orgId} + } +} + +export const enforceSuperAdminIfNotCurrentOrganization = < + T extends Record<any, any> +>( + input: T, + ctx: Ctx, +): T => { + assert(ctx.session.orgId, "missing session.orgId") + assert(input.organizationId, "missing input.organizationId") + + if (input.organizationId !== ctx.session.orgId) { + ctx.session.$authorize(GloblRole.SUPERADMIN) + } + return input +} +``` + +If you want to do more advanced authorization, check out +[Blitz Guard](https://github.com/ntgussoni/blitz-guard). From 1b4420a5da4c73158d206be29d849312b076cb49 Mon Sep 17 00:00:00 2001 From: Brandon Bayer <b@bayer.ws> Date: Wed, 28 Apr 2021 17:30:26 -0400 Subject: [PATCH 34/67] update query and mutation docs to reflect latest recommendations --- app/pages/docs/mutation-resolvers.mdx | 21 +++++++++++++++------ app/pages/docs/query-resolvers.mdx | 21 ++++++++++++++------- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/app/pages/docs/mutation-resolvers.mdx b/app/pages/docs/mutation-resolvers.mdx index 24cbd94..827f7c3 100644 --- a/app/pages/docs/mutation-resolvers.mdx +++ b/app/pages/docs/mutation-resolvers.mdx @@ -35,20 +35,29 @@ fetching from third-party APIs. ```ts // app/products/mutations/createProduct.tsx import {Ctx} from "blitz" -import db, {ProjectCreateArgs} from "db" +import db from "db" +import * as z from "zod" -type CreateProjectInput = { - data: ProjectCreateArgs["data"] -} +const CreateProject = z + .object({ + name: z.string(), + }) + .nonstrict() export default async function createProject( - {data}: CreateProjectInput, + input: z.infer<typeof CreateProject>, ctx: Ctx, ) { - // Can do any processing, fetching from other APIs, etc + // Validate input - very importnant for security + const data = CreateProject.parse(input) + + // Require user to be logged in + ctx.session.$authorize() const project = await db.project.create({data}) + // Can do any processing, fetching from other APIs, etc + return project } ``` diff --git a/app/pages/docs/query-resolvers.mdx b/app/pages/docs/query-resolvers.mdx index 602f26e..df67a1d 100644 --- a/app/pages/docs/query-resolvers.mdx +++ b/app/pages/docs/query-resolvers.mdx @@ -35,19 +35,26 @@ fetching from third-party APIs. ```ts // app/products/queries/getProduct.tsx import {Ctx} from "blitz" -import db, {FindOneProjectArgs} from "db" +import db from "db" +import * as z from "zod" -type GetProjectInput = { - where: FindOneProjectArgs["where"] -} +const GetProject = z.object({ + id: z.number(), +}) export default async function getProject( - {where}: GetProjectInput, + input: z.infer<typeof GetProject>, ctx: Ctx, ) { - // Can do any processing, fetching from other APIs, etc + // Validate the input + const data = GetProject.parse(input) + + // Require user to be logged in + ctx.session.$authorize() - const project = await db.project.findOne({where}) + const project = await db.project.findOne({where: {id: data.id}}) + + // Can do any processing, fetching from other APIs, etc return project } From 8d31fabeeeb2327bb06d63dc36c42524d982545f Mon Sep 17 00:00:00 2001 From: Brandon Bayer <b@bayer.ws> Date: Thu, 29 Apr 2021 12:44:52 -0400 Subject: [PATCH 35/67] add example of creating entity to multitenancy page --- app/pages/docs/multitenancy.mdx | 35 ++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/app/pages/docs/multitenancy.mdx b/app/pages/docs/multitenancy.mdx index d716ead..79bf262 100644 --- a/app/pages/docs/multitenancy.mdx +++ b/app/pages/docs/multitenancy.mdx @@ -176,9 +176,38 @@ const projects = await db.project.findMany({ ## Mutations {#mutations} -You must also filter mutations by `organizationId` to ensure another -user's data can't be updated. And when creating new entities, ensure you -set `organizationId` to the current organization (`ctx.session.orgId`). +When creating new entities, make sure you attach them to the current +organization. Here's an example of how to do that: + +```ts +import {resolver} from "blitz" +import db from "db" +import * as z from "zod" + +const CreateProject = z + .object({ + name: z.string(), + }) + .nonstrict() + +export default resolver.pipe( + resolver.zod(CreateProject), + resolver.authorize(), + async (input, ctx) => { + const project = await db.project.create({ + data: { + ...input, + organizationId: ctx.session.orgId, + }, + }) + + return project + }, +) +``` + +You must also filter update and delete mutations by `organizationId` to +ensure another user's data can't be changed. Here's an example where a mutation accepts `id` that could be the id of an entity belonging to a different organization. You could first make a From bf793f80813b8e7492014c0d83c3cc396517d560 Mon Sep 17 00:00:00 2001 From: Brandon Bayer <b@bayer.ws> Date: Thu, 29 Apr 2021 15:36:31 -0400 Subject: [PATCH 36/67] update docs for blitz.config.ts --- app/pages/docs/blitz-config.mdx | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/app/pages/docs/blitz-config.mdx b/app/pages/docs/blitz-config.mdx index bf6d511..82c65bf 100644 --- a/app/pages/docs/blitz-config.mdx +++ b/app/pages/docs/blitz-config.mdx @@ -3,11 +3,8 @@ title: blitz.config.js sidebar_label: blitz.config.js --- -You can customize advanced behavior of Blitz with `blitz.config.js`. - -`blitz.config.js` is a regular Node.js module, not a JSON file. It gets -used by the Blitz server and build phases, and it's not included in the -browser build. +You can customize advanced behavior of Blitz with `blitz.config.js` or +`blitz.config.ts`. Take a look at the following `blitz.config.js` example: @@ -33,7 +30,7 @@ can see the available phases Phases can be imported from `next/constants`: ```js -const {PHASE_DEVELOPMENT_SERVER} = require("next/constants") +import {PHASE_DEVELOPMENT_SERVER} from "next/constants" module.exports = (phase, {defaultConfig}) => { if (phase === PHASE_DEVELOPMENT_SERVER) { @@ -57,10 +54,6 @@ understand what each config does, instead, search for the features you need to enable or modify in this section and they will show you what to do. -> Avoid using new JavaScript features not available in your target Node.js -> version. `blitz.config.js` will not be parsed by Webpack, Babel or -> TypeScript. - ## Webpack Config {#webpack-config} You can customize the Blitz webpack config. See @@ -93,9 +86,6 @@ module.exports = { } ``` -> Deployments to [Vercel](https://vercel.com) will automatically enable -> this target. You should not opt-into it yourself. - ## Middleware {#middleware} HTTP middleware can be added queries and mutations. @@ -144,8 +134,9 @@ module.exports = { ### Proxy support {#proxy-support} -Proxy support is enabled automatically for recipe install if either `http_proxy` or `https_proxy` -environment variable is present. You can also set proxy using: +Proxy support is enabled automatically for recipe install if either +`http_proxy` or `https_proxy` environment variable is present. You can +also set proxy using: ```js module.exports = { @@ -157,7 +148,8 @@ module.exports = { } ``` -Please note that proxy configuration in `blitz.config.js` will override environment proxy configuration. +Please note that proxy configuration in `blitz.config.js` will override +environment proxy configuration. ## CDN Support with Asset Prefix {#cdn-support-with-asset-prefix} From 5ed8f32bbe86f89bebe7a273296bfe5842f48c67 Mon Sep 17 00:00:00 2001 From: Brandon Bayer <b@bayer.ws> Date: Thu, 29 Apr 2021 18:15:08 -0400 Subject: [PATCH 37/67] update docs for nextjs 10.2 --- app/core/layouts/ContentsLayout.js | 2 +- app/core/navs/documentation.json | 4 +- app/pages/docs/app-component.mdx | 15 +- app/pages/docs/blitz-config.mdx | 726 ----------------------------- app/pages/docs/code-splitting.mdx | 8 + app/pages/docs/headers.mdx | 390 ++++++++++++++++ app/pages/docs/i18n-routing.mdx | 9 +- app/pages/docs/link.mdx | 8 +- app/pages/docs/preview-mode.mdx | 16 + app/pages/docs/redirects.mdx | 292 ++++++++++++ app/pages/docs/rewrites.mdx | 400 ++++++++++++++++ 11 files changed, 1137 insertions(+), 733 deletions(-) create mode 100644 app/pages/docs/headers.mdx create mode 100644 app/pages/docs/rewrites.mdx diff --git a/app/core/layouts/ContentsLayout.js b/app/core/layouts/ContentsLayout.js index b9035fa..a2dc3d9 100644 --- a/app/core/layouts/ContentsLayout.js +++ b/app/core/layouts/ContentsLayout.js @@ -176,7 +176,7 @@ export function ContentsLayout({children, meta, tableOfContents: toc}) { <a href={ "https://github.com/blitz-js/blitzjs.com/edit/main/app/pages" + - router.asPath + + router.asPath.split("#")[0] + ".mdx" } target="_blank" diff --git a/app/core/navs/documentation.json b/app/core/navs/documentation.json index 0979214..cecc05a 100644 --- a/app/core/navs/documentation.json +++ b/app/core/navs/documentation.json @@ -48,7 +48,6 @@ }, "pages": [ "pages", - "redirects", "error-pages", "head-component", "document-component", @@ -73,6 +72,9 @@ "link", "route-params-query", "router", + "redirects", + "rewrites", + "headers", "api-routes", "shallow-routing", "route-manifest" diff --git a/app/pages/docs/app-component.mdx b/app/pages/docs/app-component.mdx index 4539675..5a88098 100644 --- a/app/pages/docs/app-component.mdx +++ b/app/pages/docs/app-component.mdx @@ -28,4 +28,17 @@ props you send to `Component` will be received by the `page`. `pageProps` is an object with the initial props that were preloaded for your page by one of our data fetching methods like [`getStaticProps`](./get-static-props) or -[`getServerSideProps`](./get-server-side-props) +[`getServerSideProps`](./get-server-side-props), otherwise it's an empty +object. + +### Caveats {#caveats} + +- Adding a custom `getInitialProps` in your `App` will disable + [Automatic Static Optimization](./pages#automatic-static-optimization) + in pages without [Static Generation](./get-static-props). +- When you add `getInitialProps` in your custom app, you must + `import App from "blitz"`, call `App.getInitialProps(appContext)` inside + `getInitialProps` and merge the returned object into the return value. +- `App` currently does not support page data fetching methods like + [`getStaticProps`](./get-static-props) or + [`getServerSideProps`](./get-server-side-props). diff --git a/app/pages/docs/blitz-config.mdx b/app/pages/docs/blitz-config.mdx index 82c65bf..b1dc657 100644 --- a/app/pages/docs/blitz-config.mdx +++ b/app/pages/docs/blitz-config.mdx @@ -342,732 +342,6 @@ Output html: This makes sure that you don't have to change all links in your application when changing the `basePath` value. -## Rewrites {#rewrites} - -Rewrites allow you to map an incoming request path to a different -destination path. - -Rewrites are only available on the Node.js environment and do not affect -client-side routing. - -Rewrites are not able to override public files or routes in the pages -directory as these have higher priority than rewrites. For example, if you -have `pages/index.js` you are not able to rewrite `/` to another location -unless you rename the `pages/index.js` file. - -To use rewrites you can use the `rewrites` key in `blitz.config.js`: - -```js -module.exports = { - async rewrites() { - return [ - { - source: "/about", - destination: "/", - }, - ] - }, -} -``` - -`rewrites` is an async function that expects an array to be returned -holding objects with `source` and `destination` properties: - -- `source` is the incoming request path pattern. -- `destination` is the path you want to route to. - -### Rewrite parameters {#rewrite-parameters} - -When using parameters in a rewrite the parameters will be passed in the -query by default when none of the parameters are used in the -`destination`. - -```js -module.exports = { - async rewrites() { - return [ - { - source: "/old-about/:path*", - destination: "/about", // The :path parameter isn't used here so will be automatically passed in the query - }, - ] - }, -} -``` - -If a parameter is used in the destination none of the parameters will be -automatically passed in the query. - -```js -module.exports = { - async rewrites() { - return [ - { - source: "/docs/:path*", - destination: "/:path*", // The :path parameter is used here so will not be automatically passed in the query - }, - ] - }, -} -``` - -You can still pass the parameters manually in the query if one is already -used in the destination by specifying the query in the `destination`. - -```js -module.exports = { - async rewrites() { - return [ - { - source: "/:first/:second", - destination: "/:first?second=:second", - // Since the :first parameter is used in the destination the :second parameter - // will not automatically be added in the query although we can manually add it - // as shown above - }, - ] - }, -} -``` - -### Path Matching {#path-matching} - -Path matches are allowed, for example `/blog/:slug` will match -`/blog/hello-world` (no nested paths): - -```js -module.exports = { - async rewrites() { - return [ - { - source: "/blog/:slug", - destination: "/news/:slug", // Matched parameters can be used in the destination - }, - ] - }, -} -``` - -#### Wildcard Path Matching - -To match a wildcard path you can use `*` after a parameter, for example -`/blog/:slug*` will match `/blog/a/b/c/d/hello-world`: - -```js -module.exports = { - async rewrites() { - return [ - { - source: "/blog/:slug*", - destination: "/news/:slug*", // Matched parameters can be used in the destination - }, - ] - }, -} -``` - -#### Regex Path Matching - -To match a regex path you can wrap the regex in parenthesis after a -parameter, for example `/blog/:slug(\\d{1,})` will match `/blog/123` but -not `/blog/abc`: - -```js -module.exports = { - async rewrites() { - return [ - { - source: "/old-blog/:post(\\d{1,})", - destination: "/blog/:post", // Matched parameters can be used in the destination - }, - ] - }, -} -``` - -### Rewriting to an external URL {#rewriting-to-an-external-url} - -Rewrites allow you to rewrite to an external url. This is especially -useful for incrementally adopting Next.js. - -```js -module.exports = { - async rewrites() { - return [ - { - source: "/blog/:slug", - destination: "https://example.com/blog/:slug", // Matched parameters can be used in the destination - }, - ] - }, -} -``` - -#### Incremental adoption of Blitz - -You can also make Blitz check the application routes before falling back -to proxying to the previous website. - -This way you don't have to change the rewrites configuration when -migrating more pages to Blitz - -```js -module.exports = { - async rewrites() { - return [ - // we need to define a no-op rewrite to trigger checking - // all pages/static files before we attempt proxying - { - source: "/:path*", - destination: "/:path*", - }, - { - source: "/:path*", - destination: `https://custom-routes-proxying-endpoint.vercel.app/:path*`, - }, - ] - }, -} -``` - -#### Rewrites with basePath support - -When leveraging [`basePath` support](#base-path) with rewrites each -`source` and `destination` is automatically prefixed with the `basePath` -unless you add `basePath: false` to the rewrite: - -```js -module.exports = { - basePath: "/docs", - - async rewrites() { - return [ - { - source: "/with-basePath", // automatically becomes /docs/with-basePath - destination: "/another", // automatically becomes /docs/another - }, - { - // does not add /docs to /without-basePath since basePath: false is set - // Note: this can not be used for internal rewrites e.g. `destination: '/another'` - source: "/without-basePath", - destination: "https://example.com", - basePath: false, - }, - ] - }, -} -``` - -#### Rewrites with i18n support - -When leveraging [`i18n` support](/docs/i18n-routing) with rewrites each -`source` and `destination` is automatically prefixed to handle the -configured `locales` unless you add `locale: false` to the rewrite. If -`locale: false` is used you must prefix the `source` and `destination` -with a locale for it to be matched correctly. - -```js -module.exports = { - i18n: { - locales: ["en", "fr", "de"], - defaultLocale: "en", - }, - - async rewrites() { - return [ - { - source: "/with-locale", // automatically handles all locales - destination: "/another", // automatically passes the locale on - }, - { - // does not handle locales automatically since locale: false is set - source: "/nl/with-locale-manual", - destination: "/nl/another", - locale: false, - }, - { - // this matches '/' since `en` is the defaultLocale - source: "/en", - destination: "/en/another", - locale: false, - }, - ] - }, -} -``` - -## Redirects {#redirects} - -Redirects allow you to redirect an incoming request path to a different -destination path. - -Redirects are only available on the Node.js environment and do not affect -client-side routing. - -To use Redirects you can use the `redirects` key in `blitz.config.js`: - -```js -module.exports = { - async redirects() { - return [ - { - source: "/about", - destination: "/", - permanent: true, - }, - ] - }, -} -``` - -`redirects` is an async function that expects an array to be returned -holding objects with `source`, `destination`, and `permanent` properties: - -- `source` is the incoming request path pattern. -- `destination` is the path you want to route to. -- `permanent` if the redirect is permanent or not. - -### Path Matching {#path-matching-1} - -Path matches are allowed, for example `/old-blog/:slug` will match -`/old-blog/hello-world` (no nested paths): - -```js -module.exports = { - async redirects() { - return [ - { - source: "/old-blog/:slug", - destination: "/news/:slug", // Matched parameters can be used in the destination - permanent: true, - }, - ] - }, -} -``` - -#### Wildcard Path Matching - -To match a wildcard path you can use `*` after a parameter, for example -`/blog/:slug*` will match `/blog/a/b/c/d/hello-world`: - -```js -module.exports = { - async redirects() { - return [ - { - source: "/blog/:slug*", - destination: "/news/:slug*", // Matched parameters can be used in the destination - permanent: true, - }, - ] - }, -} -``` - -#### Regex Path Matching - -To match a regex path you can wrap the regex in parenthesis after a -parameter, for example `/post/:slug(\\d{1,})` will match `/post/123` but -not `/post/abc`: - -```js -module.exports = { - async redirects() { - return [ - { - source: "/post/:slug(\\d{1,})", - destination: "/news/:slug", // Matched parameters can be used in the destination - permanent: false, - }, - ] - }, -} -``` - -#### Redirects with basePath support - -When leveraging [`basePath` support](#base-path) with redirects each -`source` and `destination` is automatically prefixed with the `basePath` -unless you add `basePath: false` to the redirect: - -```js -module.exports = { - basePath: "/docs", - - async redirects() { - return [ - { - source: "/with-basePath", // automatically becomes /docs/with-basePath - destination: "/another", // automatically becomes /docs/another - permanent: false, - }, - { - // does not add /docs since basePath: false is set - source: "/without-basePath", - destination: "/another", - basePath: false, - permanent: false, - }, - ] - }, -} -``` - -#### Redirects with i18n support - -When leveraging [`i18n` support](/docs/i18n-routing) with redirects each -`source` and `destination` is automatically prefixed to handle the -configured `locales` unless you add `locale: false` to the redirect. If -`locale: false` is used you must prefix the `source` and `destination` -with a locale for it to be matched correctly. - -```js -module.exports = { - i18n: { - locales: ["en", "fr", "de"], - defaultLocale: "en", - }, - - async redirects() { - return [ - { - source: "/with-locale", // automatically handles all locales - destination: "/another", // automatically passes the locale on - permanent: false, - }, - { - // does not handle locales automatically since locale: false is set - source: "/nl/with-locale-manual", - destination: "/nl/another", - locale: false, - permanent: false, - }, - { - // this matches '/' since `en` is the defaultLocale - source: "/en", - destination: "/en/another", - locale: false, - permanent: false, - }, - ] - }, -} -``` - -In some rare cases, you might need to assign a custom status code for -older HTTP Clients to properly redirect. In these cases, you can use the -`statusCode` property instead of the `permanent` property, but not both. -Note: to ensure IE11 compatibility a `Refresh` header is automatically -added for the 308 status code. - -## Headers {#headers} - -Headers allow you to set custom HTTP headers for an incoming request path. - -To set custom HTTP headers you can use the `headers` key in -`blitz.config.js`: - -```js -module.exports = { - async headers() { - return [ - { - source: "/about", - headers: [ - { - key: "x-custom-header", - value: "my custom header value", - }, - { - key: "x-another-custom-header", - value: "my other custom header value", - }, - ], - }, - ] - }, -} -``` - -`headers` is an async function that expects an array to be returned -holding objects with `source` and `headers` properties: - -- `source` is the incoming request path pattern. -- `headers` is an array of header objects with the `key` and `value` - properties. - -### Header Overriding Behavior {#header-overriding-behavior} - -If two headers match the same path and set the same header key, the last -header key will override the first. Using the below headers, the path -`/hello` will result in the header `x-hello` being `world` due to the last -header value set being `world`. - -```js -module.exports = { - async headers() { - return [ - { - source: '/:path*', - headers: [ - { - key: 'x-hello', - value: 'there', - }, - ], - }, - { - source: '/hello', - headers: [ - { - key: 'x-hello', - value: 'world', - }, - ], - }, - ], - }, -} -``` - -### Path Matching {#path-matching-2} - -Path matches are allowed, for example `/blog/:slug` will match -`/blog/hello-world` (no nested paths): - -```js -module.exports = { - async headers() { - return [ - { - source: '/blog/:slug', - headers: [ - { - key: 'x-slug', - value: ':slug', // Matched parameters can be used in the value - }, - { - key: 'x-slug-:slug', // Matched parameters can be used in the key - value: 'my other custom header value', - }, - ], - }, - ], - }, -} -``` - -#### Wildcard Path Matching - -To match a wildcard path you can use `*` after a parameter, for example -`/blog/:slug*` will match `/blog/a/b/c/d/hello-world`: - -```js -module.exports = { - async headers() { - return [ - { - source: '/blog/:slug*', - headers: [ - { - key: 'x-slug', - value: ':slug*', // Matched parameters can be used in the value - }, - { - key: 'x-slug-:slug*', // Matched parameters can be used in the key - value: 'my other custom header value', - }, - ], - }, - ], - }, -} -``` - -#### Regex Path Matching - -To match a regex path you can wrap the regex in parenthesis after a -parameter, for example `/blog/:slug(\\d{1,})` will match `/blog/123` but -not `/blog/abc`: - -```js -module.exports = { - async headers() { - return [ - { - source: '/blog/:post(\\d{1,})', - headers: [ - { - key: 'x-post', - value: ':post', - }, - ], - }, - ], - }, -} -``` - -#### Headers with basePath support - -When leveraging [`basePath` support](#base-path) with headers each -`source` is automatically prefixed with the `basePath` unless you add -`basePath: false` to the header: - -```js -module.exports = { - basePath: "/docs", - - async headers() { - return [ - { - source: "/with-basePath", // becomes /docs/with-basePath - headers: [ - { - key: "x-hello", - value: "world", - }, - ], - }, - { - source: "/without-basePath", // is not modified since basePath: false is set - headers: [ - { - key: "x-hello", - value: "world", - }, - ], - basePath: false, - }, - ] - }, -} -``` - -#### Headers with i18n support - -When leveraging [`i18n` support](/docs/i18n-routing) with headers each -`source` is automatically prefixed to handle the configured `locales` -unless you add `locale: false` to the header. If `locale: false` is used -you must prefix the `source` with a locale for it to be matched correctly. - -```js -module.exports = { - i18n: { - locales: ["en", "fr", "de"], - defaultLocale: "en", - }, - - async headers() { - return [ - { - source: "/with-locale", // automatically handles all locales - headers: [ - { - key: "x-hello", - value: "world", - }, - ], - }, - { - // does not handle locales automatically since locale: false is set - source: "/nl/with-locale-manual", - locale: false, - headers: [ - { - key: "x-hello", - value: "world", - }, - ], - }, - { - // this matches '/' since `en` is the defaultLocale - source: "/en", - locale: false, - headers: [ - { - key: "x-hello", - value: "world", - }, - ], - }, - ] - }, -} -``` - -#### Cache-Control - -Cache-Control headers set in blitz.config.js will be overwritten in -production to ensure that static assets can be cached effectively. If you -need to revalidate the cache of a page that has been -[statically generated](/docs/get-static-props#when-should-i-use-getstaticprops), -you can do so by setting `revalidate` in the page's -[`getStaticProps`](/docs/get-static-props) function. - -## Export Path Map {#export-path-map} - -`exportPathMap` allows you to specify a mapping of request paths to page -destinations, to be used during [export](static-html-export). Paths -defined in `exportPathMap` will also be available when using `blitz dev`. - -`exportPathMap` is an async function that receives 2 arguments: the first -one is `defaultPathMap`, which is the default map used by Blitz. The -second argument is an object with: - -- `dev` - `true` when `exportPathMap` is being called in development. - `false` when running `blitz export`. In development `exportPathMap` is - used to define routes. -- `dir` - Absolute path to the project directory -- `outDir` - Absolute path to the `out/` directory - ([configurable with `-o`](cli-export)). When `dev` is `true` the value - of `outDir` will be `null`. -- `distDir` - Absolute path to the `.next/` directory (configurable with - the [`distDir` config](#setting-a-custom-build-directory)) -- `buildId` - The generated build id - -The returned object is a map of pages where the `key` is the `pathname` -and the `value` is an object that accepts the following fields: - -- `page`: `String` - the page inside the pages directory to render -- `query`: `Object` - the query object passed to `getStaticProps` when - prerendering. Defaults to `{}` - -> The exported `pathname` can also be a filename (for example, -> `/readme.md`), but you may need to set the `Content-Type` header to -> `text/html` when serving its content if it is different than `.html`. - -#### Example - -Original pages: - -- `pages/index.js` -- `pages/about.js` -- `pages/post.js` - -`blitz.config.js`: - -```js -module.exports = { - exportPathMap: async function ( - defaultPathMap, - {dev, dir, outDir, distDir, buildId}, - ) { - return { - "/": {page: "/"}, - "/about": {page: "/about"}, - "/p/hello-nextjs": {page: "/post", query: {title: "hello-nextjs"}}, - "/p/learn-nextjs": {page: "/post", query: {title: "learn-nextjs"}}, - "/p/deploy-nextjs": { - page: "/post", - query: {title: "deploy-nextjs"}, - }, - } - }, -} -``` - ## Trailing Slash {#trailing-slash} By default Blitz will redirect urls with trailing slashes to their diff --git a/app/pages/docs/code-splitting.mdx b/app/pages/docs/code-splitting.mdx index e5dea78..7bc5abf 100644 --- a/app/pages/docs/code-splitting.mdx +++ b/app/pages/docs/code-splitting.mdx @@ -55,6 +55,14 @@ export default Home `../components/hello`. It works like a regular React Component, and you can pass props to it as you normally would. +> **Note**: In `import('path/to/component')`, the path must be explicitly +> written. It can't be a template string nor a variable. Furthermore the +> `import()` has to be inside the `dynamic()` call for Next.js to be able +> to match webpack bundles / module ids to the specific `dynamic()` call +> and preload them before rendering. `dynamic()` can't be used inside of +> React rendering as it needs to be marked in the top level of the module +> for preloading to work, similar to `React.lazy`. + ## With named exports {#with-named-exports} If the dynamic component is not the default export, you can use a named diff --git a/app/pages/docs/headers.mdx b/app/pages/docs/headers.mdx new file mode 100644 index 0000000..c9812ab --- /dev/null +++ b/app/pages/docs/headers.mdx @@ -0,0 +1,390 @@ +--- +title: HTTP Headers +sidebar_label: HTTP Headers +--- + +Headers allow you to set custom HTTP headers for an incoming request path. + +To set custom HTTP headers you can use the `headers` key in +`blitz.config.js`: + +```js +module.exports = { + async headers() { + return [ + { + source: "/about", + headers: [ + { + key: "x-custom-header", + value: "my custom header value", + }, + { + key: "x-another-custom-header", + value: "my other custom header value", + }, + ], + }, + ] + }, +} +``` + +`headers` is an async function that expects an array to be returned +holding objects with `source` and `headers` properties: + +- `source` is the incoming request path pattern. +- `headers` is an array of header objects with the `key` and `value` + properties. +- `basePath`: `false` or `undefined` - if false the basePath won't be + included when matching, can be used for external rewrites only. +- `locale`: `false` or `undefined` - whether the locale should not be + included when matching. +- `has` is an array of [has objects](#header-cookie-and-query-matching) + with the `type`, `key` and `value` properties. + +Headers are checked before the filesystem which includes pages and +`/public` files. + +## Header Overriding Behavior {#overriding} + +If two headers match the same path and set the same header key, the last +header key will override the first. Using the below headers, the path +`/hello` will result in the header `x-hello` being `world` due to the last +header value set being `world`. + +```js +module.exports = { + async headers() { + return [ + { + source: '/:path*', + headers: [ + { + key: 'x-hello', + value: 'there', + }, + ], + }, + { + source: '/hello', + headers: [ + { + key: 'x-hello', + value: 'world', + }, + ], + }, + ], + }, +} +``` + +## Path Matching {#path-matching} + +Path matches are allowed, for example `/blog/:slug` will match +`/blog/hello-world` (no nested paths): + +```js +module.exports = { + async headers() { + return [ + { + source: '/blog/:slug', + headers: [ + { + key: 'x-slug', + value: ':slug', // Matched parameters can be used in the value + }, + { + key: 'x-slug-:slug', // Matched parameters can be used in the key + value: 'my other custom header value', + }, + ], + }, + ], + }, +} +``` + +### Wildcard Path Matching {#wildcard} + +To match a wildcard path you can use `*` after a parameter, for example +`/blog/:slug*` will match `/blog/a/b/c/d/hello-world`: + +```js +module.exports = { + async headers() { + return [ + { + source: '/blog/:slug*', + headers: [ + { + key: 'x-slug', + value: ':slug*', // Matched parameters can be used in the value + }, + { + key: 'x-slug-:slug*', // Matched parameters can be used in the key + value: 'my other custom header value', + }, + ], + }, + ], + }, +} +``` + +### Regex Path Matching {#regex} + +To match a regex path you can wrap the regex in parenthesis after a +parameter, for example `/blog/:slug(\\d{1,})` will match `/blog/123` but +not `/blog/abc`: + +```js +module.exports = { + async headers() { + return [ + { + source: '/blog/:post(\\d{1,})', + headers: [ + { + key: 'x-post', + value: ':post', + }, + ], + }, + ], + }, +} +``` + +The following characters `(`, `)`, `{`, `}`, `:`, `*`, `+`, `?` are used +for regex path matching, so when used in the `source` as non-special +values they must be escaped by adding `\\` before them: + +```js +module.exports = { + async redirects() { + return [ + { + // this will match `/english(default)/something` being requested + source: "/english\\(default\\)/:slug", + destination: "/en-us/:slug", + permanent: false, + }, + ] + }, +} +``` + +## Header, Cookie, and Query Matching {#header-cookie-matching} + +To only apply a header when either header, cookie, or query values also +match the `has` field can be used. Both the `source` and all `has` items +must match for the header to be applied. + +`has` items have the following fields: + +- `type`: `String` - must be either `header`, `cookie`, `host`, or + `query`. +- `key`: `String` - the key from the selected type to match against. +- `value`: `String` or `undefined` - the value to check for, if undefined + any value will match. A regex like string can be used to capture a + specific part of the value, e.g. if the value `first-(?<paramName>.*)` + is used for `first-second` then `second` will be usable in the + destination with `:paramName`. + +```js +module.exports = { + async headers() { + return [ + // if the header `x-add-header` is present, + // the `x-another-header` header will be applied + { + source: "/:path*", + has: [ + { + type: "header", + key: "x-add-header", + }, + ], + headers: [ + { + key: "x-another-header", + value: "hello", + }, + ], + }, + // if the source, query, and cookie are matched, + // the `x-authorized` header will be applied + { + source: "/specific/:path*", + has: [ + { + type: "query", + key: "page", + // the page value will not be available in the + // header key/values since value is provided and + // doesn't use a named capture group e.g. (?<page>home) + value: "home", + }, + { + type: "cookie", + key: "authorized", + value: "true", + }, + ], + headers: [ + { + key: "x-authorized", + value: ":authorized", + }, + ], + }, + // if the header `x-authorized` is present and + // contains a matching value, the `x-another-header` will be applied + { + source: "/:path*", + has: [ + { + type: "header", + key: "x-authorized", + value: "(?<authorized>yes|true)", + }, + ], + headers: [ + { + key: "x-another-header", + value: ":authorized", + }, + ], + }, + // if the host is `example.com`, + // this header will be applied + { + source: "/:path*", + has: [ + { + type: "host", + value: "example.com", + }, + ], + headers: [ + { + key: "x-another-header", + value: ":authorized", + }, + ], + }, + ] + }, +} +``` + +### Headers with basePath support {#basepath} + +When leveraging [`basePath` support](./blitz-config#base-path) with +headers each `source` is automatically prefixed with the `basePath` unless +you add `basePath: false` to the header: + +```js +module.exports = { + basePath: "/docs", + + async headers() { + return [ + { + source: "/with-basePath", // becomes /docs/with-basePath + headers: [ + { + key: "x-hello", + value: "world", + }, + ], + }, + { + source: "/without-basePath", // is not modified since basePath: false is set + headers: [ + { + key: "x-hello", + value: "world", + }, + ], + basePath: false, + }, + ] + }, +} +``` + +### Headers with i18n support {#i18n} + +When leveraging [`i18n` support](./i18n-routing) with headers each +`source` is automatically prefixed to handle the configured `locales` +unless you add `locale: false` to the header. If `locale: false` is used +you must prefix the `source` with a locale for it to be matched correctly. + +```js +module.exports = { + i18n: { + locales: ["en", "fr", "de"], + defaultLocale: "en", + }, + + async headers() { + return [ + { + source: "/with-locale", // automatically handles all locales + headers: [ + { + key: "x-hello", + value: "world", + }, + ], + }, + { + // does not handle locales automatically since locale: false is set + source: "/nl/with-locale-manual", + locale: false, + headers: [ + { + key: "x-hello", + value: "world", + }, + ], + }, + { + // this matches '/' since `en` is the defaultLocale + source: "/en", + locale: false, + headers: [ + { + key: "x-hello", + value: "world", + }, + ], + }, + { + // this gets converted to /(en|fr|de)/(.*) so will not match the top-level + // `/` or `/fr` routes like /:path* would + source: "/(.*)", + headers: [ + { + key: "x-hello", + value: "worlld", + }, + ], + }, + ] + }, +} +``` + +### Cache-Control {#cache-control} + +Cache-Control headers set in blitz.config.js will be overwritten in +production to ensure that static assets can be cached effectively. If you +need to revalidate the cache of a page that has been +[statically generated](./pages#automatic-static-optimization), you can do +so by setting `revalidate` in the page's +[`getStaticProps`](./get-static-props) function. diff --git a/app/pages/docs/i18n-routing.mdx b/app/pages/docs/i18n-routing.mdx index 4af6f30..4d3065c 100644 --- a/app/pages/docs/i18n-routing.mdx +++ b/app/pages/docs/i18n-routing.mdx @@ -9,8 +9,13 @@ routing. You can provide a list of locales, the default locale, and domain-specific locales and Blitz will automatically handle the routing. The i18n routing support is currently meant to complement existing i18n -library solutions like `react-intl`, `react-i18next`, `lingui`, `rosetta`, -and others by streamlining the routes and locale parsing. +library solutions like +[`react-intl`](https://formatjs.io/docs/getting-started/installation), +[`react-i18next`](https://react.i18next.com/), +[`lingui`](https://lingui.js.org/), +[`rosetta`](https://github.com/lukeed/rosetta), +[`next-intl`](https://github.com/amannn/next-intl) and others by +streamlining the routes and locale parsing. ## Getting started {#getting-started} diff --git a/app/pages/docs/link.mdx b/app/pages/docs/link.mdx index 5344555..841c136 100644 --- a/app/pages/docs/link.mdx +++ b/app/pages/docs/link.mdx @@ -48,8 +48,12 @@ you can link to it with `Routes.About`. For more information, see the `false` - `prefetch` - Prefetch the page in the background. Defaults to `true`. Any `<Link />` that is in the viewport (initially or through scroll) - will be preloaded. Pages using [Static Generation](./get-static-props) - will preload `JSON` files with the data for faster page transitions. + will be preloaded. Prefetch can be disabled by passing + `prefetch={false}`. When `prefetch` is set to `false`, prefetching will + still occur on hover. Pages using + [Static Generation](./get-static-props.mdx) will preload `JSON` files + with the data for faster page transitions. Prefetching is only enabled + in production. - [`replace`](#replace-the-url-instead-of-push) - Replace the current `history` state instead of adding a new url into the stack. Defaults to `false` diff --git a/app/pages/docs/preview-mode.mdx b/app/pages/docs/preview-mode.mdx index d235e6d..e25d2c4 100644 --- a/app/pages/docs/preview-mode.mdx +++ b/app/pages/docs/preview-mode.mdx @@ -244,8 +244,24 @@ there’s a size limitation. Currently, preview data is limited to 2KB. The preview mode works on `getServerSideProps` as well. It will also be available on the `context` object containing `preview` and `previewData`. +### Works with API Routes {#works-with-api-routes} + +API Routes will have access to `preview` and `previewData` under the +request object. For example: + +```js +export default function myApiRoute(req, res) { + const isPreview = req.preview + const previewData = req.previewData + // ... +} +``` + ### Unique per `blitz build` {#unique-per-blitz-build} The bypass cookie value and private key for encrypting the `previewData` changes when a `blitz build` is ran, this ensures that the bypass cookie can’t be guessed. + +> **Note:** To test Preview Mode locally over HTTP your browser will need +> to allow third-party cookies and local storage access. diff --git a/app/pages/docs/redirects.mdx b/app/pages/docs/redirects.mdx index 85f3c13..98a8650 100644 --- a/app/pages/docs/redirects.mdx +++ b/app/pages/docs/redirects.mdx @@ -3,6 +3,12 @@ title: Redirects sidebar_label: Redirects --- +Redirects allow you to redirect an incoming request path to a different +destination path. + +Redirects are only available on the Node.js environment and do not affect +client-side routing. + ## On the Client {#on-the-client} One common use case is conditionally redirecting a user to a different @@ -50,3 +56,289 @@ export const getServerSideProps = async ({req, res}) => { return {props: {}} } ``` + +## Global Redirects {#global} + +To use global redirects, you can use the `redirects` key in +`blitz.config.js`: + +```js +module.exports = { + async redirects() { + return [ + { + source: "/about", + destination: "/", + permanent: true, + }, + ] + }, +} +``` + +`redirects` is an async function that expects an array to be returned +holding objects with `source`, `destination`, and `permanent` properties: + +- `source` is the incoming request path pattern. +- `destination` is the path you want to route to. +- `permanent` if the redirect is permanent or not. +- `basePath`: `false` or `undefined` - if false the basePath won't be + included when matching, can be used for external rewrites only. +- `locale`: `false` or `undefined` - whether the locale should not be + included when matching. +- `has` is an array of [has objects](#header-cookie-and-query-matching) + with the `type`, `key` and `value` properties. + +Redirects are checked before the filesystem which includes pages and +`/public` files. + +### Path Matching {#path-matching} + +Path matches are allowed, for example `/old-blog/:slug` will match +`/old-blog/hello-world` (no nested paths): + +```js +module.exports = { + async redirects() { + return [ + { + source: "/old-blog/:slug", + destination: "/news/:slug", // Matched parameters can be used in the destination + permanent: true, + }, + ] + }, +} +``` + +#### Wildcard Path Matching + +To match a wildcard path you can use `*` after a parameter, for example +`/blog/:slug*` will match `/blog/a/b/c/d/hello-world`: + +```js +module.exports = { + async redirects() { + return [ + { + source: "/blog/:slug*", + destination: "/news/:slug*", // Matched parameters can be used in the destination + permanent: true, + }, + ] + }, +} +``` + +#### Regex Path Matching + +To match a regex path you can wrap the regex in parentheses after a +parameter, for example `/post/:slug(\\d{1,})` will match `/post/123` but +not `/post/abc`: + +```js +module.exports = { + async redirects() { + return [ + { + source: "/post/:slug(\\d{1,})", + destination: "/news/:slug", // Matched parameters can be used in the destination + permanent: false, + }, + ] + }, +} +``` + +The following characters `(`, `)`, `{`, `}`, `:`, `*`, `+`, `?` are used +for regex path matching, so when used in the `source` as non-special +values they must be escaped by adding `\\` before them: + +```js +module.exports = { + async redirects() { + return [ + { + // this will match `/english(default)/something` being requested + source: "/english\\(default\\)/:slug", + destination: "/en-us/:slug", + permanent: false, + }, + ] + }, +} +``` + +### Header, Cookie, and Query Matching {#header-cooking-matching} + +To only match a redirect when header, cookie, or query values also match +the `has` field can be used. Both the `source` and all `has` items must +match for the redirect to be applied. + +`has` items have the following fields: + +- `type`: `String` - must be either `header`, `cookie`, `host`, or + `query`. +- `key`: `String` - the key from the selected type to match against. +- `value`: `String` or `undefined` - the value to check for, if undefined + any value will match. A regex like string can be used to capture a + specific part of the value, e.g. if the value `first-(?<paramName>.*)` + is used for `first-second` then `second` will be usable in the + destination with `:paramName`. + +```js +module.exports = { + async redirects() { + return [ + // if the header `x-redirect-me` is present, + // this redirect will be applied + { + source: "/:path*", + has: [ + { + type: "header", + key: "x-redirect-me", + }, + ], + permanent: false, + destination: "/another-page", + }, + // if the source, query, and cookie are matched, + // this redirect will be applied + { + source: "/specific/:path*", + has: [ + { + type: "query", + key: "page", + // the page value will not be available in the + // destination since value is provided and doesn't + // use a named capture group e.g. (?<page>home) + value: "home", + }, + { + type: "cookie", + key: "authorized", + value: "true", + }, + ], + permanent: false, + destination: "/:path*/:page", + }, + // if the header `x-authorized` is present and + // contains a matching value, this redirect will be applied + { + source: "/:path*", + has: [ + { + type: "header", + key: "x-authorized", + value: "(?<authorized>yes|true)", + }, + ], + permanent: false, + destination: "/home?authorized=:authorized", + }, + // if the host is `example.com`, + // this redirect will be applied + { + source: "/:path*", + has: [ + { + type: "host", + value: "example.com", + }, + ], + destination: "/another-page", + }, + ] + }, +} +``` + +#### Redirects with basePath support + +When leveraging [`basePath` support](./blitz-config#base-path) with +redirects each `source` and `destination` is automatically prefixed with +the `basePath` unless you add `basePath: false` to the redirect: + +```js +module.exports = { + basePath: "/docs", + + async redirects() { + return [ + { + source: "/with-basePath", // automatically becomes /docs/with-basePath + destination: "/another", // automatically becomes /docs/another + permanent: false, + }, + { + // does not add /docs since basePath: false is set + source: "/without-basePath", + destination: "/another", + basePath: false, + permanent: false, + }, + ] + }, +} +``` + +#### Redirects with i18n support + +When leveraging [`i18n` support](./i18n-routing) with redirects each +`source` and `destination` is automatically prefixed to handle the +configured `locales` unless you add `locale: false` to the redirect. If +`locale: false` is used you must prefix the `source` and `destination` +with a locale for it to be matched correctly. + +```js +module.exports = { + i18n: { + locales: ["en", "fr", "de"], + defaultLocale: "en", + }, + + async redirects() { + return [ + { + source: "/with-locale", // automatically handles all locales + destination: "/another", // automatically passes the locale on + permanent: false, + }, + { + // does not handle locales automatically since locale: false is set + source: "/nl/with-locale-manual", + destination: "/nl/another", + locale: false, + permanent: false, + }, + { + // this matches '/' since `en` is the defaultLocale + source: "/en", + destination: "/en/another", + locale: false, + permanent: false, + }, + { + // this gets converted to /(en|fr|de)/(.*) so will not match the top-level + // `/` or `/fr` routes like /:path* would + source: "/(.*)", + destination: "/another", + permanent: false, + }, + ] + }, +} +``` + +In some rare cases, you might need to assign a custom status code for +older HTTP Clients to properly redirect. In these cases, you can use the +`statusCode` property instead of the `permanent` property, but not both. +Note: to ensure IE11 compatibility a `Refresh` header is automatically +added for the 308 status code. + +### Other Redirects {#other} + +- Inside [API Routes](./api-routes), you can use `res.redirect()`. diff --git a/app/pages/docs/rewrites.mdx b/app/pages/docs/rewrites.mdx new file mode 100644 index 0000000..07726f9 --- /dev/null +++ b/app/pages/docs/rewrites.mdx @@ -0,0 +1,400 @@ +--- +title: Rewrites +sidebar_label: Rewrites +--- + +Rewrites allow you to map an incoming request path to a different +destination path. + +Rewrites are only available on the Node.js environment and do not affect +client-side routing. + +To use rewrites you can use the `rewrites` key in `blitz.config.js`: + +```js +module.exports = { + async rewrites() { + return [ + { + source: "/about", + destination: "/", + }, + ] + }, +} +``` + +`rewrites` is an async function that expects an array to be returned +holding objects with `source` and `destination` properties: + +- `source`: `String` - is the incoming request path pattern. +- `destination`: `String` is the path you want to route to. +- `basePath`: `false` or `undefined` - if false the basePath won't be + included when matching, can be used for external rewrites only. +- `locale`: `false` or `undefined` - whether the locale should not be + included when matching. +- `has` is an array of [has objects](#header-cookie-and-query-matching) + with the `type`, `key` and `value` properties. + +Rewrites are applied after checking the filesystem (pages and `/public` +files) and before dynamic routes by default. + +```js +module.exports = { + async rewrites() { + return { + beforeFiles: [ + // These rewrites are checked after headers/redirects + // and before pages/public files which allows overriding + // page files + { + source: "/some-page", + destination: "/somewhere-else", + has: [{type: "query", key: "overrideMe"}], + }, + ], + afterFiles: [ + // These rewrites are checked after pages/public files + // are checked but before dynamic routes + { + source: "/non-existent", + destination: "/somewhere-else", + }, + ], + fallback: [ + // These rewrites are checked after both pages/public files + // and dynamic routes are checked + { + source: "/:path*", + destination: "https://my-old-site.com", + }, + ], + } + }, +} +``` + +## Rewrite parameters {#parameters} + +When using parameters in a rewrite the parameters will be passed in the +query by default when none of the parameters are used in the +`destination`. + +```js +module.exports = { + async rewrites() { + return [ + { + source: "/old-about/:path*", + destination: "/about", // The :path parameter isn't used here so will be automatically passed in the query + }, + ] + }, +} +``` + +If a parameter is used in the destination none of the parameters will be +automatically passed in the query. + +```js +module.exports = { + async rewrites() { + return [ + { + source: "/docs/:path*", + destination: "/:path*", // The :path parameter is used here so will not be automatically passed in the query + }, + ] + }, +} +``` + +You can still pass the parameters manually in the query if one is already +used in the destination by specifying the query in the `destination`. + +```js +module.exports = { + async rewrites() { + return [ + { + source: "/:first/:second", + destination: "/:first?second=:second", + // Since the :first parameter is used in the destination the :second parameter + // will not automatically be added in the query although we can manually add it + // as shown above + }, + ] + }, +} +``` + +## Path Matching {#path-matching} + +Path matches are allowed, for example `/blog/:slug` will match +`/blog/hello-world` (no nested paths): + +```js +module.exports = { + async rewrites() { + return [ + { + source: "/blog/:slug", + destination: "/news/:slug", // Matched parameters can be used in the destination + }, + ] + }, +} +``` + +### Wildcard Path Matching {#wildcard} + +To match a wildcard path you can use `*` after a parameter, for example +`/blog/:slug*` will match `/blog/a/b/c/d/hello-world`: + +```js +module.exports = { + async rewrites() { + return [ + { + source: "/blog/:slug*", + destination: "/news/:slug*", // Matched parameters can be used in the destination + }, + ] + }, +} +``` + +### Regex Path Matching {#regex} + +To match a regex path you can wrap the regex in parenthesis after a +parameter, for example `/blog/:slug(\\d{1,})` will match `/blog/123` but +not `/blog/abc`: + +```js +module.exports = { + async rewrites() { + return [ + { + source: "/old-blog/:post(\\d{1,})", + destination: "/blog/:post", // Matched parameters can be used in the destination + }, + ] + }, +} +``` + +The following characters `(`, `)`, `{`, `}`, `:`, `*`, `+`, `?` are used +for regex path matching, so when used in the `source` as non-special +values they must be escaped by adding `\\` before them: + +```js +module.exports = { + async redirects() { + return [ + { + // this will match `/english(default)/something` being requested + source: "/english\\(default\\)/:slug", + destination: "/en-us/:slug", + permanent: false, + }, + ] + }, +} +``` + +## Header, Cookie, and Query Matching {#header-cookie-matching} + +To only match a rewrite when header, cookie, or query values also match +the `has` field can be used. Both the `source` and all `has` items must +match for the rewrite to be applied. + +`has` items have the following fields: + +- `type`: `String` - must be either `header`, `cookie`, `host`, or + `query`. +- `key`: `String` - the key from the selected type to match against. +- `value`: `String` or `undefined` - the value to check for, if undefined + any value will match. A regex like string can be used to capture a + specific part of the value, e.g. if the value `first-(?<paramName>.*)` + is used for `first-second` then `second` will be usable in the + destination with `:paramName`. + +```js +module.exports = { + async rewrites() { + return [ + // if the header `x-rewrite-me` is present, + // this rewrite will be applied + { + source: "/:path*", + has: [ + { + type: "header", + key: "x-rewrite-me", + }, + ], + destination: "/another-page", + }, + // if the source, query, and cookie are matched, + // this rewrite will be applied + { + source: "/specific/:path*", + has: [ + { + type: "query", + key: "page", + // the page value will not be available in the + // destination since value is provided and doesn't + // use a named capture group e.g. (?<page>home) + value: "home", + }, + { + type: "cookie", + key: "authorized", + value: "true", + }, + ], + destination: "/:path*/home", + }, + // if the header `x-authorized` is present and + // contains a matching value, this rewrite will be applied + { + source: "/:path*", + has: [ + { + type: "header", + key: "x-authorized", + value: "(?<authorized>yes|true)", + }, + ], + destination: "/home?authorized=:authorized", + }, + // if the host is `example.com`, + // this rewrite will be applied + { + source: "/:path*", + has: [ + { + type: "host", + value: "example.com", + }, + ], + destination: "/another-page", + }, + ] + }, +} +``` + +## Rewriting to an external URL {#external} + +Rewrites allow you to rewrite to an external url. This is especially +useful for incrementally adopting Blitz.js + +```js +module.exports = { + async rewrites() { + return [ + { + source: "/blog/:slug", + destination: "https://example.com/blog/:slug", // Matched parameters can be used in the destination + }, + ] + }, +} +``` + +### Incremental adoption of Blitz.js {#incremental} + +You can also have Blitz.js fall back to proxying to an existing website +after checking all Blitz.js routes. + +This way you don't have to change the rewrites configuration when +migrating more pages to Blitz.js + +```js +module.exports = { + async rewrites() { + return { + fallback: [ + { + source: "/:path*", + destination: `https://custom-routes-proxying-endpoint.vercel.app/:path*`, + }, + ], + } + }, +} +``` + +### Rewrites with basePath support {#basepath} + +When leveraging [`basePath` support](./blitz-config#base-path) with +rewrites each `source` and `destination` is automatically prefixed with +the `basePath` unless you add `basePath: false` to the rewrite: + +```js +module.exports = { + basePath: "/docs", + + async rewrites() { + return [ + { + source: "/with-basePath", // automatically becomes /docs/with-basePath + destination: "/another", // automatically becomes /docs/another + }, + { + // does not add /docs to /without-basePath since basePath: false is set + // Note: this can not be used for internal rewrites e.g. `destination: '/another'` + source: "/without-basePath", + destination: "https://example.com", + basePath: false, + }, + ] + }, +} +``` + +### Rewrites with i18n support {#18n} + +When leveraging [`i18n` support](./i18n-routing) with rewrites each +`source` and `destination` is automatically prefixed to handle the +configured `locales` unless you add `locale: false` to the rewrite. If +`locale: false` is used you must prefix the `source` and `destination` +with a locale for it to be matched correctly. + +```js +module.exports = { + i18n: { + locales: ["en", "fr", "de"], + defaultLocale: "en", + }, + + async rewrites() { + return [ + { + source: "/with-locale", // automatically handles all locales + destination: "/another", // automatically passes the locale on + }, + { + // does not handle locales automatically since locale: false is set + source: "/nl/with-locale-manual", + destination: "/nl/another", + locale: false, + }, + { + // this matches '/' since `en` is the defaultLocale + source: "/en", + destination: "/en/another", + locale: false, + }, + { + // this gets converted to /(en|fr|de)/(.*) so will not match the top-level + // `/` or `/fr` routes like /:path* would + source: "/(.*)", + destination: "/another", + }, + ] + }, +} +``` From 072cb16a202efde536ffa57d56be6350a627f115 Mon Sep 17 00:00:00 2001 From: Brandon Bayer <b@bayer.ws> Date: Thu, 29 Apr 2021 18:18:05 -0400 Subject: [PATCH 38/67] another update --- app/pages/docs/blitz-config.mdx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/app/pages/docs/blitz-config.mdx b/app/pages/docs/blitz-config.mdx index b1dc657..0c74d16 100644 --- a/app/pages/docs/blitz-config.mdx +++ b/app/pages/docs/blitz-config.mdx @@ -380,19 +380,18 @@ module.exports = { } ``` -### React Mode {#react-mode} +### React Root Mode {#react-mode} By default Blitz uses [React Concurrent Mode](https://reactjs.org/docs/concurrent-mode-intro.html). -You can disable it by changing `experimental.reactMode` to `legacy`. +You can disable it by changing `experimental.reactRoot` to `false`. -- Default: `concurrent` -- Options: `concurrent` | `legacy` +- Default: `true` ```js module.exports = { experimental: { - reactMode: "legacy", + reactRoot: false, }, } ``` From a383c864e95fd17cc97fa81682ec122329928c7e Mon Sep 17 00:00:00 2001 From: JuanM04 <me@juanm04.com> Date: Thu, 29 Apr 2021 19:22:53 -0300 Subject: [PATCH 39/67] Changed Prettier config to match new app config --- .prettierrc | 4 +- app/pages/docs/api-routes.mdx | 24 ++++----- app/pages/docs/app-component.mdx | 2 +- app/pages/docs/auth-utils.mdx | 28 +++++----- app/pages/docs/authorization.mdx | 27 +++++----- .../background-processing-with-quirrel.mdx | 30 +++++------ app/pages/docs/blitz-config.mdx | 24 +++++---- app/pages/docs/code-splitting.mdx | 14 ++--- app/pages/docs/css.mdx | 2 +- app/pages/docs/custom-server.mdx | 12 ++--- app/pages/docs/database-seeds.mdx | 2 +- app/pages/docs/document-component.mdx | 2 +- app/pages/docs/error-handling.mdx | 20 ++++--- app/pages/docs/error-pages.mdx | 12 ++--- app/pages/docs/get-server-side-props.mdx | 2 +- app/pages/docs/get-static-paths.mdx | 2 +- app/pages/docs/get-static-props.mdx | 10 ++-- app/pages/docs/head-component.mdx | 4 +- app/pages/docs/i18n-routing.mdx | 16 +++--- app/pages/docs/image-optimization.mdx | 8 +-- app/pages/docs/impersonation.mdx | 20 +++---- app/pages/docs/link.mdx | 14 ++--- app/pages/docs/measuring-performance.mdx | 6 +-- app/pages/docs/middleware.mdx | 18 +++---- app/pages/docs/multitenancy.mdx | 44 +++++++-------- app/pages/docs/mutation-resolvers.mdx | 6 +-- app/pages/docs/mutation-usage.mdx | 28 +++++----- app/pages/docs/passportjs.mdx | 38 ++++++------- app/pages/docs/preview-mode.mdx | 8 +-- app/pages/docs/prisma.mdx | 4 +- app/pages/docs/query-resolvers.mdx | 6 +-- app/pages/docs/query-usage.mdx | 26 ++++----- app/pages/docs/redirects.mdx | 8 +-- app/pages/docs/resolver-client-utilities.mdx | 34 +++++++----- app/pages/docs/resolver-server-utilities.mdx | 33 ++++++------ app/pages/docs/route-params-query.mdx | 14 ++--- app/pages/docs/router.mdx | 42 +++++++-------- app/pages/docs/routing.mdx | 14 ++--- app/pages/docs/session-management.mdx | 54 +++++++++---------- app/pages/docs/shallow-routing.mdx | 8 +-- app/pages/docs/stickers.mdx | 2 +- app/pages/docs/tutorial.mdx | 23 ++++---- app/pages/docs/use-infinite-query.mdx | 28 +++++----- app/pages/docs/use-paginated-query.mdx | 18 +++---- app/pages/docs/use-query.mdx | 6 +-- app/pages/docs/webpack-config.mdx | 2 +- app/pages/docs/writing-recipes.mdx | 28 +++++----- 47 files changed, 408 insertions(+), 369 deletions(-) diff --git a/.prettierrc b/.prettierrc index 77b066d..abe4813 100644 --- a/.prettierrc +++ b/.prettierrc @@ -8,7 +8,9 @@ "files": "*.mdx", "options": { "printWidth": 74, - "proseWrap": "always" + "proseWrap": "always", + "bracketSpacing": true, + "trailingComma": "es5" } } ] diff --git a/app/pages/docs/api-routes.mdx b/app/pages/docs/api-routes.mdx index 83573a2..ff15244 100644 --- a/app/pages/docs/api-routes.mdx +++ b/app/pages/docs/api-routes.mdx @@ -22,7 +22,7 @@ response: const handler = (req: BlitzApiRequest, res: BlitzApiResponse) => { res.statusCode = 200 res.setHeader("Content-Type", "application/json") - res.end(JSON.stringify({name: "John Doe"})) + res.end(JSON.stringify({ name: "John Doe" })) } export default handler ``` @@ -44,7 +44,7 @@ If you want to avoid writting the types `BlitzApiRequest` and const handler: BlitzApiHandler = (req, res) => { res.statusCode = 200 res.setHeader("Content-Type", "application/json") - res.end(JSON.stringify({name: "John Doe"})) + res.end(JSON.stringify({ name: "John Doe" })) } export default handler ``` @@ -81,25 +81,25 @@ You can use the `getSession` function to get the session of the user. Here is an example using session in a API route `app/api/customRoute.tsx`: ```ts -import {getSession, BlitzApiRequest, BlitzApiResponse} from "blitz" +import { getSession, BlitzApiRequest, BlitzApiResponse } from "blitz" export default async function customRoute( req: BlitzApiRequest, - res: BlitzApiResponse, + res: BlitzApiResponse ) { const session = await getSession(req, res) console.log("User ID:", session.userId) res.statusCode = 200 res.setHeader("Content-Type", "application/json") - res.end(JSON.stringify({userId: session.userId})) + res.end(JSON.stringify({ userId: session.userId })) } ``` This is called in the frontend like this: ```ts -import {getAntiCSRFToken} from "blitz" +import { getAntiCSRFToken } from "blitz" const antiCSRFToken = getAntiCSRFToken() const response = await window.fetch("/api/customRoute", { @@ -121,7 +121,7 @@ For example, the API route `app/api/post/[pid].js` has the following code: ```ts const handler: BlitzApiHandler = (req, res) => { const { - query: {pid}, + query: { pid }, } = req res.end(`Post: ${pid}`) @@ -165,14 +165,14 @@ example) to the api handler, and it will always be an array, so, the path `/api/post/a` will have the following `query` object: ```json -{"slug": ["a"]} +{ "slug": ["a"] } ``` And in the case of `/api/post/a/b`, and any other matching path, new parameters will be added to the array, like so: ```json -{"slug": ["a", "b"]} +{ "slug": ["a", "b"] } ``` An API route for `app/api/post/[...slug].js` could look like this: @@ -180,7 +180,7 @@ An API route for `app/api/post/[...slug].js` could look like this: ```ts const handler: BlitzApiHandler = (req, res) => { const { - query: {slug}, + query: { slug }, } = req res.end(`Post: ${slug.join(", ")}`) @@ -226,7 +226,7 @@ endpoints, take a look at the following example: ```ts const handler: BlitzApiHandler = (req, res) => { - res.status(200).json({name: "Blitz.js"}) + res.status(200).json({ name: "Blitz.js" }) } export default handler ``` @@ -355,7 +355,7 @@ async function handler(req: BlitzApiRequest, res: BlitzApiResponse) { await runMiddleware(req, res, cors) // Rest of the API logic - res.json({message: "Hello Everyone!"}) + res.json({ message: "Hello Everyone!" }) } export default handler diff --git a/app/pages/docs/app-component.mdx b/app/pages/docs/app-component.mdx index 4539675..8e9a36e 100644 --- a/app/pages/docs/app-component.mdx +++ b/app/pages/docs/app-component.mdx @@ -16,7 +16,7 @@ The default `_app.tsx` file looks like this: ```tsx // app/pages/_app.tsx -export default function App({Component, pageProps}) { +export default function App({ Component, pageProps }) { return <Component {...pageProps} /> } ``` diff --git a/app/pages/docs/auth-utils.mdx b/app/pages/docs/auth-utils.mdx index 3c3780b..a88abb8 100644 --- a/app/pages/docs/auth-utils.mdx +++ b/app/pages/docs/auth-utils.mdx @@ -10,7 +10,7 @@ sidebar_label: Hooks & Utilities ### Example {#example} ```ts -import {useSession} from "blitz" +import { useSession } from "blitz" const session = useSession() ``` @@ -36,7 +36,7 @@ This will throw `AuthenticationError` if the user is not logged in ### Example {#example-1} ```ts -import {useAuthenticatedSession} from "blitz" +import { useAuthenticatedSession } from "blitz" const session = useAuthenticatedSession() ``` @@ -62,7 +62,7 @@ This will throw `AuthenticationError` if the user is not logged in ### Example {#example-2} ```ts -import {useAuthorize} from "blitz" +import { useAuthorize } from "blitz" useAuthorize() ``` @@ -85,7 +85,7 @@ for logged out users. ### Example {#example-3} ```ts -import {useRedirectAuthenticated} from "blitz" +import { useRedirectAuthenticated } from "blitz" useRedirectAuthenticated("/dashboard") ``` @@ -111,7 +111,7 @@ like password resets. #### Example Usage ```ts -import {generateToken} from "blitz" +import { generateToken } from "blitz" const token = generateToken() ``` @@ -128,7 +128,7 @@ tokens before saving them in the database. #### Example Usage ```ts -import {hash256} from "blitz" +import { hash256 } from "blitz" const hashedToken = hash256(token) ``` @@ -140,7 +140,7 @@ const hashedToken = hash256(token) a nice way to hash passwords and verify password hashes. ```ts -import {SecurePassword} from "blitz" +import { SecurePassword } from "blitz" await SecurePassword.hash(password) await SecurePassword.verify(passwordHash, password) @@ -180,31 +180,31 @@ Size of the `hash` Buffer returned by `hash` and `hashSync` and used by #### Example Usage ```ts -import {SecurePassword, AuthenticationError} from "blitz" +import { SecurePassword, AuthenticationError } from "blitz" import db from "db" export const authenticateUser = async ( email: string, - password: string, + password: string ) => { - const user = await db.user.findFirst({where: {email}}) + const user = await db.user.findFirst({ where: { email } }) if (!user) throw new AuthenticationError() const result = await SecurePassword.verify( user.hashedPassword, - password, + password ) if (result === SecurePassword.VALID_NEEDS_REHASH) { // Upgrade hashed password with a more secure hash const improvedHash = await SecurePassword.hash(password) await db.user.update({ - where: {id: user.id}, - data: {hashedPassword: improvedHash}, + where: { id: user.id }, + data: { hashedPassword: improvedHash }, }) } - const {hashedPassword, ...rest} = user + const { hashedPassword, ...rest } = user return rest } ``` diff --git a/app/pages/docs/authorization.mdx b/app/pages/docs/authorization.mdx index 3e3c562..b9fe196 100644 --- a/app/pages/docs/authorization.mdx +++ b/app/pages/docs/authorization.mdx @@ -78,7 +78,7 @@ which we include in all our code generation templates. Without this, users may be able to access data or perform actions that are forbidden. ```ts -import {resolver} from "blitz" +import { resolver } from "blitz" import db from "db" import * as z from "zod" @@ -97,9 +97,9 @@ export default resolver.pipe( resolver.authorize(), async (input, ctx) => { // TODO: in multi-tenant app, you must add validation to ensure correct tenant - const projects = await db.projects.create({data: input}) + const projects = await db.projects.create({ data: input }) return projects - }, + } ) ``` @@ -221,7 +221,7 @@ on first load. You can fix that by setting [`Page.suppressFirstRenderFlicker = true`](./pages##automatic-static-optimization) ```tsx -import {useSession} from "blitz" +import { useSession } from "blitz" const session = useSession() @@ -238,7 +238,7 @@ New Blitz apps by default have a `useCurrentUser()` hook and a corresponding `getCurrentUser` query. ```tsx -import {useCurrentUser} from "app/hooks/useCurrentUser" +import { useCurrentUser } from "app/hooks/useCurrentUser" const user = useCurrentUser() @@ -265,15 +265,15 @@ Always returns a boolean indicating if user is authorized commonly use to secure your queries and mutations. ```ts -import {Ctx} from "blitz" -import {GetUserInput} from "./somewhere" +import { Ctx } from "blitz" +import { GetUserInput } from "./somewhere" -export default async function getUser({where}: GetUserInput, ctx: Ctx) { +export default async function getUser({ where }: GetUserInput, ctx: Ctx) { // highlight-start ctx.session.$authorize("admin") // highlight-end - return await db.user.findOne({where}) + return await db.user.findOne({ where }) } ``` @@ -286,7 +286,7 @@ set up by default in new apps). ```js // blitz.config.js -const {sessionMiddleware, simpleRolesIsAuthorized} = require("blitz") +const { sessionMiddleware, simpleRolesIsAuthorized } = require("blitz") module.exports = { middleware: [ @@ -302,7 +302,7 @@ module.exports = { And if using Typescript, set the type in `types.ts` like this: ```ts -import {SimpleRolesIsAuthorized} from "blitz" +import { SimpleRolesIsAuthorized } from "blitz" type Role = "ADMIN" | "USER" @@ -354,7 +354,10 @@ type CustomIsAuthorizedArgs = { ctx: any args: [/* args that you want for session.$authorize(...args) */] } -export function customIsAuthorized({ctx, args}: CustomIsAuthorizedArgs) { +export function customIsAuthorized({ + ctx, + args, +}: CustomIsAuthorizedArgs) { // can access ctx.session, ctx.session.userId, etc } ``` diff --git a/app/pages/docs/background-processing-with-quirrel.mdx b/app/pages/docs/background-processing-with-quirrel.mdx index 7b4bd08..4a84534 100644 --- a/app/pages/docs/background-processing-with-quirrel.mdx +++ b/app/pages/docs/background-processing-with-quirrel.mdx @@ -57,7 +57,7 @@ First, you define your ```ts // app/api/booking-reminder import db from "db" -import {Queue} from "quirrel/blitz" +import { Queue } from "quirrel/blitz" import sms from "some-sms-provider" // it's important to export it as default @@ -65,15 +65,15 @@ export default Queue( "api/booking-reminder", // 👈 the route that it's reachable on async (bookingId: number) => { const booking = await db.booking.findUnique({ - where: {id: bookingId}, - include: {user: true, event: true}, + where: { id: bookingId }, + include: { user: true, event: true }, }) await sms.send({ to: booking.user.phoneNumber, content: `Put on your dancing shoes for ${booking.event.title} 🕺`, }) - }, + } ) ``` @@ -137,7 +137,7 @@ the perfect fit. ```ts // app/api/monthly-invoice import db from "db" -import {CronJob} from "quirrel/blitz" +import { CronJob } from "quirrel/blitz" import stripe from "stripe" export default CronJob( @@ -148,9 +148,9 @@ export default CronJob( await Promise.all( customers.map(async (customer) => { await stripe.finalizeInvoice(customer.stripeId) - }), + }) ) - }, + } ) ``` @@ -161,8 +161,8 @@ Again, [`CronJob`](https://docs.quirrel.dev/api/cronjob) is a great fit. ```ts // app/api/remove-old-data import db from "db" -import {CronJob} from "quirrel/blitz" -import {subDays} from "date-fns" +import { CronJob } from "quirrel/blitz" +import { subDays } from "date-fns" export default CronJob( "api/remove-old-data", // 👈 the route that it's reachable on @@ -178,7 +178,7 @@ export default CronJob( }, }, }) - }, + } ) ``` @@ -204,7 +204,7 @@ import csvProcessingQueue from "app/api/process-csv" export default async function uploadCsvForProcessing(data: string) { const record = await db.uploadedCsv.create({ - data: {data}, + data: { data }, }) await csvProcessingQueue.enqueue(record.id) @@ -221,16 +221,16 @@ set it to true). ```ts // app/api/process-csv import db from "db" -import {Queue} from "quirrel/blitz" +import { Queue } from "quirrel/blitz" export default Queue("api/process-csv", async (uploadId: number) => { const upload = await db.uplodadedCsv.findUnique({ - where: {id: uploadId}, + where: { id: uploadId }, }) await doYourProcessing(upload.data) - await db.uplodadedCsv.delete({where: {id: uploadId}}) + await db.uplodadedCsv.delete({ where: { id: uploadId } }) }) ``` @@ -242,7 +242,7 @@ can look it up in your own database: import db from "db" export default async function hasFinishedProcessing(uploadId: number) { - const count = await db.uploadedCsv.count({where: {uploadId}}) + const count = await db.uploadedCsv.count({ where: { uploadId } }) return count === 0 } ``` diff --git a/app/pages/docs/blitz-config.mdx b/app/pages/docs/blitz-config.mdx index f389057..42604a8 100644 --- a/app/pages/docs/blitz-config.mdx +++ b/app/pages/docs/blitz-config.mdx @@ -20,7 +20,7 @@ module.exports = { You can also use a function: ```js -module.exports = (phase, {defaultConfig}) => { +module.exports = (phase, { defaultConfig }) => { return { /* config options here */ } @@ -33,9 +33,9 @@ can see the available phases Phases can be imported from `next/constants`: ```js -const {PHASE_DEVELOPMENT_SERVER} = require("next/constants") +const { PHASE_DEVELOPMENT_SERVER } = require("next/constants") -module.exports = (phase, {defaultConfig}) => { +module.exports = (phase, { defaultConfig }) => { if (phase === PHASE_DEVELOPMENT_SERVER) { return { /* development only config options here */ @@ -1041,16 +1041,22 @@ Original pages: module.exports = { exportPathMap: async function ( defaultPathMap, - {dev, dir, outDir, distDir, buildId}, + { dev, dir, outDir, distDir, buildId } ) { return { - "/": {page: "/"}, - "/about": {page: "/about"}, - "/p/hello-nextjs": {page: "/post", query: {title: "hello-nextjs"}}, - "/p/learn-nextjs": {page: "/post", query: {title: "learn-nextjs"}}, + "/": { page: "/" }, + "/about": { page: "/about" }, + "/p/hello-nextjs": { + page: "/post", + query: { title: "hello-nextjs" }, + }, + "/p/learn-nextjs": { + page: "/post", + query: { title: "learn-nextjs" }, + }, "/p/deploy-nextjs": { page: "/post", - query: {title: "deploy-nextjs"}, + query: { title: "deploy-nextjs" }, }, } }, diff --git a/app/pages/docs/code-splitting.mdx b/app/pages/docs/code-splitting.mdx index e5dea78..7d5f409 100644 --- a/app/pages/docs/code-splitting.mdx +++ b/app/pages/docs/code-splitting.mdx @@ -34,7 +34,7 @@ In the following example, the module `../components/hello` will be dynamically loaded by the page: ```jsx -import {dynamic} from "blitz" +import { dynamic } from "blitz" const DynamicComponent = dynamic(() => import("../components/hello")) @@ -74,10 +74,10 @@ returned by like so: ```jsx -import {dynamic} from "blitz" +import { dynamic } from "blitz" const DynamicComponent = dynamic(() => - import("../components/hello").then((mod) => mod.Hello), + import("../components/hello").then((mod) => mod.Hello) ) function Home() { @@ -99,13 +99,13 @@ An optional `loading` component can be added to render a loading state while the dynamic component is being loaded. For example: ```jsx -import {dynamic} from "blitz" +import { dynamic } from "blitz" const DynamicComponentWithCustomLoading = dynamic( () => import("../components/hello"), { loading: () => <p>...</p>, - }, + } ) function Home() { @@ -129,13 +129,13 @@ when the module includes a library that only works in the browser. Take a look at the following example: ```jsx -import {dynamic} from "blitz" +import { dynamic } from "blitz" const DynamicComponentWithNoSSR = dynamic( () => import("../components/hello3"), { ssr: false, - }, + } ) function Home() { diff --git a/app/pages/docs/css.mdx b/app/pages/docs/css.mdx index a943398..e13fd76 100644 --- a/app/pages/docs/css.mdx +++ b/app/pages/docs/css.mdx @@ -27,7 +27,7 @@ Then, import the `styles.css` file. ```jsx import "../styles.css" -export default function App({Component, pageProps}) { +export default function App({ Component, pageProps }) { return <Component {...pageProps} /> } ``` diff --git a/app/pages/docs/custom-server.mdx b/app/pages/docs/custom-server.mdx index aaf3f14..4528a03 100644 --- a/app/pages/docs/custom-server.mdx +++ b/app/pages/docs/custom-server.mdx @@ -20,13 +20,13 @@ Here's an example custom server: ```ts // server.ts import blitz from "blitz/custom-server" -import {createServer} from "http" -import {parse} from "url" -import {log} from "@blitzjs/display" +import { createServer } from "http" +import { parse } from "url" +import { log } from "@blitzjs/display" -const {PORT = "3000"} = process.env +const { PORT = "3000" } = process.env const dev = process.env.NODE_ENV !== "production" -const app = blitz({dev}) +const app = blitz({ dev }) const handle = app.getRequestHandler() app.prepare().then(() => { @@ -34,7 +34,7 @@ app.prepare().then(() => { // Be sure to pass `true` as the second argument to `url.parse`. // This tells it to parse the query portion of the URL. const parsedUrl = parse(req.url!, true) - const {pathname} = parsedUrl + const { pathname } = parsedUrl if (pathname === "/hello") { res.writeHead(200).end("world") diff --git a/app/pages/docs/database-seeds.mdx b/app/pages/docs/database-seeds.mdx index 1b689ef..6cb9974 100644 --- a/app/pages/docs/database-seeds.mdx +++ b/app/pages/docs/database-seeds.mdx @@ -40,7 +40,7 @@ model Task { import db from "./index" const seed = async () => { - const project = await db.project.create({data: {name: "FooBar"}}) + const project = await db.project.create({ data: { name: "FooBar" } }) for (let i = 0; i < 5; i++) { await db.task.create({ diff --git a/app/pages/docs/document-component.mdx b/app/pages/docs/document-component.mdx index 22fe218..e5068e9 100644 --- a/app/pages/docs/document-component.mdx +++ b/app/pages/docs/document-component.mdx @@ -92,7 +92,7 @@ The `ctx` parameter is an object containing the following keys: It takes as argument an options object for further customization: ```jsx -import {Document} from "blitz" +import { Document } from "blitz" class MyDocument extends Document { static async getInitialProps(ctx) { diff --git a/app/pages/docs/error-handling.mdx b/app/pages/docs/error-handling.mdx index df0f071..c8453ca 100644 --- a/app/pages/docs/error-handling.mdx +++ b/app/pages/docs/error-handling.mdx @@ -30,7 +30,7 @@ curious, you can [see the source code for these](https://github.com/blitz-js/blitz/blob/canary/packages/core/src/errors.ts). ```ts -import {AuthenticationError} from "blitz" +import { AuthenticationError } from "blitz" try { throw new AuthenticationError() @@ -57,14 +57,18 @@ It looks something like this: ```tsx // app/pages/_app.tsx -import {AppProps, ErrorComponent, useQueryErrorResetBoundary} from "blitz" -import {ErrorBoundary} from "react-error-boundary" +import { + AppProps, + ErrorComponent, + useQueryErrorResetBoundary, +} from "blitz" +import { ErrorBoundary } from "react-error-boundary" import LoginForm from "app/auth/components/LoginForm" -export default function App({Component, pageProps}: AppProps) { +export default function App({ Component, pageProps }: AppProps) { // This ensures the Blitz useQuery hooks will automatically refetch // data any time you reset the error boundary - const {reset} = useQueryErrorResetBoundary() + const { reset } = useQueryErrorResetBoundary() return ( <ErrorBoundary FallbackComponent={RootErrorFallback} onReset={reset}> @@ -73,7 +77,7 @@ export default function App({Component, pageProps}: AppProps) { ) } -function RootErrorFallback({error, resetErrorBoundary}) { +function RootErrorFallback({ error, resetErrorBoundary }) { if (error.name === "AuthenticationError") { return <LoginForm onSuccess={resetErrorBoundary} /> } else if (error.name === "AuthorizationError") { @@ -127,7 +131,7 @@ import SuperJson from "superjson" export class UsernameTakenError extends Error { name = "UsernameTakenError" - constructor({suggestedUserName}) { + constructor({ suggestedUserName }) { super() this.suggestedUserName = suggestedUserName } @@ -136,7 +140,7 @@ export class UsernameTakenError extends Error { SuperJson.registerClass(UsernameTakenError) SuperJson.allowErrorProps("suggestedUserName") -throw new UsernameTakenError({suggestedUserName: "second_best"}) +throw new UsernameTakenError({ suggestedUserName: "second_best" }) ``` Note that you **must register it with SuperJson** as shown above in order diff --git a/app/pages/docs/error-pages.mdx b/app/pages/docs/error-pages.mdx index 15d3d81..9b9e587 100644 --- a/app/pages/docs/error-pages.mdx +++ b/app/pages/docs/error-pages.mdx @@ -34,7 +34,7 @@ component. If you wish to override it, define the file `app/pages/_error.js` and add the following code: ```jsx -function Error({statusCode}) { +function Error({ statusCode }) { return ( <p> {statusCode @@ -44,9 +44,9 @@ function Error({statusCode}) { ) } -Error.getInitialProps = ({res, err}) => { +Error.getInitialProps = ({ res, err }) => { const statusCode = res ? res.statusCode : err ? err.statusCode : 404 - return {statusCode} + return { statusCode } } export default Error @@ -66,7 +66,7 @@ in suspense. In essence, the default configuration is: ```tsx -export default function App({Component, pageProps}: AppProps) { +export default function App({ Component, pageProps }: AppProps) { return ( <ErrorBoundary FallbackComponent={RootErrorFallback} {...etc}> {getLayout(<Component {...pageProps} />)} @@ -159,7 +159,7 @@ It accepts two props: - `title` - a string to display as the error message ```jsx -import {ErrorComponent} from "blitz" +import { ErrorComponent } from "blitz" export default function Page() { return <ErrorComponent statusCode={404} /> @@ -167,7 +167,7 @@ export default function Page() { ``` ```jsx -import {ErrorComponent} from "blitz" +import { ErrorComponent } from "blitz" export default function Page() { return <ErrorComponent statusCode={401} title="Unauthorized" /> diff --git a/app/pages/docs/get-server-side-props.mdx b/app/pages/docs/get-server-side-props.mdx index 199c139..3fbbc59 100644 --- a/app/pages/docs/get-server-side-props.mdx +++ b/app/pages/docs/get-server-side-props.mdx @@ -134,7 +134,7 @@ fetching data on the client side. For TypeScript, you can use the `GetServerSideProps` type from `blitz`: ```ts -import {GetServerSideProps} from "blitz" +import { GetServerSideProps } from "blitz" export const getServerSideProps: GetServerSideProps = async (context) => { // ... diff --git a/app/pages/docs/get-static-paths.mdx b/app/pages/docs/get-static-paths.mdx index 1297a11..ea77fec 100644 --- a/app/pages/docs/get-static-paths.mdx +++ b/app/pages/docs/get-static-paths.mdx @@ -239,7 +239,7 @@ that use dynamic routes. For TypeScript, you can use the `GetStaticPaths` type from `blitz`: ```ts -import {GetStaticPaths} from "blitz" +import { GetStaticPaths } from "blitz" export const getStaticPaths: GetStaticPaths = async () => { // ... diff --git a/app/pages/docs/get-static-props.mdx b/app/pages/docs/get-static-props.mdx index 830227b..8304fbb 100644 --- a/app/pages/docs/get-static-props.mdx +++ b/app/pages/docs/get-static-props.mdx @@ -170,7 +170,7 @@ You should use `getStaticProps` if: For TypeScript, you can use the `GetStaticProps` type from `blitz`: ```ts -import {GetStaticProps} from "blitz" +import { GetStaticProps } from "blitz" export const getStaticProps: GetStaticProps = async (context) => { // ... @@ -181,7 +181,7 @@ If you want to get inferred typings for your props, you can use `InferGetStaticPropsType<typeof getStaticProps>`, like this: ```tsx -import {InferGetStaticPropsType} from "blitz" +import { InferGetStaticPropsType } from "blitz" type Post = { author: string @@ -199,7 +199,7 @@ export const getStaticProps = async () => { } } -function Blog({posts}: InferGetStaticPropsType<typeof getStaticProps>) { +function Blog({ posts }: InferGetStaticPropsType<typeof getStaticProps>) { // will resolve posts to type Post[] } @@ -222,7 +222,7 @@ Consider our previous [`getStaticProps` example](#simple-example), but now with regeneration enabled: ```jsx -function Blog({posts}) { +function Blog({ posts }) { return ( <ul> {posts.map((post) => ( @@ -297,7 +297,7 @@ import fs from "fs" import path from "path" // posts will be populated at build time by getStaticProps() -function Blog({posts}) { +function Blog({ posts }) { return ( <ul> {posts.map((post) => ( diff --git a/app/pages/docs/head-component.mdx b/app/pages/docs/head-component.mdx index e82b78b..e1da3a6 100644 --- a/app/pages/docs/head-component.mdx +++ b/app/pages/docs/head-component.mdx @@ -7,7 +7,7 @@ We expose a built-in component for appending elements to the `head` of the page: ```jsx -import {Head} from "blitz" +import { Head } from "blitz" function IndexPage() { return ( @@ -32,7 +32,7 @@ which will make sure the tag is only rendered once, as in the following example: ```jsx -import {Head} from "blitz" +import { Head } from "blitz" function IndexPage() { return ( diff --git a/app/pages/docs/i18n-routing.mdx b/app/pages/docs/i18n-routing.mdx index 4af6f30..39aff5f 100644 --- a/app/pages/docs/i18n-routing.mdx +++ b/app/pages/docs/i18n-routing.mdx @@ -194,7 +194,7 @@ locale from the currently active one. If no `locale` prop is provided, the currently active `locale` is used during client-transitions. For example: ```jsx -import {Link} from "blitz" +import { Link } from "blitz" export default function IndexPage(props) { return ( @@ -209,7 +209,7 @@ When using the `Router` methods directly, you can specify the `locale` that should be used via the transition options. For example: ```jsx -import {useRouter} from "blitz" +import { useRouter } from "blitz" export default function IndexPage(props) { const router = useRouter() @@ -217,7 +217,7 @@ export default function IndexPage(props) { return ( <div onClick={() => { - router.push("/another", "/another", {locale: "fr"}) + router.push("/another", "/another", { locale: "fr" }) }} > to /fr/another @@ -230,7 +230,7 @@ If you have a `href` that already includes the locale you can opt-out of automatically handling the locale prefixing: ```jsx -import {Link} from "blitz" +import { Link } from "blitz" export default function IndexPage(props) { return ( @@ -279,7 +279,7 @@ being pre-rendered, you can return `notFound: true` from `getStaticProps` and this variant of the page will not be generated. ```js -export async function getStaticProps({locale}) { +export async function getStaticProps({ locale }) { // Call an external API endpoint to get posts. // You can use any data fetching library const res = await fetch(`https://.../posts?locale=${locale}`) @@ -311,11 +311,11 @@ specifying which locale you want to render. For example: ```js // pages/blog/[slug].js -export const getStaticPaths = ({locales}) => { +export const getStaticPaths = ({ locales }) => { return { paths: [ - {params: {slug: "post-1"}, locale: "en-US"}, - {params: {slug: "post-1"}, locale: "fr"}, + { params: { slug: "post-1" }, locale: "en-US" }, + { params: { slug: "post-1" }, locale: "fr" }, ], fallback: true, } diff --git a/app/pages/docs/image-optimization.mdx b/app/pages/docs/image-optimization.mdx index a5ccc58..d725eaf 100644 --- a/app/pages/docs/image-optimization.mdx +++ b/app/pages/docs/image-optimization.mdx @@ -39,7 +39,7 @@ Images are always rendered in such a way as to avoid To add an image to your application, import the `Image` component: ```jsx -import {Image} from "blitz" +import { Image } from "blitz" function Home() { return ( @@ -185,7 +185,7 @@ For an example, consider a project with the following files: We can serve an optimized image like so: ```jsx -import {Image} from "blitz" +import { Image } from "blitz" function Home() { return ( @@ -270,9 +270,9 @@ A custom function used to resolve URLs. Defaults to - `quality` ```js -import {Image} from "blitz" +import { Image } from "blitz" -const myLoader = ({src, width, quality}) => { +const myLoader = ({ src, width, quality }) => { return `https://example.com/${src}?w=${width}&q=${quality || 75}` } diff --git a/app/pages/docs/impersonation.mdx b/app/pages/docs/impersonation.mdx index 4a4ae94..0d2c6e0 100644 --- a/app/pages/docs/impersonation.mdx +++ b/app/pages/docs/impersonation.mdx @@ -15,8 +15,8 @@ First, add a `impersonatingFromUserId` type to your ```ts // types.ts -import {DefaultCtx, SessionContext, DefaultPublicData} from "blitz" -import {User} from "db" +import { DefaultCtx, SessionContext, DefaultPublicData } from "blitz" +import { User } from "db" declare module "blitz" { export interface Ctx extends DefaultCtx { @@ -36,7 +36,7 @@ start the impersonation as well as stopping it. ```ts // app/auth/mutations/impersonateUser.ts -import {resolver} from "blitz" +import { resolver } from "blitz" import db from "db" import * as z from "zod" @@ -47,8 +47,8 @@ export const ImpersonateUserInput = z.object({ export default resolver.pipe( resolver.zod(ImpersonateUserInput), resolver.authorize(), - async ({userId}, ctx) => { - const user = await db.user.findFirst({where: {id: userId}}) + async ({ userId }, ctx) => { + const user = await db.user.findFirst({ where: { id: userId } }) if (!user) throw new Error("Could not find user id " + userId) await ctx.session.$create({ @@ -59,13 +59,13 @@ export default resolver.pipe( }) return user - }, + } ) ``` ```ts // app/auth/mutations/stopImpersonating.ts -import {resolver} from "blitz" +import { resolver } from "blitz" import db from "db" export default resolver.pipe(resolver.authorize(), async (_, ctx) => { @@ -76,7 +76,7 @@ export default resolver.pipe(resolver.authorize(), async (_, ctx) => { } const user = await db.user.findFirst({ - where: {id: userId}, + where: { id: userId }, }) if (!user) throw new Error("Could not find user id " + userId) @@ -94,7 +94,7 @@ export default resolver.pipe(resolver.authorize(), async (_, ctx) => { Add a form similar to this in order to switch users. ```tsx -import {useMutation, queryClient} from "blitz" +import { useMutation, queryClient } from "blitz" import impersonateUser, { ImpersonateUserInput, } from "app/auth/mutations/impersonateUser" @@ -131,7 +131,7 @@ Lastly, add the following component at the top of your Layout(s). ```tsx // app/core/components/ImpersonatingUserNotice.tsx -import {invoke, useSession, queryClient} from "blitz" +import { invoke, useSession, queryClient } from "blitz" import stopImpersonating from "app/auth/mutations/stopImpersonating" export const ImpersonatingUserNotice = () => { diff --git a/app/pages/docs/link.mdx b/app/pages/docs/link.mdx index 5344555..9cbe5e0 100644 --- a/app/pages/docs/link.mdx +++ b/app/pages/docs/link.mdx @@ -69,7 +69,7 @@ A `Link` to a dynamic route works like the other links. A link to the page `pages/post/[pid].js` will look like this: ```jsx -<Link href={Routes.Post({pid: "abc"})}> +<Link href={Routes.Post({ pid: "abc" })}> <a>First Post</a> </Link> ``` @@ -97,7 +97,7 @@ the `<a>` tag will not have the `href` attribute, which might hurt your site’s SEO. ```jsx -import {Link} from "blitz" +import { Link } from "blitz" import styled from "styled-components" // This creates a custom component that wraps an <a> tag @@ -105,7 +105,7 @@ const RedLink = styled.a` color: red; ` -function NavLink({href, name}) { +function NavLink({ href, name }) { // Must add passHref to Link return ( <Link href={href} passHref> @@ -128,11 +128,11 @@ If the child of `Link` is a function component, in addition to using [`React.forwardRef`](https://reactjs.org/docs/react-api.html#reactforwardref): ```jsx -import {Link, Routes} from "blitz" +import { Link, Routes } from "blitz" // `onClick`, `href`, and `ref` need to be passed to the DOM element // for proper handling -const MyButton = React.forwardRef(({onClick, href}, ref) => { +const MyButton = React.forwardRef(({ onClick, href }, ref) => { return ( <a href={href} onClick={onClick} ref={ref}> Click Me @@ -157,12 +157,12 @@ export default Home to create the URL string. Here's how to do it: ```jsx -import {Link} from "blitz" +import { Link } from "blitz" function Home() { return ( <div> - <Link href={{pathname: "/about", query: {name: "test"}}}> + <Link href={{ pathname: "/about", query: { name: "test" } }}> <a>About us</a> </Link> </div> diff --git a/app/pages/docs/measuring-performance.mdx b/app/pages/docs/measuring-performance.mdx index dc3ac1b..2c17aeb 100644 --- a/app/pages/docs/measuring-performance.mdx +++ b/app/pages/docs/measuring-performance.mdx @@ -15,7 +15,7 @@ export function reportWebVitals(metric) { console.log(metric) } -function MyApp({Component, pageProps}) { +function MyApp({ Component, pageProps }) { return <Component {...pageProps} /> } @@ -161,7 +161,7 @@ export function reportWebVitals(metric) { if (navigator.sendBeacon) { navigator.sendBeacon(url, body) } else { - fetch(url, {body, method: "POST", keepalive: true}) + fetch(url, { body, method: "POST", keepalive: true }) } } ``` @@ -172,7 +172,7 @@ export function reportWebVitals(metric) { > (to calculate percentiles, etc...). > > ```js -> export function reportWebVitals({id, name, label, value}) { +> export function reportWebVitals({ id, name, label, value }) { > ga("send", "event", { > eventCategory: > label === "web-vital" ? "Web Vitals" : "Blitz custom metric", diff --git a/app/pages/docs/middleware.mdx b/app/pages/docs/middleware.mdx index b76fe68..72a9b1d 100644 --- a/app/pages/docs/middleware.mdx +++ b/app/pages/docs/middleware.mdx @@ -43,7 +43,7 @@ project (this is included in new apps by default). ```ts // types.ts -import {DefaultCtx, SessionContext} from "blitz" +import { DefaultCtx, SessionContext } from "blitz" declare module "blitz" { export interface Ctx extends DefaultCtx { @@ -56,7 +56,7 @@ Whatever types you add to `Ctx` add will be automatically available in your queries and mutations as shown here. ```ts -import {Ctx} from "blitz" +import { Ctx } from "blitz" export default async function getThing(input, ctx: Ctx) { // Properly typed @@ -71,8 +71,8 @@ by exporting a `middleware` array from a query or mutation. ```ts // app/products/queries/getProduct.tsx -import {Middleware} from "blitz" -import db, {FindOneProjectArgs} from "db" +import { Middleware } from "blitz" +import db, { FindOneProjectArgs } from "db" type GetProjectInput = { where: FindOneProjectArgs["where"] @@ -89,11 +89,11 @@ export const middleware: Middleware[] = [ ] export default async function getProject( - {where}: GetProjectInput, - ctx: Record<any, unknown> = {}, + { where }: GetProjectInput, + ctx: Record<any, unknown> = {} ) { console.log("Referer:", ctx.referer) - return await db.project.findOne({where}) + return await db.project.findOne({ where }) } ``` @@ -106,7 +106,7 @@ middleware by doing `return next()` or `await next()`. See below for the `connectMiddleware()` adapter for existing connect middleware. ```js -import {Middleware} from "blitz" +import { Middleware } from "blitz" const middleware: Middleware = async (req, res, next) => { res.blitzCtx.referer = req.headers.referer @@ -213,7 +213,7 @@ middleware to Blitz middleware. ```ts // blitz.config.js -const {connectMiddleware} = require("blitz") +const { connectMiddleware } = require("blitz") const Cors = require("cors") const cors = Cors({ diff --git a/app/pages/docs/multitenancy.mdx b/app/pages/docs/multitenancy.mdx index 79bf262..8d676ce 100644 --- a/app/pages/docs/multitenancy.mdx +++ b/app/pages/docs/multitenancy.mdx @@ -180,7 +180,7 @@ When creating new entities, make sure you attach them to the current organization. Here's an example of how to do that: ```ts -import {resolver} from "blitz" +import { resolver } from "blitz" import db from "db" import * as z from "zod" @@ -202,7 +202,7 @@ export default resolver.pipe( }) return project - }, + } ) ``` @@ -217,7 +217,7 @@ entity belonging to a different organization. You could first make a fail if the organizationId doesn't match. ```ts -import {resolver} from "blitz" +import { resolver } from "blitz" import db from "db" import * as z from "zod" @@ -231,7 +231,7 @@ const UpdateProject = z export default resolver.pipe( resolver.zod(UpdateProject), resolver.authorize(), - async ({id, ...data}, ctx) => { + async ({ id, ...data }, ctx) => { const project = await db.project.update({ where: { id, @@ -242,7 +242,7 @@ export default resolver.pipe( }) return project - }, + } ) ``` @@ -252,8 +252,8 @@ You can do more advanced things like calling `ctx.session.$authorize()` inside an if/else ```ts -import {resolver} from "blitz" -import db, {GlobalRole, MembershipRole} from "db" +import { resolver } from "blitz" +import db, { GlobalRole, MembershipRole } from "db" import * as z from "zod" const UpdateProject = z @@ -268,7 +268,7 @@ export default resolver.pipe( resolver.zod(UpdateProject), // Ensure all users are logged in resolver.authorize(), - async ({id, organizationId, ...data}, ctx) => { + async ({ id, organizationId, ...data }, ctx) => { // if organizationId doesn't match current organization if (organizationId !== ctx.session.orgId) { // Require SUPERADMIN role @@ -288,7 +288,7 @@ export default resolver.pipe( }) return project - }, + } ) ``` @@ -300,7 +300,7 @@ regular users to access the organization they are currently logged in to. ```ts // app/orders/queries/getOrder.ts -import {NotFoundError, resolver} from "blitz" +import { NotFoundError, resolver } from "blitz" import db from "db" import * as z from "zod" import { @@ -323,7 +323,7 @@ export default resolver.pipe( // But now we need to enforce input.organizationId matches // session.orgId unless user is a SUPERADMIN enforceSuperAdminIfNotCurrentOrganization, //highlight-line - async ({id, organizationId}) => { + async ({ id, organizationId }) => { const order = await db.getOrder({ where: { id, @@ -333,39 +333,39 @@ export default resolver.pipe( }) if (!order) throw new NotFoundError() return order - }, + } ) ``` ```ts // app/core/utils.ts -import {Ctx} from "blitz" -import {Prisma, GloblRole} from "db" +import { Ctx } from "blitz" +import { Prisma, GloblRole } from "db" export default function assert( condition: any, - message: string, + message: string ): asserts condition { if (!condition) throw new Error(message) } export const setDefaultOrganizationId = <T extends Record<any, any>>( input: T, - {session}: Ctx, -): T & {organizationId: Prisma.IntNullableFilter | number} => { + { session }: Ctx +): T & { organizationId: Prisma.IntNullableFilter | number } => { assert( session.orgId, - "Missing session.orgId in setDefaultOrganizationId", + "Missing session.orgId in setDefaultOrganizationId" ) if (input.organizationId) { // Pass through the input - return input as T & {organizationId: number} + return input as T & { organizationId: number } } else if (session.roles?.includes(GloblRole.SUPERADMIN)) { // Allow viewing any organization - return {...input, organizationId: {not: 0}} + return { ...input, organizationId: { not: 0 } } } else { // Set organizationId to session.orgId - return {...input, organizationId: session.orgId} + return { ...input, organizationId: session.orgId } } } @@ -373,7 +373,7 @@ export const enforceSuperAdminIfNotCurrentOrganization = < T extends Record<any, any> >( input: T, - ctx: Ctx, + ctx: Ctx ): T => { assert(ctx.session.orgId, "missing session.orgId") assert(input.organizationId, "missing input.organizationId") diff --git a/app/pages/docs/mutation-resolvers.mdx b/app/pages/docs/mutation-resolvers.mdx index 827f7c3..5ab7018 100644 --- a/app/pages/docs/mutation-resolvers.mdx +++ b/app/pages/docs/mutation-resolvers.mdx @@ -34,7 +34,7 @@ fetching from third-party APIs. ```ts // app/products/mutations/createProduct.tsx -import {Ctx} from "blitz" +import { Ctx } from "blitz" import db from "db" import * as z from "zod" @@ -46,7 +46,7 @@ const CreateProject = z export default async function createProject( input: z.infer<typeof CreateProject>, - ctx: Ctx, + ctx: Ctx ) { // Validate input - very importnant for security const data = CreateProject.parse(input) @@ -54,7 +54,7 @@ export default async function createProject( // Require user to be logged in ctx.session.$authorize() - const project = await db.project.create({data}) + const project = await db.project.create({ data }) // Can do any processing, fetching from other APIs, etc diff --git a/app/pages/docs/mutation-usage.mdx b/app/pages/docs/mutation-usage.mdx index f1a21c6..7b58e4a 100644 --- a/app/pages/docs/mutation-usage.mdx +++ b/app/pages/docs/mutation-usage.mdx @@ -66,9 +66,9 @@ exactly like you'd expect. So you can wrap the `invoke` in passing an options object `{refetch: false}` as the second argument. ```tsx -export default function (props: {query: {id: number}}) { - const [product, {setQueryData}] = useQuery(getProduct, { - where: {id: props.query.id}, +export default function (props: { query: { id: number } }) { + const [product, { setQueryData }] = useQuery(getProduct, { + where: { id: props.query.id }, }) const [updateProjectMutation] = useMutation(updateProject) @@ -97,8 +97,8 @@ trigger a query reload. ```tsx export default function (props) { - const [product, {refetch}] = useQuery(getProduct, { - where: {id: props.id}, + const [product, { refetch }] = useQuery(getProduct, { + where: { id: props.id }, }) const [updateProjectMutation] = useMutation(updateProject) @@ -131,10 +131,10 @@ function provided by `blitz`. ##### Example ```tsx -import {invalidateQuery} from "blitz" // highlight-line +import { invalidateQuery } from "blitz" // highlight-line import getProducts from "app/products/queries/getProducts" -const Page = function ({products}) { +const Page = function ({ products }) { return ( <div> <button @@ -176,9 +176,9 @@ Here's a simple form component with an `onSubmit` function that calls an `updateProduct` mutation to save edits. ```tsx -export default function (props: {query: {id: number}}) { - const [product, {setQueryData}] = useQuery(getProduct, { - where: {id: props.query.id}, +export default function (props: { query: { id: number } }) { + const [product, { setQueryData }] = useQuery(getProduct, { + where: { id: props.query.id }, }) const [updateProjectMutation] = useMutation(updateProject) @@ -208,9 +208,9 @@ function to prevent the mutation triggering a refetch of stale data while we wait for the response. ```tsx -export default function (props: {query: {id: number}}) { - const [product, {setQueryData}] = useQuery(getProduct, { - where: {id: props.query.id}, +export default function (props: { query: { id: number } }) { + const [product, { setQueryData }] = useQuery(getProduct, { + where: { id: props.query.id }, }) const [updateProjectMutation] = useMutation(updateProject) @@ -220,7 +220,7 @@ export default function (props: {query: {id: number}}) { onSubmit={async (values) => { try { // Update local cache with form values without triggering refetch - setQueryData(values, {refetch: false}) // highlight-line + setQueryData(values, { refetch: false }) // highlight-line const product = await updateProjectMutation(values) // Update local cache with result, then refetch setQueryData(product) diff --git a/app/pages/docs/passportjs.mdx b/app/pages/docs/passportjs.mdx index a679620..fd58d1a 100644 --- a/app/pages/docs/passportjs.mdx +++ b/app/pages/docs/passportjs.mdx @@ -19,7 +19,7 @@ contents. ```ts // app/api/auth/[...auth].ts -import {passportAuth} from "blitz" +import { passportAuth } from "blitz" import db from "db" export default passportAuth({ @@ -64,7 +64,7 @@ located behind SSL proxy (Nginx). Proxy should be set to manage ```ts // app/api/auth/[...auth].ts -import {passportAuth} from "blitz" +import { passportAuth } from "blitz" import db from "db" export default passportAuth({ @@ -85,7 +85,7 @@ can then access the session context via `ctx.session` ```ts // app/api/auth/[...auth].ts -import {passportAuth} from "blitz" +import { passportAuth } from "blitz" import db from "db" export default passportAuth((ctx) => ({ @@ -118,9 +118,9 @@ Note that the `callbackURL` uses the callback endpoint as described above (`/api/auth/twitter/callback`) ```ts -import {passportAuth} from "blitz" +import { passportAuth } from "blitz" import db from "db" -import {Strategy as TwitterStrategy} from "passport-twitter" +import { Strategy as TwitterStrategy } from "passport-twitter" export default passportAuth({ successRedirectUrl: "/", @@ -144,17 +144,17 @@ export default passportAuth({ if (!email) { // This can happen if you haven't enabled email access in your twitter app permissions return done( - new Error("Twitter OAuth response doesn't have email."), + new Error("Twitter OAuth response doesn't have email.") ) } const user = await db.user.upsert({ - where: {email}, + where: { email }, create: { email, name: profile.displayName, }, - update: {email}, + update: { email }, }) const publicData = { @@ -162,8 +162,8 @@ export default passportAuth({ roles: [user.role], source: "twitter", } - done(null, {publicData}) - }, + done(null, { publicData }) + } ), }, // highlight-end @@ -268,9 +268,9 @@ inside the `passport.authenticate()` method. Add these options to the `passportAuth` object like this: ```ts -import {passportAuth} from "blitz" +import { passportAuth } from "blitz" import db from "db" -import {Strategy as Auth0Strategy} from "passport-auth0" +import { Strategy as Auth0Strategy } from "passport-auth0" export default passportAuth({ successRedirectUrl: "/", @@ -278,7 +278,7 @@ export default passportAuth({ strategies: [ { // highlight-start - authenticateOptions: {scope: "openid email profile"}, + authenticateOptions: { scope: "openid email profile" }, // highlight-end strategy: new Auth0Strategy( { @@ -295,24 +295,24 @@ export default passportAuth({ _tokenSecret, extraParams, profile, - done, + done ) { const email = profile.emails && profile.emails[0]?.value if (!email) { // This can happen if you haven't enabled email access in your twitter app permissions return done( - new Error("GitHub OAuth response doesn't have email."), + new Error("GitHub OAuth response doesn't have email.") ) } const user = await db.user.upsert({ - where: {email}, + where: { email }, create: { email, name: profile.displayName, }, - update: {email}, + update: { email }, }) const publicData = { @@ -320,8 +320,8 @@ export default passportAuth({ roles: [user.role], source: "auth0", } - done(undefined, {publicData}) - }, + done(undefined, { publicData }) + } ), }, ], diff --git a/app/pages/docs/preview-mode.mdx b/app/pages/docs/preview-mode.mdx index d235e6d..7b84fba 100644 --- a/app/pages/docs/preview-mode.mdx +++ b/app/pages/docs/preview-mode.mdx @@ -112,7 +112,7 @@ export default async (req, res) => { // Check the secret and next parameters // This secret should only be known to this API route and the CMS if (req.query.secret !== "MY_SECRET_TOKEN" || !req.query.slug) { - return res.status(401).json({message: "Invalid token"}) + return res.status(401).json({ message: "Invalid token" }) } // Fetch the headless CMS to check if the provided `slug` exists @@ -121,7 +121,7 @@ export default async (req, res) => { // If the slug doesn't exist prevent preview mode from being enabled if (!post) { - return res.status(401).json({message: "Invalid slug"}) + return res.status(401).json({ message: "Invalid slug" }) } // Enable Preview Mode by setting the cookies @@ -129,7 +129,7 @@ export default async (req, res) => { // Redirect to the path from the fetched post // We don't redirect to req.query.slug as that might lead to open redirect vulnerabilities - res.writeHead(307, {Location: post.slug}) + res.writeHead(307, { Location: post.slug }) res.end() } ``` @@ -183,7 +183,7 @@ export async function getStaticProps(context) { // to request draft data instead of published data. This will vary // based on which headless CMS you're using. const res = await fetch( - `https://.../${context.preview ? "preview" : ""}`, + `https://.../${context.preview ? "preview" : ""}` ) // ... } diff --git a/app/pages/docs/prisma.mdx b/app/pages/docs/prisma.mdx index 2dd8bf7..16c8e43 100644 --- a/app/pages/docs/prisma.mdx +++ b/app/pages/docs/prisma.mdx @@ -27,8 +27,8 @@ beforeEach(async () => { #### Example Usage ```ts -import {enhancePrisma} from "blitz" -import {PrismaClient} from "@prisma/client" +import { enhancePrisma } from "blitz" +import { PrismaClient } from "@prisma/client" const EnhancedPrisma = enhancePrisma(PrismaClient) diff --git a/app/pages/docs/query-resolvers.mdx b/app/pages/docs/query-resolvers.mdx index df67a1d..ab1b924 100644 --- a/app/pages/docs/query-resolvers.mdx +++ b/app/pages/docs/query-resolvers.mdx @@ -34,7 +34,7 @@ fetching from third-party APIs. ```ts // app/products/queries/getProduct.tsx -import {Ctx} from "blitz" +import { Ctx } from "blitz" import db from "db" import * as z from "zod" @@ -44,7 +44,7 @@ const GetProject = z.object({ export default async function getProject( input: z.infer<typeof GetProject>, - ctx: Ctx, + ctx: Ctx ) { // Validate the input const data = GetProject.parse(input) @@ -52,7 +52,7 @@ export default async function getProject( // Require user to be logged in ctx.session.$authorize() - const project = await db.project.findOne({where: {id: data.id}}) + const project = await db.project.findOne({ where: { id: data.id } }) // Can do any processing, fetching from other APIs, etc diff --git a/app/pages/docs/query-usage.mdx b/app/pages/docs/query-usage.mdx index 6bb024b..6c1561a 100644 --- a/app/pages/docs/query-usage.mdx +++ b/app/pages/docs/query-usage.mdx @@ -21,11 +21,11 @@ automatically provides awesome features such as automatic caching and revalidation. ```ts -import {useQuery} from "blitz" +import { useQuery } from "blitz" import getProject from "app/projects/queries/getProject" function App() { - const [project] = useQuery(getProject, {where: {id: 1}}) + const [project] = useQuery(getProject, { where: { id: 1 } }) } ``` @@ -88,10 +88,10 @@ before they can execute. To do this, use the enabled option to tell a query when it is ready to turn on: ```tsx -const [user] = useQuery(getUser, {where: {id: props.query.id}}) +const [user] = useQuery(getUser, { where: { id: props.query.id } }) const [projects] = useQuery( getProjects, - () => ({where: {userId: user.id}}, {enabled: user}), + () => ({ where: { userId: user.id } }, { enabled: user }) ) ``` @@ -111,11 +111,11 @@ Use the [`useInfiniteQuery`](./use-infinite-query) hook Both of the following will cache the `getProject` query. ```ts -const project = await getProject({where: {id: props.id}}) +const project = await getProject({ where: { id: props.id } }) ``` ```tsx -<a onMouseEnter={() => getProject({where: {id: projectId}})}> +<a onMouseEnter={() => getProject({ where: { id: projectId } })}> View Project </a> ``` @@ -130,7 +130,7 @@ rendered on the server. The following example demonstrates how prefetching works in Blitz. ```tsx -import {QueryClient, getQueryKey, useQuery, dehydrate} from "blitz" +import { QueryClient, getQueryKey, useQuery, dehydrate } from "blitz" import getProject from "app/projects/queries/getProject" @@ -139,7 +139,7 @@ export const getStaticProps = async (context) => { const queryKey = getQueryKey(getProject) await queryClient.prefetchQuery(queryKey, () => - getProject({where: {id: context.params?.projectId}}), + getProject({ where: { id: context.params?.projectId } }) ) return { @@ -149,8 +149,8 @@ export const getStaticProps = async (context) => { } } -function ProjectPage({project}) { - const [project] = useQuery(getProject, {where: {id: props.id}}) +function ProjectPage({ project }) { + const [project] = useQuery(getProject, { where: { id: props.id } }) return <div>{project.name}</div> } @@ -177,12 +177,12 @@ import getProject from "app/projects/queries/getProject" export const getStaticProps = async (context) => { const project = await getProject({ - where: {id: context.params?.projectId}, + where: { id: context.params?.projectId }, }) - return {props: {project}} + return { props: { project } } } -function ProjectPage({project}) { +function ProjectPage({ project }) { return <div>{project.name}</div> } diff --git a/app/pages/docs/redirects.mdx b/app/pages/docs/redirects.mdx index 85f3c13..2a744a9 100644 --- a/app/pages/docs/redirects.mdx +++ b/app/pages/docs/redirects.mdx @@ -9,7 +9,7 @@ One common use case is conditionally redirecting a user to a different page based on their session data. Here's how you can do that: ```tsx -import {useRouter, useSession} from "blitz" +import { useRouter, useSession } from "blitz" const LoginPage: BlitzPage = () => { const router = useRouter() @@ -33,9 +33,9 @@ side redirects, you can use from the server. Here's how you can do that: ```ts -import {getSession} from "blitz" +import { getSession } from "blitz" -export const getServerSideProps = async ({req, res}) => { +export const getServerSideProps = async ({ req, res }) => { const session = await getSession(req, res) if (!session.userId) { @@ -47,6 +47,6 @@ export const getServerSideProps = async ({req, res}) => { } } - return {props: {}} + return { props: {} } } ``` diff --git a/app/pages/docs/resolver-client-utilities.mdx b/app/pages/docs/resolver-client-utilities.mdx index 102f76b..d2a3ba8 100644 --- a/app/pages/docs/resolver-client-utilities.mdx +++ b/app/pages/docs/resolver-client-utilities.mdx @@ -11,15 +11,17 @@ components. ### Example {#example} ```tsx -import {invoke} from "blitz" +import { invoke } from "blitz" import getProducts from "app/products/queries/getProducts" -const Page = function ({products}) { +const Page = function ({ products }) { return ( <div> <button onClick={async () => { - const products = await invoke(getProducts, {where: {orgId: 1}}) + const products = await invoke(getProducts, { + where: { orgId: 1 }, + }) }} > Get Products @@ -58,10 +60,10 @@ This allows you to invalidate a specific query. ### Example {#example-1} ```tsx -import {invalidateQuery} from "blitz" // highlight-line +import { invalidateQuery } from "blitz" // highlight-line import getProducts from "app/products/queries/getProducts" -const Page = function ({products}) { +const Page = function ({ products }) { return ( <div> <button @@ -108,10 +110,10 @@ This allows you to get the query key for a query ### Example {#example-2} ```tsx -import {getQueryKey, queryClient} from "blitz" +import { getQueryKey, queryClient } from "blitz" import getProducts from "app/products/queries/getProducts" -const Page = function ({products}) { +const Page = function ({ products }) { return ( <div> <button @@ -119,7 +121,9 @@ const Page = function ({products}) { const queryKey = getQueryKey(getProducts) queryClient.invalidateQueries(queryKey) - const queryKey = getQueryKey(getProducts, {where: {ordId: 1}}) + const queryKey = getQueryKey(getProducts, { + where: { ordId: 1 }, + }) queryClient.invalidateQueries(queryKey) }} > @@ -168,10 +172,12 @@ causing a refetch. Disable refetch by passing an options object ### Example {#example-3} ```tsx -import {setQueryData} from "blitz" +import { setQueryData } from "blitz" -export default function (props: {query: {id: number}}) { - const [product] = useQuery(getProduct, {where: {id: props.query.id}}) +export default function (props: { query: { id: number } }) { + const [product] = useQuery(getProduct, { + where: { id: props.query.id }, + }) const [updateProjectMutation] = useMutation(updateProject) return ( @@ -180,7 +186,11 @@ export default function (props: {query: {id: number}}) { onSubmit={async (values) => { try { const product = await updateProjectMutation(values) - setQueryData(getProduct, {where: {id: props.query.id}}, product) + setQueryData( + getProduct, + { where: { id: props.query.id } }, + product + ) } catch (error) { alert("Error saving product") } diff --git a/app/pages/docs/resolver-server-utilities.mdx b/app/pages/docs/resolver-server-utilities.mdx index f73835e..727d71e 100644 --- a/app/pages/docs/resolver-server-utilities.mdx +++ b/app/pages/docs/resolver-server-utilities.mdx @@ -52,7 +52,7 @@ export pipe( ### Example {#example} ```ts -import {resolver} from "blitz" +import { resolver } from "blitz" import db from "db" import * as z from "zod" @@ -66,7 +66,10 @@ export default resolver.pipe( resolver.zod(CreateProject), resolver.authorize(), // Set default orgId - (input, {session}) => ({...input, orgId: input.orgId ?? session.orgId}), + (input, { session }) => ({ + ...input, + orgId: input.orgId ?? session.orgId, + }), async (input, ctx) => { console.log("Creating project...", input.orgId) const project = await db.project.create({ @@ -74,7 +77,7 @@ export default resolver.pipe( }) console.log("Created project") return project - }, + } ) ``` @@ -217,8 +220,8 @@ This is a handy utility for query pagination ### Example {#example-3} ```ts -import {paginate, Ctx} from "blitz" -import db, {Prisma} from "db" +import { paginate, Ctx } from "blitz" +import db, { Prisma } from "db" interface GetProjectSInput extends Pick< @@ -227,15 +230,15 @@ interface GetProjectSInput > {} export default async function getProjects( - {where, orderBy, skip = 0, take = 100}: GetProjectsInput, - ctx: Ctx, + { where, orderBy, skip = 0, take = 100 }: GetProjectsInput, + ctx: Ctx ) { ctx.session.$authorize() - const {items: projects, hasMore, nextPage, count} = await paginate({ - count: () => db.project.count({where}), + const { items: projects, hasMore, nextPage, count } = await paginate({ + count: () => db.project.count({ where }), query: (paginateArgs) => - db.project.findMany({...paginateArgs, where, orderBy}), + db.project.findMany({ ...paginateArgs, where, orderBy }), skip, take, }) @@ -312,23 +315,23 @@ export const getServerSideProps: GetServerSideProps<PageProps> = async ({ }) => { const products = await invokeWithMiddleware( getProducts, - {orderBy: {id: "desc"}}, - {req, res}, + { orderBy: { id: "desc" } }, + { req, res } ) return { - props: {products}, + props: { products }, } } -const Page: BlitzPage<PageProps> = function ({products}) { +const Page: BlitzPage<PageProps> = function ({ products }) { return ( <div> <h1>Products</h1> <div id="products"> {products.map((product) => ( <p key={product.id}> - <Link href={Routes.Product({handle: product.handle})}> + <Link href={Routes.Product({ handle: product.handle })}> <a>{product.name}</a> </Link> </p> diff --git a/app/pages/docs/route-params-query.mdx b/app/pages/docs/route-params-query.mdx index 7bd77e6..f22d80d 100644 --- a/app/pages/docs/route-params-query.mdx +++ b/app/pages/docs/route-params-query.mdx @@ -11,7 +11,7 @@ optionally provide a type argument to narrow the parameters and types that are returned. ```tsx -import {useParams} from "blitz" +import { useParams } from "blitz" const params = useParams() // ReturnType: Record<string, undefined | string | string[]> @@ -31,7 +31,7 @@ const params = useParams("array") ```tsx // File: app/products/pages/products/[id].tsx // URL: /products/2 -import {useParams} from "blitz" +import { useParams } from "blitz" const params = useParams() // params = {id: "2"} @@ -40,7 +40,7 @@ const params = useParams() ```tsx // File: app/pages/blog/[category]/[...slug].tsx // URL: /blog/tech/2020/1 -import {useParams} from "blitz" +import { useParams } from "blitz" const params = useParams() // params = {category: "tech", slug: ["2020", "1"]} @@ -59,7 +59,7 @@ return type is `undefined | string | string[]`, but you can optionally provide a type argument to cast the return type. ```tsx -import {useParam} from "blitz" +import { useParam } from "blitz" const param = useParam("id") // ReturnType: undefined | string | string[] @@ -79,7 +79,7 @@ const param = useParam("id", "array") ```tsx // File: app/locations/pages/locations/[id]/[[...zipcode]].tsx // URL: /locations/2/02137 -import {useParam} from "blitz" +import { useParam } from "blitz" const id = useParam("id", "number") // id = 2 @@ -90,7 +90,7 @@ const zipcodes = useParam("zipcode", "array") ```tsx // File: app/pages/blog/[slug].tsx // URL: /blog/hello-world -import {useParam} from "blitz" +import { useParam } from "blitz" const slug = useParam("slug", "string") // slug = "hello-world" @@ -104,7 +104,7 @@ Parameter type is always `string`. ```tsx // URL: /products?sort=asc&limit=10 -import {useRouterQuery} from "blitz" +import { useRouterQuery } from "blitz" const query = useRouterQuery() diff --git a/app/pages/docs/router.mdx b/app/pages/docs/router.mdx index 7464463..ab49349 100644 --- a/app/pages/docs/router.mdx +++ b/app/pages/docs/router.mdx @@ -11,9 +11,9 @@ There are two ways to access `router` within components. ## `useRouter` {#use-router} ```jsx -import {useRouter} from "blitz" +import { useRouter } from "blitz" -function Thing({href}) { +function Thing({ href }) { const router = useRouter() return ( @@ -36,9 +36,9 @@ export default Thing ## `withRouter` {#with-router} ```jsx -import {withRouter} from "blitz" +import { withRouter } from "blitz" -function Page({router}) { +function Page({ router }) { return <p>{router.pathname}</p> } @@ -80,7 +80,7 @@ Handles client-side transitions, this method is useful for cases where [`<Link>`](./link) is not enough. ```jsx -import {Router} from "blitz" +import { Router } from "blitz" Router.push(url, as, options) ``` @@ -102,7 +102,7 @@ Router.push(url, as, options) Navigating to `pages/about.js`, which is a predefined route: ```jsx -import {Router} from "blitz" +import { Router } from "blitz" function Page() { return <span onClick={() => Router.push("/about")}>Click me</span> @@ -112,7 +112,7 @@ function Page() { Navigating `pages/post/[pid].js`, which is a dynamic route: ```jsx -import {Router} from "blitz" +import { Router } from "blitz" function Page() { return <span onClick={() => Router.push("/post/abc")}>Click me</span> @@ -125,12 +125,12 @@ You can use an URL object in the same way you can use it for [`<Link>`](./link). Works for both the `url` and `as` parameters: ```jsx -import {Router} from "blitz" +import { Router } from "blitz" const handler = () => { Router.push({ pathname: "/about", - query: {name: "Vercel"}, + query: { name: "Vercel" }, }) } @@ -152,7 +152,7 @@ prevent adding a new URL entry into the `history` stack, take a look at the following example: ```jsx -import {Router} from "blitz" +import { Router } from "blitz" Router.replace("/home") ``` @@ -166,7 +166,7 @@ Navigate back in history. Equivalent to clicking the browser’s back button. It executes `window.history.back()`. ```jsx -import {Router} from "blitz" +import { Router } from "blitz" Router.back() ``` @@ -177,7 +177,7 @@ Reload the current URL. Equivalent to clicking the browser’s refresh button. It executes `window.location.reload()`. ```jsx -import {Router} from "blitz" +import { Router } from "blitz" Router.reload() ``` @@ -192,7 +192,7 @@ prefetching pages automatically. > development. ```jsx -import {Router} from "blitz" +import { Router } from "blitz" Router.prefetch(url, as) ``` @@ -208,7 +208,7 @@ to the dashboard. For that case, we can prefetch the dashboard to make a faster transition, like in the following example: ```jsx -import {Router} from "blitz" +import { Router } from "blitz" export default function Login() { const handleSubmit = React.useCallback((e) => { @@ -216,7 +216,7 @@ export default function Login() { fetch("/api/login", { method: "POST", - headers: {"Content-Type": "application/json"}, + headers: { "Content-Type": "application/json" }, body: JSON.stringify({ /* Form data */ }), @@ -265,7 +265,7 @@ For example, to listen to the router event `routeChangeStart`, do the following: ```jsx -import {Router} from "blitz" +import { Router } from "blitz" const handleRouteChange = (url) => { console.log("App is changing to: ", url) @@ -278,7 +278,7 @@ If you no longer want to listen to the event, unsubscribe with the `off` method: ```jsx -import {Router} from "blitz" +import { Router } from "blitz" Router.events.off("routeChangeStart", handleRouteChange) ``` @@ -288,7 +288,7 @@ in succession), `routeChangeError` will fire. And the passed `err` will contain a `cancelled` property set to `true`, as in the following example: ```jsx -import {Router} from "blitz" +import { Router } from "blitz" Router.events.on("routeChangeError", (err, url) => { if (err.cancelled) { @@ -305,7 +305,7 @@ Router events should be registered when a component mounts or imperatively when an event happens, as in the following example: ```jsx -import {Router} from "blitz" +import { Router } from "blitz" useEffect(() => { const handleRouteChange = (url) => { @@ -330,9 +330,9 @@ You could use this to manipulate the request, or force a SSR refresh, as in the following example: ```jsx -import {Router} from "blitz" +import { Router } from "blitz" -Router.beforePopState(({url, as, options}) => { +Router.beforePopState(({ url, as, options }) => { // I only want to allow these two routes! if (as !== "/" && as !== "/other") { // Have SSR render bad routes as a 404. diff --git a/app/pages/docs/routing.mdx b/app/pages/docs/routing.mdx index 1271232..9e3144a 100644 --- a/app/pages/docs/routing.mdx +++ b/app/pages/docs/routing.mdx @@ -45,7 +45,7 @@ to match named parameters. Consider the following page `app/pages/post/[pid].js`: ```jsx -import {useParam} from "blitz" +import { useParam } from "blitz" const Post = () => { const pid = useParam("pid") @@ -64,14 +64,14 @@ parameters. For example, the route `/post/abc` will have the following `query` object: ```json -{"pid": "abc"} +{ "pid": "abc" } ``` Similarly, the route `/post/abc?foo=bar` will have the following `query` object: ```json -{"foo": "bar", "pid": "abc"} +{ "foo": "bar", "pid": "abc" } ``` However, route parameters will override query parameters with the same @@ -79,7 +79,7 @@ name. For example, the route `/post/abc?pid=123` will have the following `query` object: ```json -{"pid": "abc"} +{ "pid": "abc" } ``` Multiple dynamic route segments work the same way. The page @@ -87,7 +87,7 @@ Multiple dynamic route segments work the same way. The page `/post/abc/a-comment` and its `query` object will be: ```json -{"pid": "abc", "comment": "a-comment"} +{ "pid": "abc", "comment": "a-comment" } ``` ### Catch all routes {#catch-all-routes} @@ -105,14 +105,14 @@ example) to the page, and it will always be an array, so, the path `/post/a` will have the following `query` object: ```json -{"slug": ["a"]} +{ "slug": ["a"] } ``` And in the case of `/post/a/b`, and any other matching path, new parameters will be added to the array, like so: ```json -{"slug": ["a", "b"]} +{ "slug": ["a", "b"] } ``` ### Optional catch all routes {#optional-catch-all-routes} diff --git a/app/pages/docs/session-management.mdx b/app/pages/docs/session-management.mdx index 0c3c0e2..b194792 100644 --- a/app/pages/docs/session-management.mdx +++ b/app/pages/docs/session-management.mdx @@ -26,7 +26,7 @@ mutation like the one shown below. ```ts // app/auth/mutations/login.ts -import {Ctx} from "blitz" +import { Ctx } from "blitz" export default async function login(input: SomeTSInputType, ctx: Ctx) { // 1. Validate input data @@ -34,7 +34,7 @@ export default async function login(input: SomeTSInputType, ctx: Ctx) { // 3. Fetch user data // 4. Create a new session (log in) - await ctx.session.$create({userId: user.id, role: user.role}) // highlight-line + await ctx.session.$create({ userId: user.id, role: user.role }) // highlight-line } ``` @@ -49,7 +49,7 @@ sensitive data in the cache is deleted. ```ts // app/auth/muations/logout.ts -import {Ctx} from "blitz" +import { Ctx } from "blitz" export default async function logout(_: any, ctx: Ctx) { // 1. Revoke the current user session, logging them out. @@ -69,11 +69,11 @@ You can change the session public data in any query or mutation like this: ```ts // app/mutations/someMutation.ts -import {Ctx} from "blitz" +import { Ctx } from "blitz" export default async function someMutation(input: any, ctx: Ctx) { // This merges the input data with whatever is already in current publicData - await ctx.session.$setPublicData({orgId: 1}) // highlight-line + await ctx.session.$setPublicData({ orgId: 1 }) // highlight-line } ``` @@ -87,7 +87,7 @@ the `sessionMiddleware` that's in `blitz.config.js`. ```ts // app/queries/someQuery.ts -import {Ctx} from "blitz" +import { Ctx } from "blitz" export default async function someQuery(input: any, ctx: Ctx) { // Access the SessionContext class @@ -105,13 +105,13 @@ You can also get the session context inside `getServerSideProps` or inside API routes with `getSession` like this: ```ts -import {getSession} from "blitz" +import { getSession } from "blitz" -export const getServerSideProps = async ({req, res}) => { +export const getServerSideProps = async ({ req, res }) => { const session = await getSession(req, res) console.log("User ID:", session.userId) - return {props: {}} + return { props: {} } } ``` @@ -126,7 +126,7 @@ component above it in the tree. Or you can set `useSession({suspense: false})` to disable suspense. ```ts -import {useSession} from "blitz" +import { useSession } from "blitz" function SomeComponent() { const session = useSession() @@ -143,7 +143,7 @@ can pass the session public data to `useSession()` with the `initialPublicData` option. ```ts -import {useSession, GetServerSideProps} from "blitz" +import { useSession, GetServerSideProps } from "blitz" export const getServerSideProps: GetServerSideProps = async ({ req, @@ -151,11 +151,11 @@ export const getServerSideProps: GetServerSideProps = async ({ }) => { const session = await getSession(req, res) - return {props: {publicData: session.$publicData}} + return { props: { publicData: session.$publicData } } } -const SomePage: BlitzPage = ({initialPublicData}) => { - const session = useSession({initialPublicData}) +const SomePage: BlitzPage = ({ initialPublicData }) => { + const session = useSession({ initialPublicData }) return /*... */ } @@ -232,7 +232,7 @@ declare module "blitz" { Then change all uses of `ctx.session.$create()` to pass in the new fields. ```ts -ctx.session.$create({userId: 1, role: "ADMIN", orgId: 1}) +ctx.session.$create({ userId: 1, role: "ADMIN", orgId: 1 }) ``` You can also use `ctx.session.$setPublicData()` to update session data for @@ -240,13 +240,13 @@ an already logged in user. This will _merge_ values with the existing public data. ```ts -ctx.session.$setPublicData({orgId: 1}) +ctx.session.$setPublicData({ orgId: 1 }) ``` To access public data on the client: ```ts -import {useSession} from "blitz" +import { useSession } from "blitz" function SomeComponent() { const session = useSession() @@ -261,7 +261,7 @@ To access public data on the server: ```ts // app/queries/someQuery.ts -import {Ctx} from "blitz" +import { Ctx } from "blitz" export default async function someQuery(input: any, ctx: Ctx) { // Access the SessionContext class @@ -278,7 +278,7 @@ You can customize session management by passing an object to the ```js // blitz.config.js -const {sessionMiddleware, simpleRolesIsAuthorized} = require("blitz") +const { sessionMiddleware, simpleRolesIsAuthorized } = require("blitz") module.exports = { middleware: [ @@ -305,10 +305,10 @@ type SessionConfig = { createSession: (session: SessionModel) => Promise<SessionModel> updateSession: ( handle: string, - session: Partial<SessionModel>, + session: Partial<SessionModel> ) => Promise<SessionModel> deleteSession: (handle: string) => Promise<SessionModel> - isAuthorized: ({ctx: any, args: [...unknown]}) => boolean + isAuthorized: ({ ctx: any, args: [...unknown] }) => boolean } ``` @@ -344,7 +344,7 @@ When making a request from the client to an API route, you need to include the anti-CSRF token in the `anti-csrf` header like this: ```ts -import {getAntiCSRFToken} from "blitz" +import { getAntiCSRFToken } from "blitz" const antiCSRFToken = getAntiCSRFToken() @@ -356,13 +356,13 @@ if (antiCSRFToken) { And then you can get the sessionContext in the API route like this: ```ts -import {getSession} from "blitz" +import { getSession } from "blitz" -export default async function ({req, res}) { +export default async function ({ req, res }) { const session = await getSession(req, res) console.log("User ID:", session.userId) - res.json({userId}) + res.json({ userId }) } ``` @@ -429,14 +429,14 @@ interface SessionContext extends PublicData { ) => this is AuthenticatedSessionContext $create: ( publicData: PublicData, - privateData?: Record<any, any>, + privateData?: Record<any, any> ) => Promise<void> $revoke: () => Promise<void> $revokeAll: () => Promise<void> $getPrivateData: () => Promise<Record<any, any>> $setPrivateData: (data: Record<any, any>) => Promise<void> $setPublicData: ( - data: Partial<Omit<PublicData, "userId">>, + data: Partial<Omit<PublicData, "userId">> ) => Promise<void> } ``` diff --git a/app/pages/docs/shallow-routing.mdx b/app/pages/docs/shallow-routing.mdx index 61b24ab..63f1d53 100644 --- a/app/pages/docs/shallow-routing.mdx +++ b/app/pages/docs/shallow-routing.mdx @@ -16,8 +16,8 @@ To enable shallow routing, set the `shallow` option to `true`. Consider the following example: ```ts -import {useEffect} from "react" -import {useRouter} from "blitz" +import { useEffect } from "react" +import { useRouter } from "blitz" // Current URL is '/' function Page() { @@ -25,7 +25,7 @@ function Page() { useEffect(() => { // Always do navigations after the first render - router.push("/?counter=10", undefined, {shallow: true}) + router.push("/?counter=10", undefined, { shallow: true }) }, []) useEffect(() => { @@ -60,7 +60,7 @@ let's assume we have another page called `pages/about.ts`, and you run this: ```ts -router.push("/?counter=10", "/about?counter=10", {shallow: true}) +router.push("/?counter=10", "/about?counter=10", { shallow: true }) ``` Since that's a new page, it'll unload the current page, load the new one diff --git a/app/pages/docs/stickers.mdx b/app/pages/docs/stickers.mdx index b92fa52..093a92f 100644 --- a/app/pages/docs/stickers.mdx +++ b/app/pages/docs/stickers.mdx @@ -13,5 +13,5 @@ hideFooter: true onmousewheel="" width="100%" height="1400" - style={{marginTop: "2rem", background: "transparent", border: "none"}} + style={{ marginTop: "2rem", background: "transparent", border: "none" }} ></iframe> diff --git a/app/pages/docs/tutorial.mdx b/app/pages/docs/tutorial.mdx index 4da4539..9917824 100644 --- a/app/pages/docs/tutorial.mdx +++ b/app/pages/docs/tutorial.mdx @@ -428,20 +428,21 @@ Jump over to `app/pages/questions/index.tsx`. Notice that a export const QuestionsList = () => { const router = useRouter() const page = Number(router.query.page) || 0 - const [{questions, hasMore}, {isPreviousData}] = usePaginatedQuery( + const [{ questions, hasMore }, { isPreviousData }] = usePaginatedQuery( getQuestions, { - orderBy: {id: "asc"}, + orderBy: { id: "asc" }, skip: ITEMS_PER_PAGE * page, take: ITEMS_PER_PAGE, - }, + } ) - const goToPreviousPage = () => router.push({query: {page: page - 1}}) + const goToPreviousPage = () => + router.push({ query: { page: page - 1 } }) const goToNextPage = () => { if (!isPreviousData && hasMore) { - router.push({query: {page: page + 1}}) + router.push({ query: { page: page + 1 } }) } } @@ -585,7 +586,7 @@ deleting a `Question`. We need to temporarily augment the generated the following to the top of the function body: ```ts -await db.choice.deleteMany({where: {questionId: id}}) +await db.choice.deleteMany({ where: { questionId: id } }) ``` The end result should be as such: @@ -918,12 +919,14 @@ export const Question = () => { const router = useRouter() const questionId = useParam("questionId", "number") const [deleteQuestionMutation] = useMutation(deleteQuestion) - const [question, {refetch}] = useQuery(getQuestion, {id: questionId}) + const [question, { refetch }] = useQuery(getQuestion, { + id: questionId, + }) const [updateChoiceMutation] = useMutation(updateChoice) const handleVote = async (id: number) => { try { - await updateChoiceMutation({id}) + await updateChoiceMutation({ id }) refetch() } catch (error) { alert("Error updating choice " + JSON.stringify(error, null, 2)) @@ -955,11 +958,11 @@ export const Question = () => { type="button" onClick={async () => { if (window.confirm("This will be deleted")) { - await deleteQuestionMutation({id: question.id}) + await deleteQuestionMutation({ id: question.id }) router.push("/questions") } }} - style={{marginLeft: "0.5rem"}} + style={{ marginLeft: "0.5rem" }} > Delete </button> diff --git a/app/pages/docs/use-infinite-query.mdx b/app/pages/docs/use-infinite-query.mdx index 6efad17..abb9842 100644 --- a/app/pages/docs/use-infinite-query.mdx +++ b/app/pages/docs/use-infinite-query.mdx @@ -12,16 +12,20 @@ this case, you can `useInfiniteQuery` as shown in the following example. ### Example {#example} ```ts -import {useInfiniteQuery} from "blitz" +import { useInfiniteQuery } from "blitz" import getProjects from "app/projects/queries/getProjects" function Projects(props) { const [ projectPages, - {isFetching, isFetchingNextPage, fetchNextPage, hasNextPage}, - ] = useInfiniteQuery(getProjects, (page = {take: 3, skip: 0}) => page, { - getNextPageParam: (lastPage) => lastPage.nextPage, - }) + { isFetching, isFetchingNextPage, fetchNextPage, hasNextPage }, + ] = useInfiniteQuery( + getProjects, + (page = { take: 3, skip: 0 }) => page, + { + getNextPageParam: (lastPage) => lastPage.nextPage, + } + ) return ( <> {projectPages.map((group, i) => ( @@ -54,8 +58,8 @@ function Projects(props) { And here's the query to work with that: ```ts -import {paginate} from "blitz" -import db, {Prisma, Project} from "db" +import { paginate } from "blitz" +import db, { Prisma, Project } from "db" type GetProjectsInput = { where?: Prisma.ProjectFindManyArgs["where"] @@ -65,8 +69,8 @@ type GetProjectsInput = { } export default async function getProjects( - {where, orderBy, take, skip}: GetProjectsInput, - ctx: Record<any, unknown> = {}, + { where, orderBy, take, skip }: GetProjectsInput, + ctx: Record<any, unknown> = {} ) { const projects = await db.project.findMany({ where, @@ -78,12 +82,12 @@ export default async function getProjects( console.log("HTTP referer:", ctx.referer) } - const {items: projects, hasMore, nextPage, count} = await paginate({ + const { items: projects, hasMore, nextPage, count } = await paginate({ skip, take, - count: () => db.project.count({where}), + count: () => db.project.count({ where }), query: (paginateArgs) => - db.project.findMany({...paginateArgs, where, orderBy}), + db.project.findMany({ ...paginateArgs, where, orderBy }), }) return { diff --git a/app/pages/docs/use-paginated-query.mdx b/app/pages/docs/use-paginated-query.mdx index c721bc8..5f12515 100644 --- a/app/pages/docs/use-paginated-query.mdx +++ b/app/pages/docs/use-paginated-query.mdx @@ -13,7 +13,7 @@ the following example. ### Example {#example} ```ts -import {Suspense} from "react" +import { Suspense } from "react" import { Link, BlitzPage, @@ -28,20 +28,20 @@ const ITEMS_PER_PAGE = 3 const Projects = () => { const router = useRouter() - const {page = 0} = useRouterQuery() - const [{projects, hasMore}, {isPreviousData}] = usePaginatedQuery( + const { page = 0 } = useRouterQuery() + const [{ projects, hasMore }, { isPreviousData }] = usePaginatedQuery( getProjects, { skip: ITEMS_PER_PAGE * Number(page), take: ITEMS_PER_PAGE, - }, + } ) const goToPreviousPage = () => - router.push({query: {page: Number(page) - 1}}) + router.push({ query: { page: Number(page) - 1 } }) const goToNextPage = () => { if (!isPreviousData && hasMore) { - router.push({query: {page: Number(page) + 1}}) + router.push({ query: { page: Number(page) + 1 } }) } } @@ -49,7 +49,7 @@ const Projects = () => { <div> {projects.map((project) => ( <p key={project.id}> - <Link href={Routes.Project({handle: project.handle})}> + <Link href={Routes.Project({ handle: project.handle })}> <a>{project.name}</a> </Link> </p> @@ -72,8 +72,8 @@ And here's the query to work with that: ```ts export default async function getProjects( - {where, orderBy, cursor, take, skip}: GetProjectsInput, - ctx: Record<any, any> = {}, + { where, orderBy, cursor, take, skip }: GetProjectsInput, + ctx: Record<any, any> = {} ) { const projects = await db.project.findMany({ where, diff --git a/app/pages/docs/use-query.mdx b/app/pages/docs/use-query.mdx index 5f863c8..0e287ae 100644 --- a/app/pages/docs/use-query.mdx +++ b/app/pages/docs/use-query.mdx @@ -11,15 +11,15 @@ used to display errors. If you need, you can read the [React Concurrent Mode Docs](https://reactjs.org/docs/concurrent-mode-intro.html). ```tsx -import {Suspense} from "react" -import {useQuery, useRouter, useParam} from "blitz" +import { Suspense } from "react" +import { useQuery, useRouter, useParam } from "blitz" import getProject from "app/projects/queries/getProject" import ErrorBoundary from "app/components/ErrorBoundary" function Project() { const router = useRouter() const projectId = useParam("projectId", "number") - const [project] = useQuery(getProject, {where: {id: projectId}}) + const [project] = useQuery(getProject, { where: { id: projectId } }) return <div>{project.name}</div> } diff --git a/app/pages/docs/webpack-config.mdx b/app/pages/docs/webpack-config.mdx index a9fef17..843ebc7 100644 --- a/app/pages/docs/webpack-config.mdx +++ b/app/pages/docs/webpack-config.mdx @@ -10,7 +10,7 @@ extends its config inside `blitz.config.js`, like so: module.exports = { webpack: ( config, - {buildId, dev, isServer, defaultLoaders, webpack}, + { buildId, dev, isServer, defaultLoaders, webpack } ) => { // Note: we provide webpack above so you should not `require` it // Perform customizations to webpack config diff --git a/app/pages/docs/writing-recipes.mdx b/app/pages/docs/writing-recipes.mdx index d0e654e..d14cc04 100644 --- a/app/pages/docs/writing-recipes.mdx +++ b/app/pages/docs/writing-recipes.mdx @@ -44,7 +44,7 @@ set that up. ```typescript // index.ts -import {RecipeBuilder} from "@blitzjs/installer" +import { RecipeBuilder } from "@blitzjs/installer" export default RecipeBuilder().build() ``` @@ -60,7 +60,7 @@ they can look for support if they need it. RecipeBuilder() .setName("My Package") .setDescription( - "A little bit of information about what exactly is being installed.", + "A little bit of information about what exactly is being installed." ) .setOwner("Fake Author <blitz@blitzjs.com>") .setRepoLink("https://github.com/fake-author/my-recipe") @@ -107,8 +107,8 @@ builder.addAddDependenciesStep({ stepName: "Add npm dependencies", explanation: `We'll install the Tailwind library itself, as well as PostCSS for removing unused styles from our production bundles.`, packages: [ - {name: "tailwindcss", version: "1"}, - {name: "postcss-preset-env", version: "latest", isDevDep: true}, + { name: "tailwindcss", version: "1" }, + { name: "postcss-preset-env", version: "latest", isDevDep: true }, ], }) ``` @@ -130,7 +130,7 @@ anywhere in your recipe's file structure, you supply the path as a part of the recipe definition. ```typescript -import {join} from "path" +import { join } from "path" builder.addNewFilesStep({ stepId: "addStyles", @@ -161,7 +161,7 @@ path is a glob pattern, the installer process will prompt the user to select a file matching the pattern. ```typescript -import {addImport, paths} from "@blitzjs/installer" +import { addImport, paths } from "@blitzjs/installer" import j from "jscodeshift" import Collection from "jscodeshift/src/Collection" @@ -173,7 +173,7 @@ builder.addTransformFilesStep({ transform(program: Collection<j.Program>) { const stylesImport = j.importDeclaration( [], - j.literal("app/styles/index.css"), + j.literal("app/styles/index.css") ) return addImport(program, stylesImport)! }, @@ -187,15 +187,19 @@ testing, the tests are quick to write: ```typescript import j from "jscodeshift" -import { customTsParser } from "@blitzjs/installer"; - +import { customTsParser } from "@blitzjs/installer" const sampleFile = `export default function Comp() { return <div>hello!</div>; })` -expect(addImport(j(sampleFile, { - parser: customTsParser -}), newImport).toSource()).toMatchSnapshot() +expect( + addImport( + j(sampleFile, { + parser: customTsParser, + }), + newImport + ).toSource() +).toMatchSnapshot() ``` #### Modifying Non-JS files From 0275b46dd56f19c12afb3828e1e7041293198979 Mon Sep 17 00:00:00 2001 From: DawnOfMidnight <78233879+dawnofmidnight@users.noreply.github.com> Date: Fri, 30 Apr 2021 11:17:06 -0400 Subject: [PATCH 40/67] Add `scroll-behavior: smooth;` to main.css (#461) This commit is rather simple. It simply makes the scrolling when you scroll by id much smoother. For example, when you use the Back to top button in the footer, it won't jump up so quickly, it'll instead scroll. This is useful because it will surprise people less and it'll seem cleaner and less choppy too. --- app/core/styles/main.css | 1 + 1 file changed, 1 insertion(+) diff --git a/app/core/styles/main.css b/app/core/styles/main.css index 75cb599..12ffc70 100644 --- a/app/core/styles/main.css +++ b/app/core/styles/main.css @@ -11,6 +11,7 @@ html { font-family: "Libre Franklin", sans-serif; + scroll-behavior: smooth; } .player-wrapper { From 9b573d27271bd03f02ce30056d23d0ac5e16c4c4 Mon Sep 17 00:00:00 2001 From: Brandon Bayer <b@bayer.ws> Date: Tue, 4 May 2021 12:27:33 -0400 Subject: [PATCH 41/67] Update use-paginated-query.mdx --- app/pages/docs/use-paginated-query.mdx | 62 +++++++++++++------------- 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/app/pages/docs/use-paginated-query.mdx b/app/pages/docs/use-paginated-query.mdx index 5f12515..4371291 100644 --- a/app/pages/docs/use-paginated-query.mdx +++ b/app/pages/docs/use-paginated-query.mdx @@ -28,22 +28,18 @@ const ITEMS_PER_PAGE = 3 const Projects = () => { const router = useRouter() - const { page = 0 } = useRouterQuery() - const [{ projects, hasMore }, { isPreviousData }] = usePaginatedQuery( + const page = Number(router.query.page) || 0 + const [{ projects, hasMore }] = usePaginatedQuery( getProjects, { - skip: ITEMS_PER_PAGE * Number(page), + orderBy: {id: "asc"}, + skip: ITEMS_PER_PAGE * page, take: ITEMS_PER_PAGE, } ) - const goToPreviousPage = () => - router.push({ query: { page: Number(page) - 1 } }) - const goToNextPage = () => { - if (!isPreviousData && hasMore) { - router.push({ query: { page: Number(page) + 1 } }) - } - } + const goToPreviousPage = () => router.push({query: {page: page - 1}}) + const goToNextPage = () => router.push({query: {page: page + 1}}) return ( <div> @@ -57,10 +53,7 @@ const Projects = () => { <button disabled={page === 0} onClick={goToPreviousPage}> Previous </button> - <button - disabled={isPreviousData || !hasMore} - onClick={goToNextPage} - > + <button disabled={!hasMore} onClick={goToNextPage}> Next </button> </div> @@ -71,25 +64,30 @@ const Projects = () => { And here's the query to work with that: ```ts -export default async function getProjects( - { where, orderBy, cursor, take, skip }: GetProjectsInput, - ctx: Record<any, any> = {} -) { - const projects = await db.project.findMany({ - where, - orderBy, - take, - skip, - }) - - const count = await db.project.count() - const hasMore = skip! + take! < count - - return { - projects, - hasMore, +import { paginate, resolver } from "blitz" +import db, { Prisma } from "db" + +interface GetProjectsInput + extends Pick<Prisma.ProjectFindManyArgs, "where" | "orderBy" | "skip" | "take"> {} + +export default resolver.pipe( + resolver.authorize(), + async ({ where, orderBy, skip = 0, take = 100 }: GetProjectsInput) => { + const { items: projects, hasMore, nextPage, count } = await paginate({ + skip, + take, + count: () => db.project.count({ where }), + query: (paginateArgs) => db.project.findMany({ ...paginateArgs, where, orderBy }), + }) + + return { + projects, + nextPage, + hasMore, + count, + } } -} +) ``` ## API {#api} From e3632e915a26a2b7f6aa28c719d11c3d94b587d3 Mon Sep 17 00:00:00 2001 From: Brandon Bayer <b@bayer.ws> Date: Thu, 6 May 2021 11:02:13 -0400 Subject: [PATCH 42/67] Update use-infinite-query.mdx --- app/pages/docs/use-infinite-query.mdx | 61 ++++++++++----------------- 1 file changed, 23 insertions(+), 38 deletions(-) diff --git a/app/pages/docs/use-infinite-query.mdx b/app/pages/docs/use-infinite-query.mdx index abb9842..065c53b 100644 --- a/app/pages/docs/use-infinite-query.mdx +++ b/app/pages/docs/use-infinite-query.mdx @@ -58,45 +58,30 @@ function Projects(props) { And here's the query to work with that: ```ts -import { paginate } from "blitz" -import db, { Prisma, Project } from "db" - -type GetProjectsInput = { - where?: Prisma.ProjectFindManyArgs["where"] - orderBy?: Prisma.ProjectFindManyArgs["orderBy"] - skip?: number - take?: number -} - -export default async function getProjects( - { where, orderBy, take, skip }: GetProjectsInput, - ctx: Record<any, unknown> = {} -) { - const projects = await db.project.findMany({ - where, - orderBy, - take, - skip, - }) - if (ctx.referer) { - console.log("HTTP referer:", ctx.referer) - } - - const { items: projects, hasMore, nextPage, count } = await paginate({ - skip, - take, - count: () => db.project.count({ where }), - query: (paginateArgs) => - db.project.findMany({ ...paginateArgs, where, orderBy }), - }) - - return { - projects, - nextPage, - hasMore, - count, +import { paginate, resolver } from "blitz" +import db, { Prisma } from "db" + +interface GetProjectsInput + extends Pick<Prisma.ProjectFindManyArgs, "where" | "orderBy" | "skip" | "take"> {} + +export default resolver.pipe( + resolver.authorize(), + async ({ where, orderBy, skip = 0, take = 100 }: GetProjectsInput) => { + const { items: projects, hasMore, nextPage, count } = await paginate({ + skip, + take, + count: () => db.project.count({ where }), + query: (paginateArgs) => db.project.findMany({ ...paginateArgs, where, orderBy }), + }) + + return { + projects, + nextPage, + hasMore, + count, + } } -} +) ``` ## API {#api} From 50481880e9a0ee93ba07d4679310d166bb36cf6e Mon Sep 17 00:00:00 2001 From: Abu Uzayr <abu.uzayr@builtforfifty.com> Date: Thu, 6 May 2021 23:15:56 +0800 Subject: [PATCH 43/67] Create guide to deploy to Railway (#462) Co-authored-by: JuanM04 <me@juanm04.com> --- app/core/navs/documentation.json | 2 +- app/pages/docs/deploy-railway.mdx | 121 ++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 app/pages/docs/deploy-railway.mdx diff --git a/app/core/navs/documentation.json b/app/core/navs/documentation.json index 0979214..0fb1cc3 100644 --- a/app/core/navs/documentation.json +++ b/app/core/navs/documentation.json @@ -134,7 +134,7 @@ "iconPath": "/img/deploying-to-production.svg", "iconDarkPath": "/img/deploying-to-production-white.svg" }, - "pages": ["deploy-render", "deploy-heroku", "deploy-vercel"] + "pages": ["deploy-render", "deploy-heroku", "deploy-vercel", "deploy-railway"] }, { "title": { diff --git a/app/pages/docs/deploy-railway.mdx b/app/pages/docs/deploy-railway.mdx new file mode 100644 index 0000000..bb4674a --- /dev/null +++ b/app/pages/docs/deploy-railway.mdx @@ -0,0 +1,121 @@ +--- +title: Deploy to a Server on Railway +sidebar_label: To Railway +--- + +## If you don't have a Railway account {#signup} + +Sign up for an account on [Railway](https://railway.app) - they have a + free tier that is sufficient for small applications. + +![Screen Shot 2021-05-04 at 6 11 09 PM](https://user-images.githubusercontent.com/19371989/117007314-13ee3580-ad1c-11eb-9705-2ca9b5f545bd.png) + +## Set up a new project {#setup} + +1. Create a new project (via your + [Dashboard](https://railway.app/dashboard) or `⌘ + /` shortcut) + +![Screen Shot 2021-05-04 at 5 19 28 PM](https://user-images.githubusercontent.com/19371989/117007376-28323280-ad1c-11eb-90a5-9fa5c165f5df.png) + +2. If a database is required, select **Provision PostgreSQL** (or + MongoDB/MySQL/Redis - whichever flavour your app requires). Once the + database is provisioned, you will see the Project Setup page. + +![Screen Shot 2021-05-04 at 6 01 16 PM](https://user-images.githubusercontent.com/19371989/117007600-6deefb00-ad1c-11eb-876c-22a374c1f535.png) + +If a database is not required, select **Deploy From Repo**. + +There are two ways to set up your project on Railway: + +1. [Via the command line interface (CLI)](#cli) +2. [Via Railway's user interface (GUI)](#gui) + +### Via CLI {#cli} + +1. Install Railway + +```bash +npm i -g @railway/cli +# or +yarn global add @railway/cli +``` + +2. Login to Railway + +``` +railway login +``` + +3. Go to your project folder and link to Railway + +``` +cd /path/to/project +railway link your-project-id +``` + +The project ID is taken from the **Project Setup** page. + +4. Enter your required environment variables + +Blitz requires the `SESSION_SECRET_KEY` variable to be set. Go to your +project on the GUI, select the Variables link and enter +`SESSION_SECRET_KEY` as the name of the variables a random 32-key secret +as its value. Click **Add** to add the variable. + +Enter other environment variables that you require. The `DATABASE_URL` +variable will be automatically set by Railway if you provision a +PostgreSQL database. + +Verify that the variables are set correctly via your linked project: + +``` +railway variables +``` + +5. Deploy your project + +``` +railway up +``` + +Once the build is successful and deployment is completed, you will receive +the URL for your application that is now accessible via the internet. To +get the URL, go to the **Deployments** section in the project's dashboard +and the URL will be listed under a successful deployment. + +![Screen Shot 2021-05-04 at 8 49 57 PM](https://user-images.githubusercontent.com/19371989/117007722-8e1eba00-ad1c-11eb-9a9e-91908e4c281c.png) + +You may also view your app logs by running + +``` +railway logs +``` + +In order to set Railway to build + deploy automatically when you push to a +branch on your GitHub repository, connect your GitHub repository by +following the [GUI](#gui) steps below from Step 2 onward. + +### Via GUI {#gui} + +1. Enter your required environment variables + +Blitz requires the `SESSION_SECRET_KEY` variable to be set. Go to your +project on the GUI, select the **Variables** link and enter +`SESSION_SECRET_KEY` as the name of the variable and a random 32-key secret +as its value. Click **Add** to add the variable. + +Enter other environment variables that you require. The `DATABASE_URL` +variable will be automatically set by Railway if you provision a database. + +2. Go to the **Deployments** section. If your project is on GitHub, you + can click on **Connect GitHub** to deploy and connect directly to your + repositories on GitHub. Otherwise, you will have to use the [CLI](#cli) + to deploy your application. + +3. Select the repository and the branch to deploy from and click + **Connect + Deploy** + +![Screen Shot 2021-05-04 at 8 55 36 PM](https://user-images.githubusercontent.com/19371989/117007814-a5f63e00-ad1c-11eb-84d6-d75019930ea1.png) + +4. Railway will now build and deploy your app on every push to this branch + on your selected repository. From 30f8d9a619bdcbe03897981a018cf2abeb31bbe8 Mon Sep 17 00:00:00 2001 From: Brandon Bayer <b@bayer.ws> Date: Thu, 6 May 2021 11:25:21 -0400 Subject: [PATCH 44/67] fix tutorial --- app/pages/docs/tutorial.mdx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/pages/docs/tutorial.mdx b/app/pages/docs/tutorial.mdx index 9917824..c694040 100644 --- a/app/pages/docs/tutorial.mdx +++ b/app/pages/docs/tutorial.mdx @@ -1016,6 +1016,9 @@ export default resolver.pipe( + })), + }, + }, ++ include: { ++ choices: true, ++ }, + }) return question @@ -1029,6 +1032,12 @@ create it". This is perfect for this case because we didn't require the user to add three choices when creating the question. So if later the user adds another choice by editing the question, then it'll be created here. +## Cleanup + +In order for `yarn tsc` or `git push` to succeed, you'll need to delete +`app/choices/mutations/createChoice.ts` (unused) or update the +CreateChoice zod schema to include the required fields. + ## Conclusion {#conclusion} 🥳 Congrats! You created your very own Blitz app! Have fun playing around From 7aca5c613fb5704613528832b0041c55e972743b Mon Sep 17 00:00:00 2001 From: Brandon Bayer <b@bayer.ws> Date: Fri, 7 May 2021 11:36:23 -0400 Subject: [PATCH 45/67] update link --- blitz.config.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blitz.config.js b/blitz.config.js index eb06b31..c2a9435 100644 --- a/blitz.config.js +++ b/blitz.config.js @@ -27,8 +27,8 @@ module.exports = withBundleAnalyzer({ permanent: false, }, { - source: "/meetup", - destination: "https://us02web.zoom.us/j/85901497017?pwd=MVFMUXJOQndqbDNJaU1BK2N0ZjNpQT09", + source: "/joinmeetup", + destination: "https://us02web.zoom.us/j/85901497017?pwd=eVo4YlhsU2E3UHQvUmgxTmtRUDBIZz09", permanent: false, }, ] From ed76c181601f96e923a3aca5e5baa768fc6b6988 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kevin=20=C3=98sterkilde?= <kevin@oesterkilde.dk> Date: Fri, 7 May 2021 20:15:14 +0200 Subject: [PATCH 46/67] Update done method in passportjs.mdx (#464) --- app/pages/docs/passportjs.mdx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/pages/docs/passportjs.mdx b/app/pages/docs/passportjs.mdx index fd58d1a..50db5b5 100644 --- a/app/pages/docs/passportjs.mdx +++ b/app/pages/docs/passportjs.mdx @@ -162,7 +162,7 @@ export default passportAuth({ roles: [user.role], source: "twitter", } - done(null, { publicData }) + done(undefined, { publicData }) } ), }, @@ -200,7 +200,7 @@ app**. from your `verify` callback. ```ts -done(null, result) +done(undefined, result) ``` where `result` is an object of type `VerifyCallbackResult` @@ -245,7 +245,7 @@ should be sent after they are authenticated. They are listed here in order of priority. A URL provided with method #1 will override all other URLs. 1. Add `redirectUrl` to the `verify` callback result - - Example: `done(null, {publicData, redirectUrl: '/'})` + - Example: `done(undefined, {publicData, redirectUrl: '/'})` 2. Add a `redirectUrl` query parameter to the "initiate login" url - Example: `example.com/api/auth/twitter?redirectUrl=/dashboard` - Example: From af4fae47595156d1ad435eeba78c05a754a5a046 Mon Sep 17 00:00:00 2001 From: Brandon Bayer <b@bayer.ws> Date: Sat, 8 May 2021 15:15:14 -0400 Subject: [PATCH 47/67] Update use-infinite-query.mdx --- app/pages/docs/use-infinite-query.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/pages/docs/use-infinite-query.mdx b/app/pages/docs/use-infinite-query.mdx index 065c53b..ea26819 100644 --- a/app/pages/docs/use-infinite-query.mdx +++ b/app/pages/docs/use-infinite-query.mdx @@ -28,7 +28,7 @@ function Projects(props) { ) return ( <> - {projectPages.map((group, i) => ( + {projectPages.map((page, i) => ( <React.Fragment key={i}> {page.projects.map((project) => ( <p key={project.id}>{project.name}</p> From bfbb00492a38c56fed7fd256b2f2399d1b3484a3 Mon Sep 17 00:00:00 2001 From: meepdeew <43303008+meepdeew@users.noreply.github.com> Date: Sun, 9 May 2021 05:42:33 -0700 Subject: [PATCH 48/67] Fix typo in tutorial.mdx (#467) --- app/pages/docs/tutorial.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/pages/docs/tutorial.mdx b/app/pages/docs/tutorial.mdx index c694040..a32fc3b 100644 --- a/app/pages/docs/tutorial.mdx +++ b/app/pages/docs/tutorial.mdx @@ -187,7 +187,7 @@ const Home: BlitzPage = () => { ``` Save the file and you should see the page update in your browser. You can -add customize this as much as you want. When you’re ready, move on to the +customize this as much as you want. When you’re ready, move on to the next section. ## Database setup {#database-setup} From 9ea25006e37afb59aecaaf55c59324b8fd22ef59 Mon Sep 17 00:00:00 2001 From: Brandon Bayer <b@bayer.ws> Date: Mon, 10 May 2021 15:37:34 -0400 Subject: [PATCH 49/67] fix build --- app/pages/docs/tutorial.mdx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/pages/docs/tutorial.mdx b/app/pages/docs/tutorial.mdx index a32fc3b..384f804 100644 --- a/app/pages/docs/tutorial.mdx +++ b/app/pages/docs/tutorial.mdx @@ -187,8 +187,8 @@ const Home: BlitzPage = () => { ``` Save the file and you should see the page update in your browser. You can -customize this as much as you want. When you’re ready, move on to the -next section. +customize this as much as you want. When you’re ready, move on to the next +section. ## Database setup {#database-setup} @@ -1032,7 +1032,7 @@ create it". This is perfect for this case because we didn't require the user to add three choices when creating the question. So if later the user adds another choice by editing the question, then it'll be created here. -## Cleanup +## Cleanup {#cleanup} In order for `yarn tsc` or `git push` to succeed, you'll need to delete `app/choices/mutations/createChoice.ts` (unused) or update the From 1d9533668ae924b680248631153a07a1fc17dd41 Mon Sep 17 00:00:00 2001 From: Hardik Gaur <hardikgaur3296@gmail.com> Date: Tue, 11 May 2021 01:13:12 +0530 Subject: [PATCH 50/67] Toggling text and removing outline (#469) Co-authored-by: Brandon Bayer <b@bayer.ws> --- app/core/components/DarkModeToggle.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/core/components/DarkModeToggle.js b/app/core/components/DarkModeToggle.js index 42bdc22..6fe8d5f 100644 --- a/app/core/components/DarkModeToggle.js +++ b/app/core/components/DarkModeToggle.js @@ -18,7 +18,7 @@ const DarkModeToggle = ({className}) => { return ( <button onClick={switchTheme} - className={`pr-2 rounded focus:outline-none focus:ring-inset focus:ring-white focus:ring-2 ${className}`} + className={`pr-2 rounded focus:outline-none focus:ring-inset focus:ring-white ${className}`} > {theme === "dark" ? ( <BiToggleRight size="2rem" className="inline" /> @@ -26,7 +26,8 @@ const DarkModeToggle = ({className}) => { <BiToggleLeft size="2rem" className="inline" /> )} <span className="mx-1"> - Dark<span className="lg:hidden"> Mode</span> + {theme === "dark" ? "Dark" : "Light"} + <span className="lg:hidden"> Mode</span> </span> </button> ) From e334eb81e2e2f2c940514654f2b86c8f56270034 Mon Sep 17 00:00:00 2001 From: Brandon Bayer <b@bayer.ws> Date: Mon, 10 May 2021 16:06:39 -0400 Subject: [PATCH 51/67] Update query-usage.mdx --- app/pages/docs/query-usage.mdx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/app/pages/docs/query-usage.mdx b/app/pages/docs/query-usage.mdx index 6c1561a..0f87385 100644 --- a/app/pages/docs/query-usage.mdx +++ b/app/pages/docs/query-usage.mdx @@ -89,10 +89,7 @@ query when it is ready to turn on: ```tsx const [user] = useQuery(getUser, { where: { id: props.query.id } }) -const [projects] = useQuery( - getProjects, - () => ({ where: { userId: user.id } }, { enabled: user }) -) +const [projects] = useQuery(getProjects, { where: { userId: user.id } }, { enabled: user })) ``` ### Pagination {#pagination} From e6f46efd1502e8c98c137c69cf7fac4653466a5d Mon Sep 17 00:00:00 2001 From: Brandon Bayer <b@bayer.ws> Date: Mon, 10 May 2021 16:24:30 -0400 Subject: [PATCH 52/67] add docs for setting up node.js --- app/pages/docs/get-started.mdx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/pages/docs/get-started.mdx b/app/pages/docs/get-started.mdx index 9fbd16b..7f920d8 100644 --- a/app/pages/docs/get-started.mdx +++ b/app/pages/docs/get-started.mdx @@ -5,7 +5,11 @@ sidebar_label: Get Started ### Set Up Your Computer {#set-up-your-computer} -You need Node.js 12 or newer +You need Node.js 12 or newer. You can verify this by running `node -v` in +your terminal. If you don't have Node or need a newer version, we +recommend using a node version manager like +[fnm](https://github.com/Schniz/fnm). That will allow you to easily change +node versions and even have different versions for each project. ### Install Blitz {#install-blitz} From 7209765e8264098bfb25c8d11f2a36a29ad243bd Mon Sep 17 00:00:00 2001 From: Jeremy Liberman <jeremy@jeremyliberman.com> Date: Mon, 10 May 2021 15:38:29 -0500 Subject: [PATCH 53/67] Documentation about prefetching and dehydratedState (#465) --- app/pages/docs/get-server-side-props.mdx | 21 +++++-- app/pages/docs/get-static-props.mdx | 21 +++++-- app/pages/docs/pages.mdx | 77 ++++++++++++++++++++++-- 3 files changed, 104 insertions(+), 15 deletions(-) diff --git a/app/pages/docs/get-server-side-props.mdx b/app/pages/docs/get-server-side-props.mdx index 199c139..4e7b609 100644 --- a/app/pages/docs/get-server-side-props.mdx +++ b/app/pages/docs/get-server-side-props.mdx @@ -3,9 +3,9 @@ title: getServerSideProps API sidebar_label: getServerSideProps API --- -If you export an `async` function called `getServerSideProps` from a page, -Blitz will pre-render this page on each request using the data returned by -`getServerSideProps`. +If you export an `async` function called `getServerSideProps` from a +[page](./pages), Blitz will pre-render this page on each request using the +data returned by `getServerSideProps`. ```js export async function getServerSideProps(context) { @@ -189,9 +189,20 @@ what Blitz eliminates from the client-side bundle. #### Only allowed in a page -`getServerSideProps` can only be exported from a **page**. You can’t -export it from non-page files. +`getServerSideProps` can only be exported from a [page](./pages). You +can’t export it from non-page files. Also, you must use `export async function getServerSideProps() {}` — it will **not** work if you add `getServerSideProps` as a property of the page component. + +#### First Render UX + +Even though a page with `getServerSideProps` will be pre-rendered on the +server, it may fallback to the page's loading state if your page contains +queries that request data missing from the initial page props. This can +create a flickering effect where the contents of the page rapidly changes +when the query data gets loaded into the app. + +[Click here](./pages#first-render-ux) to discover ways you can improve the +first render User Experience (UX) of your pages. diff --git a/app/pages/docs/get-static-props.mdx b/app/pages/docs/get-static-props.mdx index 830227b..bcb70d9 100644 --- a/app/pages/docs/get-static-props.mdx +++ b/app/pages/docs/get-static-props.mdx @@ -16,9 +16,9 @@ runtime. And at build time, there is no user http request to handle. </Card> -If you export an `async` function called `getStaticProps` from a page, -Blitz will pre-render this page at build time using the props returned by -`getStaticProps`. +If you export an `async` function called `getStaticProps` from a +[page](./pages), Blitz will pre-render this page at build time using the +props returned by `getStaticProps`. ```jsx export async function getStaticProps(context) { @@ -376,8 +376,8 @@ the exported JSON is used. #### Only allowed in a page -`getStaticProps` can only be exported from a **page**. You can’t export it -from non-page files. +`getStaticProps` can only be exported from a [page](./pages). You can’t +export it from non-page files. One of the reasons for this restriction is that React needs to have all the required data before the page is rendered. @@ -391,6 +391,17 @@ component. In development (`blitz dev`), `getStaticProps` will be called on every request. +#### First Render UX + +Even though a page with `getStaticProps` will be pre-rendered on the +server, it may fallback to the page's loading state if your page contains +queries that request data missing from the initial page props. This can +create a flickering effect where the contents of the page rapidly changes +when the query data gets loaded into the app. + +[Click here](./pages#first-render-ux) to discover ways you can improve the +first render User Experience (UX) of your pages. + #### Preview Mode In some cases, you might want to temporarily bypass Static Generation and diff --git a/app/pages/docs/pages.mdx b/app/pages/docs/pages.mdx index e894cc6..364cd09 100644 --- a/app/pages/docs/pages.mdx +++ b/app/pages/docs/pages.mdx @@ -39,15 +39,26 @@ file called `app/pages/posts/[id].js`, then it will be accessible at By default, Blitz pre-renders the static HTML for every page unless you explicitly opt-in to server-side rendering. -For pages with dynamic data, the page's loading state will be -pre-rendered. - -#### First Render UX +For pages with dynamic data, the page's loading fallback state will be +rendered unless you [prefetch](./query-usage#prefetching) the data to +populate the cache. In some cases, the static optimization can cause an undesirable UX where the first render shows one thing but the second render shows another. For example this happens when using `useSession()`. +Next, we'll introduce some ways to improve the way Blitz pre-renders your +pages. + +### First Render UX {#first-render-ux} + +To provide a better User Experience (UX), Blitz tries to automatically +pre-render the HTML for your pages, but if your page contains dynamic data +fetched with [useQuery](./use-query) hooks, Blitz will fallback to the +page's loading state and you will not see much benefit from the automatic +pre-rendering, because the application will need to immediately make +another request to fetch the data it needs to satisfy the query. + In this case you can set `Page.suppressFirstRenderFlicker = true`, and Blitz will hide the first render's content. This will result in a tiny delay of first paint but will greatly improve the perceived UX. @@ -64,6 +75,62 @@ Page.suppressFirstRenderFlicker = true export default Page ``` +You can also consider prefetching all of the necessary queries that your +page needs to complete its first render. You can use either the +`getStaticProps` or `getServerSideProps` page functions, depending on what +you need. + +If you create an instance of QueryClient and populate it with query data, +then you can pass it as `dehydratedState` to your page props. Blitz will +automatically use the state to build the query cache later when the page +tries to render. + +```tsx +import { + useQuery, + getQueryKey, + invokeWithMiddleware, + dehydrate, + QueryClient, + BlitzPage, + GetServerSidePropsContext +} from 'blitz' +const Page: BlitzPage = () => { + // highlight-start + const [organization] = useQuery(getCurrentOrganization, null) + // highlight-end + + return <div>You have selected: {organization.name}</div> +} + +export async function getServerSideProps(ctx: GetServerSidePropsContext) { + const queryClient = new QueryClient() + + // highlight-start + const queryKey = getQueryKey(getCurrentOrganization, null) + await queryClient.prefetchQuery(queryKey, () => + invokeWithMiddleware(getCurrentOrganization, null, ctx), + ) + // highlight-end + + return { + props: { + dehydratedState: dehydrate(queryClient), + }, + } +} +``` + +In this way, the pre-rendered version of your page HTML will not have to +make a request as soon as it loads to fetch any data and will avoid any +screen flickering. This is very useful for Search Engine Optimization and +the Link Previews used by Social Media sites like Facebook and Twitter. + +You can also use prefetching to pre-populate queries that a user might +need shortly after the page loads--such a common searches or filter +criteria--by setting a [staleTime](./use-query#options) on the query being +prefetched. + ### Static Page Generation for Unauthenticated Pages {#static-page-generation-for-unauthenticated-pages} For pages accessible by anyone without authentication, we recommend using @@ -91,5 +158,5 @@ To use Server-side Rendering for a page, you need to `export` an `async` function called `getServerSideProps`. This function will be called by the server on every request. -See the [`getServerSideProps` documentation](./get-server-side-props) for +See the `getServerSideProps` [documentation](./get-server-side-props) for more details. From 1c11b918dbfcd9f1a9ddd11f77ce5ba20a98360f Mon Sep 17 00:00:00 2001 From: Brandon Bayer <b@bayer.ws> Date: Mon, 10 May 2021 18:50:08 -0400 Subject: [PATCH 54/67] improve custom server doc --- app/pages/docs/custom-server.mdx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/pages/docs/custom-server.mdx b/app/pages/docs/custom-server.mdx index 4528a03..d47d1ed 100644 --- a/app/pages/docs/custom-server.mdx +++ b/app/pages/docs/custom-server.mdx @@ -8,8 +8,9 @@ some other direct control over the web server itself. ### How {#how} -1. Add a `server.ts` or `server.js` file in your project root. See below - for an example. +1. Add a `server.ts` or `server.js` file in your project root. Or you can + use `server/index.js` or `server/index.ts`. See below for an example + server. 2. Now `blitz dev` and `blitz start` will automatically detect and use your custom server. From d348d41d56848f3e7d1326b781584e1e55a6838f Mon Sep 17 00:00:00 2001 From: craigglennie <craig.glennie@gmail.com> Date: Tue, 11 May 2021 22:28:12 +1200 Subject: [PATCH 55/67] Fixes import path for useCurrentUser in authorization.mdx (#471) --- app/pages/docs/authorization.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/pages/docs/authorization.mdx b/app/pages/docs/authorization.mdx index b9fe196..f12393a 100644 --- a/app/pages/docs/authorization.mdx +++ b/app/pages/docs/authorization.mdx @@ -238,7 +238,7 @@ New Blitz apps by default have a `useCurrentUser()` hook and a corresponding `getCurrentUser` query. ```tsx -import { useCurrentUser } from "app/hooks/useCurrentUser" +import { useCurrentUser } from "app/core/hooks/useCurrentUser" const user = useCurrentUser() From fdaa2cfeb6dccc748c149d29164cc969200c1781 Mon Sep 17 00:00:00 2001 From: Brandon Bayer <b@bayer.ws> Date: Wed, 12 May 2021 11:52:56 -0400 Subject: [PATCH 56/67] Update session-management.mdx --- app/pages/docs/session-management.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/pages/docs/session-management.mdx b/app/pages/docs/session-management.mdx index b194792..2a9eeb1 100644 --- a/app/pages/docs/session-management.mdx +++ b/app/pages/docs/session-management.mdx @@ -336,7 +336,7 @@ defined above in `SessionConfig`. The functions can do anything, but they must conform to the defined input and outputs types. For reference, here's -[the default config that works with Prisma](https://github.com/blitz-js/blitz/blob/canary/packages/server/src/supertokens.ts#L62-L85). +[the default config that works with Prisma](https://github.com/blitz-js/blitz/blob/canary/packages/core/src/server/auth/sessions.ts#L65-L88). ## Manual API Requests {#manual-api-requests} From bd8bfc9d9db48e244b7e0be009cf58f991cc78ee Mon Sep 17 00:00:00 2001 From: Roshan Manuel <31125563+Roesh@users.noreply.github.com> Date: Wed, 12 May 2021 16:57:42 -0400 Subject: [PATCH 57/67] Images optimization (#474) * typo fix Small typo fix: you'll need to do the chech => you'll need to do the check * Update session.create > session.$create * Update api routes doc examples - no anon exports * Edit preview mode doc examples - no anon exports * Change export name to handler * Change export name to handler * lazy load random contributor images * lazy load appropriate player, url * switch to Image tag * fix image sizing * change user track image to github url * make layout intrinsic Co-authored-by: Roshan Manuel <Roshan,manuel@angelic-group.com> --- app/core/components/SponsorPack.js | 2 +- app/core/components/home/VideoPlayer.js | 3 ++- app/pages/index.js | 5 ++++- blitz.config.js | 13 +++++++++++++ 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/app/core/components/SponsorPack.js b/app/core/components/SponsorPack.js index 9fe62de..3832735 100644 --- a/app/core/components/SponsorPack.js +++ b/app/core/components/SponsorPack.js @@ -61,7 +61,7 @@ const sponsors = [ { name: "userTrack", href: "https://www.usertrack.net/?ref=blitzjs_web", - imageUrl: "https://i.imgur.com/UDBeazC.png", + imageUrl: "https://raw.githubusercontent.com/blitz-js/blitz/canary/assets/usertrack.png", tier: 4, cost: 100, }, diff --git a/app/core/components/home/VideoPlayer.js b/app/core/components/home/VideoPlayer.js index 9606cb2..a5e31d2 100644 --- a/app/core/components/home/VideoPlayer.js +++ b/app/core/components/home/VideoPlayer.js @@ -1,4 +1,4 @@ -import ReactPlayer from "react-player" +import ReactPlayer from "react-player/lazy" const VideoPlayer = ({url, className = ""}) => { return ( @@ -9,6 +9,7 @@ const VideoPlayer = ({url, className = ""}) => { width="100%" height="100%" controls={true} + light={true} /> </div> ) diff --git a/app/pages/index.js b/app/pages/index.js index 55c255b..240c1b5 100644 --- a/app/pages/index.js +++ b/app/pages/index.js @@ -224,7 +224,10 @@ const Home = ({randomContributors}) => { target="_blank" rel="noopener noreferrer" > - <img + <Image + layout="intrinsic" + width="100%" + height="100%" src={contributor.avatar_url} alt={contributor.name} title={contributor.name} diff --git a/blitz.config.js b/blitz.config.js index c2a9435..14122c4 100644 --- a/blitz.config.js +++ b/blitz.config.js @@ -19,6 +19,19 @@ const fallbackDefaultExports = { module.exports = withBundleAnalyzer({ pageExtensions: ["js", "jsx", "mdx"], + images: { + domains: [ + "raw.githubusercontent.com", + "avatars.githubusercontent.com", + "avatars0.githubusercontent.com", + "avatars1.githubusercontent.com", + "avatars2.githubusercontent.com", + "avatars3.githubusercontent.com", + "avatars4.githubusercontent.com", + "avatars5.githubusercontent.com", + "avatars6.githubusercontent.com", + ] + }, async redirects() { return [ { From df4bc2551074a9fb524b8d0ebd1a8c70d42dcbb9 Mon Sep 17 00:00:00 2001 From: Simon Knott <info@simonknott.de> Date: Sat, 15 May 2021 14:33:13 +0200 Subject: [PATCH 58/67] redirectAuthenticatedTo with Routes Manifest in authorization.mdx (#475) --- app/pages/docs/authorization.mdx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/pages/docs/authorization.mdx b/app/pages/docs/authorization.mdx index f12393a..0b92563 100644 --- a/app/pages/docs/authorization.mdx +++ b/app/pages/docs/authorization.mdx @@ -132,12 +132,17 @@ pages, set `Page.redirectAuthenticatedTo = '/'` to automatically redirect logged in users to another page. ```tsx +import { Routes } from "blitz" + const Page: BlitzPage = () => { return <div>{/* ... */}</div> } // highlight-start +// using full path Page.redirectAuthenticatedTo = "/" +// using route manifest +Page.redirectAuthenticatedTo = Routes.Home() // highlight-end export default Page From 70a4ef1de11636f6d26d6c46ccbd29b5c68fecd5 Mon Sep 17 00:00:00 2001 From: swiftgaruda <16741392+swiftgaruda@users.noreply.github.com> Date: Mon, 17 May 2021 00:43:48 +0200 Subject: [PATCH 59/67] Typo in tradeoffs.mdx (#477) --- app/pages/docs/tradeoffs.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/pages/docs/tradeoffs.mdx b/app/pages/docs/tradeoffs.mdx index bb71939..4f00ad5 100644 --- a/app/pages/docs/tradeoffs.mdx +++ b/app/pages/docs/tradeoffs.mdx @@ -69,7 +69,7 @@ clients. This can be an excellent choice and some folks are doing this. ## Advanced Backend Architecture {#advanced-backend-architecture} Currently Blitz is fairly minimal on backend architecture, especially -compared so something like Nest.js or AdonisJs. However, that doesn't mean +compared to something like Nest.js or AdonisJs. However, that doesn't mean you can't use those patterns in Blitz, it means you have to set it up yourself. In fact you can use Nest.js in your Blitz app if you need. From bc32f825fadc6a402d8556b98d6ffaccf8e99d4a Mon Sep 17 00:00:00 2001 From: Brandon Bayer <b@bayer.ws> Date: Mon, 17 May 2021 14:02:13 -0400 Subject: [PATCH 60/67] Update query-usage.mdx --- app/pages/docs/query-usage.mdx | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/app/pages/docs/query-usage.mdx b/app/pages/docs/query-usage.mdx index 0f87385..74e4efc 100644 --- a/app/pages/docs/query-usage.mdx +++ b/app/pages/docs/query-usage.mdx @@ -102,17 +102,16 @@ Use the [`useInfiniteQuery`](./use-infinite-query) hook ### Prefetching {#prefetching} -- All queries are automatically cached, so manually calling a query - function will cache its data - -Both of the following will cache the `getProject` query. +All queries are automatically cached, so both of the following will cache the `getProject` query. ```ts -const project = await getProject({ where: { id: props.id } }) +import {invoke} from 'blitz' + +const project = await invoke(getProject, { where: { id: props.id } }) ``` ```tsx -<a onMouseEnter={() => getProject({ where: { id: projectId } })}> +<a onMouseEnter={() => invoke(getProject, { where: { id: props.id } })}> View Project </a> ``` From 5375fa0750b544423133aba9be4da879badce091 Mon Sep 17 00:00:00 2001 From: Pankaj Patil <pankaj.patil2099@hotmail.com> Date: Tue, 18 May 2021 02:48:28 +0530 Subject: [PATCH 61/67] Documentation Improvement: Page.authenticate implies Page.suppressFirstRenderFlicker (#476) --- app/pages/docs/pages.mdx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/pages/docs/pages.mdx b/app/pages/docs/pages.mdx index e894cc6..710afdf 100644 --- a/app/pages/docs/pages.mdx +++ b/app/pages/docs/pages.mdx @@ -63,6 +63,10 @@ Page.suppressFirstRenderFlicker = true export default Page ``` +In case you have set `Page.authenticate = true` or `Page.redirectAuthenticatedTo = true`, +Blitz will hide the first render's content. For these cases, you can simply ignore setting +`Page.suppressFirstRenderFlicker = true`. + ### Static Page Generation for Unauthenticated Pages {#static-page-generation-for-unauthenticated-pages} From 7e1e2a7ab25c511bf875da3d0accf38524509f8d Mon Sep 17 00:00:00 2001 From: Brandon Bayer <b@bayer.ws> Date: Mon, 17 May 2021 18:39:58 -0400 Subject: [PATCH 62/67] add cookie prefix to docs --- app/pages/docs/session-management.mdx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/pages/docs/session-management.mdx b/app/pages/docs/session-management.mdx index b194792..a507645 100644 --- a/app/pages/docs/session-management.mdx +++ b/app/pages/docs/session-management.mdx @@ -284,6 +284,7 @@ module.exports = { middleware: [ sessionMiddleware({ // highlight-start + cookiePrefix: "my-app", sessionExpiryMinutes: 1234, isAuthorized: simpleRolesIsAuthorized, // highlight-end @@ -296,6 +297,7 @@ Available options: ```ts type SessionConfig = { + cookiePrefix?: string /* default: 'blitz' */ sessionExpiryMinutes?: number /* Default: 30 days */ sameSite?: "strict" | "lax" | "none" /* Default: 'lax' */ domain?: string /* Default: undefined. Can set as `.yourDomain.com` to work across subdomains */ From 28e83ccf43b4f6edba48e1b458a9bdbdc97e60dc Mon Sep 17 00:00:00 2001 From: Roshan Manuel <31125563+Roesh@users.noreply.github.com> Date: Wed, 19 May 2021 10:45:11 -0400 Subject: [PATCH 63/67] Add doc page for eslint config and fix typo (#478) Co-authored-by: Roshan Manuel <Roshan,manuel@angelic-group.com> --- app/core/navs/documentation.json | 1 + app/pages/docs/contributing.mdx | 2 +- app/pages/docs/eslint-config.mdx | 43 ++++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 app/pages/docs/eslint-config.mdx diff --git a/app/core/navs/documentation.json b/app/core/navs/documentation.json index c53c108..ae42b95 100644 --- a/app/core/navs/documentation.json +++ b/app/core/navs/documentation.json @@ -156,6 +156,7 @@ "blitz-config", "webpack-config", "postcss-config", + "eslint-config", "rpc-specification", "measuring-performance" ] diff --git a/app/pages/docs/contributing.mdx b/app/pages/docs/contributing.mdx index b85185c..56184d6 100644 --- a/app/pages/docs/contributing.mdx +++ b/app/pages/docs/contributing.mdx @@ -202,7 +202,7 @@ The typical workflow will be only run next.js integation tests related to your change. Then push to CI and see if any other intergration tests fail. If they do, then locally run the one that fails and fix the issue. -### Testing Development Version of Blitz Inside and App {#testing-development-version-of-blitz} +### Testing Development Version of Blitz Inside an App {#testing-development-version-of-blitz} <Card type="info"> diff --git a/app/pages/docs/eslint-config.mdx b/app/pages/docs/eslint-config.mdx new file mode 100644 index 0000000..53ac31c --- /dev/null +++ b/app/pages/docs/eslint-config.mdx @@ -0,0 +1,43 @@ +--- +title: Custom ESLint Config +sidebar_label: ESLint Config +--- + +Blitz extends the +[create-react-app](https://create-react-app.dev/docs/setting-up-your-editor/#experimental-extending-the-eslint-config) +eslint config. + +Our configuration contains a few modifications, and the full preset can be +found on github +[here](https://github.com/blitz-js/blitz/blob/canary/packages/eslint-config/index.js), + +This configuration is found in .eslintrc.js, and by default is + +```js +module.exports = { + extends: ["blitz"], +} +``` + +## Extending or replacing the default Blitz ESLint config {#extending} + +You can extend the base Blitz ESLint config, or replace it completely if you need. + +There are a few things to remember: + +1. We highly recommend extending the base config, as removing it could introduce hard-to-find issues. +2. It's important to note that any rules that are set to "error" will stop the project from building. + +In the below example: + + - the base config has been extended by a shared ESLint config, + - a new rule has been set that applies to all JavaScript and TypeScript files + +```js +module.exports = { + extends: ["blitz", "shared-config"], + rules: { + "additional-rule": "warn" + }, +} +``` \ No newline at end of file From e501d278247f6338ecca64c8499fee55047a863a Mon Sep 17 00:00:00 2001 From: Francesco Sardo <francesco.deploy@gmail.com> Date: Wed, 19 May 2021 15:45:31 +0100 Subject: [PATCH 64/67] Missing import (#480) If you try to copy-paste the examples there's a missing import --- app/pages/docs/impersonation.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/pages/docs/impersonation.mdx b/app/pages/docs/impersonation.mdx index 0d2c6e0..b491d63 100644 --- a/app/pages/docs/impersonation.mdx +++ b/app/pages/docs/impersonation.mdx @@ -98,7 +98,7 @@ import { useMutation, queryClient } from "blitz" import impersonateUser, { ImpersonateUserInput, } from "app/auth/mutations/impersonateUser" -import Form from "app/core/components/Form" +import { Form, FORM_ERROR } from "app/core/components/Form" import LabeledTextField from "app/core/components/LabeledTextField" export const ImpersonateUserForm = () => { From 5129c9696bb23a1c93ef1bb6350185431383bc3c Mon Sep 17 00:00:00 2001 From: Francesco Sardo <francesco.deploy@gmail.com> Date: Wed, 19 May 2021 15:46:08 +0100 Subject: [PATCH 65/67] Avoid hardcoding "admin" role during impersonation (#481) When you impersonate another use it would be a good idea to assumer their role too. Most endpoints will be protected with some for of authorizer e.g. `resolver.authorize("seller")` or `resolver.authorize("buyer")`. If you try and invoke these endpoints as an admin, they all need to be changed to `resolver.authorize(["seller", "admin"])` or `resolver.authorize(["buyer", "admin"])` etc. If you assume the role of the user you're impersonating, you don't need to change anything. In terms of security: - `startImpersonating` should only be available to admins, so it should be secured with `resolver.authorize("admin")`. - we can allow `stopImpersonating` to be called without a role restriction as it is: the code already checks if `impersonatingFromUserId` is present in the session. If present, the user simply regains whatever role they had before ("admin", "support", etc), otherwise the endpoint is no-op. --- app/pages/docs/impersonation.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/pages/docs/impersonation.mdx b/app/pages/docs/impersonation.mdx index b491d63..3f6ed84 100644 --- a/app/pages/docs/impersonation.mdx +++ b/app/pages/docs/impersonation.mdx @@ -53,7 +53,7 @@ export default resolver.pipe( await ctx.session.$create({ userId: user.id, - role: "admin", + role: user.role, orgId: user.organizationId, impersonatingFromUserId: ctx.session.userId, }) From 03e52b734258df3db9d94f5d6564f87f2caedeb0 Mon Sep 17 00:00:00 2001 From: Antony Kamp <tony.kamp@web.de> Date: Fri, 21 May 2021 20:58:31 +0200 Subject: [PATCH 66/67] `resolver.authorize` description at resolver-server-utilities.mdx (#484) --- app/pages/docs/resolver-server-utilities.mdx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/pages/docs/resolver-server-utilities.mdx b/app/pages/docs/resolver-server-utilities.mdx index 727d71e..4766de1 100644 --- a/app/pages/docs/resolver-server-utilities.mdx +++ b/app/pages/docs/resolver-server-utilities.mdx @@ -180,6 +180,12 @@ A function to give `resolver.pipe` of type ## `resolver.authorize` {#resolver-authorize} +Using `resolver.authorize` in `resolver.pipe` is a simple way to check +whether the user has the authorization to call the query or mutation or +not. For this, blitz uses +[`session.$authorize`](./authorization#isauthorized-adapters) from the +context object. + ### Example {#example-2} <!-- prettier-ignore --> From 9fe39d8ebeff642759bb10ad1b0cd2f1114ec7aa Mon Sep 17 00:00:00 2001 From: Nikolay <enemycnt@users.noreply.github.com> Date: Sat, 22 May 2021 01:37:46 +0300 Subject: [PATCH 67/67] Fix theme toggle (#485) --- app/pages/_app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/pages/_app.js b/app/pages/_app.js index 2863164..28fcaae 100644 --- a/app/pages/_app.js +++ b/app/pages/_app.js @@ -71,7 +71,7 @@ export default function App({Component, pageProps, router}) { <meta key="og:type" property="og:type" content="article" /> <meta key="og:description" property="og:description" content={meta.description} /> </Head> - <ThemeProvider defaultTheme="dark" attribute="class"> + <ThemeProvider defaultTheme="dark" enableSystem={false} attribute="class"> <Component {...pageProps} /> </ThemeProvider> </>