Skip to content

Commit 5125640

Browse files
authored
Merge pull request #61 from theKashey/react-18-streaming-example
create react-18 streaming example
2 parents 3274774 + 4ef6f43 commit 5125640

28 files changed

+1861
-128
lines changed

.npmrc

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
registry=https://registry.npmjs.org

.nvmrc

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
16
1+
18

.size-limit.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
module.exports = [
2-
{
3-
path: ['moveStyles.js'],
4-
ignore: ['tslib'],
5-
limit: '0.5 KB',
6-
},
2+
// {
3+
// path: ['src/moveStyles.ts'],
4+
// ignore: ['tslib'],
5+
// limit: '0.5 KB',
6+
// },
77
];

.size.json

+3-7
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
[
2-
{
3-
"name": "moveStyles.js",
4-
"passed": true,
5-
"size": 162
6-
}
7-
]
1+
{
2+
"error": "WebpackOptionsValidationError: Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.\n - configuration.entry['index'] should be an non-empty array.\n -> A non-empty array of non-empty strings\n at webpack (/Users/akorzunov/dev/github/loaders/used-styles/node_modules/webpack/lib/webpack.js:31:9)\n at /Users/akorzunov/dev/github/loaders/used-styles/node_modules/@size-limit/webpack/run-webpack.js:5:20\n at new Promise (<anonymous>)\n at runWebpack (/Users/akorzunov/dev/github/loaders/used-styles/node_modules/@size-limit/webpack/run-webpack.js:4:10)\n at Object.step40 (/Users/akorzunov/dev/github/loaders/used-styles/node_modules/@size-limit/webpack/index.js:59:38)\n at /Users/akorzunov/dev/github/loaders/used-styles/node_modules/size-limit/calc.js:8:62\n at Array.map (<anonymous>)\n at exec (/Users/akorzunov/dev/github/loaders/used-styles/node_modules/size-limit/calc.js:8:41)\n at calc (/Users/akorzunov/dev/github/loaders/used-styles/node_modules/size-limit/calc.js:14:42)\n at processTicksAndRejections (node:internal/process/task_queues:96:5)"
3+
}

doczrc.js

-5
This file was deleted.

example/app.tsx

-22
This file was deleted.

example/assets/.gitkeep

Whitespace-only changes.

example/index.html

-11
This file was deleted.

example/index.tsx

-5
This file was deleted.

example/react-18-streaming/index.html

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<body>
4+
<div id="app" style="display: contents"></div>
5+
<script type="module" src="/src/client.tsx" defer></script>
6+
</body>
7+
</html>
+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"name": "used-styles-react-18",
3+
"private": true,
4+
"scripts": {
5+
"start:server": "ts-node ./src/server.tsx",
6+
"start:client": "vite . --port 3001"
7+
},
8+
"dependencies": {
9+
"express": "^4.19.2",
10+
"multistream": "^4.1.0",
11+
"react": "^18.0.0",
12+
"react-dom": "^18.0.0"
13+
},
14+
"devDependencies": {
15+
"@types/express": "^4.17.21",
16+
"@types/multistream": "^4.1.3",
17+
"@types/react": "^18.2.73",
18+
"@types/react-dom": "^18.2.23",
19+
"ts-node": "^10.9.2",
20+
"vite": "^5.2.6"
21+
}
22+
}
+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import React from 'react';
2+
3+
export const App = () => (
4+
<div className="app">
5+
your app <div className="test">and your styles</div>
6+
</div>
7+
);
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import React from 'react';
2+
import ReactDOM from 'react-dom/client';
3+
4+
import { moveStyles } from '../../../moveStyles';
5+
6+
import { App } from './App';
7+
8+
// Call before `ReactDOM.hydrateRoot`
9+
moveStyles();
10+
11+
ReactDOM.hydrateRoot(
12+
document.getElementById('root'),
13+
<React.StrictMode>
14+
<App />
15+
</React.StrictMode>
16+
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// entry-server.js
2+
import { Readable } from 'node:stream';
3+
import { Transform } from 'stream';
4+
5+
import { Response } from 'express';
6+
import MultiStream from 'multistream';
7+
import React from 'react';
8+
import { renderToPipeableStream } from 'react-dom/server';
9+
10+
import { App } from './App';
11+
12+
// small utility for "readable" streams
13+
const readableString = (string: string) => {
14+
const s = new Readable();
15+
s.push(string);
16+
s.push(null);
17+
s._read = () => true;
18+
19+
return s;
20+
};
21+
22+
const ABORT_DELAY = 10000;
23+
24+
export const renderApp = async (res: Response, styledStream: Transform) => {
25+
let didError = false;
26+
27+
const { pipe, abort } = renderToPipeableStream(
28+
<React.StrictMode>
29+
<App />
30+
</React.StrictMode>,
31+
{
32+
onShellError() {
33+
res.sendStatus(500);
34+
},
35+
// wait for all pieces to be ready
36+
onAllReady() {
37+
res.status(didError ? 500 : 200);
38+
res.set({ 'Content-Type': 'text/html' });
39+
40+
// allow client to start loading js bundle
41+
res.write(`<!DOCTYPE html><html><head><script defer src="client.js"></script></head><body><div id="root">`);
42+
43+
const endStream = readableString('</div></body></html>');
44+
45+
// concatenate all streams together
46+
const streams = [
47+
styledStream, // the main content
48+
endStream, // closing tags
49+
];
50+
51+
new MultiStream(streams).pipe(res);
52+
53+
// start by piping react and styled transform stream
54+
pipe(styledStream);
55+
},
56+
onError(error) {
57+
didError = true;
58+
console.error(error);
59+
},
60+
}
61+
);
62+
63+
setTimeout(() => {
64+
abort();
65+
}, ABORT_DELAY);
66+
};
+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import express from 'express';
2+
3+
import {
4+
discoverProjectStyles,
5+
createCriticalStyleStream,
6+
// createStyleStream,
7+
// createLink,
8+
} from '../../../';
9+
10+
import { renderApp } from './entry-server';
11+
12+
const app = express();
13+
14+
// generate lookup table on server start
15+
const stylesLookup = discoverProjectStyles(__dirname);
16+
17+
app.use('*', async (_req, res) => {
18+
await stylesLookup;
19+
20+
try {
21+
// create a style steam
22+
// const styledStream = createStyleStream(stylesLookup, (style) => {
23+
// // _return_ link tag, and it will be appended to the stream output
24+
// return createLink(`${style}`) // <link href="dist/mystyle.css />
25+
// });
26+
27+
// or create critical CSS stream - it will inline all styles
28+
// console.log(stylesLookup)
29+
30+
const styledStream = createCriticalStyleStream(stylesLookup); // <style>.myClass {...
31+
32+
await renderApp(res, styledStream);
33+
} catch (err) {
34+
res.sendStatus(500);
35+
}
36+
});
37+
38+
console.log('listening on port 3000');
39+
app.listen(3000);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.app {
2+
color: red;
3+
}
4+
5+
.test {
6+
color: blue;
7+
}

0 commit comments

Comments
 (0)