-
Notifications
You must be signed in to change notification settings - Fork 32
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
0.9.0 (3) - add integration test with experimentally patched React version #219
Changes from all commits
9eaac4f
f1535a6
10817a5
ad9022a
abe6c96
43c1ef2
3d61cc1
835f780
41a9ea7
e0919aa
b330f7c
19a4a4f
1b3d70e
2ae052b
ac239d9
12b6ff3
7b6da7e
9fe9582
b81176c
2d7c8d9
9410422
b523520
fbbd152
0c0b47b
70b970b
91beaa2
a66a502
1db1623
c62ec33
7f44046
ef3d2ef
b0c0f78
66af26f
101958c
85c32a9
8f6b8d5
6fe73df
8f36c3f
131199b
48ceaad
f79953a
c6196aa
a5d525f
c1a0f20
9d22153
6b4bc5f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# Logs | ||
logs | ||
*.log | ||
npm-debug.log* | ||
yarn-debug.log* | ||
yarn-error.log* | ||
pnpm-debug.log* | ||
lerna-debug.log* | ||
|
||
node_modules | ||
dist | ||
dist-ssr | ||
*.local | ||
|
||
# Editor directories and files | ||
.vscode/* | ||
!.vscode/extensions.json | ||
.idea | ||
.DS_Store | ||
*.suo | ||
*.ntvs* | ||
*.njsproj | ||
*.sln | ||
*.sw? |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<!doctype html> | ||
<html lang="en"> | ||
|
||
<head> | ||
<meta charset="UTF-8" /> | ||
<!--app-head--> | ||
</head> | ||
|
||
<body> | ||
<div id="root"><!--app-html--></div> | ||
<script type="module" src="/src/entry-client.jsx"></script> | ||
</body> | ||
|
||
</html> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
{ | ||
"name": "@integration-test/experimental-react", | ||
"private": true, | ||
"version": "0.0.0", | ||
"type": "module", | ||
"scripts": { | ||
"prepare": "rm -rf node_modules/@apollo/client-react-streaming; mkdir node_modules/@apollo/client-react-streaming && cp -r ../../packages/client-react-streaming/{package.json,dist} node_modules/@apollo/client-react-streaming", | ||
"dev": "yarn prepare; node server", | ||
"build": "yarn prepare; npm run build:client && npm run build:server", | ||
"build:client": "vite build --ssrManifest --outDir dist/client", | ||
"build:server": "vite build --ssr src/entry-server.jsx --outDir dist/server", | ||
"preview": "cross-env NODE_ENV=production node server", | ||
"build-and-test": "yarn build && yarn test", | ||
"test": "yarn playwright test" | ||
}, | ||
"dependencies": { | ||
"@apollo/client": "^3.9.1", | ||
"@apollo/client-react-streaming": "workspace:*", | ||
"compression": "^1.7.4", | ||
"express": "^4.18.2", | ||
"graphql": "^16.8.1", | ||
"graphql-tag": "^2.12.6", | ||
"react": "npm:@phryneas/experimental-react@0.0.0-phryneas-a1c625c", | ||
"react-dom": "npm:@phryneas/experimental-react-dom@0.0.0-phryneas-a1c625c", | ||
"sirv": "^2.0.4" | ||
}, | ||
"devDependencies": { | ||
"@playwright/test": "^1.39.0", | ||
"@tsconfig/vite-react": "^3.0.0", | ||
"@types/react": "^18.2.55", | ||
"@types/react-dom": "^18.2.18", | ||
"@vitejs/plugin-react": "^4.2.1", | ||
"cross-env": "^7.0.3", | ||
"prettier": "^3.2.5", | ||
"vite": "^5.0.10" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { defineConfig } from "@playwright/test"; | ||
|
||
export default defineConfig({ | ||
webServer: { | ||
command: "node server", | ||
port: 3000, | ||
timeout: 120 * 1000, | ||
reuseExistingServer: !process.env.CI, | ||
env: { | ||
PORT: 3000, | ||
NODE_ENV: "production", | ||
}, | ||
}, | ||
timeout: 120 * 1000, | ||
use: { | ||
headless: true, | ||
viewport: { width: 1280, height: 720 }, | ||
ignoreHTTPSErrors: true, | ||
}, | ||
testDir: "src/", | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
import express from "express"; | ||
import { renderToPipeableStream } from "react-dom/server"; | ||
import { readFile } from "node:fs/promises"; | ||
|
||
// Constants | ||
const isProduction = process.env.NODE_ENV === "production"; | ||
const port = process.env.PORT || 5173; | ||
const base = process.env.BASE || "/"; | ||
const ABORT_DELAY = 10000; | ||
|
||
// Create http server | ||
const app = express(); | ||
|
||
// Add Vite or respective production middlewares | ||
let vite; | ||
let bootstrapModules = []; | ||
let assets = []; | ||
if (!isProduction) { | ||
const { createServer } = await import("vite"); | ||
vite = await createServer({ | ||
server: { middlewareMode: true, hmr: true }, | ||
appType: "custom", | ||
base, | ||
}); | ||
app.use(vite.middlewares); | ||
} else { | ||
const compression = (await import("compression")).default; | ||
const sirv = (await import("sirv")).default; | ||
app.use(compression()); | ||
app.use(base, sirv("./dist/client", { extensions: [] })); | ||
const index = await readFile("./dist/client/index.html", "utf-8"); | ||
for (const script of index.matchAll( | ||
/<script type="module" \w+ src="(.*)">/g | ||
)) { | ||
bootstrapModules.push(script[1]); | ||
} | ||
for (const link of index.matchAll( | ||
/<link rel="stylesheet" \w+ href="(.*)">/g | ||
)) { | ||
assets.push(link[1]); | ||
} | ||
} | ||
Comment on lines
+18
to
+42
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I know this is meant to be more of a "demo" (ish), but any chance we could flip the conditional here? I always find it easier with Rather than: if (!isProduction) {
// other envs
} else {
// production builds
} Use this: if (isProduction) {
// production stuff
} else {
// other environments
} This keeps the "production" configuration closer to the conditional test for it, rather than breaking it apart. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is based on the Vite React SSR template and I'm trying to keep the diff as small as possible, so while I agree and would really like to swap it, it's probably better we don't :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I didn't realize these were copy-pasted so makes sense! Thanks for pointing me to that. |
||
|
||
// based on https://github.com/facebook/react/blob/9cdf8a99edcfd94d7420835ea663edca04237527/fixtures/fizz/server/render-to-stream.js | ||
app.use("*", async (req, res) => { | ||
// The new wiring is a bit more involved. | ||
res.socket.on("error", (error) => { | ||
console.error("Fatal", error); | ||
}); | ||
let didError = false; | ||
let didFinish = false; | ||
const App = ( | ||
await (isProduction | ||
? import("./dist/server/entry-server.js") | ||
: vite.ssrLoadModule("/src/entry-server.jsx")) | ||
).render({ | ||
isProduction, | ||
assets, | ||
}); | ||
const { pipe, abort } = renderToPipeableStream(App, { | ||
bootstrapModules, | ||
onAllReady() { | ||
// Full completion. | ||
// You can use this for SSG or crawlers. | ||
didFinish = true; | ||
}, | ||
onShellReady() { | ||
// If something errored before we started streaming, we set the error code appropriately. | ||
res.statusCode = didError ? 500 : 200; | ||
res.setHeader("Content-type", "text/html"); | ||
setImmediate(() => pipe(res)); | ||
}, | ||
onShellError(x) { | ||
// Something errored before we could complete the shell so we emit an alternative shell. | ||
res.statusCode = 500; | ||
res.send("<!doctype><p>Error</p>"); | ||
}, | ||
onError(x) { | ||
didError = true; | ||
console.error(x); | ||
}, | ||
}); | ||
// Abandon and switch to client rendering if enough time passes. | ||
// Try lowering this to see the client recover. | ||
setTimeout(() => { | ||
if (!didFinish) { | ||
abort(); | ||
} | ||
}, ABORT_DELAY); | ||
}); | ||
|
||
// Start http server | ||
app.listen(port, () => { | ||
console.log(`Server started at http://localhost:${port}`); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
:root { | ||
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; | ||
line-height: 1.5; | ||
font-weight: 400; | ||
|
||
color-scheme: light dark; | ||
color: rgba(255, 255, 255, 0.87); | ||
background-color: #242424; | ||
|
||
font-synthesis: none; | ||
text-rendering: optimizeLegibility; | ||
-webkit-font-smoothing: antialiased; | ||
-moz-osx-font-smoothing: grayscale; | ||
-webkit-text-size-adjust: 100%; | ||
} | ||
|
||
a { | ||
font-weight: 500; | ||
color: #646cff; | ||
text-decoration: inherit; | ||
} | ||
a:hover { | ||
color: #535bf2; | ||
} | ||
|
||
body { | ||
margin: 0; | ||
display: flex; | ||
place-items: center; | ||
min-width: 320px; | ||
min-height: 100vh; | ||
} | ||
|
||
h1 { | ||
font-size: 3.2em; | ||
line-height: 1.1; | ||
} | ||
|
||
button { | ||
border-radius: 8px; | ||
border: 1px solid transparent; | ||
padding: 0.6em 1.2em; | ||
font-size: 1em; | ||
font-weight: 500; | ||
font-family: inherit; | ||
background-color: #1a1a1a; | ||
cursor: pointer; | ||
transition: border-color 0.25s; | ||
} | ||
button:hover { | ||
border-color: #646cff; | ||
} | ||
button:focus, | ||
button:focus-visible { | ||
outline: 4px auto -webkit-focus-ring-color; | ||
} | ||
|
||
@media (prefers-color-scheme: light) { | ||
:root { | ||
color: #213547; | ||
background-color: #ffffff; | ||
} | ||
a:hover { | ||
color: #747bff; | ||
} | ||
button { | ||
background-color: #f9f9f9; | ||
} | ||
} | ||
|
||
|
||
#root { | ||
max-width: 1280px; | ||
margin: 0 auto; | ||
padding: 2rem; | ||
text-align: center; | ||
} | ||
|
||
.card { | ||
padding: 2em; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is necessary so it doesn't pick up the
react
folder frompackages/client-react-streaming/node_modules
and mixes two react versions.