@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
|
@@ -1,19 +1,17 @@
|
|
|
1
|
-
import { type Plugin } from
|
|
1
|
+
import { type Plugin } from "vite";
|
|
2
2
|
|
|
3
|
-
import { loadEnvVars } from './envFile.js'
|
|
4
3
|
import {
|
|
4
|
+
formatZodEnvError,
|
|
5
5
|
getValidatedEnvOrError,
|
|
6
|
-
|
|
7
|
-
} from
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
const redColorFormatString = getColorizedConsoleFormatString('red');
|
|
6
|
+
} from "../../../env/validation.js";
|
|
7
|
+
import { colorize } from "../../../universal/ansiColors.js";
|
|
8
|
+
import { getClientEnvSchema } from "../../env/schema.js";
|
|
9
|
+
import { loadEnvVars } from "./envFile.js";
|
|
12
10
|
|
|
13
11
|
export function validateEnv(): Plugin {
|
|
14
|
-
let validationResult: ReturnType<typeof getValidatedEnvOrError> | null = null
|
|
12
|
+
let validationResult: ReturnType<typeof getValidatedEnvOrError> | null = null;
|
|
15
13
|
return {
|
|
16
|
-
name:
|
|
14
|
+
name: "wasp:validate-env",
|
|
17
15
|
async configResolved(config) {
|
|
18
16
|
const env = await loadEnvVars({
|
|
19
17
|
rootDir: config.root,
|
|
@@ -23,33 +21,34 @@ export function validateEnv(): Plugin {
|
|
|
23
21
|
// We load the env file variables only in development,
|
|
24
22
|
// when building for production, users are expected to
|
|
25
23
|
// provide the environment variables inline.
|
|
26
|
-
loadDotEnvFile: config.command ===
|
|
27
|
-
})
|
|
28
|
-
|
|
24
|
+
loadDotEnvFile: config.command === "serve",
|
|
25
|
+
});
|
|
26
|
+
const schema = getClientEnvSchema(config.mode);
|
|
27
|
+
validationResult = getValidatedEnvOrError(env, schema);
|
|
29
28
|
|
|
30
29
|
// Exit if we are in build mode, because we can't show the error in the browser.
|
|
31
|
-
if (config.command ===
|
|
32
|
-
const
|
|
33
|
-
console.error(
|
|
34
|
-
process.exit(1)
|
|
30
|
+
if (config.command === "build" && !validationResult.success) {
|
|
31
|
+
const validationErrorMessage = formatZodEnvError(validationResult.error);
|
|
32
|
+
console.error(colorize("red", validationErrorMessage));
|
|
33
|
+
process.exit(1);
|
|
35
34
|
}
|
|
36
35
|
},
|
|
37
36
|
configureServer: (server) => {
|
|
38
37
|
if (validationResult === null || validationResult.success) {
|
|
39
|
-
return
|
|
38
|
+
return;
|
|
40
39
|
}
|
|
41
40
|
|
|
42
41
|
// Send the error to the browser.
|
|
43
|
-
const
|
|
44
|
-
server.ws.on(
|
|
42
|
+
const validationErrorMessage = formatZodEnvError(validationResult.error);
|
|
43
|
+
server.ws.on("connection", () => {
|
|
45
44
|
server.ws.send({
|
|
46
|
-
type:
|
|
45
|
+
type: "error",
|
|
47
46
|
err: {
|
|
48
|
-
message,
|
|
49
|
-
stack:
|
|
47
|
+
message: validationErrorMessage,
|
|
48
|
+
stack: "",
|
|
50
49
|
},
|
|
51
|
-
})
|
|
52
|
-
})
|
|
50
|
+
});
|
|
51
|
+
});
|
|
53
52
|
},
|
|
54
|
-
}
|
|
53
|
+
};
|
|
55
54
|
}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
{{={= =}=}}
|
|
2
2
|
import { type Plugin } from "vite";
|
|
3
3
|
import {
|
|
4
|
-
|
|
4
|
+
getClientEntryTsxContent,
|
|
5
5
|
getRoutesTsxContent,
|
|
6
|
+
getSsrEntryTsxContent,
|
|
6
7
|
} from "../virtual-files/index.js";
|
|
7
8
|
import { makeVirtualFilesResolver, type VirtualFiles } from "../virtual-files/resolver.js";
|
|
8
9
|
|
|
9
10
|
const resolveVirtualFiles = makeVirtualFilesResolver([
|
|
10
|
-
{ id: "{= clientEntryPointPath =}", load:
|
|
11
|
+
{ id: "{= clientEntryPointPath =}", load: getClientEntryTsxContent },
|
|
11
12
|
{ id: "{= routesEntryPointPath =}", load: getRoutesTsxContent },
|
|
13
|
+
{ id: "{= ssrEntryPointPath =}", load: getSsrEntryTsxContent },
|
|
12
14
|
]);
|
|
13
15
|
|
|
14
16
|
export function waspVirtualModules(): Plugin {
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
+
{{={= =}=}}
|
|
1
2
|
import { type PluginOption } from "vite";
|
|
2
3
|
import react, { type Options as ReactOptions } from "@vitejs/plugin-react";
|
|
4
|
+
import ssr from "@wasp.sh/lib-vite-ssr";
|
|
3
5
|
import { validateEnv } from "./validateEnv.js";
|
|
4
6
|
import { envFile } from "./envFile.js";
|
|
5
7
|
import { detectServerImports } from "./detectServerImports.js";
|
|
6
8
|
import { waspVirtualModules } from "./virtualModules.js";
|
|
7
|
-
import { waspHtmlDev } from "./html/dev.js";
|
|
8
|
-
import { waspHtmlBuild } from "./html/build.js";
|
|
9
9
|
import { typescriptCheck } from "./typescriptCheck.js";
|
|
10
10
|
import { waspConfig } from "./waspConfig.js";
|
|
11
11
|
|
|
@@ -28,9 +28,13 @@ export function wasp(options?: WaspPluginOptions): PluginOption {
|
|
|
28
28
|
* Plugins running after core Vite plugins.
|
|
29
29
|
*/
|
|
30
30
|
typescriptCheck(),
|
|
31
|
-
waspHtmlDev(),
|
|
32
|
-
waspHtmlBuild(),
|
|
33
31
|
validateEnv(),
|
|
34
32
|
react(options?.reactOptions),
|
|
33
|
+
ssr({
|
|
34
|
+
clientEntrySrc: "{= clientEntryPointPath =}",
|
|
35
|
+
ssrEntrySrc: "{= ssrEntryPointPath =}",
|
|
36
|
+
ssrPaths: [],
|
|
37
|
+
ssrFallbackFile: "{= ssrFallbackFile =}",
|
|
38
|
+
}),
|
|
35
39
|
];
|
|
36
40
|
}
|
|
@@ -1,25 +1,46 @@
|
|
|
1
1
|
{{={= =}=}}
|
|
2
|
-
|
|
2
|
+
/// <reference types="vitest/config" />
|
|
3
|
+
import { type PluginOption } from "vite";
|
|
3
4
|
import { defaultExclude } from "vitest/config"
|
|
4
5
|
|
|
6
|
+
// Vite merges `userConfig` and our `waspConfig` returned from the plugin.
|
|
7
|
+
// In that merge, primitive values from waspConfig take precedence, and
|
|
8
|
+
// arrays are concatenated.
|
|
9
|
+
//
|
|
10
|
+
// This allows us to treat config values differently:
|
|
11
|
+
// - Forced: hardcoded in the return object so they always win. If the
|
|
12
|
+
// user set one of these in their vite.config.ts, we throw an error.
|
|
13
|
+
// - Overridable: we read the user's value and use it or fall back to
|
|
14
|
+
// our default.
|
|
15
|
+
// - Additive (arrays): we only return Wasp's entries; Vite's merge
|
|
16
|
+
// appends them to whatever the user already has.
|
|
17
|
+
|
|
18
|
+
const forcedOptions = {
|
|
19
|
+
'base': "{= baseDir =}",
|
|
20
|
+
'envPrefix': "REACT_APP_",
|
|
21
|
+
'build.outDir': "{= clientBuildDirPath =}",
|
|
22
|
+
} as const;
|
|
23
|
+
|
|
5
24
|
export function waspConfig(): PluginOption {
|
|
6
25
|
return {
|
|
7
26
|
name: "wasp:config",
|
|
8
27
|
enforce: 'pre',
|
|
9
28
|
config(config) {
|
|
10
|
-
|
|
11
|
-
|
|
29
|
+
throwIfOverridingForcedOptions(config);
|
|
30
|
+
|
|
31
|
+
// Returned config is merged with the user's config by Vite (mergeConfig).
|
|
32
|
+
return {
|
|
33
|
+
base: forcedOptions['base'],
|
|
12
34
|
optimizeDeps: {
|
|
13
35
|
exclude: {=& depsExcludedFromOptimization =}
|
|
14
36
|
},
|
|
15
37
|
server: {
|
|
16
|
-
port: {= defaultClientPort =},
|
|
17
|
-
host: "0.0.0.0",
|
|
18
|
-
open: true,
|
|
38
|
+
port: useUserValue(config.server?.port, {= defaultClientPort =}),
|
|
39
|
+
host: useUserValue(config.server?.host, "0.0.0.0"),
|
|
19
40
|
},
|
|
20
|
-
envPrefix:
|
|
41
|
+
envPrefix: forcedOptions['envPrefix'],
|
|
21
42
|
build: {
|
|
22
|
-
outDir:
|
|
43
|
+
outDir: forcedOptions['build.outDir'],
|
|
23
44
|
},
|
|
24
45
|
resolve: {
|
|
25
46
|
// These packages rely on a single instance per page. Not deduping them
|
|
@@ -42,15 +63,40 @@ export function waspConfig(): PluginOption {
|
|
|
42
63
|
],
|
|
43
64
|
},
|
|
44
65
|
test: {
|
|
45
|
-
globals: true,
|
|
46
|
-
environment: "jsdom",
|
|
66
|
+
globals: useUserValue(config.test?.globals, true),
|
|
67
|
+
environment: useUserValue(config.test?.environment, "jsdom"),
|
|
47
68
|
setupFiles: {=& vitest.setupFilesArray =},
|
|
48
69
|
exclude: [
|
|
49
70
|
...defaultExclude,
|
|
50
71
|
"{= vitest.excludeWaspArtefactsPattern =}",
|
|
51
72
|
]
|
|
52
73
|
},
|
|
53
|
-
}
|
|
74
|
+
};
|
|
54
75
|
}
|
|
55
76
|
};
|
|
56
77
|
}
|
|
78
|
+
|
|
79
|
+
function useUserValue<T>(userValue: T | undefined, defaultValue: T): T {
|
|
80
|
+
return userValue ?? defaultValue;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function throwIfOverridingForcedOptions(config: Record<string, any>): void {
|
|
84
|
+
const conflicts: string[] = [];
|
|
85
|
+
for (const [path, forcedValue] of Object.entries(forcedOptions)) {
|
|
86
|
+
const userValue = getByPath(config, path);
|
|
87
|
+
if (userValue !== undefined && userValue !== forcedValue) {
|
|
88
|
+
conflicts.push(
|
|
89
|
+
` - "${path}" is set to ${JSON.stringify(userValue)}, but Wasp requires ${JSON.stringify(forcedValue)}`
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (conflicts.length > 0) {
|
|
94
|
+
throw new Error(
|
|
95
|
+
`Your vite.config.ts sets options that Wasp controls:\n${conflicts.join('\n')}\n\nRemove these from your Vite config, Wasp sets them automatically.`
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function getByPath(obj: Record<string, any>, path: string): unknown {
|
|
101
|
+
return path.split('.').reduce<any>((node, segment) => node?.[segment], obj);
|
|
102
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{{={= =}=}}
|
|
2
|
+
import { startTransition } from "react";
|
|
3
|
+
import { hydrateRoot } from "react-dom/client";
|
|
4
|
+
import { createBrowserRouter, type HydrationState } from "react-router";
|
|
5
|
+
import { RouterProvider } from "react-router/dom";
|
|
6
|
+
import { Layout } from "wasp/client/app/layout";
|
|
7
|
+
import { WaspApp } from "wasp/client/app";
|
|
8
|
+
|
|
9
|
+
{=& routeObjects.importStatement =}
|
|
10
|
+
|
|
11
|
+
// React Router will put hydration data on this property of the `window` object.
|
|
12
|
+
// https://reactrouter.com/7.13.1/start/data/custom#4-hydrate-in-the-browser
|
|
13
|
+
const hydrationData = (window as any).__staticRouterHydrationData as HydrationState | undefined;
|
|
14
|
+
|
|
15
|
+
const router = createBrowserRouter({= routeObjects.importIdentifier =}, {
|
|
16
|
+
basename: "{= baseDir =}",
|
|
17
|
+
hydrationData,
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
function App({ isFallbackPage }: { isFallbackPage: boolean }) {
|
|
21
|
+
return (
|
|
22
|
+
<Layout isFallbackPage={isFallbackPage}>
|
|
23
|
+
<WaspApp>
|
|
24
|
+
<RouterProvider router={router} />
|
|
25
|
+
</WaspApp>
|
|
26
|
+
</Layout>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
startTransition(() => {
|
|
31
|
+
const isFallbackpage = hydrationData == null;
|
|
32
|
+
hydrateRoot(document, <App isFallbackPage={isFallbackpage} />);
|
|
33
|
+
});
|
|
@@ -1,17 +1,66 @@
|
|
|
1
1
|
{{={= =}=}}
|
|
2
|
-
|
|
2
|
+
import { getRouteObjects } from "wasp/client/app/router";
|
|
3
|
+
import { initializeQueryClient } from "wasp/client/operations";
|
|
4
|
+
|
|
3
5
|
{=# isAuthEnabled =}
|
|
4
6
|
import { createAuthRequiredPage } from "wasp/client/app"
|
|
5
7
|
{=/ isAuthEnabled =}
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
{
|
|
10
|
-
|
|
11
|
-
{
|
|
9
|
+
{=# rootComponent.isDefined =}
|
|
10
|
+
{=& rootComponent.importStatement =}
|
|
11
|
+
{=/ rootComponent.isDefined =}
|
|
12
|
+
|
|
13
|
+
{=# setupFn.isDefined =}
|
|
14
|
+
{=& setupFn.importStatement =}
|
|
15
|
+
{=/ setupFn.isDefined =}
|
|
12
16
|
|
|
13
|
-
|
|
17
|
+
{=# routes =}
|
|
18
|
+
{=^ isLazy =}
|
|
19
|
+
{=& import.importStatement =}
|
|
20
|
+
{=/ isLazy =}
|
|
21
|
+
{=/ routes =}
|
|
22
|
+
|
|
23
|
+
const routesMapping = {
|
|
14
24
|
{=# routes =}
|
|
15
|
-
{
|
|
25
|
+
{=# isLazy =}
|
|
26
|
+
{= name =}: { lazy: async () => {
|
|
27
|
+
const Component = await {=& import.dynamicImportExpression =}
|
|
28
|
+
{=# isAuthRequired =}
|
|
29
|
+
return { Component: createAuthRequiredPage(Component) }
|
|
30
|
+
{=/ isAuthRequired =}
|
|
31
|
+
{=^ isAuthRequired =}
|
|
32
|
+
return { Component }
|
|
33
|
+
{=/ isAuthRequired =}
|
|
34
|
+
}},
|
|
35
|
+
{=/ isLazy =}
|
|
36
|
+
{=^ isLazy =}
|
|
37
|
+
{= name =}: {
|
|
38
|
+
{=# isAuthRequired =}
|
|
39
|
+
Component: createAuthRequiredPage({= import.importIdentifier =}),
|
|
40
|
+
{=/ isAuthRequired =}
|
|
41
|
+
{=^ isAuthRequired =}
|
|
42
|
+
Component: {= import.importIdentifier =},
|
|
43
|
+
{=/ isAuthRequired =}
|
|
44
|
+
},
|
|
45
|
+
{=/ isLazy =}
|
|
16
46
|
{=/ routes =}
|
|
17
47
|
} as const;
|
|
48
|
+
|
|
49
|
+
{=# setupFn.isDefined =}
|
|
50
|
+
await {= setupFn.importIdentifier =}()
|
|
51
|
+
{=/ setupFn.isDefined =}
|
|
52
|
+
|
|
53
|
+
initializeQueryClient()
|
|
54
|
+
|
|
55
|
+
const rootElement =
|
|
56
|
+
{=# rootComponent.isDefined =}
|
|
57
|
+
<{= rootComponent.importIdentifier =} />
|
|
58
|
+
{=/ rootComponent.isDefined =}
|
|
59
|
+
{=^ rootComponent.isDefined =}
|
|
60
|
+
undefined
|
|
61
|
+
{=/ rootComponent.isDefined =}
|
|
62
|
+
|
|
63
|
+
export const routeObjects = getRouteObjects({
|
|
64
|
+
routesMapping,
|
|
65
|
+
rootElement,
|
|
66
|
+
})
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{{={= =}=}}
|
|
2
|
+
import type { PrerenderContext, PrerenderFn } from "@wasp.sh/lib-vite-ssr/types";
|
|
3
|
+
import * as streamConsumers from "node:stream/consumers";
|
|
4
|
+
import assert from "node:assert/strict";
|
|
5
|
+
import type { ReactNode } from "react";
|
|
6
|
+
import { prerenderToNodeStream as reactPrerender } from "react-dom/static";
|
|
7
|
+
import {
|
|
8
|
+
createStaticHandler,
|
|
9
|
+
createStaticRouter,
|
|
10
|
+
RouterProvider,
|
|
11
|
+
} from "react-router";
|
|
12
|
+
import { Layout } from "wasp/client/app/layout";
|
|
13
|
+
import { WaspApp } from "wasp/client/app";
|
|
14
|
+
|
|
15
|
+
{=& routeObjects.importStatement =}
|
|
16
|
+
|
|
17
|
+
const FALLBACK_FILE = "{= ssrFallbackFile =}";
|
|
18
|
+
|
|
19
|
+
const prerenderApp: PrerenderFn = async (route, ctx) => {
|
|
20
|
+
const isFallbackPage = route === FALLBACK_FILE;
|
|
21
|
+
|
|
22
|
+
if (isFallbackPage) {
|
|
23
|
+
return await appToHtml({ isFallbackPage: true, children: null }, ctx);
|
|
24
|
+
} else {
|
|
25
|
+
const { query, dataRoutes } = createStaticHandler({= routeObjects.importIdentifier =}, {
|
|
26
|
+
basename: "{= baseDir =}",
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const req = new Request(new URL(route, "http://localhost"));
|
|
30
|
+
|
|
31
|
+
const context = await query(req);
|
|
32
|
+
assert (!(context instanceof Response), "Expected no redirect responses from static handler");
|
|
33
|
+
|
|
34
|
+
const router = createStaticRouter(dataRoutes, context);
|
|
35
|
+
|
|
36
|
+
return await appToHtml(
|
|
37
|
+
{ isFallbackPage: false, children: <RouterProvider router={router} /> },
|
|
38
|
+
ctx,
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export default prerenderApp;
|
|
44
|
+
|
|
45
|
+
async function appToHtml(
|
|
46
|
+
{
|
|
47
|
+
isFallbackPage,
|
|
48
|
+
children,
|
|
49
|
+
}: { isFallbackPage: boolean; children?: ReactNode },
|
|
50
|
+
{ clientEntrySrc }: PrerenderContext,
|
|
51
|
+
) {
|
|
52
|
+
const app = (
|
|
53
|
+
<Layout isFallbackPage={isFallbackPage} clientEntrySrc={clientEntrySrc}>
|
|
54
|
+
<WaspApp>
|
|
55
|
+
{children}
|
|
56
|
+
</WaspApp>
|
|
57
|
+
</Layout>
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
const html = await reactPrerender(app)
|
|
61
|
+
.then((result) => streamConsumers.text(result.prelude))
|
|
62
|
+
|
|
63
|
+
return html;
|
|
64
|
+
};
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
|
|
3
|
-
export function
|
|
4
|
-
return getFileContentFromRelativePath("./files/
|
|
3
|
+
export function getClientEntryTsxContent(): string {
|
|
4
|
+
return getFileContentFromRelativePath("./files/client-entry.tsx");
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
export function getRoutesTsxContent(): string {
|
|
8
8
|
return getFileContentFromRelativePath("./files/routes.tsx");
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
export function
|
|
12
|
-
return getFileContentFromRelativePath("./files/
|
|
11
|
+
export function getSsrEntryTsxContent(): string {
|
|
12
|
+
return getFileContentFromRelativePath("./files/ssr-entry.tsx");
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
function getFileContentFromRelativePath(relativePath: string): string {
|
|
@@ -1,50 +1,73 @@
|
|
|
1
1
|
export type DataStore = {
|
|
2
|
-
getPrefixedKey(key: string): string
|
|
3
|
-
set(key: string, value: unknown): void
|
|
4
|
-
get(key: string): unknown
|
|
5
|
-
remove(key: string): void
|
|
6
|
-
clear(): void
|
|
2
|
+
getPrefixedKey(key: string): string;
|
|
3
|
+
set(key: string, value: unknown): void;
|
|
4
|
+
get(key: string): unknown;
|
|
5
|
+
remove(key: string): void;
|
|
6
|
+
clear(): void;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const createStorage =
|
|
10
|
+
typeof window === "undefined" || !window.localStorage
|
|
11
|
+
? createMemoryDataStore
|
|
12
|
+
: createLocalStorageDataStore
|
|
13
|
+
|
|
14
|
+
export const storage = createStorage("wasp");
|
|
15
|
+
|
|
16
|
+
function createMemoryDataStore(prefix: string): DataStore {
|
|
17
|
+
const store: Map<string, unknown> = new Map();
|
|
18
|
+
|
|
19
|
+
function getPrefixedKey(key: string): string {
|
|
20
|
+
return `${prefix}:${key}`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
getPrefixedKey,
|
|
25
|
+
set(key, value) {
|
|
26
|
+
store.set(getPrefixedKey(key), value);
|
|
27
|
+
},
|
|
28
|
+
get(key) {
|
|
29
|
+
return store.get(getPrefixedKey(key));
|
|
30
|
+
},
|
|
31
|
+
remove(key) {
|
|
32
|
+
store.delete(getPrefixedKey(key));
|
|
33
|
+
},
|
|
34
|
+
clear() {
|
|
35
|
+
store.clear();
|
|
36
|
+
},
|
|
37
|
+
};
|
|
7
38
|
}
|
|
8
39
|
|
|
9
40
|
function createLocalStorageDataStore(prefix: string): DataStore {
|
|
41
|
+
if (!window.localStorage) {
|
|
42
|
+
throw new Error("Local storage is not available.");
|
|
43
|
+
}
|
|
44
|
+
|
|
10
45
|
function getPrefixedKey(key: string): string {
|
|
11
|
-
return `${prefix}:${key}
|
|
46
|
+
return `${prefix}:${key}`;
|
|
12
47
|
}
|
|
13
48
|
|
|
14
49
|
return {
|
|
15
50
|
getPrefixedKey,
|
|
16
51
|
set(key, value) {
|
|
17
|
-
|
|
18
|
-
localStorage.setItem(getPrefixedKey(key), JSON.stringify(value))
|
|
52
|
+
localStorage.setItem(getPrefixedKey(key), JSON.stringify(value));
|
|
19
53
|
},
|
|
20
54
|
get(key) {
|
|
21
|
-
|
|
22
|
-
const value = localStorage.getItem(getPrefixedKey(key))
|
|
55
|
+
const value = localStorage.getItem(getPrefixedKey(key));
|
|
23
56
|
try {
|
|
24
|
-
return value ? JSON.parse(value) : undefined
|
|
57
|
+
return value ? JSON.parse(value) : undefined;
|
|
25
58
|
} catch (e: any) {
|
|
26
|
-
return undefined
|
|
59
|
+
return undefined;
|
|
27
60
|
}
|
|
28
61
|
},
|
|
29
62
|
remove(key) {
|
|
30
|
-
|
|
31
|
-
localStorage.removeItem(getPrefixedKey(key))
|
|
63
|
+
localStorage.removeItem(getPrefixedKey(key));
|
|
32
64
|
},
|
|
33
65
|
clear() {
|
|
34
|
-
ensureLocalStorageIsAvailable()
|
|
35
66
|
Object.keys(localStorage).forEach((key) => {
|
|
36
67
|
if (key.startsWith(prefix)) {
|
|
37
|
-
localStorage.removeItem(key)
|
|
68
|
+
localStorage.removeItem(key);
|
|
38
69
|
}
|
|
39
|
-
})
|
|
70
|
+
});
|
|
40
71
|
},
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export const storage = createLocalStorageDataStore('wasp')
|
|
45
|
-
|
|
46
|
-
function ensureLocalStorageIsAvailable(): void {
|
|
47
|
-
if (!window.localStorage) {
|
|
48
|
-
throw new Error('Local storage is not available.')
|
|
49
|
-
}
|
|
72
|
+
};
|
|
50
73
|
}
|
|
@@ -1,38 +1,44 @@
|
|
|
1
|
-
import * as z from
|
|
1
|
+
import * as z from "zod";
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
const redColorFormatString = getColorizedConsoleFormatString('red');
|
|
3
|
+
import { colorize } from "wasp/universal/ansiColors";
|
|
6
4
|
|
|
7
5
|
// PRIVATE API (SDK)
|
|
8
|
-
export function ensureEnvSchema<Schema extends z.
|
|
6
|
+
export function ensureEnvSchema<Schema extends z.ZodType>(
|
|
9
7
|
data: unknown,
|
|
10
|
-
schema: Schema
|
|
8
|
+
schema: Schema,
|
|
11
9
|
): z.infer<Schema> {
|
|
12
|
-
const result = getValidatedEnvOrError(data, schema)
|
|
10
|
+
const result = getValidatedEnvOrError(data, schema);
|
|
13
11
|
if (result.success) {
|
|
14
|
-
return result.data
|
|
12
|
+
return result.data;
|
|
15
13
|
} else {
|
|
16
|
-
console.error(
|
|
17
|
-
throw new Error(
|
|
14
|
+
console.error(colorize("red", formatZodEnvError(result.error)));
|
|
15
|
+
throw new Error("Error parsing environment variables");
|
|
18
16
|
}
|
|
19
17
|
}
|
|
20
18
|
|
|
21
19
|
// PRIVATE API (SDK, Vite config)
|
|
22
|
-
export function getValidatedEnvOrError<Schema extends z.
|
|
20
|
+
export function getValidatedEnvOrError<Schema extends z.ZodType>(
|
|
23
21
|
env: unknown,
|
|
24
|
-
schema: Schema
|
|
25
|
-
): z.
|
|
26
|
-
return schema.safeParse(env)
|
|
22
|
+
schema: Schema,
|
|
23
|
+
): z.ZodSafeParseResult<z.infer<Schema>> {
|
|
24
|
+
return schema.safeParse(env);
|
|
27
25
|
}
|
|
28
26
|
|
|
29
27
|
// PRIVATE API (SDK, Vite config)
|
|
30
|
-
export function
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
28
|
+
export function formatZodEnvError(error: z.ZodError): string {
|
|
29
|
+
const flattenedIssues = z.flattenError(error);
|
|
30
|
+
|
|
31
|
+
return [
|
|
32
|
+
"══ Env vars validation failed ══",
|
|
33
|
+
"",
|
|
34
|
+
// Top-level errors
|
|
35
|
+
...flattenedIssues.formErrors,
|
|
36
|
+
"",
|
|
37
|
+
// Errors per field
|
|
38
|
+
...Object.entries(flattenedIssues.fieldErrors).map(
|
|
39
|
+
([prop, error]) => `${prop} - ${error}`,
|
|
40
|
+
),
|
|
41
|
+
"",
|
|
42
|
+
"════════════════════════════════",
|
|
43
|
+
].join("\n");
|
|
38
44
|
}
|
|
@@ -121,6 +121,8 @@
|
|
|
121
121
|
"./client/env/schema": "./dist/client/env/schema.js",
|
|
122
122
|
{=! Private: [client] =}
|
|
123
123
|
"./client/app": "./dist/client/app/index.jsx",
|
|
124
|
+
"./client/app/layout": "./dist/client/app/layout.jsx",
|
|
125
|
+
"./client/app/router": "./dist/client/app/router.jsx",
|
|
124
126
|
{=! Private: [client] =}
|
|
125
127
|
"./client/vite": "./dist/client/vite/index.js",
|
|
126
128
|
|