Skip to content

Commit 979563f

Browse files
committed
extract preload and require logic to user land to customize between dev and prod, use cjs build format for sync require in prod
1 parent a8492b6 commit 979563f

File tree

12 files changed

+202
-92
lines changed

12 files changed

+202
-92
lines changed

.eslintrc.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,7 @@ module.exports = {
417417
},
418418
{
419419
files: [
420-
'packages/react-native-renderer/**/*.js',
420+
'packages/react-native-renderer/**/*.js'
421421
],
422422
globals: {
423423
nativeFabricUIManager: 'readonly',
@@ -433,8 +433,8 @@ module.exports = {
433433
{
434434
files: ['packages/react-server-dom-vite/**/*.js'],
435435
globals: {
436+
__vite_preload__: 'readonly',
436437
__vite_require__: 'readonly',
437-
__vite_module_cache__: 'readonly',
438438
},
439439
},
440440
{

fixtures/flight-vite/scripts/build.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,10 @@ async function build() {
6060
return `${hash(chunk)}`;
6161
}
6262
},
63+
format: 'cjs',
6364
// we want to control the chunk names so that we can load them
6465
// individually when server actions are called
65-
chunkFileNames: '[name].js',
66+
chunkFileNames: '[name].cjs',
6667
},
6768
},
6869
ssr: true,
@@ -113,13 +114,15 @@ async function build() {
113114
},
114115
output: {
115116
entryFileNames: chunk => {
116-
return chunk.name + '.js';
117+
return chunk.name + '.cjs';
117118
},
119+
format: 'cjs',
118120
},
119121
},
120122
ssr: true,
121123
ssrManifest: true,
122124
ssrEmitAssets: true,
125+
target: 'node18',
123126
manifest: true,
124127
outDir: 'build/server',
125128
},

fixtures/flight-vite/server/global.js

Lines changed: 57 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,47 @@ async function createApp() {
5959
});
6060

