Repository navigation
In RSC entrypoint, RSC import paths must be string literals - is there a way round this? #1498
|
Hi, first of all I'd like to state I am rather new to the world of creating RSC frameworks but was spurred into doing it through a mixture of curiosity and my chosen existing framework being moved to archive mode (@lazarv/react-server, which I really liked) - so I thought I'd attempt to create my own. Anyway, I'm experimenting with the Vite RSC plugin to attempt to create my own mini-framework as an npm package. It's been going OK but I am hitting a brick wall with one particular issue. If, from the RSC entrypoint, I try to import the main RSC of my app (e.g. an Code snippet from RSC entrypoint: type ComponentImport = {
default: ComponentType<AppProps>
};
export default async function handleRequest(request: Request): Promise<Response> {
const appPath = resolve(process.cwd(), 'src/index.tsx');
const appModule = await import.meta.viteRsc.import<
ComponentImport
>(
appPath, { environment: 'rsc' }
);
const App = appModule.default;
// Convert RSC JSX to RSC payload
let rscStream = ReactServer.renderToReadableStream(
<App url={request.url} />
);
// ... pass to SSR ...
}If the path is a string literal, everything works fine - no problems. I can render an RSC containing a client component. The RSC queries a database, and the client component uses state correctly. If I do not provide a string literal, as shown in the snippet above, it fails at the build stage with the below. I have a GitHub repo which illustrates the error, see https://github.com/nickw1/vite-rsc-expts/ The directories represent different stages of experimentation. Note that I have not implemented dev mode yet, you have to build and run. I'm not sure whether this is a fundamental misunderstanding on my part, a fundamental limitation, or a bug in I can think of a way round the problem by using an RSC entrypoint file with a template for the path and then replacing this with the actual path in my Vite builder script, writing out the result as a temporary file just before building, and then building with the resulting temporary file as my RSC entrypoint. However it seems a bit of a clumsy solution. I presume this must be solvable more elegantly. So am I going about this the wrong way? Thanks. |
Replies: 1 comment 1 reply
|
That's expected with The usual way around this in a framework is to let your Vite plugin do the resolving, since that's where you know the project root. Two options:
// in your framework's Vite plugin
let root: string
export function myFramework(): Plugin {
return {
name: 'my-framework',
configResolved(config) {
root = config.root
},
resolveId(id) {
if (id === 'virtual:my-framework/app') {
return path.resolve(root, 'src/index.tsx')
}
},
}
}// rsc entry
import App from 'virtual:my-framework/app'Since the RSC entry and
Either way the user's paths live in your plugin config, and the entry files only contain fixed names. |
That's expected with
import.meta.viteRsc.import(). During the production build the plugin finds these calls and emits each specifier as an entry of the target environment, so it has to be a string it can read from the source. Anything computed at runtime, likeprocess.cwd(), can't be discovered (see theimport.meta.viteRsc.importsection of the plugin-rsc README).The usual way around this in a framework is to let your Vite plugin do the resolving, since that's where you know the project root. Two options: