Skip to content

Commit

Permalink
extract preload and require logic to user land to customize between d…
Browse files Browse the repository at this point in the history
…ev and prod, use cjs build format for sync require in prod
  • Loading branch information
nksaraf committed Jun 12, 2023
1 parent a8492b6 commit 979563f
Show file tree
Hide file tree
Showing 12 changed files with 202 additions and 92 deletions.
4 changes: 2 additions & 2 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,7 @@ module.exports = {
},
{
files: [
'packages/react-native-renderer/**/*.js',
'packages/react-native-renderer/**/*.js'
],
globals: {
nativeFabricUIManager: 'readonly',
Expand All @@ -433,8 +433,8 @@ module.exports = {
{
files: ['packages/react-server-dom-vite/**/*.js'],
globals: {
__vite_preload__: 'readonly',
__vite_require__: 'readonly',
__vite_module_cache__: 'readonly',
},
},
{
Expand Down
7 changes: 5 additions & 2 deletions fixtures/flight-vite/scripts/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,10 @@ async function build() {
return `${hash(chunk)}`;
}
},
format: 'cjs',
// we want to control the chunk names so that we can load them
// individually when server actions are called
chunkFileNames: '[name].js',
chunkFileNames: '[name].cjs',
},
},
ssr: true,
Expand Down Expand Up @@ -113,13 +114,15 @@ async function build() {
},
output: {
entryFileNames: chunk => {
return chunk.name + '.js';
return chunk.name + '.cjs';
},
format: 'cjs',
},
},
ssr: true,
ssrManifest: true,
ssrEmitAssets: true,
target: 'node18',
manifest: true,
outDir: 'build/server',
},
Expand Down
63 changes: 57 additions & 6 deletions fixtures/flight-vite/server/global.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,47 @@ async function createApp() {
});

