Skip to content

Commit

Permalink
RSC: Support 'use client' in 3pp packages (#9191)
Browse files Browse the repository at this point in the history
By configuring `noExternal` for the server side build correctly we now
support using npm modules with client side only React components, i.e.
components with `'use client'` at the top of the file.

Currently you have to patch vite for this to work. Still waiting for
vitejs/vite#13487 to be merged.
  • Loading branch information
Tobbe committed Sep 16, 2023
1 parent cde64bb commit a436b1c
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 20 deletions.
45 changes: 25 additions & 20 deletions packages/vite/src/waku-lib/build-server.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
// TODO (RSC) Take ownership of this file and move it out ouf the waku-lib folder
import path from 'node:path'

import react from '@vitejs/plugin-react'
import { build as viteBuild } from 'vite'

Expand All @@ -12,14 +14,6 @@ export async function serverBuild(
serverEntryFiles: Record<string, string>,
customModules: Record<string, string>
) {
// const noExternal = Array.from(clientEntryFileSet).map(
// // FIXME this might not work with pnpm
// (fname) =>
// path
// .relative(path.join(config.root, "node_modules"), fname)
// .split("/")[0]!
// );
//
const input = {
entries: entriesFile,
...clientEntryFiles,
Expand All @@ -35,19 +29,30 @@ export async function serverBuild(
// ...configFileConfig,
root: rwPaths.web.base,
ssr: {
// Externalize everything except files that have 'use client' in them
// (this includes packages in node_modules that you use that have
// 'use client' in them)
// Externalize everything except packages with files that have
// 'use client' in them
// Files included in `noExternal` are files we want Vite to analyze
noExternal: Object.values(clientEntryFiles),
// TODO (RSC) This is the original code from waku. I think we can simplify it as above
// The code below will for most basic cases just be `[ '..' ]`, which we
// believe to be overly broad
// noExternal: Object.values(clientEntryFiles).map((fname) => {
// return path
// .relative(path.join(rwPaths.base, 'node_modules'), fname)
// .split('/')[0]
// }),
// The values in the array here are compared to npm package names, like
// 'react', 'core-js', @anthropic-ai/sdk', @redwoodjs/vite', etc
// The map function below will return '..' for local files. That's not
// very pretty, but it works. It just won't match anything.
noExternal: Object.values(clientEntryFiles).map((fname) => {
const relativePath = path.relative(
path.join(rwPaths.base, 'node_modules'),
fname
)
const splitPath = relativePath.split('/')

// TODO (RSC): Verify this is correct. Need to find a scoped packages
// that uses 'use client'
// Handle scoped packages
if (relativePath.startsWith('@')) {
return splitPath[0] + '/' + splitPath[1]
}

// Packages without scope
return splitPath[0]
}),
},
plugins: [react()],
resolve: {
Expand Down
1 change: 1 addition & 0 deletions packages/vite/src/waku-lib/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export async function build() {
),
],
ssr: {
// TODO (RSC): Is this still relevant?
// FIXME Without this, waku/router isn't considered to have client
// entries, and "No client entry" error occurs.
// Unless we fix this, RSC-capable packages aren't supported.
Expand Down
3 changes: 3 additions & 0 deletions packages/vite/src/waku-lib/rsc-handler-worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -238,12 +238,15 @@ const resolveClientEntry = (
filePath: string
) => {
const clientEntry = absoluteClientEntries[filePath]

if (!clientEntry) {
if (absoluteClientEntries['*'] === '*') {
return config.base + path.relative(config.root, filePath)
}

throw new Error('No client entry found for ' + filePath)
}

return clientEntry
}

Expand Down

0 comments on commit a436b1c

Please sign in to comment.