6161
globalThis.__vite_module_cache__ = new Map();
62-
globalThis.__vite_require__ = id => {
63-
return viteServer.ssrLoadModule(id);
62+
globalThis.__vite_preload__ = metadata => {
63+
const existingPromise = __vite_module_cache__.get(metadata.specifier);
64+
if (existingPromise) {
65+
if (existingPromise.status === 'fulfilled') {
66+
return null;
67+
}
68+
return existingPromise;
69+
} else {
70+
const modulePromise = viteServer.ssrLoadModule(metadata.specifier);
71+
modulePromise.then(
72+
value => {
73+
const fulfilledThenable = modulePromise;
74+
fulfilledThenable.status = 'fulfilled';
75+
fulfilledThenable.value = value;
76+
},
77+
reason => {
78+
const rejectedThenable = modulePromise;
79+
rejectedThenable.status = 'rejected';
80+
rejectedThenable.reason = reason;
81+
}
82+
);
83+
__vite_module_cache__.set(metadata.specifier, modulePromise);
84+
return modulePromise;
85+
}
86+
};
87+
88+
globalThis.__vite_require__ = metadata => {
89+
let moduleExports;
90+
// We assume that preloadModule has been called before, which
91+
// should have added something to the module cache.
92+
const promise = __vite_module_cache__.get(metadata.specifier);
93+
if (promise) {
94+
if (promise.status === 'fulfilled') {
95+
moduleExports = promise.value;
96+
} else {
97+
throw promise.reason;
98+
}
99+
return moduleExports[metadata.name];
100+
} else {
101+
throw new Error('Module not found in cache: ' + id);
102+
}
64103
};
65104

66105
app.use('/__refresh', (req, res) => {
@@ -74,8 +113,22 @@ async function createApp() {
74113
};
75114
} else {
76115
globalThis.__vite_module_cache__ = new Map();
77-
globalThis.__vite_require__ = id => {
78-
return import(path.join(process.cwd(), 'build', 'server', id + '.js'));
116+
globalThis.__vite_preload__ = metadata => {
117+
return null;
118+
};
119+
120+
globalThis.__vite_require__ = metadata => {
121+
const module = require(path.join(
122+
process.cwd(),
123+
'build',
124+
'server',
125+
metadata.specifier + '.cjs'
126+
));
127+
128+
if (metadata.name === 'default') {
129+
return module;
130+
}
131+
return module[metadata.name];
79132
};
80133

81134
app.use(express.static('build/static'));
@@ -89,8 +142,6 @@ async function createApp() {
89142
}
90143

91144
app.all('/', async function (req, res) {
92-
// await viteServer.middlewares(req, res, (req, res, next) => {
93-
// // Proxy the request to the regional server.
94145
const proxiedHeaders = {
95146
'X-Forwarded-Host': req.hostname,
96147
'X-Forwarded-For': req.ips,

fixtures/flight-vite/server/region.js

Lines changed: 70 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -60,48 +60,77 @@ async function createApp() {
6060
});
6161

6262
globalThis.__vite_module_cache__ = new Map();
63-
globalThis.__vite_require__ = id => {
64-
return viteServer.ssrLoadModule(id);
63+
globalThis.__vite_preload__ = metadata => {
64+
const existingPromise = __vite_module_cache__.get(metadata.specifier);
65+
if (existingPromise) {
66+
if (existingPromise.status === 'fulfilled') {
67+
return null;
68+
}
69+
return existingPromise;
70+
} else {
71+
const modulePromise = viteServer.ssrLoadModule(metadata.specifier);
72+
modulePromise.then(
73+
value => {
74+
const fulfilledThenable = modulePromise;
75+
fulfilledThenable.status = 'fulfilled';
76+
fulfilledThenable.value = value;
77+
},
78+
reason => {
79+
const rejectedThenable = modulePromise;
80+
rejectedThenable.status = 'rejected';
81+
rejectedThenable.reason = reason;
82+
}
83+
);
84+
__vite_module_cache__.set(metadata.specifier, modulePromise);
85+
return modulePromise;
86+
}
87+
};
88+
89+
globalThis.__vite_require__ = metadata => {
90+
let moduleExports;
91+
// We assume that preloadModule has been called before, which
92+
// should have added something to the module cache.
93+
const promise = __vite_module_cache__.get(metadata.specifier);
94+
if (promise) {
95+
if (promise.status === 'fulfilled') {
96+
moduleExports = promise.value;
97+
} else {
98+
throw promise.reason;
99+
}
100+
return moduleExports[metadata.name];
101+
} else {
102+
throw new Error('Module not found in cache: ' + id);
103+
}
65104
};
66105

67106
const {collectStyles} = require('./styles.js');
68107
globalThis.__vite_find_assets__ = async entries => {
69108
return Object.keys(await collectStyles(viteServer, entries));
70109
};
71-
72-
loadModule = async entry => {
73-
return await viteServer.ssrLoadModule(
74-
path.isAbsolute(entry)
75-
? entry
76-
: path.join(viteServer.config.root, entry)
77-
);
78-
};
79110
} else {
80111
const reactServerManifest = JSON.parse(
81112
await readFile('build/react-server/manifest.json', 'utf8')
82113
);
83114

84-
loadModule = async entry => {
85-
const id = reactServerManifest[entry];
86-
if (id) {
87-
return await import(
88-
path.join(process.cwd(), 'build/react-server', id.file)
89-
);
90-
} else {
91-
// this is probably a server action module
92-
return await import(
93-
path.join(process.cwd(), 'build/react-server', entry + '.js')
94-
);
95-
}
115+
globalThis.__vite_module_cache__ = new Map();
116+
globalThis.__vite_preload__ = metadata => {
117+
return null;
96118
};
97119

98-
globalThis.__vite_module_cache__ = new Map();
99-
globalThis.__vite_require__ = id => {
100-
console.log({id});
101-
return import(
102-
path.join(process.cwd(), 'build', 'react-server', id + '.js')
103-
);
120+
globalThis.__vite_require__ = metadata => {
121+
const module = require(path.join(
122+
process.cwd(),
123+
'build',
124+
'server',
125+
metadata.specifier + '.cjs'
126+
));
127+
128+
if (metadata.name === 'default') {
129+
return module;
130+
}
131+
return module[metadata.name];
104132
};
133+
105134
const {findAssetsInManifest} = require('./manifest.js');
106135

107136
globalThis.__vite_find_assets__ = async entries => {
@@ -111,12 +140,23 @@ async function createApp() {
111140
};
112141
}
113142

143+
loadModule = async metadata => {
144+
await __vite_preload__(metadata);
145+
return __vite_require__(metadata);
146+
};
147+
114148
async function renderApp(res, returnValue) {
115149
const {renderToPipeableStream} = await import(
116150
'react-server-dom-vite/server'
117151
);
118152

119-
const {default: App} = await loadModule('src/App.jsx');
153+
const App = await loadModule({
154+
specifier:
155+
process.env.NODE_ENV === 'development'
156+
? path.join(process.cwd(), 'src/App.jsx')
157+
: 'App',
158+
name: 'default',
159+
});
120160
const root = React.createElement(App);
121161

122162
// For client-invoked server actions we refresh the tree and return a return value.
@@ -140,7 +180,7 @@ async function createApp() {
140180
if (serverReference) {
141181
// This is the client-side case
142182
const [filepath, name] = serverReference.split('#');
143-
const action = (await loadModule(filepath))[name];
183+
const action = await loadModule({specifier: filepath, name});
144184
// Validate that this is actually a function we intended to expose and
145185
// not the client trying to invoke arbitrary functions. In a real app,
146186
// you'd have a manifest verifying this before even importing it.

fixtures/flight-vite/src/App.jsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {like, greet} from './actions.js';
55
import {getServerState} from './ServerState.js';
66
import {Counter} from './Counter2.jsx';
77
import './style.css';
8+
import {cache, useState} from 'react';
89

910
const REACT_REFRESH_PREAMBLE = `
1011
import RefreshRuntime from "/@react-refresh"
@@ -25,9 +26,14 @@ async function Assets() {
2526
);
2627
}
2728

29+
const data = cache(async () => {
30+
return {foo: 'bar'};
31+
});
32+
2833
export default async function App() {
2934
const res = await fetch('http://localhost:3001/todos');
3035
const todos = await res.json();
36+
const cachedData = await data();
3137
return (
3238
<html lang="en">
3339
<head>
@@ -60,6 +66,7 @@ export default async function App() {
6066
<Button action={like}>Like</Button>
6167
</div>
6268
<Counter />
69+
<pre>{JSON.stringify(cachedData)}</pre>
6370
</div>
6471
</body>
6572
</html>

fixtures/flight-vite/src/index.js

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,53 @@ let updateRoot;
88

99
if (typeof window !== 'undefined') {
1010
globalThis.__vite_module_cache__ = new Map();
11-
globalThis.__vite_require__ = id => {
12-
if (process.env.NODE_ENV === 'development') {
13-
let moduleId = `/@fs${id}`;
14-
return import(/* @vite-ignore */ moduleId);
11+
globalThis.__vite_preload__ = metadata => {
12+
const existingPromise = __vite_module_cache__.get(metadata.specifier);
13+
if (existingPromise) {
14+
if (existingPromise.status === 'fulfilled') {
15+
return null;
16+
}
17+
return existingPromise;
1518
} else {
16-
let moduleId = `/${id}.js`;
17-
return import(/* @vite-ignore */ moduleId);
19+
let moduleId;
20+
if (process.env.NODE_ENV === 'development') {
21+
moduleId = `/@fs${metadata.specifier}`;
22+
} else {
23+
moduleId = `/${metadata.specifier}.js`;
24+
}
25+
26+
const modulePromise = import(/* @vite-ignore */ moduleId);
27+
modulePromise.then(
28+
value => {
29+
const fulfilledThenable = modulePromise;
30+
fulfilledThenable.status = 'fulfilled';
31+
fulfilledThenable.value = value;
32+
},
33+
reason => {
34+
const rejectedThenable = modulePromise;
35+
rejectedThenable.status = 'rejected';
36+
rejectedThenable.reason = reason;
37+
}
38+
);
39+
__vite_module_cache__.set(metadata.specifier, modulePromise);
40+
return modulePromise;
41+
}
42+
};
43+
44+
globalThis.__vite_require__ = metadata => {
45+
let moduleExports;
46+
// We assume that preloadModule has been called before, which
47+
// should have added something to the module cache.
48+
const promise = __vite_module_cache__.get(metadata.specifier);
49+
if (promise) {
50+
if (promise.status === 'fulfilled') {
51+
moduleExports = promise.value;
52+
} else {
53+
throw promise.reason;
54+
}
55+
return moduleExports[metadata.name];
56+
} else {
57+
throw new Error('Module not found in cache: ' + id);
1858
}
1959
};
2060
}

0 commit comments

Comments
 (0)