globalThis.__vite_module_cache__ = new Map();
globalThis.__vite_require__ = id => {
return viteServer.ssrLoadModule(id);
globalThis.__vite_preload__ = metadata => {
const existingPromise = __vite_module_cache__.get(metadata.specifier);
if (existingPromise) {
if (existingPromise.status === 'fulfilled') {
return null;
}
return existingPromise;
} else {
const modulePromise = viteServer.ssrLoadModule(metadata.specifier);
modulePromise.then(
value => {
const fulfilledThenable = modulePromise;
fulfilledThenable.status = 'fulfilled';
fulfilledThenable.value = value;
},
reason => {
const rejectedThenable = modulePromise;
rejectedThenable.status = 'rejected';
rejectedThenable.reason = reason;
}
);
__vite_module_cache__.set(metadata.specifier, modulePromise);
return modulePromise;
}
};

globalThis.__vite_require__ = metadata => {
let moduleExports;
// We assume that preloadModule has been called before, which
// should have added something to the module cache.
const promise = __vite_module_cache__.get(metadata.specifier);
if (promise) {
if (promise.status === 'fulfilled') {
moduleExports = promise.value;
} else {
throw promise.reason;
}
return moduleExports[metadata.name];
} else {
throw new Error('Module not found in cache: ' + id);
}
};

app.use('/__refresh', (req, res) => {
Expand All @@ -74,8 +113,22 @@ async function createApp() {
};
} else {
globalThis.__vite_module_cache__ = new Map();
globalThis.__vite_require__ = id => {
return import(path.join(process.cwd(), 'build', 'server', id + '.js'));
globalThis.__vite_preload__ = metadata => {
return null;
};

globalThis.__vite_require__ = metadata => {
const module = require(path.join(
process.cwd(),
'build',
'server',
metadata.specifier + '.cjs'
));

if (metadata.name === 'default') {
return module;
}
return module[metadata.name];
};

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

app.all('/', async function (req, res) {
// await viteServer.middlewares(req, res, (req, res, next) => {
// // Proxy the request to the regional server.
const proxiedHeaders = {
'X-Forwarded-Host': req.hostname,
'X-Forwarded-For': req.ips,
Expand Down
100 changes: 70 additions & 30 deletions fixtures/flight-vite/server/region.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,48 +60,77 @@ async function createApp() {
});

globalThis.__vite_module_cache__ = new Map();
globalThis.__vite_require__ = id => {
return viteServer.ssrLoadModule(id);
globalThis.__vite_preload__ = metadata => {
const existingPromise = __vite_module_cache__.get(metadata.specifier);
if (existingPromise) {
if (existingPromise.status === 'fulfilled') {
return null;
}
return existingPromise;
} else {
const modulePromise = viteServer.ssrLoadModule(metadata.specifier);
modulePromise.then(
value => {
const fulfilledThenable = modulePromise;
fulfilledThenable.status = 'fulfilled';
fulfilledThenable.value = value;
},
reason => {
const rejectedThenable = modulePromise;
rejectedThenable.status = 'rejected';
rejectedThenable.reason = reason;
}
);
__vite_module_cache__.set(metadata.specifier, modulePromise);
return modulePromise;
}
};

globalThis.__vite_require__ = metadata => {
let moduleExports;
// We assume that preloadModule has been called before, which
// should have added something to the module cache.
const promise = __vite_module_cache__.get(metadata.specifier);
if (promise) {
if (promise.status === 'fulfilled') {
moduleExports = promise.value;
} else {
throw promise.reason;
}
return moduleExports[metadata.name];
} else {
throw new Error('Module not found in cache: ' + id);
}
};

const {collectStyles} = require('./styles.js');
globalThis.__vite_find_assets__ = async entries => {
return Object.keys(await collectStyles(viteServer, entries));
};

loadModule = async entry => {
return await viteServer.ssrLoadModule(
path.isAbsolute(entry)
? entry
: path.join(viteServer.config.root, entry)
);
};
} else {
const reactServerManifest = JSON.parse(
await readFile('build/react-server/manifest.json', 'utf8')
);

loadModule = async entry => {
const id = reactServerManifest[entry];
if (id) {
return await import(
path.join(process.cwd(), 'build/react-server', id.file)
);
} else {
// this is probably a server action module
return await import(
path.join(process.cwd(), 'build/react-server', entry + '.js')
);
}
globalThis.__vite_module_cache__ = new Map();
globalThis.__vite_preload__ = metadata => {
return null;
};

globalThis.__vite_module_cache__ = new Map();
globalThis.__vite_require__ = id => {
console.log({id});
return import(
path.join(process.cwd(), 'build', 'react-server', id + '.js')
);
globalThis.__vite_require__ = metadata => {
const module = require(path.join(
process.cwd(),
'build',
'server',
metadata.specifier + '.cjs'
));

if (metadata.name === 'default') {
return module;
}
return module[metadata.name];
};

const {findAssetsInManifest} = require('./manifest.js');

globalThis.__vite_find_assets__ = async entries => {
Expand All @@ -111,12 +140,23 @@ async function createApp() {
};
}

loadModule = async metadata => {
await __vite_preload__(metadata);
return __vite_require__(metadata);
};

async function renderApp(res, returnValue) {
const {renderToPipeableStream} = await import(
'react-server-dom-vite/server'
);

const {default: App} = await loadModule('src/App.jsx');
const App = await loadModule({
specifier:
process.env.NODE_ENV === 'development'
? path.join(process.cwd(), 'src/App.jsx')
: 'App',
name: 'default',
});
const root = React.createElement(App);

// For client-invoked server actions we refresh the tree and return a return value.
Expand All @@ -140,7 +180,7 @@ async function createApp() {
if (serverReference) {
// This is the client-side case
const [filepath, name] = serverReference.split('#');
const action = (await loadModule(filepath))[name];
const action = await loadModule({specifier: filepath, name});
// Validate that this is actually a function we intended to expose and
// not the client trying to invoke arbitrary functions. In a real app,
// you'd have a manifest verifying this before even importing it.
Expand Down
7 changes: 7 additions & 0 deletions fixtures/flight-vite/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {like, greet} from './actions.js';
import {getServerState} from './ServerState.js';
import {Counter} from './Counter2.jsx';
import './style.css';
import {cache, useState} from 'react';

const REACT_REFRESH_PREAMBLE = `
import RefreshRuntime from "/@react-refresh"
Expand All @@ -25,9 +26,14 @@ async function Assets() {
);
}

const data = cache(async () => {
return {foo: 'bar'};
});

export default async function App() {
const res = await fetch('http://localhost:3001/todos');
const todos = await res.json();
const cachedData = await data();
return (
<html lang="en">
<head>
Expand Down Expand Up @@ -60,6 +66,7 @@ export default async function App() {
<Button action={like}>Like</Button>
</div>
<Counter />
<pre>{JSON.stringify(cachedData)}</pre>
</div>
</body>
</html>
Expand Down
52 changes: 46 additions & 6 deletions fixtures/flight-vite/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,53 @@ let updateRoot;

if (typeof window !== 'undefined') {
globalThis.__vite_module_cache__ = new Map();
globalThis.__vite_require__ = id => {
if (process.env.NODE_ENV === 'development') {
let moduleId = `/@fs${id}`;
return import(/* @vite-ignore */ moduleId);
globalThis.__vite_preload__ = metadata => {
const existingPromise = __vite_module_cache__.get(metadata.specifier);
if (existingPromise) {
if (existingPromise.status === 'fulfilled') {
return null;
}
return existingPromise;
} else {
let moduleId = `/${id}.js`;
return import(/* @vite-ignore */ moduleId);
let moduleId;
if (process.env.NODE_ENV === 'development') {
moduleId = `/@fs${metadata.specifier}`;
} else {
moduleId = `/${metadata.specifier}.js`;
}

const modulePromise = import(/* @vite-ignore */ moduleId);
modulePromise.then(
value => {
const fulfilledThenable = modulePromise;
fulfilledThenable.status = 'fulfilled';
fulfilledThenable.value = value;
},
reason => {
const rejectedThenable = modulePromise;
rejectedThenable.status = 'rejected';
rejectedThenable.reason = reason;
}
);
__vite_module_cache__.set(metadata.specifier, modulePromise);
return modulePromise;
}
};

globalThis.__vite_require__ = metadata => {
let moduleExports;
// We assume that preloadModule has been called before, which
// should have added something to the module cache.
const promise = __vite_module_cache__.get(metadata.specifier);
if (promise) {
if (promise.status === 'fulfilled') {
moduleExports = promise.value;
} else {
throw promise.reason;
}
return moduleExports[metadata.name];
} else {
throw new Error('Module not found in cache: ' + id);
}
};
}
Expand Down
Loading

0 comments on commit 979563f

Please sign in to comment.