@wasp.sh/wasp-cli-darwin-arm64-unknown 0.21.1 → 0.22.0-rc2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/data/Cli/starters/skeleton/gitignore +11 -0
- package/data/Cli/starters/ts-minimal/main.wasp.ts +15 -0
- package/data/Cli/starters/ts-minimal/package.json +18 -0
- package/data/Cli/starters/ts-minimal/schema.prisma +10 -0
- package/data/Cli/starters/ts-minimal/src/Main.css +103 -0
- package/data/Cli/starters/ts-minimal/src/MainPage.tsx +37 -0
- package/data/Cli/starters/ts-minimal/src/assets/logo.svg +1 -0
- package/data/Cli/starters/ts-minimal/tsconfig.json +7 -0
- package/data/Cli/starters/ts-minimal/tsconfig.src.json +28 -0
- package/data/Cli/starters/ts-minimal/tsconfig.wasp.json +15 -0
- package/data/Cli/starters/ts-minimal/vite.config.ts +9 -0
- package/data/Generator/libs/auth/wasp.sh-lib-auth-0.22.0.tgz +0 -0
- package/data/Generator/libs/vite-ssr/wasp.sh-lib-vite-ssr-0.22.0.tgz +0 -0
- package/data/Generator/templates/Dockerfile +5 -3
- package/data/Generator/templates/sdk/wasp/api/index.ts +18 -14
- package/data/Generator/templates/sdk/wasp/client/app/components/WaspApp.tsx +5 -24
- package/data/Generator/templates/sdk/wasp/client/app/hooks/useIsClient.ts +22 -0
- package/data/Generator/templates/sdk/wasp/client/app/index.tsx +1 -19
- package/data/Generator/templates/sdk/wasp/client/app/layout.tsx +92 -0
- package/data/Generator/templates/sdk/wasp/client/app/router.tsx +64 -0
- package/data/Generator/templates/sdk/wasp/client/env/schema.ts +28 -11
- package/data/Generator/templates/sdk/wasp/client/env.ts +6 -3
- package/data/Generator/templates/sdk/wasp/client/vite/plugins/validateEnv.ts +25 -26
- package/data/Generator/templates/sdk/wasp/client/vite/plugins/virtualModules.ts +4 -2
- package/data/Generator/templates/sdk/wasp/client/vite/plugins/wasp.ts +8 -4
- package/data/Generator/templates/sdk/wasp/client/vite/plugins/waspConfig.ts +57 -11
- package/data/Generator/templates/sdk/wasp/client/vite/virtual-files/files/client-entry.tsx +33 -0
- package/data/Generator/templates/sdk/wasp/client/vite/virtual-files/files/routes.tsx +57 -8
- package/data/Generator/templates/sdk/wasp/client/vite/virtual-files/files/ssr-entry.tsx +64 -0
- package/data/Generator/templates/sdk/wasp/client/vite/virtual-files/index.ts +4 -4
- package/data/Generator/templates/sdk/wasp/client/webSocket/WebSocketProvider.tsx +1 -1
- package/data/Generator/templates/sdk/wasp/core/storage.ts +49 -26
- package/data/Generator/templates/sdk/wasp/env/index.ts +1 -1
- package/data/Generator/templates/sdk/wasp/env/validation.ts +28 -22
- package/data/Generator/templates/sdk/wasp/package.json +2 -0
- package/data/Generator/templates/sdk/wasp/server/email/core/providers/dummy.ts +23 -25
- package/data/Generator/templates/sdk/wasp/server/env.ts +72 -67
- package/data/Generator/templates/sdk/wasp/universal/ansiColors.ts +55 -11
- package/data/packages/deploy/dist/providers/fly/flyCli.js +1 -4
- package/data/packages/deploy/dist/providers/fly/jsonOutputSchemas.js +6 -2
- package/data/packages/deploy/dist/providers/fly/tomlFile.js +8 -2
- package/data/packages/deploy/dist/providers/railway/commands/setup/setup.js +1 -12
- package/data/packages/deploy/dist/providers/railway/env.js +13 -0
- package/data/packages/deploy/dist/providers/railway/railwayService/url.js +1 -1
- package/data/packages/deploy/package-lock.json +2268 -15
- package/data/packages/deploy/package.json +6 -3
- package/data/packages/ts-inspect/dist/exports.js +1 -1
- package/data/packages/ts-inspect/package-lock.json +8 -7
- package/data/packages/ts-inspect/package.json +1 -1
- package/data/packages/wasp-config/dist/__tests__/appAnalyzer.unit.test.js +13 -0
- package/data/packages/wasp-config/dist/__tests__/mapTsAppSpecToAppSpecDecls.unit.test.js +1 -0
- package/data/packages/wasp-config/dist/__tests__/testFixtures.d.ts.map +1 -1
- package/data/packages/wasp-config/dist/__tests__/testFixtures.js +1 -0
- package/data/packages/wasp-config/dist/src/appAnalyzer.js +1 -1
- package/data/packages/wasp-config/dist/src/appSpec.d.ts +1 -0
- package/data/packages/wasp-config/dist/src/appSpec.d.ts.map +1 -1
- package/data/packages/wasp-config/dist/src/mapTsAppSpecToAppSpecDecls.d.ts.map +1 -1
- package/data/packages/wasp-config/dist/src/mapTsAppSpecToAppSpecDecls.js +2 -1
- package/data/packages/wasp-config/dist/src/publicApi/tsAppSpec.d.ts +1 -0
- package/data/packages/wasp-config/dist/src/publicApi/tsAppSpec.d.ts.map +1 -1
- package/data/packages/wasp-config/package.json +5 -4
- package/package.json +1 -1
- package/wasp-bin +0 -0
- package/data/Generator/libs/auth/wasp.sh-lib-auth-0.21.1.tgz +0 -0
- package/data/Generator/templates/sdk/wasp/client/app/router/router.tsx +0 -47
- package/data/Generator/templates/sdk/wasp/client/vite/plugins/html/build.ts +0 -38
- package/data/Generator/templates/sdk/wasp/client/vite/plugins/html/dev.ts +0 -35
- package/data/Generator/templates/sdk/wasp/client/vite/virtual-files/files/index.html +0 -21
- package/data/Generator/templates/sdk/wasp/client/vite/virtual-files/files/index.tsx +0 -34
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
.wasp/
|
|
2
|
+
node_modules/
|
|
3
|
+
|
|
4
|
+
# Ignore all dotenv files by default to prevent accidentally committing any secrets.
|
|
5
|
+
# To include specific dotenv files, use the `!` operator or adjust these rules.
|
|
6
|
+
.env
|
|
7
|
+
.env.*
|
|
8
|
+
|
|
9
|
+
# Don't ignore example dotenv files.
|
|
10
|
+
!.env.example
|
|
11
|
+
!.env.*.example
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { App } from "wasp-config";
|
|
2
|
+
|
|
3
|
+
const app = new App("__waspAppName__", {
|
|
4
|
+
title: "__waspProjectName__",
|
|
5
|
+
wasp: { version: "__waspVersion__" },
|
|
6
|
+
head: ["<link rel='icon' href='/favicon.ico' />"],
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
const mainPage = app.page("MainPage", {
|
|
10
|
+
component: { import: "MainPage", from: "@src/MainPage" },
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
app.route("RootRoute", { path: "/", to: mainPage });
|
|
14
|
+
|
|
15
|
+
export default app;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "__waspAppName__",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"workspaces": [".wasp/out/*", ".wasp/out/sdk/wasp"],
|
|
5
|
+
"dependencies": {
|
|
6
|
+
"react": "^19.2.1",
|
|
7
|
+
"react-dom": "^19.2.1",
|
|
8
|
+
"react-router": "^7.12.0"
|
|
9
|
+
},
|
|
10
|
+
"devDependencies": {
|
|
11
|
+
"@types/react": "^19.2.7",
|
|
12
|
+
"@types/react-dom": "^19.2.3",
|
|
13
|
+
"prisma": "5.19.1",
|
|
14
|
+
"typescript": "5.8.2",
|
|
15
|
+
"vite": "^7.0.6",
|
|
16
|
+
"wasp-config": "__waspConfigPackageSpecifier__"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
datasource db {
|
|
2
|
+
provider = "sqlite"
|
|
3
|
+
// Wasp requires that the url is set to the DATABASE_URL environment variable.
|
|
4
|
+
url = env("DATABASE_URL")
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
// Wasp requires the `prisma-client-js` generator to be present.
|
|
8
|
+
generator client {
|
|
9
|
+
provider = "prisma-client-js"
|
|
10
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
* {
|
|
2
|
+
-webkit-font-smoothing: antialiased;
|
|
3
|
+
-moz-osx-font-smoothing: grayscale;
|
|
4
|
+
box-sizing: border-box;
|
|
5
|
+
margin: 0;
|
|
6
|
+
padding: 0;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
body {
|
|
10
|
+
font-family:
|
|
11
|
+
-apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu",
|
|
12
|
+
"Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
#root {
|
|
16
|
+
min-height: 100vh;
|
|
17
|
+
display: flex;
|
|
18
|
+
flex-direction: column;
|
|
19
|
+
justify-content: center;
|
|
20
|
+
align-items: center;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.container {
|
|
24
|
+
margin: 3rem 3rem 10rem 3rem;
|
|
25
|
+
max-width: 726px;
|
|
26
|
+
display: flex;
|
|
27
|
+
flex-direction: column;
|
|
28
|
+
justify-content: center;
|
|
29
|
+
align-items: center;
|
|
30
|
+
text-align: center;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.logo {
|
|
34
|
+
max-height: 200px;
|
|
35
|
+
margin-bottom: 1rem;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.title {
|
|
39
|
+
font-size: 4rem;
|
|
40
|
+
font-weight: 700;
|
|
41
|
+
margin-bottom: 1rem;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.content {
|
|
45
|
+
font-size: 1.2rem;
|
|
46
|
+
font-weight: 400;
|
|
47
|
+
line-height: 2;
|
|
48
|
+
margin-bottom: 3rem;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.buttons {
|
|
52
|
+
display: flex;
|
|
53
|
+
flex-direction: row;
|
|
54
|
+
gap: 1rem;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.button {
|
|
58
|
+
font-size: 1.2rem;
|
|
59
|
+
font-weight: 700;
|
|
60
|
+
text-decoration: none;
|
|
61
|
+
padding: 1.2rem 1.5rem;
|
|
62
|
+
border-radius: 10px;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.button-filled {
|
|
66
|
+
color: black;
|
|
67
|
+
background-color: #ffcc00;
|
|
68
|
+
border: 2px solid #ffcc00;
|
|
69
|
+
|
|
70
|
+
transition: all 0.2s ease-in-out;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.button-filled:hover {
|
|
74
|
+
filter: brightness(0.95);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.button-outlined {
|
|
78
|
+
color: black;
|
|
79
|
+
background-color: transparent;
|
|
80
|
+
border: 2px solid #ffcc00;
|
|
81
|
+
|
|
82
|
+
transition: all 0.2s ease-in-out;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.button-outlined:hover {
|
|
86
|
+
filter: brightness(0.95);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
code {
|
|
90
|
+
border-radius: 5px;
|
|
91
|
+
border: 1px solid #ffcc00;
|
|
92
|
+
padding: 0.2rem;
|
|
93
|
+
background: #ffcc0044;
|
|
94
|
+
font-family:
|
|
95
|
+
Menlo,
|
|
96
|
+
Monaco,
|
|
97
|
+
Lucida Console,
|
|
98
|
+
Liberation Mono,
|
|
99
|
+
DejaVu Sans Mono,
|
|
100
|
+
Bitstream Vera Sans Mono,
|
|
101
|
+
Courier New,
|
|
102
|
+
monospace;
|
|
103
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import Logo from "./assets/logo.svg";
|
|
2
|
+
import "./Main.css";
|
|
3
|
+
|
|
4
|
+
export function MainPage() {
|
|
5
|
+
return (
|
|
6
|
+
<main className="container">
|
|
7
|
+
<img className="logo" src={Logo} alt="wasp" />
|
|
8
|
+
|
|
9
|
+
<h2 className="title">Welcome to Wasp!</h2>
|
|
10
|
+
|
|
11
|
+
<p className="content">
|
|
12
|
+
This is page <code>MainPage</code> located at route <code>/</code>.
|
|
13
|
+
<br />
|
|
14
|
+
Open <code>src/MainPage.tsx</code> to edit it.
|
|
15
|
+
</p>
|
|
16
|
+
|
|
17
|
+
<div className="buttons">
|
|
18
|
+
<a
|
|
19
|
+
className="button button-filled"
|
|
20
|
+
href="https://wasp.sh/docs/tutorial/create"
|
|
21
|
+
target="_blank"
|
|
22
|
+
rel="noreferrer noopener"
|
|
23
|
+
>
|
|
24
|
+
Take the Tutorial
|
|
25
|
+
</a>
|
|
26
|
+
<a
|
|
27
|
+
className="button button-outlined"
|
|
28
|
+
href="https://discord.com/invite/rzdnErX"
|
|
29
|
+
target="_blank"
|
|
30
|
+
rel="noreferrer noopener"
|
|
31
|
+
>
|
|
32
|
+
Chat on Discord
|
|
33
|
+
</a>
|
|
34
|
+
</div>
|
|
35
|
+
</main>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 161 161"><defs><style>.cls-1{fill:#f5cc05;}.cls-2{fill-rule:evenodd;}</style></defs><g id="Page-1"><g id="Group-23"><circle id="Oval" class="cls-1" cx="80.5" cy="80.5" r="79"/><g id="Group-36"><g id="_" data-name="}"><path id="path-2" class="cls-2" d="M88.67,114.33h2.91q6,0,7.87-1.89c1.22-1.25,1.83-3.9,1.83-7.93V93.89c0-4.46.65-7.7,1.93-9.73s3.51-3.43,6.67-4.2q-4.69-1.08-6.65-4.12c-1.3-2-2-5.28-2-9.77V55.44q0-6-1.83-7.93t-7.87-1.88H88.67V39.5h2.65q10.65,0,14.24,3.15t3.59,12.62V65.56c0,4.28.77,7.24,2.29,8.87s4.3,2.44,8.32,2.44h2.74V83h-2.74q-6,0-8.32,2.49c-1.52,1.65-2.29,4.64-2.29,9v10.25q0,9.47-3.59,12.64T91.32,120.5H88.67Z"/><path id="path-2-2" data-name="path-2" class="cls-2" d="M88.67,114.33h2.91q6,0,7.87-1.89c1.22-1.25,1.83-3.9,1.83-7.93V93.89c0-4.46.65-7.7,1.93-9.73s3.51-3.43,6.67-4.2q-4.69-1.08-6.65-4.12c-1.3-2-2-5.28-2-9.77V55.44q0-6-1.83-7.93t-7.87-1.88H88.67V39.5h2.65q10.65,0,14.24,3.15t3.59,12.62V65.56c0,4.28.77,7.24,2.29,8.87s4.3,2.44,8.32,2.44h2.74V83h-2.74q-6,0-8.32,2.49c-1.52,1.65-2.29,4.64-2.29,9v10.25q0,9.47-3.59,12.64T91.32,120.5H88.67Z"/></g><g id="text831"><g id="_2" data-name="="><path id="path-3" class="cls-2" d="M38.5,85.15H75.83v7.58H38.5Zm0-17.88H75.83v7.49H38.5Z"/><path id="path-3-2" data-name="path-3" class="cls-2" d="M38.5,85.15H75.83v7.58H38.5Zm0-17.88H75.83v7.49H38.5Z"/></g></g></g></g></g></svg>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// =============================== IMPORTANT =================================
|
|
2
|
+
// This file is mainly used for Wasp IDE support.
|
|
3
|
+
//
|
|
4
|
+
// Wasp will compile your code with slightly different (less strict) compilerOptions.
|
|
5
|
+
// You can increase the configuration's strictness (e.g., by adding
|
|
6
|
+
// "noUncheckedIndexedAccess": true), but you shouldn't reduce it (e.g., by
|
|
7
|
+
// adding "strict": false). Just keep in mind that this will only affect your
|
|
8
|
+
// IDE support, not the actual compilation.
|
|
9
|
+
//
|
|
10
|
+
// Full TypeScript configurability is coming very soon :)
|
|
11
|
+
{
|
|
12
|
+
"compilerOptions": {
|
|
13
|
+
"module": "esnext",
|
|
14
|
+
"composite": true,
|
|
15
|
+
"target": "esnext",
|
|
16
|
+
"moduleResolution": "bundler",
|
|
17
|
+
"jsx": "preserve",
|
|
18
|
+
"strict": true,
|
|
19
|
+
"esModuleInterop": true,
|
|
20
|
+
"isolatedModules": true,
|
|
21
|
+
"moduleDetection": "force",
|
|
22
|
+
"lib": ["dom", "dom.iterable", "esnext"],
|
|
23
|
+
"skipLibCheck": true,
|
|
24
|
+
"allowJs": true,
|
|
25
|
+
"outDir": ".wasp/out/user"
|
|
26
|
+
},
|
|
27
|
+
"include": ["src"]
|
|
28
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"skipLibCheck": true,
|
|
4
|
+
"target": "ES2022",
|
|
5
|
+
"isolatedModules": true,
|
|
6
|
+
"moduleDetection": "force",
|
|
7
|
+
"strict": true,
|
|
8
|
+
"noUnusedLocals": true,
|
|
9
|
+
"noUnusedParameters": true,
|
|
10
|
+
"module": "NodeNext",
|
|
11
|
+
"noEmit": true,
|
|
12
|
+
"lib": ["ES2023"]
|
|
13
|
+
},
|
|
14
|
+
"include": ["main.wasp.ts"]
|
|
15
|
+
}
|
|
Binary file
|
|
Binary file
|
|
@@ -3,9 +3,7 @@
|
|
|
3
3
|
# Because if not, we had situations where it would use the different version
|
|
4
4
|
# locally and on Github CI. This way we ensure exact version is used,
|
|
5
5
|
# and also have control over updating it (instead of update surprising us).
|
|
6
|
-
|
|
7
|
-
{=! (Alpine 3.21 at time of writing) =}
|
|
8
|
-
FROM node:{= nodeVersion =}-alpine3.20 AS node
|
|
6
|
+
FROM node:{= nodeVersion =}-alpine3.23 AS node
|
|
9
7
|
|
|
10
8
|
|
|
11
9
|
# We split Dockerfile into base, server-builder and server-production.
|
|
@@ -18,6 +16,10 @@ FROM node:{= nodeVersion =}-alpine3.20 AS node
|
|
|
18
16
|
|
|
19
17
|
FROM node AS base
|
|
20
18
|
RUN apk --no-cache -U upgrade # To ensure any potential security patches are applied.
|
|
19
|
+
# Alpine 3.21+ no longer includes openssl as a transitive dependency of the
|
|
20
|
+
# Node.js image. Prisma's native engine needs libssl at both build and runtime.
|
|
21
|
+
# TODO: Revisit once we upgrade to Prisma 6 (https://github.com/wasp-lang/wasp/issues/2504).
|
|
22
|
+
RUN apk add --no-cache openssl
|
|
21
23
|
|
|
22
24
|
|
|
23
25
|
# Todo: The 'server-builder' image stays on disk under <none>:<none> and is
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import axios, { type
|
|
1
|
+
import axios, { type AxiosError, type AxiosInstance } from 'axios'
|
|
2
2
|
|
|
3
3
|
import { config } from 'wasp/client'
|
|
4
4
|
import { storage } from 'wasp/core/storage'
|
|
@@ -75,20 +75,24 @@ api.interceptors.response.use(undefined, (error) => {
|
|
|
75
75
|
return Promise.reject(error)
|
|
76
76
|
})
|
|
77
77
|
|
|
78
|
-
// This
|
|
79
|
-
//
|
|
80
|
-
|
|
81
|
-
//
|
|
82
|
-
//
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
78
|
+
// This makes sure that the following handler won't try to run in a non-browser
|
|
79
|
+
// environment (e.g. during SSR), where `window` is not defined.
|
|
80
|
+
if (typeof window !== 'undefined') {
|
|
81
|
+
// This handler will run on other tabs (not the active one calling API functions),
|
|
82
|
+
// and will ensure they know about auth session ID changes.
|
|
83
|
+
// Ref: https://developer.mozilla.org/en-US/docs/Web/API/Window/storage_event
|
|
84
|
+
// "Note: This won't work on the same page that is making the changes — it is really a way
|
|
85
|
+
// for other pages on the domain using the storage to sync any changes that are made."
|
|
86
|
+
window.addEventListener('storage', (event) => {
|
|
87
|
+
if (event.key === storage.getPrefixedKey(WASP_APP_AUTH_SESSION_ID_NAME)) {
|
|
88
|
+
if (!!event.newValue) {
|
|
89
|
+
apiEventsEmitter.emit('sessionId.set')
|
|
90
|
+
} else {
|
|
91
|
+
apiEventsEmitter.emit('sessionId.clear')
|
|
92
|
+
}
|
|
89
93
|
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
94
|
+
})
|
|
95
|
+
}
|
|
92
96
|
|
|
93
97
|
// PRIVATE API (sdk)
|
|
94
98
|
/**
|
|
@@ -1,44 +1,25 @@
|
|
|
1
1
|
{{={= =}=}}
|
|
2
|
-
import
|
|
2
|
+
import { use, type ReactNode } from 'react'
|
|
3
3
|
import { QueryClientProvider } from '@tanstack/react-query'
|
|
4
4
|
|
|
5
|
-
import { getRouter } from '../router/router'
|
|
6
5
|
import { queryClientInitialized } from '../../operations/index'
|
|
7
6
|
|
|
8
7
|
{=# areWebSocketsUsed =}
|
|
9
8
|
import { WebSocketProvider } from '../../webSocket/WebSocketProvider'
|
|
10
9
|
{=/ areWebSocketsUsed =}
|
|
11
10
|
|
|
12
|
-
export
|
|
13
|
-
|
|
14
|
-
routesMapping: Record<string, React.ComponentType>;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function WaspApp({ rootElement, routesMapping }: Required<WaspAppProps>) {
|
|
18
|
-
const [queryClient, setQueryClient] = React.useState<any>(null)
|
|
19
|
-
|
|
20
|
-
React.useEffect(() => {
|
|
21
|
-
queryClientInitialized.then(setQueryClient)
|
|
22
|
-
}, [])
|
|
23
|
-
|
|
24
|
-
if (!queryClient) {
|
|
25
|
-
return null
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const router = getRouter({
|
|
29
|
-
rootElement,
|
|
30
|
-
routesMapping,
|
|
31
|
-
})
|
|
11
|
+
export function WaspApp({ children }: { children: ReactNode }) {
|
|
12
|
+
const queryClient = use(queryClientInitialized)
|
|
32
13
|
|
|
33
14
|
return (
|
|
34
15
|
<QueryClientProvider client={queryClient}>
|
|
35
16
|
{=# areWebSocketsUsed =}
|
|
36
17
|
<WebSocketProvider>
|
|
37
|
-
{
|
|
18
|
+
{children}
|
|
38
19
|
</WebSocketProvider>
|
|
39
20
|
{=/ areWebSocketsUsed =}
|
|
40
21
|
{=^ areWebSocketsUsed =}
|
|
41
|
-
{
|
|
22
|
+
{children}
|
|
42
23
|
{=/ areWebSocketsUsed =}
|
|
43
24
|
</QueryClientProvider>
|
|
44
25
|
)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { useSyncExternalStore } from "react"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Returns `true` if the component is running on the client (browser) and
|
|
5
|
+
* `false` if on the server (SSR). Doesn't cause hydration mismatches, so it is
|
|
6
|
+
* safe to use for conditional rendering.
|
|
7
|
+
*/
|
|
8
|
+
export function useIsClient() {
|
|
9
|
+
// We use `useSyncExternalStore` to get a value that is `true` on the client
|
|
10
|
+
// and `false` on the server, while avoiding hydration mismatches. It looks
|
|
11
|
+
// like a hack, but it conforms to the semantics of `useSyncExternalStore`,
|
|
12
|
+
// with *the environment* being the "external store" in this case. We just
|
|
13
|
+
// don't have any real subscription logic, since the value is static.
|
|
14
|
+
return useSyncExternalStore(emptySubscribe, getClientValue, getServerValue)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// These functions are just to satisfy the API of `useSyncExternalStore` in
|
|
18
|
+
// `useIsClient`, and defined outside the hook so they are stable references.
|
|
19
|
+
function emptySubscribe() { return emptyUnsubscribe }
|
|
20
|
+
function emptyUnsubscribe() {}
|
|
21
|
+
function getClientValue() { return true }
|
|
22
|
+
function getServerValue() { return false }
|
|
@@ -1,24 +1,6 @@
|
|
|
1
1
|
{{={= =}=}}
|
|
2
|
-
import { Outlet } from 'react-router'
|
|
3
|
-
import { initializeQueryClient } from '../operations'
|
|
4
|
-
import { WaspApp, type WaspAppProps } from './components/WaspApp'
|
|
5
|
-
|
|
6
|
-
const DefaultRootComponent = () => <Outlet />
|
|
7
|
-
|
|
8
|
-
let isAppInitialized = false
|
|
9
|
-
|
|
10
2
|
// PRIVATE API (web-app)
|
|
11
|
-
export
|
|
12
|
-
rootElement = <DefaultRootComponent />,
|
|
13
|
-
routesMapping,
|
|
14
|
-
}: WaspAppProps): React.ReactNode {
|
|
15
|
-
if (!isAppInitialized) {
|
|
16
|
-
initializeQueryClient()
|
|
17
|
-
isAppInitialized = true
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
return <WaspApp rootElement={rootElement} routesMapping={routesMapping} />
|
|
21
|
-
}
|
|
3
|
+
export { WaspApp } from './components/WaspApp'
|
|
22
4
|
|
|
23
5
|
{=# isAuthEnabled =}
|
|
24
6
|
// PRIVATE API (web-app)
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
{{={= =}=}}
|
|
2
|
+
import { StrictMode, type ReactNode } from "react";
|
|
3
|
+
import { useIsClient } from "./hooks/useIsClient.js"
|
|
4
|
+
|
|
5
|
+
export function Layout({
|
|
6
|
+
children,
|
|
7
|
+
isFallbackPage = false,
|
|
8
|
+
clientEntrySrc,
|
|
9
|
+
}: {
|
|
10
|
+
children?: ReactNode;
|
|
11
|
+
isFallbackPage?: boolean;
|
|
12
|
+
clientEntrySrc?: string;
|
|
13
|
+
}) {
|
|
14
|
+
const isClient = useIsClient()
|
|
15
|
+
|
|
16
|
+
/*
|
|
17
|
+
From the Vite SSR plugin, we inherit the concept of a "prerendered page" vs.
|
|
18
|
+
a "fallback page".
|
|
19
|
+
- A prerendered page is a page that is rendered on the server, and then
|
|
20
|
+
hydrated on the client.
|
|
21
|
+
- A fallback page is a page which only prerenders the common HTML structure
|
|
22
|
+
on the server, and then renders the actual page content on the client.
|
|
23
|
+
|
|
24
|
+
To use an analogy, a fallback page is a pluripotent stem cell that can turn
|
|
25
|
+
into any page in the client; while a prerendered page is already specialized
|
|
26
|
+
and can only render its specific content.
|
|
27
|
+
|
|
28
|
+
So, if we are prerendering a fallback page, we want to avoid rendering the
|
|
29
|
+
actual page content, so that it can turn into anything. If we're
|
|
30
|
+
prerendering a non-fallback page, we'll give it its content.
|
|
31
|
+
|
|
32
|
+
But, if we're already in the client, we always want to render the page
|
|
33
|
+
content. Whether prerendered as a fallback or not, now it's showtime, so we
|
|
34
|
+
must show the user the content.
|
|
35
|
+
|
|
36
|
+
Thus, we end up with the line below:
|
|
37
|
+
*/
|
|
38
|
+
const shouldRenderChildren = isClient || !isFallbackPage
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<StrictMode>
|
|
42
|
+
<html lang="en">
|
|
43
|
+
<head>
|
|
44
|
+
<meta charSet="utf-8" />
|
|
45
|
+
<meta
|
|
46
|
+
name="viewport"
|
|
47
|
+
content="minimum-scale=1, initial-scale=1, width=device-width, shrink-to-fit=no"
|
|
48
|
+
/>
|
|
49
|
+
|
|
50
|
+
{=& head =}
|
|
51
|
+
|
|
52
|
+
<title>{= title =}</title>
|
|
53
|
+
</head>
|
|
54
|
+
<body>
|
|
55
|
+
<noscript>You need to enable JavaScript to run this app.</noscript>
|
|
56
|
+
|
|
57
|
+
{
|
|
58
|
+
// We don't really need to wrap the app in a div nor name it "root",
|
|
59
|
+
// but we keep it for backwards compatibility with older Wasp
|
|
60
|
+
// versions.
|
|
61
|
+
}
|
|
62
|
+
<div id="root">
|
|
63
|
+
{shouldRenderChildren ? children : null}
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
{
|
|
67
|
+
// We pass that argument in SSR builds and not in client builds.
|
|
68
|
+
// This would usually cause a hydration mismatch, but React has an
|
|
69
|
+
// exception for `<script>` tags, for this specific usecase, so it
|
|
70
|
+
// will work fine.
|
|
71
|
+
clientEntrySrc ? (
|
|
72
|
+
// We'd usually use React prerender's `bootstrapModules` options for
|
|
73
|
+
// injecting this script, but it would also add a `<link
|
|
74
|
+
// rel="modulepreload">` tag that Vite doesn't handle correctly. So
|
|
75
|
+
// we just add the script ourselves in the regular way.
|
|
76
|
+
//
|
|
77
|
+
// https://react.dev/reference/react-dom/static/prerenderToNodeStream
|
|
78
|
+
<script
|
|
79
|
+
type="module"
|
|
80
|
+
src={clientEntrySrc}
|
|
81
|
+
// We make it `async` to decouple the tag's position from its
|
|
82
|
+
// execution phase. This way Vite can move it anywhere in the
|
|
83
|
+
// document to optimize loading performance.
|
|
84
|
+
async
|
|
85
|
+
/>
|
|
86
|
+
) : null
|
|
87
|
+
}
|
|
88
|
+
</body>
|
|
89
|
+
</html>
|
|
90
|
+
</StrictMode>
|
|
91
|
+
);
|
|
92
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{{={= =}=}}
|
|
2
|
+
import type { ReactNode, ComponentType } from 'react'
|
|
3
|
+
import { createBrowserRouter, RouterProvider, type RouteObject } from 'react-router'
|
|
4
|
+
|
|
5
|
+
{=# isExternalAuthEnabled =}
|
|
6
|
+
import { OAuthCallbackPage } from "./pages/OAuthCallback"
|
|
7
|
+
{=/ isExternalAuthEnabled =}
|
|
8
|
+
|
|
9
|
+
import { DefaultRootErrorBoundary } from './components/DefaultRootErrorBoundary'
|
|
10
|
+
|
|
11
|
+
import { routes } from '../router/index'
|
|
12
|
+
|
|
13
|
+
type RouteMapping = Record<
|
|
14
|
+
string,
|
|
15
|
+
| { lazy: () => Promise<{ Component: ComponentType }> }
|
|
16
|
+
| { Component: ComponentType }
|
|
17
|
+
>;
|
|
18
|
+
|
|
19
|
+
export function getRouteObjects({
|
|
20
|
+
routesMapping,
|
|
21
|
+
rootElement,
|
|
22
|
+
}: {
|
|
23
|
+
routesMapping: RouteMapping,
|
|
24
|
+
rootElement: ReactNode,
|
|
25
|
+
}): RouteObject[] {
|
|
26
|
+
const waspDefinedRoutes = [
|
|
27
|
+
{=# isExternalAuthEnabled =}
|
|
28
|
+
{
|
|
29
|
+
path: "{= oAuthCallbackPath =}",
|
|
30
|
+
Component: OAuthCallbackPage,
|
|
31
|
+
},
|
|
32
|
+
{=/ isExternalAuthEnabled =}
|
|
33
|
+
]
|
|
34
|
+
const userDefinedRoutes = Object.entries(routes).map(([routeKey, route]) => {
|
|
35
|
+
return {
|
|
36
|
+
path: route.to,
|
|
37
|
+
...routesMapping[routeKey],
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
return [{
|
|
42
|
+
path: '/',
|
|
43
|
+
element: rootElement,
|
|
44
|
+
ErrorBoundary: DefaultRootErrorBoundary,
|
|
45
|
+
children: [
|
|
46
|
+
...waspDefinedRoutes,
|
|
47
|
+
...userDefinedRoutes,
|
|
48
|
+
],
|
|
49
|
+
}]
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function getRouter({
|
|
53
|
+
routesMapping,
|
|
54
|
+
rootElement,
|
|
55
|
+
}: {
|
|
56
|
+
routesMapping: RouteMapping,
|
|
57
|
+
rootElement: ReactNode,
|
|
58
|
+
}) {
|
|
59
|
+
const routeObjects = getRouteObjects({ routesMapping, rootElement })
|
|
60
|
+
const browserRouter = createBrowserRouter(routeObjects, {
|
|
61
|
+
basename: '{= baseDir =}',
|
|
62
|
+
})
|
|
63
|
+
return <RouterProvider router={browserRouter} />;
|
|
64
|
+
}
|
|
@@ -1,22 +1,39 @@
|
|
|
1
1
|
{{={= =}=}}
|
|
2
|
-
import * as z from
|
|
2
|
+
import * as z from "zod"
|
|
3
3
|
|
|
4
4
|
{=# envValidationSchema.isDefined =}
|
|
5
5
|
{=& envValidationSchema.importStatement =}
|
|
6
|
-
const userClientEnvSchema = {= envValidationSchema.importIdentifier =}
|
|
6
|
+
const userClientEnvSchema = {= envValidationSchema.importIdentifier =};
|
|
7
7
|
{=/ envValidationSchema.isDefined =}
|
|
8
8
|
{=^ envValidationSchema.isDefined =}
|
|
9
|
-
const userClientEnvSchema = z.object({})
|
|
9
|
+
const userClientEnvSchema = z.object({});
|
|
10
10
|
{=/ envValidationSchema.isDefined =}
|
|
11
11
|
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
.url({
|
|
16
|
-
message: '{= serverUrlEnvVarName =} must be a valid URL',
|
|
12
|
+
const serverUrlSchema =
|
|
13
|
+
z.string({
|
|
14
|
+
error: '{= serverUrlEnvVarName =} is required',
|
|
17
15
|
})
|
|
18
|
-
.
|
|
19
|
-
|
|
16
|
+
.pipe(
|
|
17
|
+
z.url({
|
|
18
|
+
error: '{= serverUrlEnvVarName =} must be a valid URL',
|
|
19
|
+
})
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
const waspDevClientEnvSchema = z.object({
|
|
23
|
+
"{= serverUrlEnvVarName =}": serverUrlSchema
|
|
24
|
+
.default("{= defaultServerUrl =}"),
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const waspProdClientEnvSchema = z.object({
|
|
28
|
+
"{= serverUrlEnvVarName =}": serverUrlSchema,
|
|
29
|
+
});
|
|
20
30
|
|
|
21
31
|
// PRIVATE API (sdk, Vite config)
|
|
22
|
-
|
|
32
|
+
// TODO(franjo): Remove passing mode as param when this is no longer a plugin.
|
|
33
|
+
// See: https://github.com/wasp-lang/wasp/issues/3875.
|
|
34
|
+
export function getClientEnvSchema(mode: string) {
|
|
35
|
+
const waspClientEnvSchema = mode === "production"
|
|
36
|
+
? waspProdClientEnvSchema
|
|
37
|
+
: waspDevClientEnvSchema;
|
|
38
|
+
return z.object({ ...userClientEnvSchema.shape, ...waspClientEnvSchema.shape })
|
|
39
|
+
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { ensureEnvSchema } from "../env/validation.js";
|
|
2
|
+
import { getClientEnvSchema } from "./env/schema.js";
|
|
3
3
|
|
|
4
4
|
// PUBLIC API
|
|
5
|
-
export const env = ensureEnvSchema(
|
|
5
|
+
export const env = ensureEnvSchema(
|
|
6
|
+
import.meta.env,
|
|
7
|
+
getClientEnvSchema(import.meta.env.MODE),
|
|
8
|
+
);
|