-
-
Notifications
You must be signed in to change notification settings - Fork 138
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: keep server assets on server build (#1258)
- Related #1245 While trying to come up with a simpler example, I realized that server-only asset concept doesn't actually exist as a builtin Vite feature and this can be only achieved by using Rollup's `emitFile + import.meta.ROLLUP_FILE_URL_(id)` feature on Vite ssr build via custom plugin. So, I think cloudflare wasm import scenario I mentioned in the issue is fairly an edge case. Nonetheless, there might be other vite/rollup plugins relying on `ROLLUP_FILE_URL` feature on server build, so simply replacing `rename` with `copyFile` to save this specific case might be still worth it. --------- Co-authored-by: Daishi Kato <[email protected]>
- Loading branch information
Showing
12 changed files
with
205 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
{ | ||
"name": "rsc-asset", | ||
"version": "0.1.0", | ||
"type": "module", | ||
"private": true, | ||
"scripts": { | ||
"dev": "waku dev", | ||
"build": "waku build", | ||
"start": "waku start" | ||
}, | ||
"dependencies": { | ||
"react": "19.0.0", | ||
"react-dom": "19.0.0", | ||
"react-server-dom-webpack": "19.0.0", | ||
"waku": "workspace:*" | ||
}, | ||
"devDependencies": { | ||
"@types/react": "^19.0.10", | ||
"@types/react-dom": "^19.0.4", | ||
"typescript": "^5.7.3", | ||
"vite": "6.1.0" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import fs from 'node:fs'; | ||
import testClientTxtUrl from './test-client.txt?no-inline'; | ||
|
||
const App = (_: { name: string }) => { | ||
// vite doesn't handle `new URL` for ssr, | ||
// so this is handled by a custom plugin in waku.config.ts | ||
const testServerTxtUrl = new URL('./test-server.txt', import.meta.url); | ||
|
||
return ( | ||
<html> | ||
<head> | ||
<title>e2e-rsc-asset</title> | ||
</head> | ||
<body> | ||
<main> | ||
<div> | ||
client asset:{' '} | ||
<a href={testClientTxtUrl} data-testid="client-link"> | ||
{testClientTxtUrl} | ||
</a> | ||
</div> | ||
<div data-testid="server-file"> | ||
server asset: {fs.readFileSync(testServerTxtUrl, 'utf-8')} | ||
</div> | ||
</main> | ||
</body> | ||
</html> | ||
); | ||
}; | ||
|
||
export default App; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
test-client-ok |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
test-server-ok |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { unstable_defineEntries as defineEntries } from 'waku/minimal/server'; | ||
|
||
import App from './components/App.js'; | ||
|
||
const entries: ReturnType<typeof defineEntries> = defineEntries({ | ||
handleRequest: async (input, { renderRsc }) => { | ||
if (input.type === 'component') { | ||
return renderRsc({ App: <App name={input.rscPath || 'Waku'} /> }); | ||
} | ||
if (input.type === 'function') { | ||
const value = await input.fn(...input.args); | ||
return renderRsc({ _value: value }); | ||
} | ||
}, | ||
handleBuild: () => null, | ||
}); | ||
|
||
export default entries; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { StrictMode } from 'react'; | ||
import { createRoot } from 'react-dom/client'; | ||
import { Root, Slot } from 'waku/minimal/client'; | ||
|
||
const rootElement = ( | ||
<StrictMode> | ||
<Root> | ||
<Slot id="App" /> | ||
</Root> | ||
</StrictMode> | ||
); | ||
|
||
createRoot(document as any).render(rootElement); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
{ | ||
"compilerOptions": { | ||
"strict": true, | ||
"target": "esnext", | ||
"downlevelIteration": true, | ||
"esModuleInterop": true, | ||
"module": "nodenext", | ||
"skipLibCheck": true, | ||
"noUncheckedIndexedAccess": true, | ||
"exactOptionalPropertyTypes": true, | ||
"types": ["react/experimental", "vite/client"], | ||
"jsx": "react-jsx", | ||
"outDir": "./dist", | ||
"composite": true | ||
}, | ||
"include": ["./src", "./waku.config.ts"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import { defineConfig } from 'waku/config'; | ||
import type { Plugin } from 'vite'; | ||
import path from 'node:path'; | ||
import fs from 'node:fs'; | ||
|
||
export default defineConfig({ | ||
unstable_viteConfigs: { | ||
'build-server': () => ({ | ||
plugins: [importMetaUrlServerPlugin()], | ||
}), | ||
}, | ||
}); | ||
|
||
// emit asset and rewrite `new URL("./xxx", import.meta.url)` syntax for build. | ||
function importMetaUrlServerPlugin(): Plugin { | ||
// https://github.com/vitejs/vite/blob/0f56e1724162df76fffd5508148db118767ebe32/packages/vite/src/node/plugins/assetImportMetaUrl.ts#L51-L52 | ||
const assetImportMetaUrlRE = | ||
/\bnew\s+URL\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*,\s*import\.meta\.url\s*(?:,\s*)?\)/dg; | ||
|
||
return { | ||
name: 'test-server-asset', | ||
transform(code, id) { | ||
return code.replace(assetImportMetaUrlRE, (s, match) => { | ||
const absPath = path.resolve(path.dirname(id), match.slice(1, -1)); | ||
if (fs.existsSync(absPath)) { | ||
const referenceId = this.emitFile({ | ||
type: 'asset', | ||
name: path.basename(absPath), | ||
source: new Uint8Array(fs.readFileSync(absPath)), | ||
}); | ||
return `new URL(import.meta.ROLLUP_FILE_URL_${referenceId})`; | ||
} | ||
return s; | ||
}); | ||
}, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import { expect } from '@playwright/test'; | ||
|
||
import { test, prepareNormalSetup } from './utils.js'; | ||
|
||
const startApp = prepareNormalSetup('rsc-asset'); | ||
|
||
for (const mode of ['DEV', 'PRD'] as const) { | ||
test.describe(`rsc-asset: ${mode}`, () => { | ||
let port: number; | ||
let stopApp: () => Promise<void>; | ||
test.beforeAll(async () => { | ||
({ port, stopApp } = await startApp(mode)); | ||
}); | ||
test.afterAll(async () => { | ||
await stopApp(); | ||
}); | ||
|
||
test('basic', async ({ page }) => { | ||
await page.goto(`http://localhost:${port}/`); | ||
|
||
// server asset | ||
await expect(page.getByTestId('server-file')).toContainText( | ||
'server asset: test-server-ok', | ||
); | ||
|
||
// client asset | ||
await page.getByTestId('client-link').click(); | ||
await page.getByText('test-client-ok').click(); | ||
}); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters