diff --git a/waspc/data/Generator/templates/react-app/react-router.config.ts b/waspc/data/Generator/templates/react-app/react-router.config.ts
index 3ff1b9fa81..ae08cbd646 100644
--- a/waspc/data/Generator/templates/react-app/react-router.config.ts
+++ b/waspc/data/Generator/templates/react-app/react-router.config.ts
@@ -1,6 +1,12 @@
import type { Config } from "@react-router/dev/config";
+let isSSR = false;
+
+{=# ssr.isDefined =}
+ isSSR = Boolean("{= ssr.value =}")
+{=/ ssr.isDefined =},
+
export default {
appDirectory: "src",
- ssr: false,
+ ssr: isSSR,
} satisfies Config;
diff --git a/waspc/data/Generator/templates/react-app/src/catchall.tsx b/waspc/data/Generator/templates/react-app/src/catchall.tsx
deleted file mode 100644
index 77b120de5a..0000000000
--- a/waspc/data/Generator/templates/react-app/src/catchall.tsx
+++ /dev/null
@@ -1,5 +0,0 @@
-import { router } from './router'
-
-export default function Component() {
- return router;
-}
\ No newline at end of file
diff --git a/waspc/data/Generator/templates/react-app/src/root.tsx b/waspc/data/Generator/templates/react-app/src/root.tsx
index e486034913..0de566da8c 100644
--- a/waspc/data/Generator/templates/react-app/src/root.tsx
+++ b/waspc/data/Generator/templates/react-app/src/root.tsx
@@ -6,6 +6,7 @@ import {
Scripts,
ScrollRestoration,
} from "react-router";
+import { DefaultRootErrorBoundary } from "./components/DefaultRootErrorBoundary";
export function Layout({
children,
@@ -35,6 +36,9 @@ export function Layout({
);
}
+
export default function Root() {
return ;
-}
\ No newline at end of file
+}
+
+export const ErrorBoundary = DefaultRootErrorBoundary
\ No newline at end of file
diff --git a/waspc/data/Generator/templates/react-app/src/router.tsx b/waspc/data/Generator/templates/react-app/src/router.tsx
deleted file mode 100644
index 95ff418147..0000000000
--- a/waspc/data/Generator/templates/react-app/src/router.tsx
+++ /dev/null
@@ -1,57 +0,0 @@
-{{={= =}=}}
-import React from 'react'
-import { createBrowserRouter, RouterProvider } from 'react-router'
-{=# rootComponent.isDefined =}
-{=& rootComponent.importStatement =}
-{=/ rootComponent.isDefined =}
-
-{=# isAuthEnabled =}
-import createAuthRequiredPage from "./auth/pages/createAuthRequiredPage"
-{=/ isAuthEnabled =}
-
-{=# pagesToImport =}
-{=& importStatement =}
-{=/ pagesToImport =}
-
-{=# isExternalAuthEnabled =}
-import { OAuthCallbackPage } from "./auth/pages/OAuthCallback"
-{=/ isExternalAuthEnabled =}
-
-import { DefaultRootErrorBoundary } from './components/DefaultRootErrorBoundary'
-
-import { routes } from 'wasp/client/router'
-
-export const routeNameToRouteComponent = {
- {=# routes =}
- {= name =}: {= targetComponent =},
- {=/ routes =}
-} as const;
-
-const waspDefinedRoutes = [
- {=# isExternalAuthEnabled =}
- {
- path: "{= oAuthCallbackPath =}",
- Component: OAuthCallbackPage,
- },
- {=/ isExternalAuthEnabled =}
-]
-const userDefinedRoutes = Object.entries(routes).map(([routeKey, route]) => {
- return {
- path: route.to,
- Component: routeNameToRouteComponent[routeKey],
- }
-})
-
-const browserRouter = createBrowserRouter([{
- path: '/',
- {=# rootComponent.isDefined =}
- element: <{= rootComponent.importIdentifier =} />,
- {=/ rootComponent.isDefined =}
- ErrorBoundary: DefaultRootErrorBoundary,
- children: [
- ...waspDefinedRoutes,
- ...userDefinedRoutes,
- ],
-}])
-
-export const router =
diff --git a/waspc/data/Generator/templates/react-app/src/routes.ts b/waspc/data/Generator/templates/react-app/src/routes.ts
index 9b48bfbc21..5426dede75 100644
--- a/waspc/data/Generator/templates/react-app/src/routes.ts
+++ b/waspc/data/Generator/templates/react-app/src/routes.ts
@@ -2,8 +2,34 @@ import {
type RouteConfig,
route,
} from "@react-router/dev/routes";
+import { layout, route } from "@react-router/dev/routes";
+import { routes } from 'wasp/client/router'
+
+export const routeNameToRouteComponent = {
+ {=# routes =}
+ {= name =}: {= targetComponent =},
+ {=/ routes =}
+} as const;
+
+const waspDefinedRoutes = [
+ {=# isExternalAuthEnabled =}
+ route("{= oAuthCallbackPath =}", "./auth/pages/OAuthCallback"),
+ {=/ isExternalAuthEnabled =}
+]
+const userDefinedRoutes = Object.entries(routes).map(([routeKey, route]) => {
+ // TODO: This should be the path to the route module file (eg. /src/routes/home.tsx)
+ return route(routeKey, route)
+
+})
+
export default [
- // * matches all URLs, the ? makes it optional so it will match / as well
- route("*?", "catchall.tsx"),
+ layout(
+ // TODO: This should maybe always be defined, if not,
+ // TODO: the code should be (if defined => layout, otherwise => spread the routes normally in the array)
+ {=# rootComponent.isDefined =}
+ "{= rootComponent.importIdentifier =}"
+ {=/ rootComponent.isDefined =},
+ [...waspDefinedRoutes, ...userDefinedRoutes]
+ ),
] satisfies RouteConfig;
\ No newline at end of file
diff --git a/waspc/data/Generator/templates/react-app/vite.config.ts b/waspc/data/Generator/templates/react-app/vite.config.ts
index 686fd3cf06..4b6f9daf6d 100644
--- a/waspc/data/Generator/templates/react-app/vite.config.ts
+++ b/waspc/data/Generator/templates/react-app/vite.config.ts
@@ -3,7 +3,7 @@
import { mergeConfig } from "vite";
import { reactRouter } from "@react-router/dev/vite";
import { defaultExclude } from "vitest/config"
-
+import { reactRouterDevtools } from "react-router-devtools";
{=# customViteConfig.isDefined =}
// Ignoring the TS error because we are importing a file outside of TS root dir.
// @ts-ignore
@@ -16,7 +16,7 @@ const _waspUserProvidedConfig = {};
const defaultViteConfig = {
base: "{= baseDir =}",
- plugins: [reactRouter()],
+ plugins: [reactRouterDevtools(), reactRouter()],
optimizeDeps: {
exclude: ['wasp']
},
diff --git a/waspc/data/Generator/templates/sdk/wasp/api/index.ts b/waspc/data/Generator/templates/sdk/wasp/api/index.ts
index 69631e8f8b..f99f179100 100644
--- a/waspc/data/Generator/templates/sdk/wasp/api/index.ts
+++ b/waspc/data/Generator/templates/sdk/wasp/api/index.ts
@@ -11,7 +11,7 @@ export const api: AxiosInstance = axios.create({
const WASP_APP_AUTH_SESSION_ID_NAME = 'sessionId'
-let waspAppAuthSessionId: string | undefined = undefined // storage.get(WASP_APP_AUTH_SESSION_ID_NAME) as string | undefined
+let waspAppAuthSessionId: string | undefined = storage.get(WASP_APP_AUTH_SESSION_ID_NAME)
// PRIVATE API (sdk)
export function setSessionId(sessionId: string): void {
@@ -39,37 +39,39 @@ export function removeLocalUserData(): void {
apiEventsEmitter.emit('sessionId.clear')
}
-// api.interceptors.request.use((request) => {
-// const sessionId = getSessionId()
-// if (sessionId) {
-// request.headers['Authorization'] = `Bearer ${sessionId}`
-// }
-// return request
-// })
+api.interceptors.request.use((request) => {
+ const sessionId = getSessionId()
+ if (sessionId) {
+ request.headers['Authorization'] = `Bearer ${sessionId}`
+ }
+ return request
+})
-// api.interceptors.response.use(undefined, (error) => {
-// if (error.response?.status === 401) {
-// clearSessionId()
-// }
-// return Promise.reject(error)
-// })
+api.interceptors.response.use(undefined, (error) => {
+ if (error.response?.status === 401) {
+ clearSessionId()
+ }
+ return Promise.reject(error)
+})
// This handler will run on other tabs (not the active one calling API functions),
// and will ensure they know about auth session ID changes.
// Ref: https://developer.mozilla.org/en-US/docs/Web/API/Window/storage_event
// "Note: This won't work on the same page that is making the changes — it is really a way
// for other pages on the domain using the storage to sync any changes that are made."
-// window.addEventListener('storage', (event) => {
-// if (event.key === storage.getPrefixedKey(WASP_APP_AUTH_SESSION_ID_NAME)) {
-// if (!!event.newValue) {
-// waspAppAuthSessionId = event.newValue
-// apiEventsEmitter.emit('sessionId.set')
-// } else {
-// waspAppAuthSessionId = undefined
-// apiEventsEmitter.emit('sessionId.clear')
-// }
-// }
-// })
+if (typeof window !== 'undefined') {
+ window.addEventListener('storage', (event) => {
+ if (event.key === storage.getPrefixedKey(WASP_APP_AUTH_SESSION_ID_NAME)) {
+ if (!!event.newValue) {
+ waspAppAuthSessionId = event.newValue
+ apiEventsEmitter.emit('sessionId.set')
+ } else {
+ waspAppAuthSessionId = undefined
+ apiEventsEmitter.emit('sessionId.clear')
+ }
+ }
+ })
+}
// PRIVATE API (sdk)
/**
diff --git a/waspc/data/Generator/templates/sdk/wasp/core/storage.ts b/waspc/data/Generator/templates/sdk/wasp/core/storage.ts
index 5c9e3b3121..d382e36983 100644
--- a/waspc/data/Generator/templates/sdk/wasp/core/storage.ts
+++ b/waspc/data/Generator/templates/sdk/wasp/core/storage.ts
@@ -6,7 +6,7 @@ export type DataStore = {
clear(): void
}
-function createLocalStorageDataStore(prefix: string): DataStore {
+function createStorageDataStore(prefix: string): DataStore {
function getPrefixedKey(key: string): string {
return `${prefix}:${key}`
}
@@ -14,12 +14,12 @@ function createLocalStorageDataStore(prefix: string): DataStore {
return {
getPrefixedKey,
set(key, value) {
- ensureLocalStorageIsAvailable()
- localStorage.setItem(getPrefixedKey(key), JSON.stringify(value))
+ const storage =getStorage()
+ storage?.setItem(getPrefixedKey(key), JSON.stringify(value))
},
get(key) {
- ensureLocalStorageIsAvailable()
- const value = localStorage.getItem(getPrefixedKey(key))
+ const storage = getStorage()
+ const value = storage?.getItem(getPrefixedKey(key))
try {
return value ? JSON.parse(value) : undefined
} catch (e: any) {
@@ -27,24 +27,29 @@ function createLocalStorageDataStore(prefix: string): DataStore {
}
},
remove(key) {
- ensureLocalStorageIsAvailable()
- localStorage.removeItem(getPrefixedKey(key))
+ const storage = getStorage()
+ storage?.removeItem(getPrefixedKey(key))
},
clear() {
- ensureLocalStorageIsAvailable()
- Object.keys(localStorage).forEach((key) => {
+ const storage = getStorage()
+ if(!storage) {
+ return
+ }
+ Object.keys(storage).forEach((key) => {
if (key.startsWith(prefix)) {
- localStorage.removeItem(key)
+ storage.removeItem(key)
}
})
},
}
}
-export const storage = createLocalStorageDataStore('wasp')
+export const storage = createStorageDataStore('wasp')
-function ensureLocalStorageIsAvailable(): void {
- if (!window || !window.localStorage) {
- throw new Error('Local storage is not available.')
+// TODO: Make this function work in the server context as well.
+function getStorage(): Storage | undefined {
+ if (typeof localStorage === 'undefined') {
+ return undefined
}
+ return localStorage
}
diff --git a/waspc/src/Wasp/Generator/WebAppGenerator.hs b/waspc/src/Wasp/Generator/WebAppGenerator.hs
index 1fc25d89b1..fa8a9e7d13 100644
--- a/waspc/src/Wasp/Generator/WebAppGenerator.hs
+++ b/waspc/src/Wasp/Generator/WebAppGenerator.hs
@@ -147,6 +147,8 @@ npmDepsForWasp _spec =
-- NOTE: Make sure to bump the version of the tsconfig
-- when updating Vite or React versions
("@tsconfig/vite-react", "^2.0.0"),
+ -- NOTE: You can remove this if you don't want to use the React Router DevTools
+ ("react-router-devtools", "^1.1.0"),
("@react-router/dev", "^7.1.1")
]
}