Compare commits
12 Commits
@blitzjs/c
...
siddharth/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
828e745fd8 | ||
|
|
368ef1466d | ||
|
|
0fc2d71a18 | ||
|
|
3fa3a4ef30 | ||
|
|
565db3c5a8 | ||
|
|
ae04524b4c | ||
|
|
38d320fd28 | ||
|
|
8723d0053c | ||
|
|
ce23d4ed09 | ||
|
|
b6c9c4ae6d | ||
|
|
0b3286468b | ||
|
|
50f17d21ce |
@@ -4104,6 +4104,16 @@
|
||||
"code",
|
||||
"test"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "fungilation",
|
||||
"name": "Gary Fung",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/3803466?v=4",
|
||||
"profile": "https://garyfung.medium.com",
|
||||
"contributions": [
|
||||
"doc",
|
||||
"code"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
"access": "restricted",
|
||||
"baseBranch": "main",
|
||||
"updateInternalDependencies": "patch",
|
||||
"ignore": ["web", "test-*", "toolkit-*", "@blitzjs/recipe-*"]
|
||||
"ignore": ["web", "test-*", "toolkit-*", "next13"]
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<img alt="" src="https://img.shields.io/badge/Join%20our%20community-6700EB.svg?style=for-the-badge&labelColor=000000&logoWidth=20&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAQ9SURBVHgB7d3dVdtAEIbhcSpICUoH0IEogQqSVBBSAU4FSSpIOoAORAfQgSghHXzZ1U/YcMD4R9rZmf2ec3y448LyiNf27iLiGIAmPLrweC9Un3DhrzG6EarLNP09nlwJ1SOZ/lQr5N80/S/p2QMVCBf5N17XCfm1Y/rBHqjAG9PPHvBsz+mf9WAP+HLA9M/YA14cOP2payH7jpj+VCtk1wnTP+vj7xCy6cTpn7EHLMLp059iD1iD8eveJbVCNsSLheX1YA/YgOWnf8YeKB3Wmf7Ud6Fy4f/FHmtpxbl3YlC4MJ/Cj0bWdwPnPbARg+L0S54XQHS32WwuxClzd4CM0z9rPfeAuTtA5ulPXYQ7wZ04Y+oOoDD9KZc9YOoOoDj9s4dwFzgXR6w1wIPoOvPWA9buAHEJ173o3gWiy3AnuBUHLEbgmYwvAk1/wuM8vAgexThzbwPDkx7/DHwVXfFOxP2GmsKd4Ab6zPeAyU8CI7AHFmH2BRCBPXAyk18GzUrqAXCTiR4ssyj0VFw/oCU8+e+RZ33AWz6KMaYbIIWxB+JSLs1bsbkeMN0AqakHvoku9oA2sAfqBvbAQdw0QArsgb25aYBUQT3QgT2gB+yBuqGcHij2UCqXDZACe2Anlw2QYg/QAOyBuoE98CL3DZDCuK4/rh/Q7oGL6U+TOvcNkJoijN8X1C48+T+g75eQDrAH/qmqAVJgDwyqaoAUe4AGYA/UDZX3QLUNkEIZPRCd5+6BahsgVUgPROwBTSijB7jpVAvGHriHvmw9wAZ4BpX1ABvgmakHtPcbRuwBTWAPULgAV9D/jKDY9YRvwvgEaurD44uQHvAol7qBW7WKluVtIHiUS7GyvA0s6CiXDnxrpQfsgbqBS7GKk/2jYHCrVlGyfxTMrVo0ALdq1Q3sgSKofh0M9oA61a+D2QM0AHugbmAPqClmSRjK2apVVQ8UsySsoK1aHdgDesCtWnUDeyCrIpeFg1u3sylyWTi3btMA7IG6gT2wuuK3hoE9sKrit4YVslWLPaAN7IG6ocKt2zmY2h4O9sDiTG0PZw/QANy6XTewBxZj9ogYVHy025LMHhEz9cBn0We6B0yfERReBLfhx0/R1YQHPx/QBPbA0VwcEwf2wNFcHBPHHjiem3MC2QPHcXdSaJjA+KfgTPQ8hhfjBzHC40mhlzJ+Xq9lK4a4PCs43AVaGTed5mZq+iOXZwWHi3AnOj2wFWNcnxYe7gTxLtBKHuamP/J+Wnh8a5irB7ZC5Yk9gPX1QuXC+usHWqGyhYvUYR0a7zboUOFCNVhnk0krZAOW7wFOvzXhom2xnEbIHizTA1wEYhWW6YFGyC6c1gOcfg9wfA80Qj7g8B7g9HuCww+haIR8wf49wOn3Cvv9k8tGyC/s7gFOv3fY3QONkH+v9MBWqB7PeqDn9FcIT//kcitUn6kHOu/T/xfWzlQy3dEHhwAAAABJRU5ErkJggg==">
|
||||
</a>
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
||||
<a aria-label="All Contributors" href="#contributors-"><img alt="" src="https://img.shields.io/badge/all_contributors-432-17BB8A.svg?style=for-the-badge&labelColor=000000"></a>
|
||||
<a aria-label="All Contributors" href="#contributors-"><img alt="" src="https://img.shields.io/badge/all_contributors-433-17BB8A.svg?style=for-the-badge&labelColor=000000"></a>
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
||||
<a aria-label="License" href="https://github.com/blitz-js/blitz/blob/main/LICENSE">
|
||||
<img alt="" src="https://img.shields.io/npm/l/blitz.svg?style=for-the-badge&labelColor=000000&color=blue">
|
||||
@@ -761,6 +761,7 @@ Thanks to these wonderful people ([emoji key](https://allcontributors.org/docs/e
|
||||
<td align="center"><a href="https://github.com/bezalel6"><img src="https://avatars.githubusercontent.com/u/51681171?v=4?s=100" width="100px;" alt=""/><br /><sub><b>bezalel6</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=bezalel6" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=bezalel6" title="Code">💻</a></td>
|
||||
<td align="center"><a href="cherniavskii.com"><img src="https://avatars.githubusercontent.com/u/13808724?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Andrew Cherniavskii</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=cherniavskii" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://danielidoko-r3zt.vercel.app/"><img src="https://avatars.githubusercontent.com/u/95912955?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Daniel Idoko</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=doe-base" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=doe-base" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=doe-base" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="https://garyfung.medium.com"><img src="https://avatars.githubusercontent.com/u/3803466?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Gary Fung</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=fungilation" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=fungilation" title="Code">💻</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
@@ -1,5 +1,40 @@
|
||||
# next-blitz-auth
|
||||
|
||||
## 0.1.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [565db3c5a]
|
||||
- Updated dependencies [3fa3a4ef3]
|
||||
- blitz@2.2.0
|
||||
- @blitzjs/auth@2.2.0
|
||||
- @blitzjs/next@2.2.0
|
||||
- @blitzjs/rpc@2.2.0
|
||||
- @blitzjs/config@2.2.0
|
||||
|
||||
## 0.1.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [ce23d4ed0]
|
||||
- @blitzjs/next@2.1.4
|
||||
- blitz@2.1.4
|
||||
- @blitzjs/auth@2.1.4
|
||||
- @blitzjs/rpc@2.1.4
|
||||
- @blitzjs/config@2.1.4
|
||||
|
||||
## 0.1.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [0b3286468]
|
||||
- Updated dependencies [50f17d21c]
|
||||
- @blitzjs/auth@2.1.3
|
||||
- @blitzjs/next@2.1.3
|
||||
- @blitzjs/rpc@2.1.3
|
||||
- blitz@2.1.3
|
||||
- @blitzjs/config@2.1.3
|
||||
|
||||
## 0.1.14
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "next-blitz-auth",
|
||||
"version": "0.1.14",
|
||||
"version": "0.1.17",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"blitz:dev": "next dev",
|
||||
@@ -12,28 +12,28 @@
|
||||
"schema": "prisma/schema.prisma"
|
||||
},
|
||||
"dependencies": {
|
||||
"@blitzjs/auth": "2.1.2",
|
||||
"@blitzjs/config": "2.1.2",
|
||||
"@blitzjs/next": "2.1.2",
|
||||
"@blitzjs/rpc": "2.1.2",
|
||||
"@blitzjs/auth": "2.2.0",
|
||||
"@blitzjs/config": "2.2.0",
|
||||
"@blitzjs/next": "2.2.0",
|
||||
"@blitzjs/rpc": "2.2.0",
|
||||
"@hookform/error-message": "2.0.0",
|
||||
"@hookform/resolvers": "2.9.10",
|
||||
"@prisma/client": "^4.5.0",
|
||||
"@tanstack/react-query": "4.0.10",
|
||||
"blitz": "2.1.2",
|
||||
"blitz": "2.2.0",
|
||||
"flatted": "3.2.7",
|
||||
"next": "14.3.0-canary.28",
|
||||
"next": "15.0.1",
|
||||
"prisma": "^4.5.0",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"react-hook-form": "7.39.1",
|
||||
"superjson": "1.11.0",
|
||||
"zod": "3.20.2"
|
||||
"zod": "3.23.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "18.11.7",
|
||||
"@types/react": "18.0.23",
|
||||
"@types/react-dom": "18.0.7",
|
||||
"@types/react": "npm:types-react@19.0.0",
|
||||
"@types/react-dom": "npm:types-react-dom@19.0.0",
|
||||
"eslint": "8.26.0",
|
||||
"eslint-config-next": "13.0.0",
|
||||
"typescript": "4.8.4"
|
||||
|
||||
@@ -4,7 +4,7 @@ import {zodResolver} from "@hookform/resolvers/zod"
|
||||
import {z} from "zod"
|
||||
|
||||
export interface FormProps<S extends z.ZodType<any, any>>
|
||||
extends Omit<PropsWithoutRef<JSX.IntrinsicElements["form"]>, "onSubmit"> {
|
||||
extends Omit<PropsWithoutRef<React.JSX.IntrinsicElements["form"]>, "onSubmit"> {
|
||||
/** All your form fields */
|
||||
children?: ReactNode
|
||||
/** Text to display in the submit button */
|
||||
|
||||
@@ -2,14 +2,15 @@ import {forwardRef, PropsWithoutRef, ComponentPropsWithoutRef} from "react"
|
||||
import {useFormContext} from "react-hook-form"
|
||||
import {ErrorMessage} from "@hookform/error-message"
|
||||
|
||||
export interface LabeledTextFieldProps extends PropsWithoutRef<JSX.IntrinsicElements["input"]> {
|
||||
export interface LabeledTextFieldProps
|
||||
extends PropsWithoutRef<React.JSX.IntrinsicElements["input"]> {
|
||||
/** Field name. */
|
||||
name: string
|
||||
/** Field label. */
|
||||
label: string
|
||||
/** Field type. Doesn't include radio buttons and checkboxes */
|
||||
type?: "text" | "password" | "email" | "number"
|
||||
outerProps?: PropsWithoutRef<JSX.IntrinsicElements["div"]>
|
||||
outerProps?: PropsWithoutRef<React.JSX.IntrinsicElements["div"]>
|
||||
labelProps?: ComponentPropsWithoutRef<"label">
|
||||
}
|
||||
|
||||
|
||||
@@ -23,31 +23,30 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@blitzjs/auth": "2.1.2",
|
||||
"@blitzjs/config": "2.1.2",
|
||||
"@blitzjs/next": "2.1.2",
|
||||
"@blitzjs/rpc": "2.1.2",
|
||||
"@blitzjs/auth": "2.2.0",
|
||||
"@blitzjs/config": "2.2.0",
|
||||
"@blitzjs/next": "2.2.0",
|
||||
"@blitzjs/rpc": "2.2.0",
|
||||
"@hookform/error-message": "2.0.0",
|
||||
"@hookform/resolvers": "2.9.10",
|
||||
"@prisma/client": "4.6.1",
|
||||
"blitz": "2.1.2",
|
||||
"next": "14.3.0-canary.28",
|
||||
"@prisma/client": "6.1.0",
|
||||
"blitz": "2.2.0",
|
||||
"next": "15.0.1",
|
||||
"openid-client": "5.2.1",
|
||||
"prisma": "4.6.1",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"prisma": "6.1.0",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"react-hook-form": "7.39.1",
|
||||
"ts-node": "10.9.1",
|
||||
"zod": "3.20.2"
|
||||
"zod": "3.23.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@next/bundle-analyzer": "12.0.8",
|
||||
"@testing-library/react": "13.4.0",
|
||||
"@testing-library/react-hooks": "8.0.1",
|
||||
"@testing-library/react": "16.0.1",
|
||||
"@types/jest": "29.2.2",
|
||||
"@types/node": "18.11.9",
|
||||
"@types/preview-email": "2.0.1",
|
||||
"@types/react": "18.0.25",
|
||||
"@types/react": "npm:types-react@19.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "5.42.1",
|
||||
"eslint": "8.27.0",
|
||||
"eslint-config-next": "12.3.1",
|
||||
|
||||
@@ -4,7 +4,7 @@ import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import { z } from "zod"
|
||||
|
||||
export interface FormProps<S extends z.ZodType<any, any>>
|
||||
extends Omit<PropsWithoutRef<JSX.IntrinsicElements["form"]>, "onSubmit"> {
|
||||
extends Omit<PropsWithoutRef<React.JSX.IntrinsicElements["form"]>, "onSubmit"> {
|
||||
/** All your form fields */
|
||||
children?: ReactNode
|
||||
/** Text to display in the submit button */
|
||||
|
||||
@@ -2,14 +2,15 @@ import { forwardRef, PropsWithoutRef, ComponentPropsWithoutRef } from "react"
|
||||
import { useFormContext } from "react-hook-form"
|
||||
import { ErrorMessage } from "@hookform/error-message"
|
||||
|
||||
export interface LabeledTextFieldProps extends PropsWithoutRef<JSX.IntrinsicElements["input"]> {
|
||||
export interface LabeledTextFieldProps
|
||||
extends PropsWithoutRef<React.JSX.IntrinsicElements["input"]> {
|
||||
/** Field name. */
|
||||
name: string
|
||||
/** Field label. */
|
||||
label: string
|
||||
/** Field type. Doesn't include radio buttons and checkboxes */
|
||||
type?: "text" | "password" | "email" | "number"
|
||||
outerProps?: PropsWithoutRef<JSX.IntrinsicElements["div"]>
|
||||
outerProps?: PropsWithoutRef<React.JSX.IntrinsicElements["div"]>
|
||||
labelProps?: ComponentPropsWithoutRef<"label">
|
||||
}
|
||||
|
||||
|
||||
2
apps/toolkit-app/next-env.d.ts
vendored
2
apps/toolkit-app/next-env.d.ts
vendored
@@ -2,4 +2,4 @@
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
||||
// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information.
|
||||
|
||||
@@ -24,31 +24,30 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@blitzjs/auth": "2.1.2",
|
||||
"@blitzjs/config": "2.1.2",
|
||||
"@blitzjs/next": "2.1.2",
|
||||
"@blitzjs/rpc": "2.1.2",
|
||||
"@blitzjs/auth": "2.2.0",
|
||||
"@blitzjs/config": "2.2.0",
|
||||
"@blitzjs/next": "2.2.0",
|
||||
"@blitzjs/rpc": "2.2.0",
|
||||
"@hookform/error-message": "2.0.0",
|
||||
"@hookform/resolvers": "2.9.10",
|
||||
"@prisma/client": "4.6.1",
|
||||
"blitz": "2.1.2",
|
||||
"next": "14.3.0-canary.28",
|
||||
"@prisma/client": "6.1.0",
|
||||
"blitz": "2.2.0",
|
||||
"next": "15.0.1",
|
||||
"next-auth": "4.24.7",
|
||||
"prisma": "4.6.1",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"prisma": "6.1.0",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"react-hook-form": "7.39.1",
|
||||
"ts-node": "10.9.1",
|
||||
"zod": "3.20.2"
|
||||
"zod": "3.23.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@next/bundle-analyzer": "12.0.8",
|
||||
"@testing-library/jest-dom": "5.16.5",
|
||||
"@testing-library/react": "13.4.0",
|
||||
"@testing-library/react-hooks": "8.0.1",
|
||||
"@testing-library/react": "16.0.1",
|
||||
"@types/node": "18.11.9",
|
||||
"@types/preview-email": "2.0.1",
|
||||
"@types/react": "18.0.25",
|
||||
"@types/react": "npm:types-react@19.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "5.42.1",
|
||||
"@vitejs/plugin-react": "2.2.0",
|
||||
"eslint": "8.27.0",
|
||||
|
||||
@@ -4,7 +4,7 @@ import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import { z } from "zod"
|
||||
|
||||
export interface FormProps<S extends z.ZodType<any, any>>
|
||||
extends Omit<PropsWithoutRef<JSX.IntrinsicElements["form"]>, "onSubmit"> {
|
||||
extends Omit<PropsWithoutRef<React.JSX.IntrinsicElements["form"]>, "onSubmit"> {
|
||||
/** All your form fields */
|
||||
children?: ReactNode
|
||||
/** Text to display in the submit button */
|
||||
|
||||
@@ -2,14 +2,15 @@ import { ComponentPropsWithoutRef, forwardRef, PropsWithoutRef } from "react"
|
||||
import { useFormContext } from "react-hook-form"
|
||||
import { ErrorMessage } from "@hookform/error-message"
|
||||
|
||||
export interface LabeledSelectFieldProps extends PropsWithoutRef<JSX.IntrinsicElements["select"]> {
|
||||
export interface LabeledSelectFieldProps
|
||||
extends PropsWithoutRef<React.JSX.IntrinsicElements["select"]> {
|
||||
/** Field name. */
|
||||
name: string
|
||||
/** Field label. */
|
||||
label: string
|
||||
/** Field type. Doesn't include radio buttons and checkboxes */
|
||||
options: any[]
|
||||
outerProps?: PropsWithoutRef<JSX.IntrinsicElements["div"]>
|
||||
outerProps?: PropsWithoutRef<React.JSX.IntrinsicElements["div"]>
|
||||
labelProps?: ComponentPropsWithoutRef<"label">
|
||||
}
|
||||
|
||||
|
||||
@@ -2,14 +2,15 @@ import { forwardRef, PropsWithoutRef, ComponentPropsWithoutRef } from "react"
|
||||
import { useFormContext } from "react-hook-form"
|
||||
import { ErrorMessage } from "@hookform/error-message"
|
||||
|
||||
export interface LabeledTextFieldProps extends PropsWithoutRef<JSX.IntrinsicElements["input"]> {
|
||||
export interface LabeledTextFieldProps
|
||||
extends PropsWithoutRef<React.JSX.IntrinsicElements["input"]> {
|
||||
/** Field name. */
|
||||
name: string
|
||||
/** Field label. */
|
||||
label: string
|
||||
/** Field type. Doesn't include radio buttons and checkboxes */
|
||||
type?: "text" | "password" | "email" | "number"
|
||||
outerProps?: PropsWithoutRef<JSX.IntrinsicElements["div"]>
|
||||
outerProps?: PropsWithoutRef<React.JSX.IntrinsicElements["div"]>
|
||||
labelProps?: ComponentPropsWithoutRef<"label">
|
||||
}
|
||||
|
||||
|
||||
@@ -16,27 +16,27 @@
|
||||
"schema": "./db/schema.prisma"
|
||||
},
|
||||
"dependencies": {
|
||||
"@blitzjs/auth": "2.1.2",
|
||||
"@blitzjs/config": "2.1.2",
|
||||
"@blitzjs/next": "2.1.2",
|
||||
"@blitzjs/rpc": "2.1.2",
|
||||
"@prisma/client": "4.6.1",
|
||||
"@blitzjs/auth": "2.2.0",
|
||||
"@blitzjs/config": "2.2.0",
|
||||
"@blitzjs/next": "2.2.0",
|
||||
"@blitzjs/rpc": "2.2.0",
|
||||
"@prisma/client": "6.1.0",
|
||||
"@types/jest": "29.2.2",
|
||||
"@types/passport-twitter": "1.0.37",
|
||||
"blitz": "2.1.2",
|
||||
"blitz": "2.2.0",
|
||||
"jest": "29.3.0",
|
||||
"jest-environment-jsdom": "29.3.0",
|
||||
"next": "14.3.0-canary.28",
|
||||
"next": "15.0.1",
|
||||
"passport-mock-strategy": "2.0.0",
|
||||
"passport-twitter": "1.0.4",
|
||||
"prisma": "4.6.1",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"prisma": "6.1.0",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"ts-node": "10.9.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@next/bundle-analyzer": "12.0.8",
|
||||
"@types/react": "18.0.25",
|
||||
"@types/react": "npm:types-react@19.0.0",
|
||||
"eslint": "8.27.0",
|
||||
"typescript": "^4.8.4"
|
||||
}
|
||||
|
||||
@@ -17,31 +17,30 @@
|
||||
"prisma:studio": "prisma studio"
|
||||
},
|
||||
"dependencies": {
|
||||
"@blitzjs/auth": "2.1.2",
|
||||
"@blitzjs/config": "2.1.2",
|
||||
"@blitzjs/next": "2.1.2",
|
||||
"@blitzjs/rpc": "2.1.2",
|
||||
"@blitzjs/auth": "2.2.0",
|
||||
"@blitzjs/config": "2.2.0",
|
||||
"@blitzjs/next": "2.2.0",
|
||||
"@blitzjs/rpc": "2.2.0",
|
||||
"@hookform/error-message": "2.0.0",
|
||||
"@hookform/resolvers": "2.9.10",
|
||||
"@prisma/client": "4.6.1",
|
||||
"blitz": "2.1.2",
|
||||
"@prisma/client": "6.1.0",
|
||||
"blitz": "2.2.0",
|
||||
"delay": "5.0.0",
|
||||
"next": "14.3.0-canary.28",
|
||||
"prisma": "4.6.1",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"next": "15.0.1",
|
||||
"prisma": "6.1.0",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"react-hook-form": "7.39.1",
|
||||
"ts-node": "10.9.1",
|
||||
"zod": "3.20.2"
|
||||
"zod": "3.23.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@next/bundle-analyzer": "12.0.8",
|
||||
"@testing-library/jest-dom": "5.16.5",
|
||||
"@testing-library/react": "13.4.0",
|
||||
"@testing-library/react-hooks": "8.0.1",
|
||||
"@testing-library/react": "16.0.1",
|
||||
"@types/node": "18.11.9",
|
||||
"@types/preview-email": "2.0.1",
|
||||
"@types/react": "18.0.25",
|
||||
"@types/react": "npm:types-react@19.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "5.42.1",
|
||||
"@vitejs/plugin-react": "2.2.0",
|
||||
"eslint": "8.27.0",
|
||||
|
||||
@@ -17,16 +17,16 @@
|
||||
"prisma:studio": "prisma studio"
|
||||
},
|
||||
"dependencies": {
|
||||
"@blitzjs/auth": "2.1.2",
|
||||
"@blitzjs/config": "2.1.2",
|
||||
"@blitzjs/next": "2.1.2",
|
||||
"@prisma/client": "4.6.1",
|
||||
"blitz": "2.1.2",
|
||||
"@blitzjs/auth": "2.2.0",
|
||||
"@blitzjs/config": "2.2.0",
|
||||
"@blitzjs/next": "2.2.0",
|
||||
"@prisma/client": "6.1.0",
|
||||
"blitz": "2.2.0",
|
||||
"lowdb": "3.0.0",
|
||||
"next": "14.3.0-canary.28",
|
||||
"prisma": "4.6.1",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"next": "15.0.1",
|
||||
"prisma": "6.1.0",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"secure-password": "4.0.0",
|
||||
"wait-port": "1.0.4"
|
||||
},
|
||||
@@ -36,7 +36,7 @@
|
||||
"@types/fs-extra": "9.0.13",
|
||||
"@types/node": "18.7.13",
|
||||
"@types/node-fetch": "2.6.1",
|
||||
"@types/react": "18.0.25",
|
||||
"@types/react": "npm:types-react@19.0.0",
|
||||
"b64-lite": "1.4.0",
|
||||
"eslint": "8.27.0",
|
||||
"fs-extra": "10.0.1",
|
||||
|
||||
@@ -16,24 +16,24 @@
|
||||
"schema": "db/schema.prisma"
|
||||
},
|
||||
"dependencies": {
|
||||
"@blitzjs/auth": "2.1.2",
|
||||
"@blitzjs/next": "2.1.2",
|
||||
"@blitzjs/rpc": "2.1.2",
|
||||
"@prisma/client": "4.6.1",
|
||||
"blitz": "2.1.2",
|
||||
"@blitzjs/auth": "2.2.0",
|
||||
"@blitzjs/next": "2.2.0",
|
||||
"@blitzjs/rpc": "2.2.0",
|
||||
"@prisma/client": "6.1.0",
|
||||
"blitz": "2.2.0",
|
||||
"lowdb": "2.1.0",
|
||||
"next": "14.3.0-canary.28",
|
||||
"prisma": "4.6.1",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0"
|
||||
"next": "15.0.1",
|
||||
"prisma": "6.1.0",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@blitzjs/config": "2.1.2",
|
||||
"@blitzjs/config": "2.2.0",
|
||||
"@next/bundle-analyzer": "12.0.8",
|
||||
"@types/express": "4.17.13",
|
||||
"@types/fs-extra": "9.0.13",
|
||||
"@types/node-fetch": "2.6.1",
|
||||
"@types/react": "18.0.25",
|
||||
"@types/react": "npm:types-react@19.0.0",
|
||||
"b64-lite": "1.4.0",
|
||||
"eslint": "8.27.0",
|
||||
"fs-extra": "10.0.1",
|
||||
|
||||
@@ -11,20 +11,20 @@
|
||||
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf .next"
|
||||
},
|
||||
"dependencies": {
|
||||
"@blitzjs/config": "2.1.2",
|
||||
"@blitzjs/next": "2.1.2",
|
||||
"@blitzjs/rpc": "2.1.2",
|
||||
"blitz": "2.1.2",
|
||||
"next": "14.3.0-canary.28",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0"
|
||||
"@blitzjs/config": "2.2.0",
|
||||
"@blitzjs/next": "2.2.0",
|
||||
"@blitzjs/rpc": "2.2.0",
|
||||
"blitz": "2.2.0",
|
||||
"next": "15.0.1",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@next/bundle-analyzer": "12.0.8",
|
||||
"@types/express": "4.17.13",
|
||||
"@types/fs-extra": "9.0.13",
|
||||
"@types/node-fetch": "2.6.1",
|
||||
"@types/react": "18.0.25",
|
||||
"@types/react": "npm:types-react@19.0.0",
|
||||
"eslint": "8.27.0",
|
||||
"fs-extra": "10.0.1",
|
||||
"get-port": "6.1.2",
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
||||
// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.
|
||||
|
||||
@@ -17,17 +17,17 @@
|
||||
"prisma:studio": "prisma studio"
|
||||
},
|
||||
"dependencies": {
|
||||
"@blitzjs/auth": "2.1.2",
|
||||
"@blitzjs/config": "2.1.2",
|
||||
"@blitzjs/next": "2.1.2",
|
||||
"@blitzjs/rpc": "2.1.2",
|
||||
"@prisma/client": "4.6.1",
|
||||
"blitz": "2.1.2",
|
||||
"@blitzjs/auth": "2.2.0",
|
||||
"@blitzjs/config": "2.2.0",
|
||||
"@blitzjs/next": "2.2.0",
|
||||
"@blitzjs/rpc": "2.2.0",
|
||||
"@prisma/client": "6.1.0",
|
||||
"blitz": "2.2.0",
|
||||
"lowdb": "2.1.0",
|
||||
"next": "14.3.0-canary.28",
|
||||
"prisma": "4.6.1",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"next": "15.0.1",
|
||||
"prisma": "6.1.0",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"secure-password": "4.0.0",
|
||||
"wait-port": "1.0.4"
|
||||
},
|
||||
@@ -37,7 +37,7 @@
|
||||
"@types/fs-extra": "9.0.13",
|
||||
"@types/node": "18.7.13",
|
||||
"@types/node-fetch": "2.6.1",
|
||||
"@types/react": "18.0.25",
|
||||
"@types/react": "npm:types-react@19.0.0",
|
||||
"b64-lite": "1.4.0",
|
||||
"eslint": "8.27.0",
|
||||
"fs-extra": "10.0.1",
|
||||
|
||||
@@ -16,24 +16,24 @@
|
||||
"prisma:studio": "prisma studio"
|
||||
},
|
||||
"dependencies": {
|
||||
"@blitzjs/auth": "2.1.2",
|
||||
"@blitzjs/next": "2.1.2",
|
||||
"@blitzjs/rpc": "2.1.2",
|
||||
"@prisma/client": "4.6.1",
|
||||
"blitz": "2.1.2",
|
||||
"@blitzjs/auth": "2.2.0",
|
||||
"@blitzjs/next": "2.2.0",
|
||||
"@blitzjs/rpc": "2.2.0",
|
||||
"@prisma/client": "6.1.0",
|
||||
"blitz": "2.2.0",
|
||||
"lowdb": "3.0.0",
|
||||
"next": "14.3.0-canary.28",
|
||||
"prisma": "4.6.1",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0"
|
||||
"next": "15.0.1",
|
||||
"prisma": "6.1.0",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@blitzjs/config": "2.1.2",
|
||||
"@blitzjs/config": "2.2.0",
|
||||
"@next/bundle-analyzer": "12.0.8",
|
||||
"@types/express": "4.17.13",
|
||||
"@types/fs-extra": "9.0.13",
|
||||
"@types/node-fetch": "2.6.1",
|
||||
"@types/react": "18.0.25",
|
||||
"@types/react": "npm:types-react@19.0.0",
|
||||
"b64-lite": "1.4.0",
|
||||
"eslint": "8.27.0",
|
||||
"fs-extra": "10.0.1",
|
||||
|
||||
@@ -8,21 +8,21 @@
|
||||
"clean": "rm -rf .turbo && rm -rf node_modules"
|
||||
},
|
||||
"dependencies": {
|
||||
"@blitzjs/auth": "2.1.2",
|
||||
"@blitzjs/config": "2.1.2",
|
||||
"@blitzjs/next": "2.1.2",
|
||||
"@blitzjs/rpc": "2.1.2",
|
||||
"@prisma/client": "4.6.1",
|
||||
"@blitzjs/auth": "2.2.0",
|
||||
"@blitzjs/config": "2.2.0",
|
||||
"@blitzjs/next": "2.2.0",
|
||||
"@blitzjs/rpc": "2.2.0",
|
||||
"@prisma/client": "6.1.0",
|
||||
"@tanstack/react-query": "4.0.10",
|
||||
"blitz": "2.1.2",
|
||||
"next": "14.3.0-canary.28",
|
||||
"prisma": "4.6.1",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0"
|
||||
"blitz": "2.2.0",
|
||||
"next": "15.0.1",
|
||||
"prisma": "6.1.0",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@testing-library/react": "13.4.0",
|
||||
"@types/react": "18.0.25",
|
||||
"@testing-library/react": "16.0.1",
|
||||
"@types/react": "npm:types-react@19.0.0",
|
||||
"@vitejs/plugin-react": "1.3.0",
|
||||
"delay": "5.0.0",
|
||||
"eslint": "8.27.0",
|
||||
|
||||
@@ -16,23 +16,23 @@
|
||||
"schema": "db/schema.prisma"
|
||||
},
|
||||
"dependencies": {
|
||||
"@blitzjs/next": "2.1.2",
|
||||
"@blitzjs/rpc": "2.1.2",
|
||||
"@prisma/client": "4.6.1",
|
||||
"blitz": "2.1.2",
|
||||
"@blitzjs/next": "2.2.0",
|
||||
"@blitzjs/rpc": "2.2.0",
|
||||
"@prisma/client": "6.1.0",
|
||||
"blitz": "2.2.0",
|
||||
"lowdb": "3.0.0",
|
||||
"next": "14.3.0-canary.28",
|
||||
"prisma": "4.6.1",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0"
|
||||
"next": "15.0.1",
|
||||
"prisma": "6.1.0",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@blitzjs/config": "2.1.2",
|
||||
"@blitzjs/config": "2.2.0",
|
||||
"@next/bundle-analyzer": "12.0.8",
|
||||
"@types/express": "4.17.13",
|
||||
"@types/fs-extra": "9.0.13",
|
||||
"@types/node-fetch": "2.6.1",
|
||||
"@types/react": "18.0.25",
|
||||
"@types/react": "npm:types-react@19.0.0",
|
||||
"b64-lite": "1.4.0",
|
||||
"eslint": "8.27.0",
|
||||
"fs-extra": "10.0.1",
|
||||
|
||||
@@ -7,19 +7,19 @@
|
||||
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf .next"
|
||||
},
|
||||
"dependencies": {
|
||||
"@blitzjs/config": "2.1.2",
|
||||
"@blitzjs/next": "2.1.2",
|
||||
"@blitzjs/rpc": "2.1.2",
|
||||
"blitz": "2.1.2",
|
||||
"next": "14.3.0-canary.28",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0"
|
||||
"@blitzjs/config": "2.2.0",
|
||||
"@blitzjs/next": "2.2.0",
|
||||
"@blitzjs/rpc": "2.2.0",
|
||||
"blitz": "2.2.0",
|
||||
"next": "15.0.1",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "4.17.13",
|
||||
"@types/fs-extra": "9.0.13",
|
||||
"@types/node-fetch": "2.6.1",
|
||||
"@types/react": "18.0.25",
|
||||
"@types/react": "npm:types-react@19.0.0",
|
||||
"b64-lite": "1.4.0",
|
||||
"eslint": "8.27.0",
|
||||
"fs-extra": "10.0.1",
|
||||
|
||||
@@ -7,19 +7,19 @@
|
||||
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf .next"
|
||||
},
|
||||
"dependencies": {
|
||||
"@blitzjs/config": "2.1.2",
|
||||
"@blitzjs/next": "2.1.2",
|
||||
"@blitzjs/rpc": "2.1.2",
|
||||
"blitz": "2.1.2",
|
||||
"next": "14.3.0-canary.28",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0"
|
||||
"@blitzjs/config": "2.2.0",
|
||||
"@blitzjs/next": "2.2.0",
|
||||
"@blitzjs/rpc": "2.2.0",
|
||||
"blitz": "2.2.0",
|
||||
"next": "15.0.1",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "4.17.13",
|
||||
"@types/fs-extra": "9.0.13",
|
||||
"@types/node-fetch": "2.6.1",
|
||||
"@types/react": "18.0.25",
|
||||
"@types/react": "npm:types-react@19.0.0",
|
||||
"b64-lite": "1.4.0",
|
||||
"eslint": "8.27.0",
|
||||
"fs-extra": "10.0.1",
|
||||
|
||||
@@ -16,24 +16,24 @@
|
||||
"schema": "db/schema.prisma"
|
||||
},
|
||||
"dependencies": {
|
||||
"@blitzjs/auth": "2.1.2",
|
||||
"@blitzjs/next": "2.1.2",
|
||||
"@blitzjs/rpc": "2.1.2",
|
||||
"@prisma/client": "4.6.1",
|
||||
"blitz": "2.1.2",
|
||||
"@blitzjs/auth": "2.2.0",
|
||||
"@blitzjs/next": "2.2.0",
|
||||
"@blitzjs/rpc": "2.2.0",
|
||||
"@prisma/client": "6.1.0",
|
||||
"blitz": "2.2.0",
|
||||
"lowdb": "3.0.0",
|
||||
"next": "14.3.0-canary.28",
|
||||
"prisma": "4.6.1",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0"
|
||||
"next": "15.0.1",
|
||||
"prisma": "6.1.0",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@blitzjs/config": "2.1.2",
|
||||
"@blitzjs/config": "2.2.0",
|
||||
"@next/bundle-analyzer": "12.0.8",
|
||||
"@types/express": "4.17.13",
|
||||
"@types/fs-extra": "9.0.13",
|
||||
"@types/node-fetch": "2.6.1",
|
||||
"@types/react": "18.0.25",
|
||||
"@types/react": "npm:types-react@19.0.0",
|
||||
"b64-lite": "1.4.0",
|
||||
"eslint": "8.27.0",
|
||||
"fs-extra": "10.0.1",
|
||||
|
||||
@@ -3,15 +3,15 @@
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"@blitzjs/config": "workspace:2.1.2",
|
||||
"@blitzjs/next": "workspace:2.1.2",
|
||||
"@blitzjs/rpc": "workspace:2.1.2",
|
||||
"@blitzjs/config": "workspace:2.2.0",
|
||||
"@blitzjs/next": "workspace:2.2.0",
|
||||
"@blitzjs/rpc": "workspace:2.2.0",
|
||||
"@tanstack/react-query": "4.13.0",
|
||||
"@testing-library/react": "13.4.0",
|
||||
"@testing-library/react": "16.0.1",
|
||||
"@types/express": "4.17.13",
|
||||
"@types/fs-extra": "9.0.13",
|
||||
"@types/node-fetch": "2.6.1",
|
||||
"@types/react": "18.0.25",
|
||||
"@types/react": "npm:types-react@19.0.0",
|
||||
"@types/rimraf": "3.0.2",
|
||||
"@types/selenium-webdriver": "4.0.18",
|
||||
"chromedriver": "100.0.0",
|
||||
@@ -23,8 +23,8 @@
|
||||
"node-fetch": "3.2.3",
|
||||
"pkg-dir": "5.0.0",
|
||||
"playwright-chromium": "1.28.0",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"resolve-cwd": "3.0.0",
|
||||
"resolve-from": "5.0.0",
|
||||
"rimraf": "3.0.2",
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
"husky": "8.0.2",
|
||||
"jsdom": "^19.0.0",
|
||||
"lint-staged": "13.0.3",
|
||||
"next": "14.3.0-canary.28",
|
||||
"next": "15.0.1",
|
||||
"only-allow": "1.1.0",
|
||||
"prettier": "^2.8.8",
|
||||
"prettier-plugin-prisma": "4.4.0",
|
||||
@@ -51,7 +51,9 @@
|
||||
},
|
||||
"overrides": {
|
||||
"@types/mime": "3.0.4",
|
||||
"next": "14.3.0-canary.28"
|
||||
"next": "15.0.1",
|
||||
"@types/react": "npm:types-react@rc",
|
||||
"@types/react-dom": "npm:types-react-dom@rc"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,32 @@
|
||||
# @blitzjs/auth
|
||||
|
||||
## 2.2.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 3fa3a4ef3: chore: support next.js 15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [565db3c5a]
|
||||
- Updated dependencies [3fa3a4ef3]
|
||||
- blitz@2.2.0
|
||||
|
||||
## 2.1.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- blitz@2.1.4
|
||||
|
||||
## 2.1.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 0b3286468: chore: bump `next.js` and `zod` versions
|
||||
- Updated dependencies [0b3286468]
|
||||
- Updated dependencies [50f17d21c]
|
||||
- blitz@2.1.3
|
||||
|
||||
## 2.1.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@blitzjs/auth",
|
||||
"version": "2.1.2",
|
||||
"version": "2.2.0",
|
||||
"homepage": "https://blitzjs.com/",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -50,7 +50,7 @@
|
||||
"url": "0.11.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"blitz": "2.1.2",
|
||||
"blitz": "2.2.0",
|
||||
"next": "*",
|
||||
"next-auth": "*",
|
||||
"secure-password": "4.0.0"
|
||||
@@ -67,19 +67,18 @@
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@blitzjs/config": "2.1.2",
|
||||
"@testing-library/react": "13.4.0",
|
||||
"@testing-library/react-hooks": "8.0.1",
|
||||
"@blitzjs/config": "2.2.0",
|
||||
"@testing-library/react": "16.0.1",
|
||||
"@types/cookie": "0.4.1",
|
||||
"@types/debug": "4.1.7",
|
||||
"@types/jsonwebtoken": "8.5.8",
|
||||
"@types/react": "18.0.25",
|
||||
"@types/react-dom": "17.0.14",
|
||||
"blitz": "2.1.2",
|
||||
"next": "14.3.0-canary.28",
|
||||
"@types/react": "npm:types-react@19.0.0",
|
||||
"@types/react-dom": "npm:types-react-dom@19.0.0",
|
||||
"blitz": "2.2.0",
|
||||
"next": "15.0.1",
|
||||
"next-auth": "4.24.7",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"secure-password": "4.0.0",
|
||||
"typescript": "^4.8.4",
|
||||
"unbuild": "0.7.6",
|
||||
|
||||
@@ -7,7 +7,7 @@ import {parsePublicDataToken, getPublicDataStore, useSession} from "./index"
|
||||
import {COOKIE_PUBLIC_DATA_TOKEN} from "../shared"
|
||||
import {toBase64} from "b64-lite"
|
||||
import {act} from "@testing-library/react"
|
||||
import {renderHook} from "@testing-library/react-hooks"
|
||||
import {renderHook} from "@testing-library/react"
|
||||
|
||||
vi.mock("blitz", async () => {
|
||||
const blitz = await vi.importActual("blitz")
|
||||
|
||||
@@ -260,7 +260,7 @@ export type RedirectAuthenticatedToFn = (
|
||||
) => RedirectAuthenticatedTo
|
||||
|
||||
export type BlitzPage<P = {}> = React.ComponentType<P> & {
|
||||
getLayout?: (component: JSX.Element) => JSX.Element
|
||||
getLayout?: (component: React.JSX.Element) => React.JSX.Element
|
||||
authenticate?: boolean | {redirectTo?: string | RouteUrlObject; role?: string | Array<string>}
|
||||
suppressFirstRenderFlicker?: boolean
|
||||
redirectAuthenticatedTo?: RedirectAuthenticatedTo | RedirectAuthenticatedToFn
|
||||
|
||||
@@ -289,8 +289,10 @@ const makeProxyToPublicData = <T extends SessionContextClass>(ctxClass: T): T =>
|
||||
export async function getBlitzContext(): Promise<Ctx> {
|
||||
try {
|
||||
const {headers, cookies} = require("next/headers")
|
||||
const reqHeader = Object.fromEntries(headers())
|
||||
const csrfToken = cookies().get(COOKIE_CSRF_TOKEN())
|
||||
const cookieStore = await cookies()
|
||||
const headersStore = await headers()
|
||||
const reqHeader = Object.fromEntries(headersStore)
|
||||
const csrfToken = cookieStore.get(COOKIE_CSRF_TOKEN())
|
||||
if (csrfToken) {
|
||||
reqHeader[HEADER_CSRF] = csrfToken.value
|
||||
}
|
||||
|
||||
@@ -1,5 +1,36 @@
|
||||
# @blitzjs/next
|
||||
|
||||
## 2.2.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 3fa3a4ef3: chore: support next.js 15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [565db3c5a]
|
||||
- Updated dependencies [3fa3a4ef3]
|
||||
- blitz@2.2.0
|
||||
- @blitzjs/rpc@2.2.0
|
||||
|
||||
## 2.1.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- ce23d4ed0: fix: Update `turbopack-empty.js` syntax to support latest turbopack and next.js versions
|
||||
- blitz@2.1.4
|
||||
- @blitzjs/rpc@2.1.4
|
||||
|
||||
## 2.1.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 0b3286468: chore: bump `next.js` and `zod` versions
|
||||
- Updated dependencies [0b3286468]
|
||||
- Updated dependencies [50f17d21c]
|
||||
- @blitzjs/rpc@2.1.3
|
||||
- blitz@2.1.3
|
||||
|
||||
## 2.1.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@blitzjs/next",
|
||||
"version": "2.1.2",
|
||||
"version": "2.2.0",
|
||||
"homepage": "https://blitzjs.com/",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -29,7 +29,7 @@
|
||||
"eslint.js"
|
||||
],
|
||||
"dependencies": {
|
||||
"@blitzjs/rpc": "2.1.2",
|
||||
"@blitzjs/rpc": "2.2.0",
|
||||
"@types/hoist-non-react-statics": "3.3.1",
|
||||
"copy-webpack-plugin": "11.0.0",
|
||||
"debug": "4.3.3",
|
||||
@@ -39,30 +39,29 @@
|
||||
"supports-color": "8.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"blitz": "2.1.2",
|
||||
"blitz": "2.2.0",
|
||||
"next": "*",
|
||||
"react": "*",
|
||||
"tslog": "4.9.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@blitzjs/config": "2.1.2",
|
||||
"@blitzjs/config": "2.2.0",
|
||||
"@testing-library/dom": "8.13.0",
|
||||
"@testing-library/jest-dom": "5.16.3",
|
||||
"@testing-library/react": "13.4.0",
|
||||
"@testing-library/react-hooks": "8.0.1",
|
||||
"@testing-library/react": "16.0.1",
|
||||
"@testing-library/user-event": "13.5.0",
|
||||
"@types/debug": "4.1.7",
|
||||
"@types/node": "18.11.9",
|
||||
"@types/react": "18.0.25",
|
||||
"@types/react-dom": "17.0.14",
|
||||
"@types/react": "npm:types-react@19.0.0",
|
||||
"@types/react-dom": "npm:types-react-dom@19.0.0",
|
||||
"@types/testing-library__react-hooks": "4.0.0",
|
||||
"blitz": "2.1.2",
|
||||
"blitz": "2.2.0",
|
||||
"cross-spawn": "7.0.3",
|
||||
"find-up": "4.1.0",
|
||||
"next": "14.3.0-canary.28",
|
||||
"next": "15.0.1",
|
||||
"next-router-mock": "0.9.1",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"resolve-from": "5.0.0",
|
||||
"ts-jest": "27.1.4",
|
||||
"tslog": "4.9.0",
|
||||
|
||||
@@ -1 +1,8 @@
|
||||
export {}
|
||||
const exports = {
|
||||
"npm-which": {},
|
||||
"cross-spawn": {},
|
||||
fs: {},
|
||||
child_process: {},
|
||||
}
|
||||
|
||||
module.exports = exports
|
||||
|
||||
@@ -70,7 +70,7 @@ test("handleError forwards along async errors", async () => {
|
||||
//
|
||||
// React will try to recreate this component tree from scratch using the error boundary you provided, ErrorBoundary."
|
||||
// `)
|
||||
expect(consoleError).toHaveBeenCalledTimes(3)
|
||||
expect(consoleError).toHaveBeenCalledTimes(1)
|
||||
consoleError.mockClear()
|
||||
|
||||
// can recover
|
||||
@@ -116,7 +116,7 @@ test("can pass an error to useErrorHandler", async () => {
|
||||
//
|
||||
// React will try to recreate this component tree from scratch using the error boundary you provided, ErrorBoundary."
|
||||
// `)
|
||||
expect(consoleError).toHaveBeenCalledTimes(3)
|
||||
expect(consoleError).toHaveBeenCalledTimes(1)
|
||||
consoleError.mockClear()
|
||||
|
||||
// can recover
|
||||
|
||||
@@ -77,25 +77,10 @@ test("standard use-case", () => {
|
||||
const {unmount} = render(<App />)
|
||||
|
||||
userEvent.type(screen.getByRole("textbox", {name: /username/i}), "fail")
|
||||
|
||||
const [[actualError], [componentStack]] = consoleError.mock.calls
|
||||
expect(firstLine(actualError as string)).toMatchInlineSnapshot(
|
||||
`"Error: Uncaught [Error: 💥 CABOOM 💥]"`,
|
||||
)
|
||||
expect(cleanStack(componentStack)).toMatchInlineSnapshot(`
|
||||
"Error: Uncaught [Error: 💥 CABOOM 💥]
|
||||
at reportException
|
||||
at innerInvokeEventListeners
|
||||
at invokeEventListeners
|
||||
at HTMLUnknownElementImpl._dispatch
|
||||
at HTMLUnknownElementImpl.dispatchEvent
|
||||
at HTMLUnknownElement.dispatchEvent
|
||||
at Object.invokeGuardedCallbackDev
|
||||
at invokeGuardedCallback
|
||||
at beginWork\$1
|
||||
at performUnitOfWork "
|
||||
`)
|
||||
expect(consoleError).toHaveBeenCalledTimes(3)
|
||||
const calls = consoleError.mock.calls[0]
|
||||
//@ts-expect-error - it's a mock
|
||||
expect(calls[1]).toMatchInlineSnapshot("[Error: 💥 CABOOM 💥]")
|
||||
expect(consoleError).toHaveBeenCalledTimes(1)
|
||||
consoleError.mockClear()
|
||||
|
||||
expect(screen.getByRole("alert")).toMatchInlineSnapshot(`
|
||||
@@ -149,7 +134,7 @@ test("fallbackRender prop", () => {
|
||||
}
|
||||
|
||||
const {unmount} = render(<App />)
|
||||
expect(consoleError).toHaveBeenCalledTimes(3)
|
||||
expect(consoleError).toHaveBeenCalledTimes(1)
|
||||
consoleError.mockClear()
|
||||
|
||||
// the render prop API allows a single action to reset the app state
|
||||
@@ -168,7 +153,7 @@ test("simple fallback is supported", () => {
|
||||
<span>child</span>
|
||||
</ErrorBoundary>,
|
||||
)
|
||||
expect(consoleError).toHaveBeenCalledTimes(3)
|
||||
expect(consoleError).toHaveBeenCalledTimes(1)
|
||||
consoleError.mockClear()
|
||||
expect(screen.getByText(/oh no/i)).to.exist
|
||||
expect(screen.queryByText(/child/i)).to.not.exist
|
||||
@@ -183,27 +168,16 @@ test("withErrorBoundary HOC", () => {
|
||||
() => {
|
||||
throw new Error("💥 CABOOM 💥")
|
||||
},
|
||||
{FallbackComponent: ErrorFallback, onError: onErrorHandler},
|
||||
{
|
||||
FallbackComponent: ErrorFallback,
|
||||
onError: onErrorHandler,
|
||||
},
|
||||
)
|
||||
const {unmount} = render(<Boundary />)
|
||||
|
||||
const [[actualError], [componentStack]] = consoleError.mock.calls
|
||||
const firstLineOfError = firstLine(actualError as string)
|
||||
expect(firstLineOfError).toMatchInlineSnapshot(`"Error: Uncaught [Error: 💥 CABOOM 💥]"`)
|
||||
expect(cleanStack(componentStack)).toMatchInlineSnapshot(`
|
||||
"Error: Uncaught [Error: 💥 CABOOM 💥]
|
||||
at reportException
|
||||
at innerInvokeEventListeners
|
||||
at invokeEventListeners
|
||||
at HTMLUnknownElementImpl._dispatch
|
||||
at HTMLUnknownElementImpl.dispatchEvent
|
||||
at HTMLUnknownElement.dispatchEvent
|
||||
at Object.invokeGuardedCallbackDev
|
||||
at invokeGuardedCallback
|
||||
at beginWork\$1
|
||||
at performUnitOfWork "
|
||||
`)
|
||||
expect(consoleError).toHaveBeenCalledTimes(3)
|
||||
const calls = consoleError.mock.calls[0]
|
||||
//@ts-expect-error - it's a mock
|
||||
expect(calls[1]).toMatchInlineSnapshot("[Error: 💥 CABOOM 💥]")
|
||||
expect(consoleError).toHaveBeenCalledTimes(1)
|
||||
consoleError.mockClear()
|
||||
|
||||
const [error, onErrorComponentStack] = (onErrorHandler.mock.calls as [[Error, string]])[0]
|
||||
@@ -265,7 +239,6 @@ test("requires either a fallback, fallbackRender, or FallbackComponent", () => {
|
||||
let unmount: undefined | (() => void)
|
||||
expect(() => {
|
||||
const result = render(
|
||||
// @ts-expect-error we're testing the runtime check of missing props here
|
||||
<ErrorBoundary>
|
||||
<Bomb />
|
||||
</ErrorBoundary>,
|
||||
@@ -318,7 +291,7 @@ test("supports automatic reset of error boundary when resetKeys change", () => {
|
||||
// blow it up
|
||||
userEvent.click(screen.getByText("toggle explode"))
|
||||
expect(screen.getByRole("alert")).to.exist
|
||||
expect(consoleError).toHaveBeenCalledTimes(3)
|
||||
expect(consoleError).toHaveBeenCalledTimes(1)
|
||||
consoleError.mockClear()
|
||||
|
||||
// recover via try again button
|
||||
@@ -333,7 +306,7 @@ test("supports automatic reset of error boundary when resetKeys change", () => {
|
||||
// blow it up again
|
||||
userEvent.click(screen.getByText("toggle explode"))
|
||||
expect(screen.getByRole("alert")).to.exist
|
||||
expect(consoleError).toHaveBeenCalledTimes(3)
|
||||
expect(consoleError).toHaveBeenCalledTimes(1)
|
||||
consoleError.mockClear()
|
||||
|
||||
// recover via resetKeys change
|
||||
@@ -348,7 +321,7 @@ test("supports automatic reset of error boundary when resetKeys change", () => {
|
||||
// blow it up again
|
||||
userEvent.click(screen.getByText("toggle explode"))
|
||||
expect(screen.getByRole("alert")).to.exist
|
||||
expect(consoleError).toHaveBeenCalledTimes(3)
|
||||
expect(consoleError).toHaveBeenCalledTimes(1)
|
||||
consoleError.mockClear()
|
||||
|
||||
// toggles adding an extra resetKey to the array
|
||||
@@ -358,7 +331,7 @@ test("supports automatic reset of error boundary when resetKeys change", () => {
|
||||
expect(handleResetKeysChange).toHaveBeenCalledWith([true], [true, true])
|
||||
handleResetKeysChange.mockClear()
|
||||
expect(screen.getByRole("alert")).to.exist
|
||||
expect(consoleError).toHaveBeenCalledTimes(3)
|
||||
expect(consoleError).toHaveBeenCalledTimes(1)
|
||||
consoleError.mockClear()
|
||||
|
||||
// toggle explode back to false
|
||||
@@ -369,7 +342,7 @@ test("supports automatic reset of error boundary when resetKeys change", () => {
|
||||
expect(handleResetKeysChange).toHaveBeenCalledWith([true, true], [false, true])
|
||||
expect(screen.getByRole("alert")).to.exist
|
||||
handleResetKeysChange.mockClear()
|
||||
expect(consoleError).toHaveBeenCalledTimes(3)
|
||||
expect(consoleError).toHaveBeenCalledTimes(1)
|
||||
consoleError.mockClear()
|
||||
|
||||
// toggle extra resetKey
|
||||
@@ -411,7 +384,7 @@ test("supports reset via resetKeys right after error is triggered on component m
|
||||
|
||||
// it blows up on render
|
||||
expect(screen.queryByRole("alert", {})).to.exist
|
||||
expect(consoleError).toHaveBeenCalledTimes(3)
|
||||
expect(consoleError).toHaveBeenCalledTimes(1)
|
||||
consoleError.mockClear()
|
||||
|
||||
// recover via "toggle explode" button
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {RedirectError} from "blitz"
|
||||
import {useRouter} from "next/compat/router"
|
||||
import type {NextRouter} from "next/router"
|
||||
import * as React from "react"
|
||||
import React from "react"
|
||||
import {RouterContext} from "./router-context"
|
||||
import _debug from "debug"
|
||||
import type {ExcludeRouterProps, WithRouterProps} from "next/dist/client/with-router"
|
||||
@@ -77,7 +77,7 @@ const initialState: ErrorBoundaryState = {error: null}
|
||||
function withRouter<P extends WithRouterProps>(
|
||||
ComposedComponent: React.ComponentType<P>,
|
||||
): React.ComponentType<ExcludeRouterProps<P>> {
|
||||
function WithRouterWrapper(props: any): JSX.Element {
|
||||
function WithRouterWrapper(props: any): React.JSX.Element {
|
||||
return <ComposedComponent router={useRouter()} {...props} />
|
||||
}
|
||||
|
||||
@@ -114,7 +114,13 @@ export const ErrorBoundary = withRouter(
|
||||
await this.props.router.push(error.url)
|
||||
return
|
||||
}
|
||||
this.props.onError?.(error, info)
|
||||
if (this.props.onError) {
|
||||
let componentStack = info.componentStack
|
||||
if (!componentStack) {
|
||||
componentStack = new Error("Stack trace").stack || ""
|
||||
}
|
||||
this.props.onError(error, {componentStack})
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
@@ -190,7 +196,7 @@ export const ErrorBoundary = withRouter(
|
||||
},
|
||||
)
|
||||
|
||||
function withErrorBoundary<P extends JSX.IntrinsicAttributes>(
|
||||
function withErrorBoundary<P extends React.JSX.IntrinsicAttributes>(
|
||||
Component: React.ComponentType<P>,
|
||||
errorBoundaryProps: ErrorBoundaryProps,
|
||||
): React.ComponentType<P> {
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
import React from "react"
|
||||
import {NextPageContext} from "next"
|
||||
import dynamic from "next/dynamic"
|
||||
const Head = dynamic(() => import("next/head").then((mod) => mod.default), {
|
||||
ssr: false,
|
||||
loading: () => null,
|
||||
})
|
||||
const Head = dynamic(
|
||||
() =>
|
||||
import("next/head").then((mod) => ({
|
||||
default: mod.default,
|
||||
})),
|
||||
{
|
||||
ssr: false,
|
||||
loading: () => null,
|
||||
},
|
||||
)
|
||||
|
||||
const statusCodes: {[code: number]: string} = {
|
||||
400: "Bad Request",
|
||||
|
||||
@@ -9,10 +9,16 @@ import type {Router} from "next/router"
|
||||
import {BlitzProvider} from "./provider"
|
||||
import dynamic from "next/dynamic"
|
||||
export {Routes} from ".blitz"
|
||||
const Head = dynamic(() => import("next/head").then((mod) => mod.default), {
|
||||
ssr: false,
|
||||
loading: () => null,
|
||||
})
|
||||
const Head = dynamic(
|
||||
() =>
|
||||
import("next/head").then((mod) => ({
|
||||
default: mod.default,
|
||||
})),
|
||||
{
|
||||
ssr: false,
|
||||
loading: () => null,
|
||||
},
|
||||
)
|
||||
|
||||
export {BlitzProvider} from "./provider"
|
||||
|
||||
@@ -55,7 +61,7 @@ type RedirectAuthenticatedToFnCtx = {
|
||||
}
|
||||
type RedirectAuthenticatedToFn = (args: RedirectAuthenticatedToFnCtx) => RedirectAuthenticatedTo
|
||||
export type BlitzPage<P = {}> = React.ComponentType<P> & {
|
||||
getLayout?: (component: JSX.Element) => JSX.Element
|
||||
getLayout?: (component: React.JSX.Element) => React.JSX.Element
|
||||
authenticate?: boolean | {redirectTo?: string | RouteUrlObject; role?: string | Array<string>}
|
||||
suppressFirstRenderFlicker?: boolean
|
||||
redirectAuthenticatedTo?: RedirectAuthenticatedTo | RedirectAuthenticatedToFn
|
||||
|
||||
@@ -3,7 +3,7 @@ import type {QueryClient, HydrateOptions} from "@blitzjs/rpc"
|
||||
import React from "react"
|
||||
|
||||
export type BlitzProviderProps = {
|
||||
children: JSX.Element
|
||||
children: React.JSX.Element
|
||||
client?: QueryClient
|
||||
contextSharing?: boolean
|
||||
dehydratedState?: unknown
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import React from "react"
|
||||
import {describe, it, expect, vi, afterEach} from "vitest"
|
||||
import {extractRouterParams, useParam, useParams} from "./use-params"
|
||||
import {renderHook as defaultRenderHook} from "@testing-library/react-hooks"
|
||||
import {renderHook as defaultRenderHook} from "@testing-library/react"
|
||||
import {NextRouter} from "next/router"
|
||||
import {RouterContext} from "./router-context"
|
||||
|
||||
|
||||
@@ -1,5 +1,32 @@
|
||||
# @blitzjs/rpc
|
||||
|
||||
## 2.2.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 3fa3a4ef3: chore: support next.js 15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [565db3c5a]
|
||||
- Updated dependencies [3fa3a4ef3]
|
||||
- blitz@2.2.0
|
||||
|
||||
## 2.1.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- blitz@2.1.4
|
||||
|
||||
## 2.1.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 0b3286468: chore: bump `next.js` and `zod` versions
|
||||
- Updated dependencies [0b3286468]
|
||||
- Updated dependencies [50f17d21c]
|
||||
- blitz@2.1.3
|
||||
|
||||
## 2.1.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@blitzjs/rpc",
|
||||
"version": "2.1.2",
|
||||
"version": "2.2.0",
|
||||
"homepage": "https://blitzjs.com/",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -37,25 +37,25 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tanstack/query-core": "4.24.4",
|
||||
"blitz": "2.1.2",
|
||||
"blitz": "2.2.0",
|
||||
"next": "*",
|
||||
"react": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@blitzjs/auth": "2.1.2",
|
||||
"@blitzjs/config": "2.1.2",
|
||||
"@blitzjs/auth": "2.2.0",
|
||||
"@blitzjs/config": "2.2.0",
|
||||
"@tanstack/query-core": "4.24.4",
|
||||
"@types/debug": "4.1.7",
|
||||
"@types/react": "18.0.25",
|
||||
"@types/react-dom": "17.0.14",
|
||||
"blitz": "2.1.2",
|
||||
"next": "14.3.0-canary.28",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"@types/react": "npm:types-react@19.0.0",
|
||||
"@types/react-dom": "npm:types-react-dom@19.0.0",
|
||||
"blitz": "2.2.0",
|
||||
"next": "15.0.1",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"typescript": "^4.8.4",
|
||||
"unbuild": "0.7.6",
|
||||
"watch": "1.0.2",
|
||||
"zod": "3.20.2"
|
||||
"zod": "3.23.8"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
||||
@@ -221,7 +221,7 @@ async function getResolverMap(): Promise<ResolverFiles | null | undefined> {
|
||||
// Handles:
|
||||
// - Vite
|
||||
// {
|
||||
// const {resolverFilesLoaded, viteProvider} = await loadTelefuncFilesWithVite(runContext)
|
||||
// const {resolverFilesLoaded, viteProvider} = await loadTelefuncFilesWithVite(run
|
||||
// if (resolverFilesLoaded) {
|
||||
// assertUsage(
|
||||
// Object.keys(resolverFilesLoaded).length > 0,
|
||||
@@ -364,17 +364,18 @@ type Params = Record<string, unknown>
|
||||
|
||||
export function rpcAppHandler(config?: RpcConfig) {
|
||||
registerBlitzErrorClasses()
|
||||
async function handleRpcRequest(req: Request, context: {params: Params}, ctx?: Ctx) {
|
||||
async function handleRpcRequest(req: Request, segmentData: {params: Promise<Params>}, ctx?: Ctx) {
|
||||
const params = await segmentData.params
|
||||
const session = ctx?.session
|
||||
const resolverMap = await getResolverMap()
|
||||
assert(resolverMap, "No query or mutation resolvers found")
|
||||
|
||||
assert(
|
||||
Array.isArray(context.params.blitz),
|
||||
Array.isArray(params.blitz),
|
||||
"It seems your Blitz RPC endpoint file is not named [[...blitz]].(jt)s. Please ensure it is",
|
||||
)
|
||||
|
||||
const relativeRoutePath = (context.params.blitz as string[])?.join("/")
|
||||
const relativeRoutePath = (params.blitz as string[])?.join("/")
|
||||
const routePath = "/" + relativeRoutePath
|
||||
const resolverName = routePath.replace(/(\/api\/rpc)?\//, "")
|
||||
const rpcLogger = new RpcLogger(resolverName, config?.logging)
|
||||
@@ -413,14 +414,14 @@ export function rpcAppHandler(config?: RpcConfig) {
|
||||
json:
|
||||
req.method === "POST"
|
||||
? body.params
|
||||
: context.params.params
|
||||
? parse(`${context.params.params}`)
|
||||
: params.params
|
||||
? parse(`${params.params}`)
|
||||
: undefined,
|
||||
meta:
|
||||
req.method === "POST"
|
||||
? body.meta?.params
|
||||
: context.params.meta
|
||||
? parse(`${context.params.meta}`)
|
||||
: params.meta
|
||||
? parse(`${params.meta}`)
|
||||
: undefined,
|
||||
})
|
||||
rpcLogger.timer.initResolver()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {QueryClientProvider} from "@tanstack/react-query"
|
||||
import React from "react"
|
||||
|
||||
export type BlitzProviderType = ({children}: {children: React.ReactNode}) => JSX.Element
|
||||
export type BlitzProviderType = ({children}: {children: React.ReactNode}) => React.JSX.Element
|
||||
|
||||
const BlitzProvider: BlitzProviderType = ({children}) => {
|
||||
const [queryClient] = React.useState(globalThis.queryClient)
|
||||
|
||||
@@ -1,5 +1,32 @@
|
||||
# blitz
|
||||
|
||||
## 2.2.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 3fa3a4ef3: chore: support next.js 15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 565db3c5a: Fixed incorrect terminal message after creating a new Blitz project to use the correct command (`npm run dev` or `yarn dev`).
|
||||
- @blitzjs/generator@2.2.0
|
||||
|
||||
## 2.1.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [38d320fd2]
|
||||
- @blitzjs/generator@2.1.4
|
||||
|
||||
## 2.1.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 0b3286468: chore: bump `next.js` and `zod` versions
|
||||
- 50f17d21c: Update prisma-ast dependency to prevent Blitz generator from failing when Prisma keywords are used as model names
|
||||
- Updated dependencies [0b3286468]
|
||||
- @blitzjs/generator@2.1.3
|
||||
|
||||
## 2.1.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {BuildConfig} from "unbuild"
|
||||
|
||||
const config: BuildConfig = {
|
||||
entries: ["./src/index-browser", "./src/index-server", "./src/cli/index", "./src/installer"],
|
||||
entries: ["./src/index-browser", "./src/index-server", "./src/cli/index"],
|
||||
externals: ["index-browser.cjs", "index-browser.mjs", "index.cjs", "zod", "react"],
|
||||
declaration: true,
|
||||
rollup: {
|
||||
|
||||
1
packages/blitz/installer.d.ts
vendored
1
packages/blitz/installer.d.ts
vendored
@@ -1 +0,0 @@
|
||||
export * from "./dist/installer"
|
||||
@@ -1 +0,0 @@
|
||||
module.exports = require("./dist/installer.cjs")
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "blitz",
|
||||
"version": "2.1.2",
|
||||
"version": "2.2.0",
|
||||
"homepage": "https://blitzjs.com/",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -22,7 +22,6 @@
|
||||
"sideEffects": false,
|
||||
"license": "MIT",
|
||||
"files": [
|
||||
"installer.*",
|
||||
"dist/**",
|
||||
"bin/**"
|
||||
],
|
||||
@@ -30,8 +29,8 @@
|
||||
"blitz": "bin/blitz"
|
||||
},
|
||||
"dependencies": {
|
||||
"@blitzjs/generator": "2.1.2",
|
||||
"@mrleebo/prisma-ast": "0.2.6",
|
||||
"@blitzjs/generator": "2.2.0",
|
||||
"@mrleebo/prisma-ast": "0.4.1",
|
||||
"@types/global-agent": "2.1.1",
|
||||
"arg": "5.0.1",
|
||||
"ast-types": "0.14.2",
|
||||
@@ -80,7 +79,7 @@
|
||||
"watchpack": "2.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@blitzjs/config": "2.1.2",
|
||||
"@blitzjs/config": "2.2.0",
|
||||
"@types/cookie": "0.4.1",
|
||||
"@types/cross-spawn": "6.0.2",
|
||||
"@types/debug": "4.1.7",
|
||||
@@ -95,17 +94,17 @@
|
||||
"@types/npm-which": "3.0.1",
|
||||
"@types/progress": "2.0.5",
|
||||
"@types/prompts": "2.0.14",
|
||||
"@types/react": "18.0.25",
|
||||
"@types/react-dom": "17.0.14",
|
||||
"@types/react": "npm:types-react@19.0.0",
|
||||
"@types/react-dom": "npm:types-react-dom@19.0.0",
|
||||
"@types/test-listen": "1.1.0",
|
||||
"@types/watchpack": "1.1.1",
|
||||
"express": "4.17.3",
|
||||
"react": "18.2.0",
|
||||
"react": "19.0.0",
|
||||
"test-listen": "1.1.0",
|
||||
"typescript": "^4.8.4",
|
||||
"unbuild": "0.7.6",
|
||||
"watch": "1.0.2",
|
||||
"zod": "3.20.2"
|
||||
"zod": "3.23.8"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
||||
@@ -1,325 +0,0 @@
|
||||
import arg from "arg"
|
||||
import {CliCommand} from "../index"
|
||||
import prompts from "prompts"
|
||||
import {bootstrap} from "global-agent"
|
||||
import {baseLogger, log} from "../../logging"
|
||||
import Debug from "debug"
|
||||
const debug = Debug("blitz:cli")
|
||||
import {join, resolve, dirname} from "path"
|
||||
import {Stream} from "stream"
|
||||
import {promisify} from "util"
|
||||
import {RecipeCLIFlags, RecipeExecutor} from "../../installer"
|
||||
import {setupTsnode} from "../utils/setup-ts-node"
|
||||
import {isInternalBlitzMonorepoDevelopment} from "../utils/helpers"
|
||||
import findUp from "find-up"
|
||||
import resolveFrom from "resolve-from"
|
||||
import {findNodeModulesRoot} from "../utils/find-node-modules"
|
||||
|
||||
interface GlobalAgent {
|
||||
HTTP_PROXY?: string
|
||||
HTTPS_PROXY?: string
|
||||
NO_PROXY?: string
|
||||
}
|
||||
|
||||
declare global {
|
||||
var GLOBAL_AGENT: GlobalAgent
|
||||
}
|
||||
|
||||
const args = arg(
|
||||
{
|
||||
// Types
|
||||
"--help": Boolean,
|
||||
"--env": String,
|
||||
"--yes": Boolean,
|
||||
|
||||
// Aliases
|
||||
"-e": "--env",
|
||||
"-y": "--yes",
|
||||
},
|
||||
{
|
||||
permissive: true,
|
||||
},
|
||||
)
|
||||
|
||||
const pipeline = promisify(Stream.pipeline)
|
||||
|
||||
const got = async (url: string) => {
|
||||
return require("got")(url).catch((e: any) => {
|
||||
if (e.response.statusCode === 403) {
|
||||
baseLogger().error(e.response.body)
|
||||
} else {
|
||||
return e
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const gotJSON = async (url: string) => {
|
||||
debug("[gotJSON] Downloading json from ", url)
|
||||
const res = await got(url)
|
||||
return JSON.parse(res.body)
|
||||
}
|
||||
|
||||
const isUrlValid = async (url: string) => {
|
||||
return (await got(url).catch((e) => e)).statusCode === 200
|
||||
}
|
||||
|
||||
const requireJSON = (file: string) => {
|
||||
return JSON.parse(require("fs-extra").readFileSync(file).toString("utf-8"))
|
||||
}
|
||||
|
||||
const checkLockFileExists = async (filename: string) => {
|
||||
const dotBlitz = join(await findNodeModulesRoot(process.cwd()), ".blitz")
|
||||
return require("fs-extra").existsSync(resolve(join(dotBlitz, "..", "..", filename)))
|
||||
}
|
||||
|
||||
const GH_ROOT = "https://github.com/"
|
||||
const API_ROOT = "https://api.github.com/repos/"
|
||||
const RAW_ROOT = "https://raw.githubusercontent.com/"
|
||||
const CODE_ROOT = "https://codeload.github.com/"
|
||||
|
||||
export enum RecipeLocation {
|
||||
Local,
|
||||
Remote,
|
||||
}
|
||||
|
||||
interface RecipeMeta {
|
||||
path: string
|
||||
subdirectory?: string
|
||||
location: RecipeLocation
|
||||
}
|
||||
|
||||
interface Tree {
|
||||
path: string
|
||||
mode: string
|
||||
type: string
|
||||
sha: string
|
||||
size: number
|
||||
url: string
|
||||
}
|
||||
|
||||
interface GithubRepoAPITrees {
|
||||
sha: string
|
||||
url: string
|
||||
tree: Tree[]
|
||||
truncated: boolean
|
||||
}
|
||||
|
||||
const getOfficialRecipeList = async (): Promise<string[]> => {
|
||||
return await gotJSON(`${API_ROOT}blitz-js/blitz/git/trees/main?recursive=1`).then(
|
||||
(release: GithubRepoAPITrees) =>
|
||||
release.tree.reduce((recipesList: string[], item) => {
|
||||
const filePath = item.path.split("/")
|
||||
const [directory, recipeName] = filePath
|
||||
if (
|
||||
directory === "recipes" &&
|
||||
filePath.length === 2 &&
|
||||
item.type === "tree" &&
|
||||
recipeName
|
||||
) {
|
||||
recipesList.push(recipeName)
|
||||
}
|
||||
return recipesList
|
||||
}, []),
|
||||
)
|
||||
}
|
||||
|
||||
const normalizeRecipePath = (recipeArg: string): RecipeMeta => {
|
||||
const isNativeRecipe = /^([\w\-_]*)$/.test(recipeArg)
|
||||
const isUrlRecipe = recipeArg.startsWith(GH_ROOT)
|
||||
const isGitHubShorthandRecipe = /^([\w-_]*)\/([\w-_]*)$/.test(recipeArg)
|
||||
if (isNativeRecipe || isUrlRecipe || isGitHubShorthandRecipe) {
|
||||
let repoUrl
|
||||
let subdirectory
|
||||
switch (true) {
|
||||
case isUrlRecipe:
|
||||
repoUrl = recipeArg
|
||||
break
|
||||
case isNativeRecipe:
|
||||
repoUrl = `${GH_ROOT}blitz-js/blitz`
|
||||
subdirectory = `recipes/${recipeArg}`
|
||||
break
|
||||
case isGitHubShorthandRecipe:
|
||||
repoUrl = `${GH_ROOT}${recipeArg}`
|
||||
break
|
||||
default:
|
||||
throw new Error(
|
||||
"should be impossible, the 3 cases are the only way to get into this switch",
|
||||
)
|
||||
}
|
||||
return {
|
||||
path: repoUrl,
|
||||
subdirectory,
|
||||
location: RecipeLocation.Remote,
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
path: recipeArg,
|
||||
location: RecipeLocation.Local,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const cloneRepo = async (
|
||||
repoFullName: string,
|
||||
defaultBranch: string,
|
||||
subdirectory?: string,
|
||||
): Promise<string> => {
|
||||
debug("[cloneRepo] starting...")
|
||||
const dotBlitz = join(await findNodeModulesRoot(process.cwd()), ".blitz")
|
||||
const recipeDir = join(dotBlitz, "..", "..", "recipe-install")
|
||||
// clean up from previous run in case of error
|
||||
require("rimraf").sync(recipeDir)
|
||||
require("fs-extra").mkdirsSync(recipeDir)
|
||||
process.chdir(recipeDir)
|
||||
debug("Extracting recipe to ", recipeDir)
|
||||
|
||||
const repoName = repoFullName.split("/")[1]
|
||||
// `tar` top-level filter is `${repoName}-${defaultBranch}`, and then we want to get our recipe path
|
||||
// within that folder
|
||||
const extractPath = subdirectory ? [`${repoName}-${defaultBranch}/${subdirectory}`] : undefined
|
||||
const depth = subdirectory ? subdirectory.split("/").length + 1 : 1
|
||||
await pipeline(
|
||||
require("got").stream(`${CODE_ROOT}${repoFullName}/tar.gz/${defaultBranch}`),
|
||||
require("tar").extract({strip: depth}, extractPath),
|
||||
)
|
||||
|
||||
return recipeDir
|
||||
}
|
||||
|
||||
const installRecipeAtPath = async (
|
||||
recipePath: string,
|
||||
...runArgs: Parameters<RecipeExecutor<any>["run"]>
|
||||
) => {
|
||||
const recipe = require(recipePath).default as RecipeExecutor<any>
|
||||
|
||||
await recipe.run(...runArgs)
|
||||
}
|
||||
|
||||
const setupProxySupport = async () => {
|
||||
const httpProxy = process.env.http_proxy || process.env.HTTP_PROXY
|
||||
const httpsProxy = process.env.https_proxy || process.env.HTTPS_PROXY
|
||||
const noProxy = process.env.no_proxy || process.env.NO_PROXY
|
||||
|
||||
if (httpProxy || httpsProxy) {
|
||||
globalThis.GLOBAL_AGENT = {
|
||||
HTTP_PROXY: httpProxy,
|
||||
HTTPS_PROXY: httpsProxy,
|
||||
NO_PROXY: noProxy,
|
||||
}
|
||||
|
||||
bootstrap()
|
||||
}
|
||||
}
|
||||
|
||||
const install: CliCommand = async () => {
|
||||
setupTsnode()
|
||||
let selectedRecipe: string | null = args._[1] ? `${args._[1]}` : null
|
||||
await setupProxySupport()
|
||||
|
||||
if (!selectedRecipe) {
|
||||
const officialRecipeList = await getOfficialRecipeList()
|
||||
const res = await prompts({
|
||||
type: "select",
|
||||
name: "recipeName",
|
||||
message: "Select a recipe to install",
|
||||
choices: officialRecipeList.map((r) => {
|
||||
return {title: r, value: r}
|
||||
}),
|
||||
})
|
||||
selectedRecipe = res.recipeName
|
||||
}
|
||||
|
||||
if (selectedRecipe) {
|
||||
const recipeInfo = normalizeRecipePath(selectedRecipe)
|
||||
// Take all the args after the recipe string
|
||||
//
|
||||
// ['material-ui', '--yes', 'prop=true']
|
||||
// --> ['material-ui', 'prop=true']
|
||||
// --> ['prop=true']
|
||||
// --> { prop: 'true' }
|
||||
const cliArgs = args._.filter((arg) => !arg.startsWith("--"))
|
||||
.slice(2)
|
||||
.reduce(
|
||||
(acc, arg) => ({
|
||||
...acc,
|
||||
[`${arg.split("=")[0]}`]: arg.split("=")[1] ? JSON.parse(`"${arg.split("=")[1]}"`) : true, // if no value is provided, assume it's a boolean flag
|
||||
}),
|
||||
{},
|
||||
)
|
||||
|
||||
const cliFlags: RecipeCLIFlags = {
|
||||
yesToAll: args["--yes"] || false,
|
||||
}
|
||||
|
||||
const chalk = (await import("chalk")).default
|
||||
if (recipeInfo.location === RecipeLocation.Remote) {
|
||||
const apiUrl = recipeInfo.path.replace(GH_ROOT, API_ROOT)
|
||||
const rawUrl = recipeInfo.path.replace(GH_ROOT, RAW_ROOT)
|
||||
const repoInfo = await gotJSON(apiUrl)
|
||||
const packageJsonPath = join(
|
||||
`${rawUrl}`,
|
||||
repoInfo.default_branch,
|
||||
recipeInfo.subdirectory ?? "",
|
||||
"package.json",
|
||||
)
|
||||
|
||||
if (!(await isUrlValid(packageJsonPath))) {
|
||||
debug("Url is invalid for ", packageJsonPath)
|
||||
baseLogger().error(`Could not find recipe "${args._[1]}"\n`)
|
||||
console.log(`${chalk.bold("Please provide one of the following:")}
|
||||
|
||||
1. The name of a recipe to install (e.g. "tailwind")
|
||||
${chalk.dim("- Available recipes listed at https://github.com/blitz-js/blitz/tree/main/recipes")}
|
||||
2. The full name of a GitHub repository (e.g. "blitz-js/example-recipe"),
|
||||
3. A full URL to a Github repository (e.g. "https://github.com/blitz-js/example-recipe"), or
|
||||
4. A file path to a locally-written recipe.\n`)
|
||||
process.exit(1)
|
||||
} else {
|
||||
let spinner = log.spinner(`Cloning GitHub repository for ${selectedRecipe} recipe`).start()
|
||||
const recipeRepoPath = await cloneRepo(
|
||||
repoInfo.full_name,
|
||||
repoInfo.default_branch,
|
||||
recipeInfo.subdirectory,
|
||||
)
|
||||
spinner.stop()
|
||||
spinner = log.spinner("Installing package.json dependencies").start()
|
||||
|
||||
let pkgManager = "npm"
|
||||
let installArgs = ["install", "--legacy-peer-deps", "--ignore-scripts"]
|
||||
|
||||
if (await checkLockFileExists("yarn.lock")) {
|
||||
pkgManager = "yarn"
|
||||
installArgs = ["install", "--ignore-scripts"]
|
||||
} else if (await checkLockFileExists("pnpm-lock.yaml")) {
|
||||
pkgManager = "pnpm"
|
||||
installArgs = ["install", "--ignore-scripts"]
|
||||
}
|
||||
|
||||
await new Promise((resolve) => {
|
||||
const installProcess = require("cross-spawn")(pkgManager, installArgs)
|
||||
installProcess.on("exit", resolve)
|
||||
})
|
||||
spinner.stop()
|
||||
|
||||
const recipePackageMain = requireJSON("./package.json").main
|
||||
const recipeEntry = resolve(recipePackageMain)
|
||||
process.chdir(join(process.cwd(), ".."))
|
||||
|
||||
await installRecipeAtPath(recipeEntry, cliArgs, cliFlags)
|
||||
|
||||
require("rimraf").sync(recipeRepoPath)
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
await installRecipeAtPath(resolve(`${args._[1]}`), cliArgs, cliFlags)
|
||||
} catch (err) {
|
||||
if (err instanceof Error) {
|
||||
throw new Error(err.message)
|
||||
}
|
||||
console.log(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export {install}
|
||||
@@ -274,7 +274,7 @@ const newApp: CliCommand = async () => {
|
||||
)
|
||||
}
|
||||
|
||||
postInstallSteps.push(`${projectPkgManger} blitz dev`)
|
||||
postInstallSteps.push(`${projectPkgManger} ${projectPkgManger === "npm" ? "run " : ""}dev`);
|
||||
|
||||
console.log("\n Your new Blitz app is ready! Next steps:")
|
||||
postInstallSteps.forEach((step, index) => {
|
||||
|
||||
@@ -35,7 +35,6 @@ const commands = {
|
||||
generate: () => import("./commands/generate").then((i) => i.generate),
|
||||
codegen: () => import("./commands/codegen").then((i) => i.codegen),
|
||||
db: () => import("./commands/db").then((i) => i.db),
|
||||
install: () => import("./commands/install").then((i) => i.install),
|
||||
console: () => import("./commands/console").then((i) => i.consoleREPL),
|
||||
routes: () => import("./commands/routes").then((i) => i.routes),
|
||||
}
|
||||
@@ -47,7 +46,6 @@ const aliases: Record<string, keyof typeof commands> = {
|
||||
e: "export",
|
||||
n: "new",
|
||||
g: "generate",
|
||||
i: "install",
|
||||
c: "console",
|
||||
r: "routes",
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
registerBlitzErrorClasses,
|
||||
} from "./errors"
|
||||
import type {EventHooks, MiddlewareHooks} from "./types"
|
||||
import React from "react"
|
||||
export {
|
||||
AuthenticationError,
|
||||
AuthorizationError,
|
||||
@@ -26,7 +27,7 @@ export * from "./utils/enhance-prisma"
|
||||
export type BlitzProviderComponentType = <TProps = any>(
|
||||
component: ComponentType<TProps>,
|
||||
) => {
|
||||
(props: TProps): JSX.Element
|
||||
(props: TProps): React.JSX.Element
|
||||
displayName: string
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
import {Text} from "ink"
|
||||
import * as React from "react"
|
||||
import {Newline} from "./newline"
|
||||
|
||||
export const EnterToContinue: React.FC<{message?: string}> = ({
|
||||
message = "Press ENTER to continue",
|
||||
}) => (
|
||||
<>
|
||||
<Newline />
|
||||
<Text bold>{message}</Text>
|
||||
</>
|
||||
)
|
||||
@@ -1,6 +0,0 @@
|
||||
import {Box} from "ink"
|
||||
import * as React from "react"
|
||||
|
||||
export const Newline: React.FC<{count?: number}> = ({count = 1}) => {
|
||||
return <Box paddingBottom={count} />
|
||||
}
|
||||
@@ -1,198 +0,0 @@
|
||||
import {spawn} from "cross-spawn"
|
||||
import * as fs from "fs-extra"
|
||||
import {Box, Text} from "ink"
|
||||
import Spinner from "ink-spinner"
|
||||
import * as path from "path"
|
||||
import * as React from "react"
|
||||
import {Newline} from "../components/newline"
|
||||
import {RecipeCLIArgs} from "../types"
|
||||
import {useEnterToContinue} from "../utils/use-enter-to-continue"
|
||||
import {useUserInput} from "../utils/use-user-input"
|
||||
import {IExecutor, executorArgument, ExecutorConfig, getExecutorArgument} from "./executor"
|
||||
|
||||
interface NpmPackage {
|
||||
name: string
|
||||
// defaults to latest published
|
||||
version?: string
|
||||
// defaults to false
|
||||
isDevDep?: boolean
|
||||
}
|
||||
|
||||
export interface Config extends ExecutorConfig {
|
||||
packages: executorArgument<NpmPackage[]>
|
||||
}
|
||||
|
||||
export function isAddDependencyExecutor(executor: ExecutorConfig): executor is Config {
|
||||
return (executor as Config).packages !== undefined
|
||||
}
|
||||
|
||||
export const type = "add-dependency"
|
||||
|
||||
function Package({pkg, loading}: {pkg: NpmPackage; loading: boolean}) {
|
||||
return (
|
||||
<Text>
|
||||
{` `}
|
||||
{loading ? <Spinner /> : "📦"}
|
||||
{` ${pkg.name}@${pkg.version}`}
|
||||
</Text>
|
||||
)
|
||||
}
|
||||
|
||||
const DependencyList = ({
|
||||
lede = "Hang tight! Installing dependencies...",
|
||||
depsLoading = false,
|
||||
devDepsLoading = false,
|
||||
packages,
|
||||
}: {
|
||||
lede?: string
|
||||
depsLoading?: boolean
|
||||
devDepsLoading?: boolean
|
||||
packages: NpmPackage[]
|
||||
}) => {
|
||||
const prodPackages = packages.filter((p) => !p.isDevDep)
|
||||
const devPackages = packages.filter((p) => p.isDevDep)
|
||||
return (
|
||||
<Box flexDirection="column">
|
||||
<Text>{lede}</Text>
|
||||
<Newline />
|
||||
{prodPackages.length ? <Text>Dependencies to be installed:</Text> : null}
|
||||
{prodPackages.map((pkg) => (
|
||||
<Package key={pkg.name} pkg={pkg} loading={depsLoading} />
|
||||
))}
|
||||
<Newline />
|
||||
{devPackages.length ? <Text>Dev Dependencies to be installed:</Text> : null}
|
||||
{devPackages.map((pkg) => (
|
||||
<Package key={pkg.name} pkg={pkg} loading={devDepsLoading} />
|
||||
))}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Exported for unit testing purposes
|
||||
*/
|
||||
export function getPackageManager() {
|
||||
if (fs.existsSync(path.resolve("yarn.lock"))) {
|
||||
return "yarn"
|
||||
} else if (fs.existsSync(path.resolve("pnpm-lock.yaml"))) {
|
||||
return "pnpm"
|
||||
} else {
|
||||
return "npm"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exported for unit testing purposes
|
||||
*/
|
||||
export async function installPackages(packages: NpmPackage[], isDev = false) {
|
||||
const packageManager = getPackageManager()
|
||||
const isNPM = packageManager === "npm"
|
||||
const pkgInstallArg = isNPM ? "install" : "add"
|
||||
const args: string[] = [pkgInstallArg]
|
||||
|
||||
if (isDev) {
|
||||
args.push(isNPM ? "--save-dev" : "-D")
|
||||
}
|
||||
packages.forEach((pkg) => {
|
||||
pkg.version ? args.push(`${pkg.name}@${pkg.version}`) : args.push(pkg.name)
|
||||
})
|
||||
await new Promise((resolve) => {
|
||||
const cp = spawn(packageManager, args, {
|
||||
stdio: ["inherit", "pipe", "pipe"],
|
||||
})
|
||||
cp.on("exit", resolve)
|
||||
})
|
||||
}
|
||||
|
||||
export const Commit: IExecutor["Commit"] = ({cliArgs, cliFlags, step, onChangeCommitted}) => {
|
||||
const userInput = useUserInput(cliFlags)
|
||||
const [depsInstalled, setDepsInstalled] = React.useState(false)
|
||||
const [devDepsInstalled, setDevDepsInstalled] = React.useState(false)
|
||||
|
||||
const handleChangeCommitted = React.useCallback(() => {
|
||||
const packages = (step as Config).packages
|
||||
const dependencies = packages.length === 1 ? "dependency" : "dependencies"
|
||||
onChangeCommitted(`Installed ${packages.length} ${dependencies}`)
|
||||
}, [onChangeCommitted, step])
|
||||
|
||||
React.useEffect(() => {
|
||||
async function installDeps() {
|
||||
const packagesToInstall = getExecutorArgument((step as Config).packages, cliArgs).filter(
|
||||
(p) => !p.isDevDep,
|
||||
)
|
||||
await installPackages(packagesToInstall)
|
||||
setDepsInstalled(true)
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
installDeps()
|
||||
}, [cliArgs, step])
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!depsInstalled) return
|
||||
async function installDevDeps() {
|
||||
const packagesToInstall = getExecutorArgument((step as Config).packages, cliArgs).filter(
|
||||
(p) => p.isDevDep,
|
||||
)
|
||||
await installPackages(packagesToInstall, true)
|
||||
setDevDepsInstalled(true)
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
installDevDeps()
|
||||
}, [cliArgs, depsInstalled, step])
|
||||
|
||||
React.useEffect(() => {
|
||||
if (depsInstalled && devDepsInstalled) {
|
||||
handleChangeCommitted()
|
||||
}
|
||||
}, [depsInstalled, devDepsInstalled, handleChangeCommitted])
|
||||
|
||||
if (!isAddDependencyExecutor(step)) {
|
||||
onChangeCommitted()
|
||||
return null
|
||||
}
|
||||
|
||||
const childProps: CommitChildProps = {
|
||||
depsInstalled,
|
||||
devDepsInstalled,
|
||||
handleChangeCommitted,
|
||||
step,
|
||||
cliArgs,
|
||||
}
|
||||
|
||||
if (userInput) return <CommitWithInput {...childProps} />
|
||||
else return <CommitWithoutInput {...childProps} />
|
||||
}
|
||||
|
||||
interface CommitChildProps {
|
||||
depsInstalled: boolean
|
||||
devDepsInstalled: boolean
|
||||
handleChangeCommitted: () => void
|
||||
step: Config
|
||||
cliArgs: RecipeCLIArgs
|
||||
}
|
||||
|
||||
const CommitWithInput = ({
|
||||
depsInstalled,
|
||||
devDepsInstalled,
|
||||
handleChangeCommitted,
|
||||
step,
|
||||
cliArgs,
|
||||
}: CommitChildProps) => {
|
||||
useEnterToContinue(handleChangeCommitted, depsInstalled && devDepsInstalled)
|
||||
|
||||
return (
|
||||
<DependencyList
|
||||
depsLoading={!depsInstalled}
|
||||
devDepsLoading={!devDepsInstalled}
|
||||
packages={getExecutorArgument(step.packages, cliArgs)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
const CommitWithoutInput = ({depsInstalled, devDepsInstalled, step, cliArgs}: CommitChildProps) => (
|
||||
<DependencyList
|
||||
depsLoading={!depsInstalled}
|
||||
devDepsLoading={!devDepsInstalled}
|
||||
packages={getExecutorArgument(step.packages, cliArgs)}
|
||||
/>
|
||||
)
|
||||
@@ -1,71 +0,0 @@
|
||||
import {Box, Text} from "ink"
|
||||
import * as React from "react"
|
||||
import {Newline} from "../components/newline"
|
||||
import {RecipeCLIArgs, RecipeCLIFlags} from "../types"
|
||||
|
||||
export interface ExecutorConfig {
|
||||
successIcon?: string
|
||||
stepId: string | number
|
||||
stepName: string
|
||||
stepType: string
|
||||
// a bit to display to the user to give context to the change
|
||||
explanation: string
|
||||
}
|
||||
|
||||
export interface IExecutor {
|
||||
type: string
|
||||
Propose?: React.FC<{
|
||||
step: ExecutorConfig
|
||||
onProposalAccepted: (data?: any) => void
|
||||
cliArgs: RecipeCLIArgs
|
||||
cliFlags: RecipeCLIFlags
|
||||
}>
|
||||
Commit: React.FC<{
|
||||
step: ExecutorConfig
|
||||
proposalData?: any
|
||||
onChangeCommitted: (data?: any) => void
|
||||
cliArgs: RecipeCLIArgs
|
||||
cliFlags: RecipeCLIFlags
|
||||
}>
|
||||
}
|
||||
|
||||
type dynamicExecutorArgument<T> = (cliArgs: RecipeCLIArgs) => T
|
||||
|
||||
function isDynamicExecutorArgument<T>(
|
||||
input: executorArgument<T>,
|
||||
): input is dynamicExecutorArgument<T> {
|
||||
return typeof (input as dynamicExecutorArgument<T>) === "function"
|
||||
}
|
||||
|
||||
export type executorArgument<T> = T | dynamicExecutorArgument<T>
|
||||
|
||||
export function Frontmatter({executor}: {executor: ExecutorConfig}) {
|
||||
const lineLength = executor.stepName.length + 6
|
||||
const verticalBorder = `+${new Array(lineLength).fill("–").join("")}+`
|
||||
return (
|
||||
<Box flexDirection="column" paddingBottom={1}>
|
||||
<Newline />
|
||||
<Box flexDirection="column">
|
||||
<Text color="#8a3df0" bold>
|
||||
{verticalBorder}
|
||||
</Text>
|
||||
<Text color="#8a3df0" bold>
|
||||
⎪ {executor.stepName} ⎪
|
||||
</Text>
|
||||
<Text color="#8a3df0" bold>
|
||||
{verticalBorder}
|
||||
</Text>
|
||||
</Box>
|
||||
<Text color="gray" italic>
|
||||
{executor.explanation}
|
||||
</Text>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export function getExecutorArgument<T>(input: executorArgument<T>, cliArgs: RecipeCLIArgs): T {
|
||||
if (isDynamicExecutorArgument(input)) {
|
||||
return input(cliArgs)
|
||||
}
|
||||
return input
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
// import { prompt as enquirer } from 'enquirer'
|
||||
import prompts from "prompts"
|
||||
|
||||
enum SearchType {
|
||||
file,
|
||||
directory,
|
||||
}
|
||||
|
||||
interface FilePromptOptions {
|
||||
globFilter?: string
|
||||
getChoices?(context: any): string[]
|
||||
searchType?: SearchType
|
||||
context: any
|
||||
}
|
||||
|
||||
async function getMatchingFiles(filter: string = ""): Promise<string[]> {
|
||||
let {globby} = await import("globby")
|
||||
return globby(filter, {expandDirectories: true})
|
||||
}
|
||||
|
||||
export async function filePrompt(options: FilePromptOptions): Promise<string> {
|
||||
const choices = options.getChoices
|
||||
? options.getChoices(options.context)
|
||||
: await getMatchingFiles(options.globFilter)
|
||||
|
||||
if (choices.length === 1) {
|
||||
return `${choices[0]}`
|
||||
}
|
||||
|
||||
const results: {file: string} = await prompts({
|
||||
type: "autocomplete",
|
||||
name: "file",
|
||||
message: "Select the target file",
|
||||
// @ts-ignore
|
||||
limit: 10,
|
||||
choices: choices.map((choice) => {
|
||||
return {title: choice, value: choice}
|
||||
}),
|
||||
})
|
||||
return results.file
|
||||
}
|
||||
@@ -1,183 +0,0 @@
|
||||
import {createPatch} from "diff"
|
||||
import * as fs from "fs-extra"
|
||||
import {Box, Text} from "ink"
|
||||
import Spinner from "ink-spinner"
|
||||
import * as React from "react"
|
||||
import {EnterToContinue} from "../components/enter-to-continue"
|
||||
import {RecipeCLIArgs} from "../types"
|
||||
import {
|
||||
processFile,
|
||||
stringProcessFile,
|
||||
StringTransformer,
|
||||
transform,
|
||||
Transformer,
|
||||
TransformStatus,
|
||||
} from "../utils/transform"
|
||||
import {useEnterToContinue} from "../utils/use-enter-to-continue"
|
||||
import {useUserInput} from "../utils/use-user-input"
|
||||
import {IExecutor, executorArgument, ExecutorConfig, getExecutorArgument} from "./executor"
|
||||
import {filePrompt} from "./file-prompt"
|
||||
|
||||
export interface Config extends ExecutorConfig {
|
||||
selectTargetFiles?(cliArgs: RecipeCLIArgs): any[]
|
||||
singleFileSearch?: executorArgument<string>
|
||||
transform?: Transformer
|
||||
transformPlain?: StringTransformer
|
||||
}
|
||||
|
||||
export function isFileTransformExecutor(executor: ExecutorConfig): executor is Config {
|
||||
return (
|
||||
(executor as Config).transform !== undefined ||
|
||||
(executor as Config).transformPlain !== undefined
|
||||
)
|
||||
}
|
||||
|
||||
export const type = "file-transform"
|
||||
export const Propose: IExecutor["Propose"] = ({cliArgs, cliFlags, onProposalAccepted, step}) => {
|
||||
const userInput = useUserInput(cliFlags)
|
||||
const [diff, setDiff] = React.useState<string | null>(null)
|
||||
const [error, setError] = React.useState<Error | null>(null)
|
||||
const [filePath, setFilePath] = React.useState("")
|
||||
const [proposalAccepted, setProposalAccepted] = React.useState(false)
|
||||
|
||||
const acceptProposal = React.useCallback(() => {
|
||||
setProposalAccepted(true)
|
||||
onProposalAccepted(filePath)
|
||||
}, [onProposalAccepted, filePath])
|
||||
|
||||
React.useEffect(() => {
|
||||
async function generateDiff() {
|
||||
const fileToTransform: string = await filePrompt({
|
||||
context: cliArgs,
|
||||
globFilter: getExecutorArgument((step as Config).singleFileSearch, cliArgs),
|
||||
getChoices: (step as Config).selectTargetFiles,
|
||||
})
|
||||
|
||||
setFilePath(fileToTransform)
|
||||
const originalFile = fs.readFileSync(fileToTransform).toString("utf-8")
|
||||
|
||||
const newFile = await ((step as Config).transformPlain
|
||||
? stringProcessFile(originalFile, (step as Config).transformPlain!)
|
||||
: processFile(originalFile, (step as Config).transform!))
|
||||
|
||||
return createPatch(fileToTransform, originalFile, newFile)
|
||||
}
|
||||
|
||||
generateDiff().then(setDiff, setError)
|
||||
}, [cliArgs, step])
|
||||
|
||||
// Let the renderer deal with errors from file transformers, otherwise the
|
||||
// process would just hang.
|
||||
if (error) throw error
|
||||
|
||||
if (!diff) {
|
||||
return (
|
||||
<Box>
|
||||
<Text>
|
||||
<Spinner />
|
||||
Generating file diff...
|
||||
</Text>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
const childProps: ProposeChildProps = {
|
||||
diff,
|
||||
filePath,
|
||||
proposalAccepted,
|
||||
acceptProposal,
|
||||
}
|
||||
|
||||
if (userInput) return <ProposeWithInput {...childProps} />
|
||||
else return <ProposeWithoutInput {...childProps} />
|
||||
}
|
||||
|
||||
interface ProposeChildProps {
|
||||
diff: string
|
||||
filePath: string
|
||||
proposalAccepted: boolean
|
||||
acceptProposal: () => void
|
||||
}
|
||||
|
||||
const Diff = ({diff}: {diff: string}) => (
|
||||
<>
|
||||
{diff
|
||||
.split("\n")
|
||||
.slice(2)
|
||||
.map((line, idx) => {
|
||||
let styleProps: any = {}
|
||||
if (line.startsWith("-") && !line.startsWith("---")) {
|
||||
styleProps.bold = true
|
||||
styleProps.color = "red"
|
||||
} else if (line.startsWith("+") && !line.startsWith("+++")) {
|
||||
styleProps.bold = true
|
||||
styleProps.color = "green"
|
||||
}
|
||||
return (
|
||||
<Text {...styleProps} key={idx}>
|
||||
{line}
|
||||
</Text>
|
||||
)
|
||||
})}
|
||||
</>
|
||||
)
|
||||
|
||||
const ProposeWithInput = ({
|
||||
diff,
|
||||
filePath,
|
||||
proposalAccepted,
|
||||
acceptProposal,
|
||||
}: ProposeChildProps) => {
|
||||
useEnterToContinue(acceptProposal, filePath !== "" && !proposalAccepted)
|
||||
|
||||
return (
|
||||
<Box flexDirection="column">
|
||||
<Diff diff={diff} />
|
||||
<EnterToContinue message="The above changes will be made. Press ENTER to continue" />
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
const ProposeWithoutInput = ({
|
||||
diff,
|
||||
filePath,
|
||||
proposalAccepted,
|
||||
acceptProposal,
|
||||
}: ProposeChildProps) => {
|
||||
React.useEffect(() => {
|
||||
if (filePath !== "" && !proposalAccepted) {
|
||||
acceptProposal()
|
||||
}
|
||||
}, [acceptProposal, filePath, proposalAccepted])
|
||||
|
||||
return (
|
||||
<Box flexDirection="column">
|
||||
<Diff diff={diff} />
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export const Commit: IExecutor["Commit"] = ({onChangeCommitted, proposalData: filePath, step}) => {
|
||||
React.useEffect(() => {
|
||||
void (async function () {
|
||||
const results = await transform(
|
||||
async (original) =>
|
||||
await ((step as Config).transformPlain
|
||||
? stringProcessFile(original, (step as Config).transformPlain!)
|
||||
: processFile(original, (step as Config).transform!)),
|
||||
[filePath],
|
||||
)
|
||||
if (results.some((r) => r.status === TransformStatus.Failure)) {
|
||||
console.error(results)
|
||||
}
|
||||
onChangeCommitted(`Modified file: ${filePath}`)
|
||||
})()
|
||||
}, [filePath, onChangeCommitted, step])
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Spinner />
|
||||
<Text>Applying file changes</Text>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
import {Generator, GeneratorOptions, SourceRootType} from "@blitzjs/generator"
|
||||
import {Box, Text} from "ink"
|
||||
import {useEffect, useState} from "react"
|
||||
import * as React from "react"
|
||||
import {EnterToContinue} from "../components/enter-to-continue"
|
||||
import {useEnterToContinue} from "../utils/use-enter-to-continue"
|
||||
import {useUserInput} from "../utils/use-user-input"
|
||||
import {IExecutor, executorArgument, ExecutorConfig, getExecutorArgument} from "./executor"
|
||||
|
||||
export interface Config extends ExecutorConfig {
|
||||
targetDirectory?: executorArgument<string>
|
||||
templatePath: executorArgument<string>
|
||||
templateValues: executorArgument<{[key: string]: string}>
|
||||
destinationPathPrompt?: executorArgument<string>
|
||||
}
|
||||
|
||||
export function isNewFileExecutor(executor: ExecutorConfig): executor is Config {
|
||||
return (executor as Config).templatePath !== undefined
|
||||
}
|
||||
|
||||
export const type = "new-file"
|
||||
|
||||
interface TempGeneratorOptions extends GeneratorOptions {
|
||||
targetDirectory?: string
|
||||
templateRoot: string
|
||||
templateValues: any
|
||||
run?: any
|
||||
}
|
||||
|
||||
class TempGenerator extends Generator<TempGeneratorOptions> {
|
||||
sourceRoot: SourceRootType
|
||||
targetDirectory: string
|
||||
templateValues: any
|
||||
returnResults = true
|
||||
|
||||
constructor(options: TempGeneratorOptions) {
|
||||
super(options)
|
||||
this.sourceRoot = {type: "absolute", path: options.templateRoot}
|
||||
this.templateValues = options.templateValues
|
||||
this.targetDirectory = options.targetDirectory || "."
|
||||
}
|
||||
|
||||
getTemplateValues() {
|
||||
return this.templateValues
|
||||
}
|
||||
|
||||
getTargetDirectory() {
|
||||
return this.targetDirectory
|
||||
}
|
||||
}
|
||||
|
||||
export const Commit: IExecutor["Commit"] = ({cliArgs, cliFlags, onChangeCommitted, step}) => {
|
||||
const userInput = useUserInput(cliFlags)
|
||||
const generatorArgs = React.useMemo(
|
||||
() => ({
|
||||
destinationRoot: ".",
|
||||
targetDirectory: getExecutorArgument((step as Config).targetDirectory, cliArgs),
|
||||
templateRoot: getExecutorArgument((step as Config).templatePath, cliArgs),
|
||||
templateValues: getExecutorArgument((step as Config).templateValues, cliArgs),
|
||||
}),
|
||||
[cliArgs, step],
|
||||
)
|
||||
const [fileCreateOutput, setFileCreateOutput] = useState("")
|
||||
const [changeCommited, setChangeCommited] = useState(false)
|
||||
const fileCreateLines = fileCreateOutput.split("\n")
|
||||
const handleChangeCommitted = React.useCallback(() => {
|
||||
setChangeCommited(true)
|
||||
onChangeCommitted(
|
||||
`Successfully created ${fileCreateLines
|
||||
.map((l) => l.split(" ").slice(1).join("").trim())
|
||||
.join(", ")}`,
|
||||
)
|
||||
}, [fileCreateLines, onChangeCommitted])
|
||||
|
||||
useEffect(() => {
|
||||
async function createNewFiles() {
|
||||
if (!fileCreateOutput) {
|
||||
const generator = new TempGenerator(generatorArgs)
|
||||
const results = (await generator.run()) as unknown as string
|
||||
setFileCreateOutput(results)
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
createNewFiles()
|
||||
}, [fileCreateOutput, generatorArgs])
|
||||
|
||||
const childProps: CommitChildProps = {
|
||||
changeCommited,
|
||||
fileCreateOutput,
|
||||
handleChangeCommitted,
|
||||
}
|
||||
|
||||
if (userInput) return <CommitWithInput {...childProps} />
|
||||
else return <CommitWithoutInput {...childProps} />
|
||||
}
|
||||
|
||||
interface CommitChildProps {
|
||||
changeCommited: boolean
|
||||
fileCreateOutput: string
|
||||
handleChangeCommitted: () => void
|
||||
}
|
||||
|
||||
const CommitWithInput = ({
|
||||
changeCommited,
|
||||
fileCreateOutput,
|
||||
handleChangeCommitted,
|
||||
}: CommitChildProps) => {
|
||||
useEnterToContinue(handleChangeCommitted, !changeCommited && fileCreateOutput !== "")
|
||||
|
||||
return (
|
||||
<Box flexDirection="column">
|
||||
{fileCreateOutput !== "" && (
|
||||
<>
|
||||
<Text>{fileCreateOutput}</Text>
|
||||
<EnterToContinue />
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
const CommitWithoutInput = ({
|
||||
changeCommited,
|
||||
fileCreateOutput,
|
||||
handleChangeCommitted,
|
||||
}: CommitChildProps) => {
|
||||
React.useEffect(() => {
|
||||
if (!changeCommited && fileCreateOutput !== "") {
|
||||
handleChangeCommitted()
|
||||
}
|
||||
}, [changeCommited, fileCreateOutput, handleChangeCommitted])
|
||||
|
||||
return (
|
||||
<Box flexDirection="column">{fileCreateOutput !== "" && <Text>{fileCreateOutput}</Text>}</Box>
|
||||
)
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
import {Box, Text} from "ink"
|
||||
import * as React from "react"
|
||||
import {EnterToContinue} from "../components/enter-to-continue"
|
||||
import {useEnterToContinue} from "../utils/use-enter-to-continue"
|
||||
import {useUserInput} from "../utils/use-user-input"
|
||||
import {IExecutor, executorArgument, ExecutorConfig, getExecutorArgument} from "./executor"
|
||||
|
||||
export interface Config extends ExecutorConfig {
|
||||
message: executorArgument<string>
|
||||
}
|
||||
|
||||
export const type = "print-message"
|
||||
|
||||
export const Commit: IExecutor["Commit"] = ({cliArgs, cliFlags, onChangeCommitted, step}) => {
|
||||
const userInput = useUserInput(cliFlags)
|
||||
const generatorArgs = React.useMemo(
|
||||
() => ({
|
||||
message: getExecutorArgument((step as Config).message, cliArgs),
|
||||
stepName: getExecutorArgument((step as Config).stepName, cliArgs),
|
||||
}),
|
||||
[cliArgs, step],
|
||||
)
|
||||
const [changeCommited, setChangeCommited] = React.useState(false)
|
||||
|
||||
const handleChangeCommitted = React.useCallback(() => {
|
||||
setChangeCommited(true)
|
||||
onChangeCommitted(generatorArgs.stepName)
|
||||
}, [onChangeCommitted, generatorArgs])
|
||||
|
||||
const childProps: CommitChildProps = {
|
||||
changeCommited,
|
||||
generatorArgs,
|
||||
handleChangeCommitted,
|
||||
}
|
||||
|
||||
if (userInput) return <CommitWithInput {...childProps} />
|
||||
else return <CommitWithoutInput {...childProps} />
|
||||
}
|
||||
|
||||
interface CommitChildProps {
|
||||
changeCommited: boolean
|
||||
generatorArgs: {message: string; stepName: string}
|
||||
handleChangeCommitted: () => void
|
||||
}
|
||||
|
||||
const CommitWithInput = ({
|
||||
changeCommited,
|
||||
generatorArgs,
|
||||
handleChangeCommitted,
|
||||
}: CommitChildProps) => {
|
||||
useEnterToContinue(handleChangeCommitted, !changeCommited)
|
||||
|
||||
return (
|
||||
<Box flexDirection="column">
|
||||
<Text>{generatorArgs.message}</Text>
|
||||
<EnterToContinue />
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
const CommitWithoutInput = ({
|
||||
changeCommited,
|
||||
generatorArgs,
|
||||
handleChangeCommitted,
|
||||
}: CommitChildProps) => {
|
||||
React.useEffect(() => {
|
||||
if (!changeCommited) {
|
||||
handleChangeCommitted()
|
||||
}
|
||||
}, [changeCommited, handleChangeCommitted])
|
||||
|
||||
return (
|
||||
<Box flexDirection="column">
|
||||
<Text>{generatorArgs.message}</Text>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
import {spawn} from "cross-spawn"
|
||||
import {Box, Text} from "ink"
|
||||
import Spinner from "ink-spinner"
|
||||
import * as React from "react"
|
||||
import {Newline} from "../components/newline"
|
||||
import {RecipeCLIArgs} from "../types"
|
||||
import {useEnterToContinue} from "../utils/use-enter-to-continue"
|
||||
import {useUserInput} from "../utils/use-user-input"
|
||||
import {IExecutor, ExecutorConfig, getExecutorArgument} from "./executor"
|
||||
|
||||
export type CliCommand = string | [string, ...string[]]
|
||||
|
||||
export interface Config extends ExecutorConfig {
|
||||
command: CliCommand
|
||||
}
|
||||
export interface CommitChildProps {
|
||||
commandInstalled: boolean
|
||||
handleChangeCommitted: () => void
|
||||
command: CliCommand
|
||||
cliArgs: RecipeCLIArgs
|
||||
step: Config
|
||||
}
|
||||
|
||||
export const type = "run-command"
|
||||
|
||||
function Command({command, loading}: {command: CliCommand; loading: boolean}) {
|
||||
return (
|
||||
<Text>
|
||||
{` `}
|
||||
{loading ? <Spinner /> : "✅"}
|
||||
{` ${typeof command === "string" ? command : command.join(" ")}`}
|
||||
</Text>
|
||||
)
|
||||
}
|
||||
|
||||
const CommandList = ({
|
||||
lede = "Hang tight! Running...",
|
||||
commandLoading = false,
|
||||
step,
|
||||
command,
|
||||
}: {
|
||||
lede?: string
|
||||
commandLoading?: boolean
|
||||
step: Config
|
||||
command: CliCommand
|
||||
}) => {
|
||||
return (
|
||||
<Box flexDirection="column">
|
||||
<Text>{lede}</Text>
|
||||
<Newline />
|
||||
<Command key={step.stepId} command={command} loading={commandLoading} />
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* INFO: Exported for unit testing purposes
|
||||
*
|
||||
* This function calls the defined command with their optional arguments if defined
|
||||
*
|
||||
* @param {CliCommand} input The Command and arguments
|
||||
* @return Promise<void>
|
||||
*
|
||||
* @example await executeCommand("ls")
|
||||
* @example await executeCommand(["ls"])
|
||||
* @example await executeCommand(["ls", ...["-a", "-l"]])
|
||||
*/
|
||||
export async function executeCommand(input: CliCommand): Promise<void> {
|
||||
// from https://stackoverflow.com/a/43766456/9950655
|
||||
const argsRegex =
|
||||
/("[^"\\]*(?:\\[\S\s][^"\\]*)*"|'[^'\\]*(?:\\[\S\s][^'\\]*)*'|\/[^/\\]*(?:\\[\S\s][^/\\]*)*\/[gimy]*(?=\s|$)|(?:\\\s|\S)+)/g
|
||||
const command: string[] = Array.isArray(input) ? input : input.match(argsRegex) || []
|
||||
|
||||
if (command.length === 0) {
|
||||
throw new Error(`The command is too short: \`${JSON.stringify(input)}\``)
|
||||
}
|
||||
|
||||
await new Promise((resolve) => {
|
||||
const cp = spawn(`${command[0]}`, command.slice(1), {
|
||||
stdio: ["inherit", "pipe", "pipe"],
|
||||
})
|
||||
cp.on("exit", resolve)
|
||||
cp.stdout.on("data", () => {})
|
||||
})
|
||||
}
|
||||
|
||||
export const Commit: IExecutor["Commit"] = ({cliArgs, cliFlags, step, onChangeCommitted}) => {
|
||||
const userInput = useUserInput(cliFlags)
|
||||
const [commandInstalled, setCommandInstalled] = React.useState(false)
|
||||
const executorCommand = getExecutorArgument((step as Config).command, cliArgs)
|
||||
|
||||
const handleChangeCommitted = React.useCallback(() => {
|
||||
onChangeCommitted(`Executed command ${executorCommand}`)
|
||||
}, [executorCommand, onChangeCommitted])
|
||||
|
||||
React.useEffect(() => {
|
||||
async function runCommand() {
|
||||
await executeCommand(executorCommand)
|
||||
setCommandInstalled(true)
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
runCommand()
|
||||
}, [cliArgs, step, executorCommand])
|
||||
|
||||
React.useEffect(() => {
|
||||
if (commandInstalled) {
|
||||
handleChangeCommitted()
|
||||
}
|
||||
}, [commandInstalled, handleChangeCommitted])
|
||||
|
||||
const childProps: CommitChildProps = {
|
||||
commandInstalled,
|
||||
handleChangeCommitted,
|
||||
command: executorCommand,
|
||||
cliArgs,
|
||||
step: step as Config,
|
||||
}
|
||||
|
||||
if (userInput) return <CommitWithInput {...childProps} />
|
||||
else return <CommitWithoutInput {...childProps} />
|
||||
}
|
||||
|
||||
const CommitWithInput = ({
|
||||
commandInstalled,
|
||||
handleChangeCommitted,
|
||||
command,
|
||||
step,
|
||||
}: CommitChildProps) => {
|
||||
useEnterToContinue(handleChangeCommitted, commandInstalled)
|
||||
|
||||
return <CommandList commandLoading={!commandInstalled} step={step} command={command} />
|
||||
}
|
||||
|
||||
const CommitWithoutInput = ({commandInstalled, command, step}: CommitChildProps) => (
|
||||
<CommandList commandLoading={!commandInstalled} step={step} command={command} />
|
||||
)
|
||||
@@ -1,12 +0,0 @@
|
||||
export * from "./recipe-executor"
|
||||
export * from "./recipe-builder"
|
||||
export * from "./executors/executor"
|
||||
export {type as AddDependencyType} from "./executors/add-dependency-executor"
|
||||
export {type as FileTransformType} from "./executors/file-transform-executor"
|
||||
export {type as NewFileType} from "./executors/new-file-executor"
|
||||
export {type as PrintMessageType} from "./executors/print-message-executor"
|
||||
|
||||
export * from "./utils/paths"
|
||||
export * from "./transforms"
|
||||
export {customTsParser} from "./utils/transform"
|
||||
export type {Program, RecipeCLIArgs, RecipeCLIFlags} from "./types"
|
||||
@@ -1,85 +0,0 @@
|
||||
import * as AddDependencyExecutor from "./executors/add-dependency-executor"
|
||||
import * as TransformFileExecutor from "./executors/file-transform-executor"
|
||||
import * as NewFileExecutor from "./executors/new-file-executor"
|
||||
import * as PrintMessageExecutor from "./executors/print-message-executor"
|
||||
import * as RunCommandExecutor from "./executors/run-command-executor"
|
||||
import {ExecutorConfigUnion, RecipeExecutor} from "./recipe-executor"
|
||||
import {RecipeMeta} from "./types"
|
||||
|
||||
export interface IRecipeBuilder {
|
||||
setName(name: string): IRecipeBuilder
|
||||
setDescription(description: string): IRecipeBuilder
|
||||
printMessage(
|
||||
step: Omit<Omit<PrintMessageExecutor.Config, "stepType">, "explanation">,
|
||||
): IRecipeBuilder
|
||||
setOwner(owner: string): IRecipeBuilder
|
||||
setRepoLink(repoLink: string): IRecipeBuilder
|
||||
addAddDependenciesStep(step: Omit<AddDependencyExecutor.Config, "stepType">): IRecipeBuilder
|
||||
addNewFilesStep(step: Omit<NewFileExecutor.Config, "stepType">): IRecipeBuilder
|
||||
addTransformFilesStep(step: Omit<TransformFileExecutor.Config, "stepType">): IRecipeBuilder
|
||||
addRunCommandStep(step: Omit<RunCommandExecutor.Config, "stepType">): IRecipeBuilder
|
||||
|
||||
build(): RecipeExecutor<any>
|
||||
}
|
||||
|
||||
export function RecipeBuilder(): IRecipeBuilder {
|
||||
const steps: ExecutorConfigUnion[] = []
|
||||
const meta: Partial<RecipeMeta> = {}
|
||||
|
||||
return {
|
||||
setName(name: string) {
|
||||
meta.name = name
|
||||
return this
|
||||
},
|
||||
setDescription(description: string) {
|
||||
meta.description = description
|
||||
return this
|
||||
},
|
||||
printMessage(step: Omit<PrintMessageExecutor.Config, "stepType">) {
|
||||
steps.push({
|
||||
stepType: PrintMessageExecutor.type,
|
||||
...step,
|
||||
})
|
||||
return this
|
||||
},
|
||||
setOwner(owner: string) {
|
||||
meta.owner = owner
|
||||
return this
|
||||
},
|
||||
setRepoLink(repoLink: string) {
|
||||
meta.repoLink = repoLink
|
||||
return this
|
||||
},
|
||||
addAddDependenciesStep(step: Omit<AddDependencyExecutor.Config, "stepType">) {
|
||||
steps.push({
|
||||
stepType: AddDependencyExecutor.type,
|
||||
...step,
|
||||
})
|
||||
return this
|
||||
},
|
||||
addNewFilesStep(step: Omit<NewFileExecutor.Config, "stepType">) {
|
||||
steps.push({
|
||||
stepType: NewFileExecutor.type,
|
||||
...step,
|
||||
})
|
||||
return this
|
||||
},
|
||||
addTransformFilesStep(step: Omit<TransformFileExecutor.Config, "stepType">) {
|
||||
steps.push({
|
||||
stepType: TransformFileExecutor.type,
|
||||
...step,
|
||||
})
|
||||
return this
|
||||
},
|
||||
addRunCommandStep(step: Omit<RunCommandExecutor.Config, "stepType">) {
|
||||
steps.push({
|
||||
stepType: RunCommandExecutor.type,
|
||||
...step,
|
||||
})
|
||||
return this
|
||||
},
|
||||
build() {
|
||||
return new RecipeExecutor(meta as RecipeMeta, steps)
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
import {render} from "ink"
|
||||
import {baseLogger} from "../logging"
|
||||
import React from "react"
|
||||
import * as AddDependencyExecutor from "./executors/add-dependency-executor"
|
||||
import * as FileTransformExecutor from "./executors/file-transform-executor"
|
||||
import * as NewFileExecutor from "./executors/new-file-executor"
|
||||
import * as PrintMessageExecutor from "./executors/print-message-executor"
|
||||
import {RecipeRenderer} from "./recipe-renderer"
|
||||
import {RecipeCLIArgs, RecipeCLIFlags, RecipeMeta} from "./types"
|
||||
// const debug = require('debug')("blitz:installer")
|
||||
|
||||
type ExecutorConfig =
|
||||
| AddDependencyExecutor.Config
|
||||
| FileTransformExecutor.Config
|
||||
| NewFileExecutor.Config
|
||||
| PrintMessageExecutor.Config
|
||||
|
||||
export type {ExecutorConfig as ExecutorConfigUnion}
|
||||
|
||||
export class RecipeExecutor<Options extends RecipeMeta> {
|
||||
private readonly steps: ExecutorConfig[]
|
||||
private readonly options: Options
|
||||
|
||||
constructor(options: Options, steps: ExecutorConfig[]) {
|
||||
this.options = options
|
||||
this.steps = steps
|
||||
}
|
||||
|
||||
async run(
|
||||
cliArgs: RecipeCLIArgs = {},
|
||||
cliFlags: RecipeCLIFlags = {yesToAll: false},
|
||||
): Promise<void> {
|
||||
try {
|
||||
const {waitUntilExit} = render(
|
||||
<RecipeRenderer
|
||||
cliArgs={cliArgs}
|
||||
cliFlags={cliFlags}
|
||||
steps={this.steps}
|
||||
recipeMeta={this.options}
|
||||
/>,
|
||||
{exitOnCtrlC: false},
|
||||
)
|
||||
await waitUntilExit()
|
||||
baseLogger().info(`\n🎉 The ${this.options.name} recipe has been installed!\n`)
|
||||
} catch (e) {
|
||||
baseLogger().error(e as any)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,310 +0,0 @@
|
||||
import {Box, Text, useApp, useInput} from "ink"
|
||||
import React from "react"
|
||||
import {assert} from "../index-server"
|
||||
import {EnterToContinue} from "./components/enter-to-continue"
|
||||
import {Newline} from "./components/newline"
|
||||
import * as AddDependencyExecutor from "./executors/add-dependency-executor"
|
||||
import {ExecutorConfig, Frontmatter, IExecutor} from "./executors/executor"
|
||||
import * as FileTransformExecutor from "./executors/file-transform-executor"
|
||||
import * as NewFileExecutor from "./executors/new-file-executor"
|
||||
import * as PrintMessageExecutor from "./executors/print-message-executor"
|
||||
import * as RunCommandExecutor from "./executors/run-command-executor"
|
||||
import {RecipeCLIArgs, RecipeCLIFlags, RecipeMeta} from "./types"
|
||||
import {useEnterToContinue} from "./utils/use-enter-to-continue"
|
||||
import {useUserInput} from "./utils/use-user-input"
|
||||
|
||||
enum Action {
|
||||
SkipStep,
|
||||
ProposeChange,
|
||||
ApplyChange,
|
||||
CommitApproved,
|
||||
CompleteChange,
|
||||
}
|
||||
|
||||
enum Status {
|
||||
Pending,
|
||||
Proposed,
|
||||
ReadyToCommit,
|
||||
Committing,
|
||||
Committed,
|
||||
}
|
||||
|
||||
const ExecutorMap: {[key: string]: IExecutor} = {
|
||||
[AddDependencyExecutor.type]: AddDependencyExecutor,
|
||||
[NewFileExecutor.type]: NewFileExecutor,
|
||||
[PrintMessageExecutor.type]: PrintMessageExecutor,
|
||||
[FileTransformExecutor.type]: FileTransformExecutor,
|
||||
[RunCommandExecutor.type]: RunCommandExecutor,
|
||||
} as const
|
||||
|
||||
interface State {
|
||||
steps: {
|
||||
executor: ExecutorConfig
|
||||
status: Status
|
||||
proposalData?: any
|
||||
successMsg: string
|
||||
}[]
|
||||
current: number
|
||||
}
|
||||
|
||||
function recipeReducer(state: State, action: {type: Action; data?: any}) {
|
||||
const newState = {...state}
|
||||
const step = newState.steps[newState.current]
|
||||
|
||||
switch (action.type) {
|
||||
case Action.ProposeChange:
|
||||
assert(step, "Step is empty in recipeReducer function")
|
||||
step.status = Status.Proposed
|
||||
break
|
||||
case Action.CommitApproved:
|
||||
assert(step, "Step is empty in recipeReducer function")
|
||||
step.status = Status.ReadyToCommit
|
||||
step.proposalData = action.data
|
||||
break
|
||||
case Action.ApplyChange:
|
||||
assert(step, "Step is empty in recipeReducer function")
|
||||
step.status = Status.Committing
|
||||
break
|
||||
case Action.CompleteChange:
|
||||
assert(step, "Step is empty in recipeReducer function")
|
||||
step.status = Status.Committed
|
||||
step.successMsg = action.data as string
|
||||
newState.current = Math.min(newState.current + 1, newState.steps.length - 1)
|
||||
break
|
||||
case Action.SkipStep:
|
||||
newState.current += 1
|
||||
break
|
||||
}
|
||||
|
||||
return newState
|
||||
}
|
||||
|
||||
interface RecipeProps {
|
||||
cliArgs: RecipeCLIArgs
|
||||
cliFlags: RecipeCLIFlags
|
||||
steps: ExecutorConfig[]
|
||||
recipeMeta: RecipeMeta
|
||||
}
|
||||
|
||||
const DispatchContext = React.createContext<React.Dispatch<{type: Action; data?: any}>>(() => {})
|
||||
|
||||
function WelcomeMessage({
|
||||
recipeMeta,
|
||||
enterToContinue = true,
|
||||
}: {
|
||||
recipeMeta: RecipeMeta
|
||||
enterToContinue?: boolean
|
||||
}) {
|
||||
return (
|
||||
<Box flexDirection="column">
|
||||
<Text color="#8a3df0" bold>
|
||||
Recipe: {recipeMeta.name}
|
||||
</Text>
|
||||
<Newline />
|
||||
<Text color="gray">
|
||||
<Text italic>{recipeMeta.description}</Text>
|
||||
</Text>
|
||||
<Newline />
|
||||
<Text color="gray">
|
||||
Repo: <Text italic>{recipeMeta.repoLink}</Text>
|
||||
</Text>
|
||||
<Text color="gray">
|
||||
Author: <Text italic>{recipeMeta.owner}</Text>
|
||||
</Text>
|
||||
{enterToContinue && <EnterToContinue />}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
function StepMessages({state}: {state: State}) {
|
||||
const messages = state.steps
|
||||
.map((step) => ({
|
||||
msg: step.successMsg,
|
||||
icon: step.executor.successIcon ?? "✅",
|
||||
}))
|
||||
.filter((s) => s.msg)
|
||||
|
||||
return (
|
||||
<>
|
||||
{messages.map(({msg, icon}, index) => (
|
||||
<Text key={msg + index} color="green">
|
||||
{msg === "\n" ? "" : icon} {msg}
|
||||
</Text>
|
||||
))}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function StepExecutor({
|
||||
cliArgs,
|
||||
cliFlags,
|
||||
proposalData,
|
||||
step,
|
||||
status,
|
||||
}: {
|
||||
step: ExecutorConfig
|
||||
status: Status
|
||||
cliArgs: RecipeCLIArgs
|
||||
cliFlags: RecipeCLIFlags
|
||||
proposalData?: any
|
||||
}) {
|
||||
const executor = ExecutorMap[step.stepType]
|
||||
assert(executor, `Executor not found for ${step.stepType}`)
|
||||
const {Propose, Commit}: IExecutor = executor
|
||||
const dispatch = React.useContext(DispatchContext)
|
||||
|
||||
const handleProposalAccepted = React.useCallback(
|
||||
(msg: any) => {
|
||||
dispatch({type: Action.CommitApproved, data: msg})
|
||||
},
|
||||
[dispatch],
|
||||
)
|
||||
const handleChangeCommitted = React.useCallback(
|
||||
(msg: any) => {
|
||||
dispatch({type: Action.CompleteChange, data: msg})
|
||||
},
|
||||
[dispatch],
|
||||
)
|
||||
|
||||
React.useEffect(() => {
|
||||
if (status === Status.Pending) {
|
||||
dispatch({type: Action.ProposeChange})
|
||||
} else if (status === Status.ReadyToCommit) {
|
||||
dispatch({type: Action.ApplyChange})
|
||||
}
|
||||
if (status === Status.Proposed && !Propose) {
|
||||
dispatch({type: Action.CommitApproved})
|
||||
}
|
||||
}, [dispatch, status, Propose])
|
||||
|
||||
return (
|
||||
<Box flexDirection="column">
|
||||
{status !== Status.Committed ? <Frontmatter executor={step} /> : null}
|
||||
{[Status.Proposed].includes(status) && Propose ? (
|
||||
<Propose
|
||||
cliArgs={cliArgs}
|
||||
cliFlags={cliFlags}
|
||||
step={step}
|
||||
onProposalAccepted={handleProposalAccepted}
|
||||
/>
|
||||
) : null}
|
||||
{[Status.Committing].includes(status) ? (
|
||||
<Commit
|
||||
cliArgs={cliArgs}
|
||||
cliFlags={cliFlags}
|
||||
proposalData={proposalData}
|
||||
step={step}
|
||||
onChangeCommitted={handleChangeCommitted}
|
||||
/>
|
||||
) : null}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export function RecipeRenderer({cliArgs, cliFlags, steps, recipeMeta}: RecipeProps) {
|
||||
const userInput = useUserInput(cliFlags)
|
||||
const {exit} = useApp()
|
||||
const mappedSteps = steps.map((e) => ({
|
||||
executor: e,
|
||||
status: Status.Pending,
|
||||
successMsg: "",
|
||||
}))
|
||||
|
||||
if (steps.length === 0) {
|
||||
exit(new Error("This recipe has no steps"))
|
||||
}
|
||||
|
||||
const [state, dispatch] = React.useReducer(recipeReducer, {
|
||||
current: userInput ? -1 : 0,
|
||||
steps: mappedSteps,
|
||||
})
|
||||
|
||||
React.useEffect(() => {
|
||||
if (
|
||||
state.current === state.steps.length - 1 &&
|
||||
state.steps[state.current]?.status === Status.Committed
|
||||
) {
|
||||
exit()
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<DispatchContext.Provider value={dispatch}>
|
||||
{userInput ? (
|
||||
<RecipeRendererWithInput
|
||||
cliArgs={cliArgs}
|
||||
cliFlags={cliFlags}
|
||||
state={state}
|
||||
recipeMeta={recipeMeta}
|
||||
/>
|
||||
) : (
|
||||
<RecipeRendererWithoutInput
|
||||
cliArgs={cliArgs}
|
||||
cliFlags={cliFlags}
|
||||
state={state}
|
||||
recipeMeta={recipeMeta}
|
||||
/>
|
||||
)}
|
||||
</DispatchContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
function RecipeRendererWithInput({
|
||||
cliArgs,
|
||||
cliFlags,
|
||||
recipeMeta,
|
||||
state,
|
||||
}: Omit<RecipeProps, "steps"> & {state: State}) {
|
||||
const {exit} = useApp()
|
||||
const dispatch = React.useContext(DispatchContext)
|
||||
const step = state.steps[state.current]
|
||||
|
||||
useInput((input, key) => {
|
||||
if (input === "c" && key.ctrl) {
|
||||
exit(new Error("You aborted installation"))
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
useEnterToContinue(() => dispatch({type: Action.SkipStep}), state.current === -1)
|
||||
|
||||
return (
|
||||
<>
|
||||
<StepMessages state={state} />
|
||||
{state.current === -1 ? (
|
||||
<WelcomeMessage recipeMeta={recipeMeta} />
|
||||
) : step ? (
|
||||
<StepExecutor
|
||||
cliArgs={cliArgs}
|
||||
cliFlags={cliFlags}
|
||||
proposalData={state.steps[state.current]?.proposalData}
|
||||
step={step.executor}
|
||||
status={step.status}
|
||||
/>
|
||||
) : (
|
||||
new Error("Step not found in RecipeRendererWithInput")
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function RecipeRendererWithoutInput({
|
||||
cliArgs,
|
||||
cliFlags,
|
||||
recipeMeta,
|
||||
state,
|
||||
}: Omit<RecipeProps, "steps"> & {state: State}) {
|
||||
return (
|
||||
<>
|
||||
<WelcomeMessage recipeMeta={recipeMeta} enterToContinue={false} />
|
||||
<StepMessages state={state} />
|
||||
<StepExecutor
|
||||
cliArgs={cliArgs}
|
||||
cliFlags={cliFlags}
|
||||
proposalData={state.steps[state.current]?.proposalData}
|
||||
step={state.steps[state.current]?.executor as ExecutorConfig}
|
||||
status={state.steps[state.current]?.status as Status}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
import type {ExpressionKind} from "ast-types/gen/kinds"
|
||||
import j from "jscodeshift"
|
||||
import {Program} from "../types"
|
||||
import {addImport} from "./add-import"
|
||||
|
||||
export const addBlitzMiddleware = (program: Program, middleware: ExpressionKind): Program => {
|
||||
const pluginArray = program.find(j.Identifier, (node) => node.name === "plugins")
|
||||
|
||||
pluginArray.get().parentPath.value.value.elements = [
|
||||
...pluginArray.get().parentPath.value.value.elements,
|
||||
j.template.expression`BlitzServerMiddleware(${middleware})`,
|
||||
]
|
||||
const blitzServerMiddleWare = j.importDeclaration(
|
||||
[j.importSpecifier(j.identifier("BlitzServerMiddleware"))],
|
||||
j.literal("blitz"),
|
||||
)
|
||||
addImport(program, blitzServerMiddleWare)
|
||||
|
||||
return program
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import j from "jscodeshift"
|
||||
import {Program} from "../types"
|
||||
|
||||
export function addImport(program: Program, importToAdd: j.ImportDeclaration): Program {
|
||||
const importStatementCount = program.find(j.ImportDeclaration).length
|
||||
if (importStatementCount === 0) {
|
||||
program.find(j.Statement).at(0).insertBefore(importToAdd)
|
||||
return program
|
||||
}
|
||||
program.find<j.ImportDeclaration>(j.ImportDeclaration).forEach((stmt, idx) => {
|
||||
const node = stmt.node
|
||||
if (idx === importStatementCount - 1) {
|
||||
stmt.replace(node, importToAdd)
|
||||
}
|
||||
})
|
||||
return program
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import j from "jscodeshift"
|
||||
import {Program} from "../types"
|
||||
|
||||
export const findModuleExportsExpressions = (program: Program) =>
|
||||
program.find<j.AssignmentExpression>(j.AssignmentExpression).filter((path) => {
|
||||
const {left, right} = path.value
|
||||
|
||||
return (
|
||||
left.type === "MemberExpression" &&
|
||||
left.object.type === "Identifier" &&
|
||||
left.property.type === "Identifier" &&
|
||||
left.property.name === "exports" &&
|
||||
right.type === "ObjectExpression"
|
||||
)
|
||||
})
|
||||
@@ -1,9 +0,0 @@
|
||||
export * from "./add-import"
|
||||
export * from "./add-blitz-middleware"
|
||||
export * from "./find-module-exports-expressions"
|
||||
export * from "./prisma"
|
||||
export * from "./transform-next-config"
|
||||
export * from "./with-utilities"
|
||||
export * from "./wrap-blitz-config"
|
||||
export * from "./update-babel-config"
|
||||
export * from "./wrap-app-return-statement"
|
||||
@@ -1,27 +0,0 @@
|
||||
import {Enum} from "@mrleebo/prisma-ast"
|
||||
import {produceSchema} from "./produce-schema"
|
||||
|
||||
/**
|
||||
* Adds an enum to your schema.prisma data model.
|
||||
*
|
||||
* @param source - schema.prisma source file contents
|
||||
* @param enumProps - the enum to add
|
||||
* @returns The modified schema.prisma source
|
||||
* @example Usage
|
||||
* ```
|
||||
* addPrismaEnum(source, {
|
||||
type: "enum",
|
||||
name: "Role",
|
||||
enumerators: [
|
||||
{type: "enumerator", name: "USER"},
|
||||
{type: "enumerator", name: "ADMIN"},
|
||||
],
|
||||
})
|
||||
* ```
|
||||
*/
|
||||
export function addPrismaEnum(source: string, enumProps: Enum): Promise<string> {
|
||||
return produceSchema(source, (schema) => {
|
||||
const existing = schema.list.find((x) => x.type === "enum" && x.name === enumProps.name)
|
||||
existing ? Object.assign(existing, enumProps) : schema.list.push(enumProps)
|
||||
})
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
import {Field, Model} from "@mrleebo/prisma-ast"
|
||||
import {produceSchema} from "./produce-schema"
|
||||
|
||||
/**
|
||||
* Adds a field to a model in your schema.prisma data model.
|
||||
*
|
||||
* @param source - schema.prisma source file contents
|
||||
* @param modelName - name of the model to add a field to
|
||||
* @param fieldProps - the field to add
|
||||
* @returns The modified schema.prisma source
|
||||
* @example Usage
|
||||
* ```
|
||||
* addPrismaField(source, "Project", {
|
||||
type: "field",
|
||||
name: "name",
|
||||
fieldType: "String",
|
||||
optional: false,
|
||||
attributes: [{type: "attribute", kind: "field", name: "unique"}],
|
||||
})
|
||||
* ```
|
||||
*/
|
||||
export function addPrismaField(
|
||||
source: string,
|
||||
modelName: string,
|
||||
fieldProps: Field,
|
||||
): Promise<string> {
|
||||
return produceSchema(source, (schema) => {
|
||||
const model = schema.list.find((x) => x.type === "model" && x.name === modelName) as Model
|
||||
if (!model) return
|
||||
|
||||
const existing = model.properties.find((x) => x.type === "field" && x.name === fieldProps.name)
|
||||
existing ? Object.assign(existing, fieldProps) : model.properties.push(fieldProps)
|
||||
})
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
import {Generator} from "@mrleebo/prisma-ast"
|
||||
import {produceSchema} from "./produce-schema"
|
||||
|
||||
/**
|
||||
* Adds a generator to your schema.prisma data model.
|
||||
*
|
||||
* @param source - schema.prisma source file contents
|
||||
* @param generatorProps - the generator to add
|
||||
* @returns The modified schema.prisma source
|
||||
* @example Usage
|
||||
* ```
|
||||
* addPrismaGenerator(source, {
|
||||
type: "generator",
|
||||
name: "nexusPrisma",
|
||||
assignments: [{type: "assignment", key: "provider", value: '"nexus-prisma"'}],
|
||||
})
|
||||
* ```
|
||||
*/
|
||||
export function addPrismaGenerator(source: string, generatorProps: Generator): Promise<string> {
|
||||
return produceSchema(source, (schema) => {
|
||||
const existing = schema.list.find(
|
||||
(x) => x.type === "generator" && x.name === generatorProps.name,
|
||||
) as Generator
|
||||
existing ? Object.assign(existing, generatorProps) : schema.list.push(generatorProps)
|
||||
})
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
import {Model, ModelAttribute} from "@mrleebo/prisma-ast"
|
||||
import {produceSchema} from "./produce-schema"
|
||||
|
||||
/**
|
||||
* Adds a field to a model in your schema.prisma data model.
|
||||
*
|
||||
* @remarks Not ready for actual use
|
||||
* @param source - schema.prisma source file contents
|
||||
* @param modelName - name of the model to add a field to
|
||||
* @param attributeProps - the model attribute (such as an index) to add
|
||||
* @returns The modified schema.prisma source
|
||||
* @example Usage
|
||||
* ```
|
||||
* addPrismaModelAttribute(source, "Project", {
|
||||
* type: "attribute",
|
||||
* kind: "model",
|
||||
* name: "index",
|
||||
* args: [{ type: "attributeArgument", value: { type: "array", args: ["name"] } }]
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
export function addPrismaModelAttribute(
|
||||
source: string,
|
||||
modelName: string,
|
||||
attributeProps: ModelAttribute,
|
||||
): Promise<string> {
|
||||
return produceSchema(source, (schema) => {
|
||||
const model = schema.list.find((x) => x.type === "model" && x.name === modelName) as Model
|
||||
if (!model) return
|
||||
|
||||
const existing = model.properties.find(
|
||||
(x) => x.type === "attribute" && x.name === attributeProps.name,
|
||||
)
|
||||
|
||||
existing ? Object.assign(existing, attributeProps) : model.properties.push(attributeProps)
|
||||
})
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
import {Model} from "@mrleebo/prisma-ast"
|
||||
import {produceSchema} from "./produce-schema"
|
||||
|
||||
/**
|
||||
* Adds an enum to your schema.prisma data model.
|
||||
*
|
||||
* @param source - schema.prisma source file contents
|
||||
* @param modelProps - the model to add
|
||||
* @returns The modified schema.prisma source
|
||||
* @example Usage
|
||||
* ```
|
||||
* addPrismaModel(source, {
|
||||
type: "model",
|
||||
name: "Project",
|
||||
properties: [{type: "field", name: "id", fieldType: "String"}],
|
||||
})
|
||||
* ```
|
||||
*/
|
||||
export function addPrismaModel(source: string, modelProps: Model): Promise<string> {
|
||||
return produceSchema(source, (schema) => {
|
||||
const existing = schema.list.find((x) => x.type === "model" && x.name === modelProps.name)
|
||||
existing ? Object.assign(existing, modelProps) : schema.list.push(modelProps)
|
||||
})
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
export * from "./add-prisma-enum"
|
||||
export * from "./add-prisma-field"
|
||||
export * from "./add-prisma-generator"
|
||||
export * from "./add-prisma-model-attribute"
|
||||
export * from "./add-prisma-model"
|
||||
export * from "./produce-schema"
|
||||
export * from "./set-prisma-data-source"
|
||||
@@ -1,14 +0,0 @@
|
||||
import {printSchema as printer, Schema} from "@mrleebo/prisma-ast"
|
||||
|
||||
/**
|
||||
* Takes the schema.prisma document parsed from @mrleebo/prisma-ast and
|
||||
* serializes it back to a schema.prisma source string. To ensure consistent
|
||||
* formatting and prettify the document, we also execute the
|
||||
* IntrospectionEngine from @prisma/sdk.
|
||||
*
|
||||
* @param schema - the parsed prisma schema
|
||||
* @returns the schema.prisma source string
|
||||
*/
|
||||
export function printSchema(schema: Schema): string {
|
||||
return printer(schema)
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
import {getSchema, printSchema, Schema} from "@mrleebo/prisma-ast"
|
||||
|
||||
/**
|
||||
* A file transformer that parses a schema.prisma string, offers you a callback
|
||||
* of the parsed document object, then takes your changes to the document and
|
||||
* writes out a new schema.prisma string with the changes applied.
|
||||
*
|
||||
* @param source - schema.prisma source file contents
|
||||
* @param producer - a callback function that can mutate the parsed data model
|
||||
* @returns The modified schema.prisma source
|
||||
*/
|
||||
export async function produceSchema(
|
||||
source: string,
|
||||
producer: (schema: Schema) => void,
|
||||
): Promise<string> {
|
||||
const schema = await getSchema(source)
|
||||
producer(schema)
|
||||
return printSchema(schema)
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
import {Datasource} from "@mrleebo/prisma-ast"
|
||||
import {produceSchema} from "./produce-schema"
|
||||
|
||||
/**
|
||||
* Modify the prisma datasource metadata to use the provider and url specified.
|
||||
*
|
||||
* @param source - schema.prisma source file contents
|
||||
* @param datasourceProps - datasource object to assign to the schema
|
||||
* @returns The modified schema.prisma source
|
||||
* @example Usage
|
||||
* ```
|
||||
* 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"']},
|
||||
},
|
||||
],
|
||||
})
|
||||
* ```
|
||||
*/
|
||||
export function setPrismaDataSource(source: string, datasourceProps: Datasource): Promise<string> {
|
||||
return produceSchema(source, (schema) => {
|
||||
const existing = schema.list.find((x) => x.type === "datasource")
|
||||
existing ? Object.assign(existing, datasourceProps) : schema.list.push(datasourceProps)
|
||||
})
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
import type {ExpressionKind} from "ast-types/gen/kinds"
|
||||
import j from "jscodeshift"
|
||||
import {Program} from "../types"
|
||||
|
||||
function recursiveConfigSearch(
|
||||
program: Program,
|
||||
obj: ExpressionKind,
|
||||
): j.ObjectExpression | undefined {
|
||||
// Identifier being a variable name
|
||||
if (obj.type === "Identifier") {
|
||||
const {node} = j(obj).get()
|
||||
|
||||
// Get the definition of the variable
|
||||
const identifier: j.ASTPath<j.VariableDeclarator> = program
|
||||
.find(j.VariableDeclarator, {
|
||||
id: {name: node.name},
|
||||
})
|
||||
.get()
|
||||
|
||||
// Return what is after the `=`
|
||||
return identifier.value.init ? recursiveConfigSearch(program, identifier.value.init) : undefined
|
||||
} else if (obj.type === "CallExpression") {
|
||||
// If it's an function call (like `withBundleAnalyzer`), get the first argument
|
||||
if (obj.arguments.length === 0) {
|
||||
// If it has no arguments, create an empty object: `{}`
|
||||
let config = j.objectExpression([])
|
||||
obj.arguments.push(config)
|
||||
return config
|
||||
} else {
|
||||
const arg = obj.arguments[0]
|
||||
if (arg) {
|
||||
if (arg.type === "SpreadElement") return undefined
|
||||
else return recursiveConfigSearch(program, arg)
|
||||
}
|
||||
}
|
||||
} else if (obj.type === "ObjectExpression") {
|
||||
// If it's an object, return it
|
||||
return obj
|
||||
} else {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
export type TransformBlitzConfigCallback = (config: j.ObjectExpression) => j.ObjectExpression
|
||||
|
||||
export function transformBlitzConfig(
|
||||
program: Program,
|
||||
transform: TransformBlitzConfigCallback,
|
||||
): Program {
|
||||
let moduleExportsExpressions = program.find(j.AssignmentExpression, {
|
||||
operator: "=",
|
||||
left: {object: {name: "module"}, property: {name: "exports"}},
|
||||
right: {},
|
||||
})
|
||||
|
||||
// If there isn't any `module.exports = ...`, create one
|
||||
if (moduleExportsExpressions.length === 0) {
|
||||
let config = j.objectExpression([])
|
||||
|
||||
config = transform(config)
|
||||
|
||||
let moduleExportExpression = j.expressionStatement(
|
||||
j.assignmentExpression(
|
||||
"=",
|
||||
j.memberExpression(j.identifier("module"), j.identifier("exports")),
|
||||
config,
|
||||
),
|
||||
)
|
||||
|
||||
program.get().node.program.body.push(moduleExportExpression)
|
||||
} else if (moduleExportsExpressions.length === 1) {
|
||||
let moduleExportsExpression: j.ASTPath<j.AssignmentExpression> = moduleExportsExpressions.get()
|
||||
|
||||
let config: j.ObjectExpression | undefined = recursiveConfigSearch(
|
||||
program,
|
||||
moduleExportsExpression.value.right,
|
||||
)
|
||||
|
||||
if (config) {
|
||||
config = transform(config)
|
||||
} else {
|
||||
console.warn(
|
||||
"The configuration couldn't be found, but there is a 'module.exports' inside `blitz.config.js`",
|
||||
)
|
||||
}
|
||||
} else {
|
||||
console.warn("There are multiple 'module.exports' inside 'blitz.config.js'")
|
||||
}
|
||||
|
||||
return program
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
import j from "jscodeshift"
|
||||
import {assert} from "../../index-server"
|
||||
import {Program} from "../types"
|
||||
|
||||
export function transformNextConfig(program: Program): {
|
||||
program: Program
|
||||
configObj: []
|
||||
pushToConfig: (property: j.ObjectProperty) => void
|
||||
wrapConfig: (func: string | j.CallExpression) => {
|
||||
withBlitz: j.Identifier | j.CallExpression
|
||||
}
|
||||
addRequireStatement: (identifier: string, packageName: string) => void
|
||||
} {
|
||||
const configObj = program
|
||||
.find<j.VariableDeclarator>(j.VariableDeclarator, (filter) => filter.id.name === "config")
|
||||
.get().parentPath.value[0].init.properties
|
||||
|
||||
assert(configObj, "Config object not found")
|
||||
|
||||
const pushToConfig = (property: j.ObjectProperty) => {
|
||||
configObj.push(property)
|
||||
}
|
||||
|
||||
const wrapConfig = (
|
||||
func: string | j.CallExpression,
|
||||
): {
|
||||
withBlitz: j.Identifier | j.CallExpression
|
||||
} => {
|
||||
const withBlitz = program
|
||||
.find(j.CallExpression, (filter) => filter.callee.name === "withBlitz")
|
||||
.get().value.arguments
|
||||
|
||||
assert(withBlitz, "withBlitz wrapper not found")
|
||||
|
||||
if (typeof func === "string") {
|
||||
withBlitz.push(j.template.expression`${func}(${withBlitz})`)
|
||||
withBlitz.splice(0, 1)
|
||||
} else {
|
||||
withBlitz.push(func)
|
||||
withBlitz.splice(0, 1)
|
||||
}
|
||||
|
||||
return {
|
||||
withBlitz,
|
||||
}
|
||||
}
|
||||
|
||||
const addRequireStatement = (identifier: string, packageName: string) => {
|
||||
program
|
||||
.get()
|
||||
.value.program.body.unshift(
|
||||
j.expressionStatement(
|
||||
j.assignmentExpression(
|
||||
"=",
|
||||
j.identifier(identifier),
|
||||
j.callExpression(j.identifier("require"), [j.identifier(`"${packageName}"`)]),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
return {
|
||||
program,
|
||||
configObj,
|
||||
pushToConfig,
|
||||
wrapConfig,
|
||||
addRequireStatement,
|
||||
}
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
import type {ExpressionKind} from "ast-types/gen/kinds"
|
||||
import {namedTypes} from "ast-types"
|
||||
import j, {ASTPath} from "jscodeshift"
|
||||
import {JsonObject, JsonValue} from "../types"
|
||||
import {Program} from "../types"
|
||||
import {findModuleExportsExpressions} from "./find-module-exports-expressions"
|
||||
|
||||
type AddBabelItemDefinition = string | [name: string, options: JsonObject]
|
||||
|
||||
const jsonValueToExpression = (value: JsonValue): ExpressionKind =>
|
||||
typeof value === "string"
|
||||
? j.stringLiteral(value)
|
||||
: typeof value === "number"
|
||||
? j.numericLiteral(value)
|
||||
: typeof value === "boolean"
|
||||
? j.booleanLiteral(value)
|
||||
: value === null
|
||||
? j.nullLiteral()
|
||||
: Array.isArray(value)
|
||||
? j.arrayExpression(value.map(jsonValueToExpression))
|
||||
: j.objectExpression(
|
||||
Object.entries(value)
|
||||
.filter((entry): entry is [string, JsonValue] => entry[1] !== undefined)
|
||||
.map(([key, value]) =>
|
||||
j.objectProperty(j.stringLiteral(key), jsonValueToExpression(value)),
|
||||
),
|
||||
)
|
||||
|
||||
function updateBabelConfig(program: Program, item: AddBabelItemDefinition, key: string): Program {
|
||||
findModuleExportsExpressions(program).forEach((moduleExportsExpression) => {
|
||||
const foundExpression: Program = j(moduleExportsExpression)
|
||||
foundExpression
|
||||
.find<j.ObjectProperty>(j.ObjectProperty, {key: {name: key}})
|
||||
.forEach((items) => {
|
||||
// Don't add it again if it already exists,
|
||||
// that what this code does. For simplicity,
|
||||
// all the examples will be with key = 'presets'
|
||||
|
||||
const itemName = Array.isArray(item) ? item[0] : item
|
||||
|
||||
if (items.node.value.type === "Literal" || items.node.value.type === "StringLiteral") {
|
||||
// {
|
||||
// presets: "this-preset"
|
||||
// }
|
||||
if (itemName !== items.node.value.value) {
|
||||
items.node.value = j.arrayExpression([items.node.value, jsonValueToExpression(item)])
|
||||
}
|
||||
} else if (items.node.value.type === "ArrayExpression") {
|
||||
// {
|
||||
// presets: ["this-preset", "maybe-another", ...]
|
||||
// }
|
||||
// Here, it will return if it find the preset inside the
|
||||
// array, so the last line doesn't push a duplicated preset
|
||||
for (const [i, element] of items.node.value.elements.entries()) {
|
||||
if (!element) continue
|
||||
|
||||
if (element.type === "Literal" || element.type === "StringLiteral") {
|
||||
// {
|
||||
// presets: [..., "this-preset", ...]
|
||||
// }
|
||||
if (element.value === itemName) return
|
||||
} else if (element.type === "ArrayExpression") {
|
||||
// {
|
||||
// presets: [..., ["this-preset"], ...]
|
||||
// }
|
||||
if (
|
||||
(element.elements[0]?.type === "Literal" ||
|
||||
element.elements[0]?.type === "StringLiteral") &&
|
||||
element.elements[0].value === itemName
|
||||
) {
|
||||
if (
|
||||
element.elements[1]?.type === "ObjectExpression" &&
|
||||
element.elements[1].properties.length > 0
|
||||
) {
|
||||
// The preset has a config.
|
||||
// ["this-preset", {...}]
|
||||
if (Array.isArray(item)) {
|
||||
// If it has an adittional config, add the new keys
|
||||
// (don't matter if they already exists, let the user handle it later by themself)
|
||||
let obj = element.elements[1]
|
||||
|
||||
for (const key in item[1]) {
|
||||
const value = item[1][key]
|
||||
if (value === undefined) continue
|
||||
obj.properties.push(
|
||||
j.objectProperty(j.stringLiteral(key), jsonValueToExpression(value)),
|
||||
)
|
||||
}
|
||||
|
||||
items.node.value.elements[i] = obj
|
||||
}
|
||||
} else {
|
||||
// The preset has no config.
|
||||
// Its ["this-preset"]
|
||||
items.node.value.elements[i] = jsonValueToExpression(item)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
items.node.value.elements.push(jsonValueToExpression(item))
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
return program
|
||||
}
|
||||
|
||||
export const addBabelPreset = (program: Program, preset: AddBabelItemDefinition): Program =>
|
||||
updateBabelConfig(program, preset, "presets")
|
||||
export const addBabelPlugin = (program: Program, plugin: AddBabelItemDefinition): Program =>
|
||||
updateBabelConfig(program, plugin, "plugins")
|
||||
@@ -1,20 +0,0 @@
|
||||
import {CommentKind, TypeAnnotationKind, TSTypeAnnotationKind} from "ast-types/gen/kinds"
|
||||
import j from "jscodeshift"
|
||||
|
||||
export function withComments<
|
||||
Node extends {
|
||||
comments?: CommentKind[] | null
|
||||
},
|
||||
>(node: Node, comments: CommentKind[]): Node {
|
||||
node.comments = comments
|
||||
return node
|
||||
}
|
||||
|
||||
export function withTypeAnnotation<
|
||||
Node extends {
|
||||
typeAnnotation?: TypeAnnotationKind | TSTypeAnnotationKind | null
|
||||
},
|
||||
>(node: Node, type: Parameters<typeof j.tsTypeAnnotation>[0]): Node {
|
||||
node.typeAnnotation = j.tsTypeAnnotation(type)
|
||||
return node
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
import {NodePath} from "ast-types/lib/node-path"
|
||||
import j, {JSXAttribute} from "jscodeshift"
|
||||
import {assert} from "../../index-server"
|
||||
import {Program} from "../types"
|
||||
|
||||
export function wrapAppWithProvider(
|
||||
program: Program,
|
||||
element: string,
|
||||
attributes?: string[],
|
||||
): Program {
|
||||
const findMyApp = program.find(j.FunctionDeclaration, (node) => node.id.name === "MyApp")
|
||||
assert(findMyApp.length, "MyApp function not found")
|
||||
|
||||
findMyApp.forEach((path: NodePath) => {
|
||||
const statement = path.value.body.body.filter(
|
||||
(b: j.ReturnStatement) => b.type === "ReturnStatement",
|
||||
)[0]
|
||||
const argument = statement.argument
|
||||
|
||||
let attrs: JSXAttribute[] = []
|
||||
if (attributes) {
|
||||
attrs = attributes.map((i) => j.jsxAttribute(j.jsxIdentifier(i)))
|
||||
}
|
||||
|
||||
statement.argument = j.jsxElement(
|
||||
j.jsxOpeningElement(j.jsxIdentifier(element), attrs),
|
||||
j.jsxClosingElement(j.jsxIdentifier(element)),
|
||||
[j.jsxText("\n"), argument, j.jsxText("\n")],
|
||||
)
|
||||
})
|
||||
|
||||
return program
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
import j from "jscodeshift"
|
||||
import {Program} from "../types"
|
||||
|
||||
export function wrapBlitzConfig(program: Program, functionName: string): Program {
|
||||
let moduleExportsExpressions = program.find(j.AssignmentExpression, {
|
||||
operator: "=",
|
||||
left: {object: {name: "module"}, property: {name: "exports"}},
|
||||
right: {},
|
||||
})
|
||||
|
||||
// If there isn't any `module.exports = ...`, create one
|
||||
if (moduleExportsExpressions.length === 0) {
|
||||
let moduleExportExpression = j.expressionStatement(
|
||||
j.assignmentExpression(
|
||||
"=",
|
||||
j.memberExpression(j.identifier("module"), j.identifier("exports")),
|
||||
j.callExpression(j.identifier(functionName), [j.objectExpression([])]),
|
||||
),
|
||||
)
|
||||
|
||||
program.get().node.program.body.push(moduleExportExpression)
|
||||
} else if (moduleExportsExpressions.length === 1) {
|
||||
let moduleExportsExpression: j.ASTPath<j.AssignmentExpression> = moduleExportsExpressions.get()
|
||||
|
||||
moduleExportsExpression.value.right = j.callExpression(j.identifier(functionName), [
|
||||
moduleExportsExpression.value.right,
|
||||
])
|
||||
} else {
|
||||
console.warn("There are multiple 'module.exports' inside 'blitz.config.js'")
|
||||
}
|
||||
|
||||
return program
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
import type * as j from "jscodeshift"
|
||||
|
||||
export interface RecipeMeta {
|
||||
name: string
|
||||
description: string
|
||||
owner: string
|
||||
repoLink: string
|
||||
}
|
||||
|
||||
export type RecipeCLIArgs = {[Key in string]?: string | true}
|
||||
|
||||
export interface RecipeCLIFlags {
|
||||
yesToAll: boolean
|
||||
}
|
||||
|
||||
export type Program = j.Collection<j.Program>
|
||||
|
||||
/**
|
||||
Matches a JSON object.
|
||||
This type can be useful to enforce some input to be JSON-compatible or as a super-type to be extended from. Don't use this as a direct return type as the user would have to double-cast it: `jsonObject as unknown as CustomResponse`. Instead, you could extend your CustomResponse type from it to ensure your type only uses JSON-compatible types: `interface CustomResponse extends JsonObject { … }`.
|
||||
@see https://github.com/sindresorhus/type-fest
|
||||
*/
|
||||
export type JsonObject = {[Key in string]?: JsonValue}
|
||||
|
||||
/**
|
||||
Matches a JSON array.
|
||||
@see https://github.com/sindresorhus/type-fest
|
||||
*/
|
||||
export type JsonArray = JsonValue[]
|
||||
|
||||
/**
|
||||
Matches any valid JSON primitive value.
|
||||
@see https://github.com/sindresorhus/type-fest
|
||||
*/
|
||||
export type JsonPrimitive = string | number | boolean | null
|
||||
|
||||
/**
|
||||
Matches any valid JSON value.
|
||||
@see https://github.com/sindresorhus/type-fest
|
||||
*/
|
||||
export type JsonValue = JsonPrimitive | JsonObject | JsonArray
|
||||
@@ -1,77 +0,0 @@
|
||||
import * as fs from "fs-extra"
|
||||
import * as path from "path"
|
||||
|
||||
function ext(jsx = false) {
|
||||
return fs.existsSync(path.resolve("tsconfig.json")) ? (jsx ? ".tsx" : ".ts") : ".js"
|
||||
}
|
||||
|
||||
function getBlitzPath(type: string) {
|
||||
const appPath = `app/blitz-${type}${ext(false)}`
|
||||
const srcPath = `src/blitz-${type}${ext(false)}`
|
||||
const appDir = fs.existsSync(path.resolve(appPath))
|
||||
const srcDir = fs.existsSync(path.resolve(srcPath))
|
||||
|
||||
if (appDir) {
|
||||
return appPath
|
||||
} else if (srcDir) {
|
||||
return srcPath
|
||||
}
|
||||
}
|
||||
|
||||
function getAppSourceDir() {
|
||||
const srcPath = "src/pages"
|
||||
const srcDir = fs.existsSync(path.resolve(srcPath))
|
||||
|
||||
if (srcDir) {
|
||||
return "src"
|
||||
} else {
|
||||
return "app"
|
||||
}
|
||||
}
|
||||
|
||||
function findPageDir() {
|
||||
const srcPagePath = `src/pages`
|
||||
const srcPage = getAppSourceDir()
|
||||
|
||||
switch (srcPage) {
|
||||
case "src": {
|
||||
return srcPagePath
|
||||
}
|
||||
default: {
|
||||
return `pages`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const paths = {
|
||||
document() {
|
||||
return `${findPageDir()}/_document${ext(true)}`
|
||||
},
|
||||
app() {
|
||||
return `${findPageDir()}/_app${ext(true)}`
|
||||
},
|
||||
appSrcDirectory() {
|
||||
return getAppSourceDir()
|
||||
},
|
||||
blitzServer() {
|
||||
return getBlitzPath("server")
|
||||
},
|
||||
blitzClient() {
|
||||
return getBlitzPath("client")
|
||||
},
|
||||
entry() {
|
||||
return `${findPageDir()}/index${ext(true)}`
|
||||
},
|
||||
nextConfig() {
|
||||
return `next.config.js`
|
||||
},
|
||||
babelConfig() {
|
||||
return `babel.config.js`
|
||||
},
|
||||
packageJson() {
|
||||
return "package.json"
|
||||
},
|
||||
prismaSchema() {
|
||||
return "db/schema.prisma"
|
||||
},
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
import * as fs from "fs-extra"
|
||||
import j from "jscodeshift"
|
||||
import getBabelOptions, {Overrides} from "recast/parsers/_babel_options"
|
||||
import * as babel from "recast/parsers/babel"
|
||||
import {Program} from "../types"
|
||||
|
||||
export const customTsParser: {} = {
|
||||
parse(source: string, options?: Overrides) {
|
||||
const babelOptions = getBabelOptions(options)
|
||||
babelOptions.plugins.push("typescript")
|
||||
babelOptions.plugins.push("jsx")
|
||||
|
||||
return babel.parser.parse(source, babelOptions)
|
||||
},
|
||||
}
|
||||
|
||||
export enum TransformStatus {
|
||||
Success = "success",
|
||||
Failure = "failure",
|
||||
}
|
||||
export interface TransformResult {
|
||||
status: TransformStatus
|
||||
filename: string
|
||||
error?: Error
|
||||
}
|
||||
|
||||
export type StringTransformer = (program: string) => string | Promise<string>
|
||||
export type Transformer = (program: Program) => Program | Promise<Program>
|
||||
|
||||
export function stringProcessFile(
|
||||
original: string,
|
||||
transformerFn: StringTransformer,
|
||||
): string | Promise<string> {
|
||||
return transformerFn(original)
|
||||
}
|
||||
|
||||
export async function processFile(original: string, transformerFn: Transformer): Promise<string> {
|
||||
const program = j(original, {parser: customTsParser})
|
||||
return (await transformerFn(program)).toSource()
|
||||
}
|
||||
|
||||
export async function transform(
|
||||
processFile: (original: string) => Promise<string>,
|
||||
targetFilePaths: string[],
|
||||
): Promise<TransformResult[]> {
|
||||
const results: TransformResult[] = []
|
||||
for (const filePath of targetFilePaths) {
|
||||
if (!fs.existsSync(filePath)) {
|
||||
results.push({
|
||||
status: TransformStatus.Failure,
|
||||
filename: filePath,
|
||||
error: new Error(`Error: ${filePath} not found`),
|
||||
})
|
||||
}
|
||||
try {
|
||||
const fileBuffer = fs.readFileSync(filePath)
|
||||
const fileSource = fileBuffer.toString("utf-8")
|
||||
const transformedCode = await processFile(fileSource)
|
||||
fs.writeFileSync(filePath, transformedCode)
|
||||
results.push({
|
||||
status: TransformStatus.Success,
|
||||
filename: filePath,
|
||||
})
|
||||
} catch (err) {
|
||||
results.push({
|
||||
status: TransformStatus.Failure,
|
||||
filename: filePath,
|
||||
error: err as any,
|
||||
})
|
||||
}
|
||||
}
|
||||
return results
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import {useInput} from "ink"
|
||||
|
||||
export function useEnterToContinue(cb: Function, additionalCondition: boolean = true) {
|
||||
useInput((_input, key) => {
|
||||
if (additionalCondition && key.return) {
|
||||
cb()
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
import {useStdin} from "ink"
|
||||
import {RecipeCLIFlags} from "../types"
|
||||
|
||||
export function useUserInput(cliFlags: RecipeCLIFlags) {
|
||||
const {isRawModeSupported} = useStdin()
|
||||
return isRawModeSupported && !cliFlags.yesToAll
|
||||
}
|
||||
@@ -1,5 +1,31 @@
|
||||
# @blitzjs/codemod
|
||||
|
||||
## 2.2.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [565db3c5a]
|
||||
- Updated dependencies [3fa3a4ef3]
|
||||
- blitz@2.2.0
|
||||
- @blitzjs/generator@2.2.0
|
||||
|
||||
## 2.1.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [38d320fd2]
|
||||
- @blitzjs/generator@2.1.4
|
||||
- blitz@2.1.4
|
||||
|
||||
## 2.1.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [0b3286468]
|
||||
- Updated dependencies [50f17d21c]
|
||||
- blitz@2.1.3
|
||||
- @blitzjs/generator@2.1.3
|
||||
|
||||
## 2.1.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@blitzjs/codemod",
|
||||
"version": "2.1.2",
|
||||
"version": "2.2.0",
|
||||
"scripts": {
|
||||
"build": "unbuild",
|
||||
"dev": "watch unbuild src --wait=0.2",
|
||||
@@ -25,9 +25,9 @@
|
||||
"@babel/plugin-proposal-class-properties": "7.17.12",
|
||||
"@babel/plugin-syntax-jsx": "7.17.12",
|
||||
"@babel/plugin-syntax-typescript": "7.17.12",
|
||||
"@blitzjs/generator": "2.1.2",
|
||||
"@blitzjs/generator": "2.2.0",
|
||||
"arg": "5.0.1",
|
||||
"blitz": "2.1.2",
|
||||
"blitz": "2.2.0",
|
||||
"chalk": "^4.1.0",
|
||||
"cross-spawn": "7.0.3",
|
||||
"debug": "4.3.3",
|
||||
@@ -38,7 +38,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/preset-env": "7.12.10",
|
||||
"@blitzjs/config": "2.1.2",
|
||||
"@blitzjs/config": "2.2.0",
|
||||
"@types/jscodeshift": "0.11.2",
|
||||
"@types/node": "18.11.9",
|
||||
"ast-types": "0.14.2",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @blitzjs/config
|
||||
|
||||
## 2.2.0
|
||||
|
||||
## 2.1.4
|
||||
|
||||
## 2.1.3
|
||||
|
||||
## 2.1.2
|
||||
|
||||
## 2.1.1
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@blitzjs/config",
|
||||
"private": true,
|
||||
"version": "2.1.2",
|
||||
"version": "2.2.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "5.42.1",
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
# @blitzjs/generator
|
||||
|
||||
## 2.2.0
|
||||
|
||||
## 2.1.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 38d320fd2: chore: Upgrade to prisma v6. Read more about migration in https://www.prisma.io/docs/orm/more/upgrade-guides/upgrading-versions/upgrading-to-prisma-6
|
||||
|
||||
## 2.1.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 0b3286468: chore: bump `next.js` and `zod` versions
|
||||
|
||||
## 2.1.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user