Browse Source
Achieving this is rather tricky due to the nature of our multi-page project. We need to solve several problems: 1. We want a single npm project that produces two independent bundles. 2. We want our paths to be relative to the serving URL, that is `localhost:3000` in development and in production, whereever the backend is hosted. 3. We have independent backends, hence requiring different `server.proxy` configurations. We solve (1) by using vite (already prior to this commit). To solve (2), we simply remove all absolute URLs from the code and replace them with absolute paths which will be relative to the serving host. This creates a problem: Prior to this patch, we only have one devServer running that would serve both frontends under a different sub-directory (/maker and /taker). Even though this worked, it was impossible to create a proxy configuration that would: - Forward API requests from `/maker` to `localhost:8001` - Forward API requests from `/taker` to `localhost:8000` Because in both cases, the API requests would simply start with `/api`, making them indistinguishable from each other. To solve this problem, we needed to serve each frontend separately. Doing so would allow us to have dedicated proxy server configurations and forward the requests to `/api` to the correct backend. Unfortunately, the intuitive approach of solving this (have a `maker.html` and `taker.html` file) does not work. With React being a client-side routing framework, full page-reloads would be broken with this approach because they would be looking for an `index.html` file which doesn't exist. To work around this issue, our final solution is: 1. Use a dynamic ID to reference the desired app from within the `index.html`: `__app__` 2. Use a vite plugin to resolve this ID to the file in question: `maker.tsx` or `taker.tsx` Fixes #6.no-contract-setup-message
Thomas Eizinger
3 years ago
15 changed files with 73 additions and 141 deletions
@ -0,0 +1,18 @@ |
|||
import { Plugin } from "vite"; |
|||
|
|||
export default function dynamicApp(app: string): Plugin { |
|||
return { |
|||
name: "dynamicApp", // required, will show up in warnings and errors
|
|||
resolveId: (id) => { |
|||
// For some reason these are different?
|
|||
const productionBuildId = "./__app__.tsx"; |
|||
const devBuildId = "/__app__.tsx"; |
|||
|
|||
if (id === productionBuildId || id === devBuildId) { |
|||
return `${__dirname}/${app}.tsx`; |
|||
} |
|||
|
|||
return null; |
|||
}, |
|||
}; |
|||
} |
@ -1,13 +1,14 @@ |
|||
<!DOCTYPE html> |
|||
<html lang="en"> |
|||
<head> |
|||
<meta charset="UTF-8" /> |
|||
<link rel="icon" type="image/svg+xml" href="/src/favicon.svg" /> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
|||
<title>Hermes Main page</title> |
|||
</head> |
|||
<body> |
|||
<div id="root"></div> |
|||
<script type="module" src="./src/index.tsx"></script> |
|||
</body> |
|||
<head> |
|||
<meta charset="UTF-8" /> |
|||
<link rel="icon" type="image/svg+xml" href="/src/favicon.svg" /> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
|||
<title>Hermes</title> |
|||
</head> |
|||
<body> |
|||
<div id="root"></div> |
|||
<script type="module" src="./__app__.tsx"> // `__app__` is dynamically resolved by the `dynamicApp` vite plugin |
|||
</script> |
|||
</body> |
|||
</html> |
|||
|
@ -1,13 +0,0 @@ |
|||
<!DOCTYPE html> |
|||
<html lang="en"> |
|||
<head> |
|||
<meta charset="UTF-8" /> |
|||
<link rel="icon" type="image/svg+xml" href="/src/favicon.svg" /> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
|||
<title>Hermes Maker</title> |
|||
</head> |
|||
<body> |
|||
<div id="root"></div> |
|||
<script type="module" src="./src/main_maker.tsx"></script> |
|||
</body> |
|||
</html> |
@ -1,25 +0,0 @@ |
|||
import { ChakraProvider } from "@chakra-ui/react"; |
|||
import React from "react"; |
|||
import ReactDOM from "react-dom"; |
|||
import { BrowserRouter, Route, Routes } from "react-router-dom"; |
|||
import { EventSourceProvider } from "react-sse-hooks"; |
|||
import "./index.css"; |
|||
import Maker from "./Maker"; |
|||
import Taker from "./Taker"; |
|||
import theme from "./theme"; |
|||
|
|||
ReactDOM.render( |
|||
<React.StrictMode> |
|||
<ChakraProvider theme={theme}> |
|||
<EventSourceProvider> |
|||
<BrowserRouter> |
|||
<Routes> |
|||
<Route path="/maker/*" element={<Maker />} /> |
|||
<Route path="/taker/*" element={<Taker />} /> |
|||
</Routes> |
|||
</BrowserRouter> |
|||
</EventSourceProvider> |
|||
</ChakraProvider> |
|||
</React.StrictMode>, |
|||
document.getElementById("root"), |
|||
); |
@ -1,21 +0,0 @@ |
|||
import { ChakraProvider } from "@chakra-ui/react"; |
|||
import React from "react"; |
|||
import ReactDOM from "react-dom"; |
|||
import { BrowserRouter } from "react-router-dom"; |
|||
import { EventSourceProvider } from "react-sse-hooks"; |
|||
import "./index.css"; |
|||
import Maker from "./Maker"; |
|||
import theme from "./theme"; |
|||
|
|||
ReactDOM.render( |
|||
<React.StrictMode> |
|||
<ChakraProvider theme={theme}> |
|||
<EventSourceProvider> |
|||
<BrowserRouter> |
|||
<Maker /> |
|||
</BrowserRouter> |
|||
</EventSourceProvider> |
|||
</ChakraProvider> |
|||
</React.StrictMode>, |
|||
document.getElementById("root"), |
|||
); |
@ -1,13 +0,0 @@ |
|||
<!DOCTYPE html> |
|||
<html lang="en"> |
|||
<head> |
|||
<meta charset="UTF-8" /> |
|||
<link rel="icon" type="image/svg+xml" href="/src/favicon.svg" /> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
|||
<title>Hermes Taker</title> |
|||
</head> |
|||
<body> |
|||
<div id="root"></div> |
|||
<script type="module" src="./src/main_taker.tsx"></script> |
|||
</body> |
|||
</html> |
Loading…
Reference in new issue