From dce74fc6698f4c61cfd93cfb60cf34a0efc936e3 Mon Sep 17 00:00:00 2001 From: Mohammad Bagher Abiyat Date: Wed, 6 Dec 2023 19:13:46 +0330 Subject: [PATCH] init --- e2e/fixtures/rsc-basic/{src => }/index.html | 2 +- .../{rsc-router/src => ssr-basic}/index.html | 2 +- .../src => examples/01_counter}/index.html | 2 +- .../{01_counter/src => 02_async}/index.html | 2 +- examples/02_async/src/index.html | 37 ------ examples/03_promise/index.html | 37 ++++++ examples/03_promise/src/index.html | 37 ------ examples/04_callserver/index.html | 37 ++++++ examples/04_callserver/src/index.html | 37 ------ examples/05_mutation/index.html | 37 ++++++ examples/05_mutation/src/index.html | 37 ------ examples/06_nesting/index.html | 37 ++++++ examples/06_nesting/src/index.html | 37 ------ examples/07_router/index.html | 37 ++++++ examples/07_router/src/index.html | 37 ------ examples/08_cookies/index.html | 37 ++++++ examples/08_cookies/src/index.html | 37 ------ examples/09_cssmodules/index.html | 37 ++++++ examples/09_cssmodules/src/index.html | 37 ------ examples/10_dynamicroute/index.html | 37 ++++++ examples/10_dynamicroute/src/index.html | 37 ------ examples/11_form/index.html | 37 ++++++ examples/11_form/src/index.html | 37 ------ package.json | 2 +- packages/waku/src/config.ts | 17 +-- packages/waku/src/lib/builder.ts | 106 +++++++----------- packages/waku/src/lib/config.ts | 15 --- packages/waku/src/lib/middleware/rsc.ts | 56 ++++----- packages/waku/src/lib/middleware/rsc/ssr.ts | 22 ++-- .../src/lib/middleware/rsc/worker-impl.ts | 2 - packages/waku/src/lib/rsc/renderer.ts | 19 +++- packages/waku/src/lib/utils/node-fs.ts | 4 + packages/waku/src/lib/utils/stream.ts | 20 ---- packages/waku/src/router/server.ts | 4 +- packages/website/src/index.html | 4 +- packages/website/src/main.tsx | 17 +-- pnpm-lock.yaml | 40 ------- tsconfig.e2e.json | 3 - 38 files changed, 442 insertions(+), 600 deletions(-) rename e2e/fixtures/rsc-basic/{src => }/index.html (92%) rename e2e/fixtures/{rsc-router/src => ssr-basic}/index.html (92%) rename {e2e/fixtures/ssr-basic/src => examples/01_counter}/index.html (92%) rename examples/{01_counter/src => 02_async}/index.html (92%) delete mode 100644 examples/02_async/src/index.html create mode 100644 examples/03_promise/index.html delete mode 100644 examples/03_promise/src/index.html create mode 100644 examples/04_callserver/index.html delete mode 100644 examples/04_callserver/src/index.html create mode 100644 examples/05_mutation/index.html delete mode 100644 examples/05_mutation/src/index.html create mode 100644 examples/06_nesting/index.html delete mode 100644 examples/06_nesting/src/index.html create mode 100644 examples/07_router/index.html delete mode 100644 examples/07_router/src/index.html create mode 100644 examples/08_cookies/index.html delete mode 100644 examples/08_cookies/src/index.html create mode 100644 examples/09_cssmodules/index.html delete mode 100644 examples/09_cssmodules/src/index.html create mode 100644 examples/10_dynamicroute/index.html delete mode 100644 examples/10_dynamicroute/src/index.html create mode 100644 examples/11_form/index.html delete mode 100644 examples/11_form/src/index.html diff --git a/e2e/fixtures/rsc-basic/src/index.html b/e2e/fixtures/rsc-basic/index.html similarity index 92% rename from e2e/fixtures/rsc-basic/src/index.html rename to e2e/fixtures/rsc-basic/index.html index 0953caabd..49e8ff5d0 100644 --- a/e2e/fixtures/rsc-basic/src/index.html +++ b/e2e/fixtures/rsc-basic/index.html @@ -30,7 +30,7 @@
- + diff --git a/e2e/fixtures/rsc-router/src/index.html b/e2e/fixtures/ssr-basic/index.html similarity index 92% rename from e2e/fixtures/rsc-router/src/index.html rename to e2e/fixtures/ssr-basic/index.html index 0953caabd..49e8ff5d0 100644 --- a/e2e/fixtures/rsc-router/src/index.html +++ b/e2e/fixtures/ssr-basic/index.html @@ -30,7 +30,7 @@
- + diff --git a/e2e/fixtures/ssr-basic/src/index.html b/examples/01_counter/index.html similarity index 92% rename from e2e/fixtures/ssr-basic/src/index.html rename to examples/01_counter/index.html index 0953caabd..49e8ff5d0 100644 --- a/e2e/fixtures/ssr-basic/src/index.html +++ b/examples/01_counter/index.html @@ -30,7 +30,7 @@
- + diff --git a/examples/01_counter/src/index.html b/examples/02_async/index.html similarity index 92% rename from examples/01_counter/src/index.html rename to examples/02_async/index.html index 0953caabd..49e8ff5d0 100644 --- a/examples/01_counter/src/index.html +++ b/examples/02_async/index.html @@ -30,7 +30,7 @@
- + diff --git a/examples/02_async/src/index.html b/examples/02_async/src/index.html deleted file mode 100644 index 0953caabd..000000000 --- a/examples/02_async/src/index.html +++ /dev/null @@ -1,37 +0,0 @@ - - - - - Waku example - - - - - -
-
-
- - - - - - diff --git a/examples/03_promise/index.html b/examples/03_promise/index.html new file mode 100644 index 000000000..49e8ff5d0 --- /dev/null +++ b/examples/03_promise/index.html @@ -0,0 +1,37 @@ + + + + + Waku example + + + + + +
+
+
+ + + + + + diff --git a/examples/03_promise/src/index.html b/examples/03_promise/src/index.html deleted file mode 100644 index 0953caabd..000000000 --- a/examples/03_promise/src/index.html +++ /dev/null @@ -1,37 +0,0 @@ - - - - - Waku example - - - - - -
-
-
- - - - - - diff --git a/examples/04_callserver/index.html b/examples/04_callserver/index.html new file mode 100644 index 000000000..49e8ff5d0 --- /dev/null +++ b/examples/04_callserver/index.html @@ -0,0 +1,37 @@ + + + + + Waku example + + + + + +
+
+
+ + + + + + diff --git a/examples/04_callserver/src/index.html b/examples/04_callserver/src/index.html deleted file mode 100644 index 0953caabd..000000000 --- a/examples/04_callserver/src/index.html +++ /dev/null @@ -1,37 +0,0 @@ - - - - - Waku example - - - - - -
-
-
- - - - - - diff --git a/examples/05_mutation/index.html b/examples/05_mutation/index.html new file mode 100644 index 000000000..49e8ff5d0 --- /dev/null +++ b/examples/05_mutation/index.html @@ -0,0 +1,37 @@ + + + + + Waku example + + + + + +
+
+
+ + + + + + diff --git a/examples/05_mutation/src/index.html b/examples/05_mutation/src/index.html deleted file mode 100644 index 0953caabd..000000000 --- a/examples/05_mutation/src/index.html +++ /dev/null @@ -1,37 +0,0 @@ - - - - - Waku example - - - - - -
-
-
- - - - - - diff --git a/examples/06_nesting/index.html b/examples/06_nesting/index.html new file mode 100644 index 000000000..49e8ff5d0 --- /dev/null +++ b/examples/06_nesting/index.html @@ -0,0 +1,37 @@ + + + + + Waku example + + + + + +
+
+
+ + + + + + diff --git a/examples/06_nesting/src/index.html b/examples/06_nesting/src/index.html deleted file mode 100644 index 0953caabd..000000000 --- a/examples/06_nesting/src/index.html +++ /dev/null @@ -1,37 +0,0 @@ - - - - - Waku example - - - - - -
-
-
- - - - - - diff --git a/examples/07_router/index.html b/examples/07_router/index.html new file mode 100644 index 000000000..49e8ff5d0 --- /dev/null +++ b/examples/07_router/index.html @@ -0,0 +1,37 @@ + + + + + Waku example + + + + + +
+
+
+ + + + + + diff --git a/examples/07_router/src/index.html b/examples/07_router/src/index.html deleted file mode 100644 index 0953caabd..000000000 --- a/examples/07_router/src/index.html +++ /dev/null @@ -1,37 +0,0 @@ - - - - - Waku example - - - - - -
-
-
- - - - - - diff --git a/examples/08_cookies/index.html b/examples/08_cookies/index.html new file mode 100644 index 000000000..49e8ff5d0 --- /dev/null +++ b/examples/08_cookies/index.html @@ -0,0 +1,37 @@ + + + + + Waku example + + + + + +
+
+
+ + + + + + diff --git a/examples/08_cookies/src/index.html b/examples/08_cookies/src/index.html deleted file mode 100644 index 0953caabd..000000000 --- a/examples/08_cookies/src/index.html +++ /dev/null @@ -1,37 +0,0 @@ - - - - - Waku example - - - - - -
-
-
- - - - - - diff --git a/examples/09_cssmodules/index.html b/examples/09_cssmodules/index.html new file mode 100644 index 000000000..49e8ff5d0 --- /dev/null +++ b/examples/09_cssmodules/index.html @@ -0,0 +1,37 @@ + + + + + Waku example + + + + + +
+
+
+ + + + + + diff --git a/examples/09_cssmodules/src/index.html b/examples/09_cssmodules/src/index.html deleted file mode 100644 index 0953caabd..000000000 --- a/examples/09_cssmodules/src/index.html +++ /dev/null @@ -1,37 +0,0 @@ - - - - - Waku example - - - - - -
-
-
- - - - - - diff --git a/examples/10_dynamicroute/index.html b/examples/10_dynamicroute/index.html new file mode 100644 index 000000000..49e8ff5d0 --- /dev/null +++ b/examples/10_dynamicroute/index.html @@ -0,0 +1,37 @@ + + + + + Waku example + + + + + +
+
+
+ + + + + + diff --git a/examples/10_dynamicroute/src/index.html b/examples/10_dynamicroute/src/index.html deleted file mode 100644 index 0953caabd..000000000 --- a/examples/10_dynamicroute/src/index.html +++ /dev/null @@ -1,37 +0,0 @@ - - - - - Waku example - - - - - -
-
-
- - - - - - diff --git a/examples/11_form/index.html b/examples/11_form/index.html new file mode 100644 index 000000000..49e8ff5d0 --- /dev/null +++ b/examples/11_form/index.html @@ -0,0 +1,37 @@ + + + + + Waku example + + + + + +
+
+
+ + + + + + diff --git a/examples/11_form/src/index.html b/examples/11_form/src/index.html deleted file mode 100644 index 0953caabd..000000000 --- a/examples/11_form/src/index.html +++ /dev/null @@ -1,37 +0,0 @@ - - - - - Waku example - - - - - -
-
-
- - - - - - diff --git a/package.json b/package.json index aa16105d1..8e79a6a62 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "examples:dev:09_cssmodules": "NAME=09_cssmodules pnpm run examples:dev", "examples:dev:10_dynamicroute": "NAME=10_dynamicroute pnpm run examples:dev", "examples:dev:11_form": "NAME=11_form pnpm run examples:dev", - "examples:build": "(cd ./examples/${NAME} && pnpm run build)", + "examples:build": "(cd ./examples/${NAME} && NODE_ENV=production pnpm run build)", "examples:prd": "pnpm run examples:build && (cd ./examples/${NAME} && pnpm start)", "examples:prd:01_counter": "NAME=01_counter pnpm run examples:prd", "examples:prd:02_async": "NAME=02_async pnpm run examples:prd", diff --git a/packages/waku/src/config.ts b/packages/waku/src/config.ts index 013d1a2a6..eb05b716b 100644 --- a/packages/waku/src/config.ts +++ b/packages/waku/src/config.ts @@ -24,34 +24,29 @@ export interface Config { /** * The public directory relative to distDir. * It's different from Vite's build.publicDir config. - * Defaults to "public". + * Defaults to "public". */ publicDir?: string; /** * The assets directory relative to distDir and publicDir. - * Defaults to "assets". + * Defaults to "assets". */ assetsDir?: string; /** - * The htmls directory relative to distDir. - * Defaults to "htmls". - */ - htmlsDir?: string; - /** - * The index.html file for any directories. - * Defaults to "index.html". + * The index.html file relative to srcDir or distDir. + * Defaults to "index.html". */ indexHtml?: string; /** * The entries.js file relative to srcDir or distDir. * The extension should be `.js`, * but resolved with `.ts`, `.tsx` and `.jsx` in the development mode. - * Defaults to "entries.js". + * Defaults to "entries.js". */ entriesJs?: string; /** * Prefix for HTTP requests to indicate RSC requests. - * Defaults to "RSC". + * Defaults to "RSC". */ rscPath?: string; /** diff --git a/packages/waku/src/lib/builder.ts b/packages/waku/src/lib/builder.ts index 424b5c230..46a9a7495 100644 --- a/packages/waku/src/lib/builder.ts +++ b/packages/waku/src/lib/builder.ts @@ -7,7 +7,7 @@ import viteReact from '@vitejs/plugin-react'; import type { RollupLog, LoggingFunction } from 'rollup'; import type { Config, ResolvedConfig } from '../config.js'; -import { resolveConfig, viteInlineConfig } from './config.js'; +import { resolveConfig } from './config.js'; import { joinPath, extname } from './utils/path.js'; import { createReadStream, @@ -17,8 +17,8 @@ import { mkdir, readFile, writeFile, + rm, } from './utils/node-fs.js'; -import { streamToString } from './utils/stream.js'; import { encodeInput, generatePrefetchCode } from './middleware/rsc/utils.js'; import { renderRSC, getBuildConfigRSC } from './rsc/renderer.js'; import { rscIndexPlugin } from './vite-plugin/rsc-index-plugin.js'; @@ -27,8 +27,6 @@ import { rscTransformPlugin } from './vite-plugin/rsc-transform-plugin.js'; import { patchReactRefresh } from './vite-plugin/patch-react-refresh.js'; import { renderHtml, shutdown as shutdownSsr } from './middleware/rsc/ssr.js'; -// TODO this file and functions in it are too long. will fix. - // Upstream issue: https://github.com/rollup/rollup/issues/4699 const onwarn = (warning: RollupLog, defaultHandler: LoggingFunction) => { if ( @@ -64,7 +62,6 @@ const analyzeEntries = async (entriesFile: string) => { const clientFileSet = new Set(); const serverFileSet = new Set(); await viteBuild({ - ...(await viteInlineConfig()), plugins: [rscAnalyzePlugin(commonFileSet, clientFileSet, serverFileSet)], ssr: { resolve: { @@ -118,7 +115,6 @@ const buildServerBundle = async ( serverEntryFiles: Record, ) => { const serverBuildOutput = await viteBuild({ - ...(await viteInlineConfig()), plugins: [rscTransformPlugin(true)], ssr: { resolve: { @@ -127,9 +123,6 @@ const buildServerBundle = async ( }, noExternal: /^(?!node:)/, }, - define: { - 'process.env.NODE_ENV': JSON.stringify('production'), - }, publicDir: false, build: { ssr: true, @@ -173,17 +166,11 @@ const buildClientBundle = async ( clientEntryFiles: Record, serverBuildOutput: Awaited>, ) => { - const indexHtmlFile = joinPath( - config.rootDir, - config.srcDir, - config.indexHtml, - ); + const indexHtmlFile = joinPath(config.rootDir, config.indexHtml); const cssAssets = serverBuildOutput.output.flatMap(({ type, fileName }) => type === 'asset' && fileName.endsWith('.css') ? [fileName] : [], ); const clientBuildOutput = await viteBuild({ - ...(await viteInlineConfig()), - root: joinPath(config.rootDir, config.srcDir), base: config.basePath, plugins: [patchReactRefresh(viteReact()), rscIndexPlugin(cssAssets)], build: { @@ -252,7 +239,7 @@ const emitRscFiles = async (config: ResolvedConfig) => { await Promise.all( Object.entries(buildConfig).map(async ([, { entries, context }]) => { for (const [input] of entries || []) { - const destRscFile = joinPath( + const destFile = joinPath( config.rootDir, config.distDir, config.publicDir, @@ -262,9 +249,9 @@ const emitRscFiles = async (config: ResolvedConfig) => { input.split('\\').join('/'), ), ); - if (!rscFileSet.has(destRscFile)) { - rscFileSet.add(destRscFile); - await mkdir(joinPath(destRscFile, '..'), { recursive: true }); + if (!rscFileSet.has(destFile)) { + rscFileSet.add(destFile); + await mkdir(joinPath(destFile, '..'), { recursive: true }); const readable = await renderRSC({ input, method: 'GET', @@ -275,7 +262,7 @@ const emitRscFiles = async (config: ResolvedConfig) => { }); await pipeline( Readable.fromWeb(readable as any), - createWriteStream(destRscFile), + createWriteStream(destFile), ); } } @@ -287,6 +274,7 @@ const emitRscFiles = async (config: ResolvedConfig) => { const emitHtmlFiles = async ( config: ResolvedConfig, buildConfig: Awaited>, + clientBuildOutput: Awaited>, getClientModules: (input: string) => string[], ssr: boolean, ) => { @@ -300,41 +288,30 @@ const emitHtmlFiles = async ( const publicIndexHtml = await readFile(publicIndexHtmlFile, { encoding: 'utf8', }); - const publicIndexHtmlJsFile = joinPath( - config.rootDir, - config.distDir, - config.htmlsDir, - config.indexHtml + '.js', - ); - await mkdir(joinPath(publicIndexHtmlJsFile, '..'), { recursive: true }); - await writeFile( - publicIndexHtmlJsFile, - `export default ${JSON.stringify(publicIndexHtml)};`, + + clientBuildOutput.output.splice( + clientBuildOutput.output.findIndex( + (v) => v.fileName === joinPath(config.srcDir, config.indexHtml), + ), + 1, ); const htmlFiles = await Promise.all( Object.entries(buildConfig).map( async ([pathStr, { entries, customCode, context }]) => { - const destHtmlFile = joinPath( + const destFile = joinPath( config.rootDir, config.distDir, config.publicDir, - extname(pathStr) ? pathStr : pathStr + '/' + config.indexHtml, - ); - const destHtmlJsFile = joinPath( - config.rootDir, - config.distDir, - config.htmlsDir, - (extname(pathStr) ? pathStr : pathStr + '/' + config.indexHtml) + - '.js', + pathStr, + pathStr.endsWith('/') ? 'index.html' : '', ); let htmlStr: string; - if (existsSync(destHtmlFile)) { - htmlStr = await readFile(destHtmlFile, { encoding: 'utf8' }); + if (existsSync(destFile)) { + htmlStr = await readFile(destFile, { encoding: 'utf8' }); } else { - await mkdir(joinPath(destHtmlFile, '..'), { recursive: true }); + await mkdir(joinPath(destFile, '..'), { recursive: true }); htmlStr = publicIndexHtml; } - await mkdir(joinPath(destHtmlJsFile, '..'), { recursive: true }); const inputsForPrefetch = new Set(); const moduleIdsForPrefetch = new Set(); for (const [input, skipPrefetch] of entries || []) { @@ -361,29 +338,15 @@ const emitHtmlFiles = async ( const htmlResult = ssr && (await renderHtml(config, 'build', pathStr, htmlStr, context)); if (htmlResult) { - const [htmlReadable1, htmlReadable2] = htmlResult[0].tee(); - await Promise.all([ - pipeline( - Readable.fromWeb(htmlReadable1 as any), - createWriteStream(destHtmlFile), - ), - streamToString(htmlReadable2).then((str) => - writeFile( - destHtmlJsFile, - `export default ${JSON.stringify(str)};`, - ), - ), - ]); + const [htmlReadable] = htmlResult; + await pipeline( + Readable.fromWeb(htmlReadable as any), + createWriteStream(destFile), + ); } else { - await Promise.all([ - writeFile(destHtmlFile, htmlStr), - writeFile( - destHtmlJsFile, - `export default ${JSON.stringify(htmlStr)};`, - ), - ]); + await writeFile(destFile, htmlStr); } - return destHtmlFile; + return destFile; }, ), ); @@ -464,14 +427,20 @@ export default async function handler(req, res) { `, ); - const overrides = Object.fromEntries( - rscFiles + const overrides = Object.fromEntries([ + ...rscFiles .filter((file) => !path.extname(file)) .map((file) => [ path.relative(srcDir, file), { contentType: 'text/plain' }, ]), - ); + ...htmlFiles + .filter((file) => !path.extname(file)) + .map((file) => [ + path.relative(srcDir, file), + { contentType: 'text/html' }, + ]), + ]); const basePrefix = config.basePath + config.rscPath + '/'; const routes = [{ src: basePrefix + '(.*)', dest: basePrefix }]; const configJson = { version: 3, overrides, routes }; @@ -519,6 +488,7 @@ export async function build(options: { config: Config; ssr?: boolean }) { const { htmlFiles } = await emitHtmlFiles( config, buildConfig, + clientBuildOutput, getClientModules, !!options?.ssr, ); diff --git a/packages/waku/src/lib/config.ts b/packages/waku/src/lib/config.ts index e94894c8f..aa903e765 100644 --- a/packages/waku/src/lib/config.ts +++ b/packages/waku/src/lib/config.ts @@ -28,7 +28,6 @@ export async function resolveConfig(config: Config) { distDir: 'dist', publicDir: 'public', assetsDir: 'assets', - htmlsDir: 'htmls', indexHtml: 'index.html', entriesJs: 'entries.js', rscPath: 'RSC', @@ -41,17 +40,3 @@ export async function resolveConfig(config: Config) { }; return resolvedConfig; } - -// TODO we hope to eliminate this in the near future -export const viteInlineConfig = async () => { - const [{ existsSync }, path] = await Promise.all([ - import('node:fs'), - import('node:path'), - ]); - for (const file of ['vite.config.ts', 'vite.config.js']) { - if (existsSync(file)) { - return { configFile: path.resolve(file) }; - } - } - return {}; -}; diff --git a/packages/waku/src/lib/middleware/rsc.ts b/packages/waku/src/lib/middleware/rsc.ts index 6495f5aa7..2864029b2 100644 --- a/packages/waku/src/lib/middleware/rsc.ts +++ b/packages/waku/src/lib/middleware/rsc.ts @@ -2,7 +2,8 @@ import type { ViteDevServer } from 'vite'; import type { Config } from '../../config.js'; import { resolveConfig } from '../config.js'; -import { joinPath, filePathToFileURL, extname } from '../utils/path.js'; +import { joinPath } from '../utils/path.js'; +import { readFile, stat } from '../utils/node-fs.js'; // TODO no node dependency import { endStream } from '../utils/stream.js'; import { renderHtml } from './rsc/ssr.js'; import { decodeInput, hasStatusCode, deepFreeze } from './rsc/utils.js'; @@ -34,27 +35,22 @@ export function rsc< let lastViteServer: ViteDevServer | undefined; const getViteServer = async (): Promise => { + const config = await configPromise; if (lastViteServer) { return lastViteServer; } const [ - config, - { viteInlineConfig }, { createServer: viteCreateServer }, { default: viteReact }, { rscIndexPlugin }, { rscHmrPlugin, hotImport }, ] = await Promise.all([ - configPromise, - import('../config.js'), import('vite'), import('@vitejs/plugin-react'), import('../vite-plugin/rsc-index-plugin.js'), import('../vite-plugin/rsc-hmr-plugin.js'), ]); const viteServer = await viteCreateServer({ - ...(await viteInlineConfig()), - root: joinPath(config.rootDir, config.srcDir), base: config.basePath, optimizeDeps: { include: ['react-server-dom-webpack/client'], @@ -76,43 +72,31 @@ export function rsc< let publicIndexHtml: string | undefined; const getHtmlStr = async (pathStr: string): Promise => { const config = await configPromise; + if (!publicIndexHtml) { + const publicIndexHtmlFile = joinPath( + config.rootDir, + ...(command === 'dev' ? [] : [config.distDir, config.publicDir]), + config.indexHtml, + ); + publicIndexHtml = await readFile(publicIndexHtmlFile, { + encoding: 'utf8', + }); + } if (command === 'start') { - if (!publicIndexHtml) { - const publicIndexHtmlJsFile = joinPath( - config.rootDir, - config.distDir, - config.htmlsDir, - config.indexHtml + '.js', - ); - publicIndexHtml = ( - await import(filePathToFileURL(publicIndexHtmlJsFile)) - ).default as string; - } - const destHtmlJsFile = joinPath( + const destFile = joinPath( config.rootDir, config.distDir, - config.htmlsDir, - (extname(pathStr) ? pathStr : pathStr + '/' + config.indexHtml) + '.js', + config.publicDir, + pathStr, + pathStr.endsWith('/') ? 'index.html' : '', ); try { - return (await import(filePathToFileURL(destHtmlJsFile))) - .default as string; + return await readFile(destFile, { encoding: 'utf8' }); } catch (e) { return publicIndexHtml; } } // command === "dev" - const { readFile, stat } = await import('../utils/node-fs.js'); - if (!publicIndexHtml) { - const publicIndexHtmlFile = joinPath( - config.rootDir, - config.srcDir, - config.indexHtml, - ); - publicIndexHtml = await readFile(publicIndexHtmlFile, { - encoding: 'utf8', - }); - } const vite = await getViteServer(); for (const item of vite.moduleGraph.idToModuleMap.values()) { if (item.url === pathStr) { @@ -121,7 +105,7 @@ export function rsc< } const destFile = joinPath(config.rootDir, config.srcDir, pathStr); try { - // check if destFile exists + // check if exists? const stats = await stat(destFile); if (stats.isFile()) { return null; @@ -129,7 +113,7 @@ export function rsc< } catch (e) { // does not exist } - // FIXME: otherwise SSR on Windows will fail + // fixme: otherwise SSR on Windows will fail if (pathStr.startsWith('/@fs')) { return null; } diff --git a/packages/waku/src/lib/middleware/rsc/ssr.ts b/packages/waku/src/lib/middleware/rsc/ssr.ts index 23c7573de..da6253a54 100644 --- a/packages/waku/src/lib/middleware/rsc/ssr.ts +++ b/packages/waku/src/lib/middleware/rsc/ssr.ts @@ -2,7 +2,6 @@ import type { ReactNode, FunctionComponent, ComponentProps } from 'react'; import type { ViteDevServer } from 'vite'; import type { ResolvedConfig } from '../../../config.js'; -import { viteInlineConfig } from '../../config.js'; import { defineEntries } from '../../../server.js'; import { concatUint8Arrays } from '../../utils/stream.js'; import { @@ -126,7 +125,6 @@ const getViteServer = async () => { '../../vite-plugin/nonjs-resolve-plugin.js' ); const viteServer = await viteCreateServer({ - ...(await viteInlineConfig()), plugins: [nonjsResolvePlugin()], ssr: { external: ['waku'], @@ -397,22 +395,26 @@ export const renderHtml = async ( {}, { get(_target, name: string) { - const file = filePath.slice(config.basePath.length); + const file = filePath.startsWith('/@id/') + ? filePath + : filePath.slice(config.basePath.length); if (command === 'dev') { - const filePath = file.startsWith('@fs/') - ? decodeFilePathFromAbsolute(file.slice('@fs'.length)) - : joinPath(config.rootDir, config.srcDir, file); + const resolvedFilePath = file.startsWith('/@fs/') + ? decodeFilePathFromAbsolute(file.slice('/@fs'.length)) + : file; const wakuDist = joinPath( fileURLToFilePath(import.meta.url), '../../../..', ); - if (filePath.startsWith(wakuDist)) { + if (resolvedFilePath.startsWith(wakuDist)) { const id = 'waku' + - filePath.slice(wakuDist.length).replace(/\.\w+$/, ''); + resolvedFilePath + .slice(wakuDist.length) + .replace(/\.\w+$/, ''); return { id, chunks: [id], name }; } - const id = filePathToFileURL(filePath) + '#dev'; + const id = filePathToFileURL(resolvedFilePath) + '#dev'; return { id, chunks: [id], name }; } // command !== 'dev' @@ -421,7 +423,7 @@ export const renderHtml = async ( config.rootDir, config.distDir, config.publicDir, - file, + filePath, ), ); return { id, chunks: [id], name }; diff --git a/packages/waku/src/lib/middleware/rsc/worker-impl.ts b/packages/waku/src/lib/middleware/rsc/worker-impl.ts index d83e1f480..31903fb26 100644 --- a/packages/waku/src/lib/middleware/rsc/worker-impl.ts +++ b/packages/waku/src/lib/middleware/rsc/worker-impl.ts @@ -6,7 +6,6 @@ import { Server } from 'node:http'; import { createServer as viteCreateServer } from 'vite'; import type { ViteDevServer } from 'vite'; -import { viteInlineConfig } from '../../config.js'; import { fileURLToFilePath } from '../../utils/path.js'; import { hasStatusCode, deepFreeze } from './utils.js'; import type { MessageReq, MessageRes, RenderRequest } from './worker-api.js'; @@ -91,7 +90,6 @@ const getViteServer = async () => { } const dummyServer = new Server(); // FIXME we hope to avoid this hack const viteServer = await viteCreateServer({ - ...(await viteInlineConfig()), plugins: [ rscTransformPlugin(false), rscReloadPlugin((type) => { diff --git a/packages/waku/src/lib/rsc/renderer.ts b/packages/waku/src/lib/rsc/renderer.ts index 90a3db1bc..69b48159f 100644 --- a/packages/waku/src/lib/rsc/renderer.ts +++ b/packages/waku/src/lib/rsc/renderer.ts @@ -11,7 +11,6 @@ import { fileURLToFilePath, } from '../utils/path.js'; import { parseFormData } from '../utils/form.js'; -import { streamToString } from '../utils/stream.js'; const loadRSDWServer = async ( config: Omit, @@ -51,7 +50,7 @@ const resolveClientEntry = ( isDev: boolean, ) => { let filePath = file.startsWith('file://') ? fileURLToFilePath(file) : file; - const root = joinPath(config.rootDir, isDev ? config.srcDir : config.distDir); + const root = joinPath(config.rootDir, !isDev ? config.distDir : ''); // HACK on windows file url looks like file:///C:/path/to/file if (!root.startsWith('/') && filePath.startsWith('/')) { filePath = filePath.slice(1); @@ -66,7 +65,8 @@ const resolveClientEntry = ( ); } } - return config.basePath + relativePath(root, filePath); + // https://github.com/dai-shi/waku/pull/181#discussion_r1409274135 + return (isDev ? '/@id' : '') + config.basePath + relativePath(root, filePath); }; // HACK Patching stream is very fragile. @@ -171,7 +171,18 @@ export async function renderRSC( let args: unknown[] = []; let bodyStr = ''; if (body) { - bodyStr = await streamToString(body); + const decoder = new TextDecoder(); + const reader = body.getReader(); + let result: ReadableStreamReadResult; + do { + result = await reader.read(); + if (result.value) { + if (!(result.value instanceof Uint8Array)) { + throw new Error('Unexepected buffer type'); + } + bodyStr += decoder.decode(result.value); + } + } while (!result.done); } if ( typeof contentType === 'string' && diff --git a/packages/waku/src/lib/utils/node-fs.ts b/packages/waku/src/lib/utils/node-fs.ts index 3e58e13c0..f27688307 100644 --- a/packages/waku/src/lib/utils/node-fs.ts +++ b/packages/waku/src/lib/utils/node-fs.ts @@ -1,5 +1,6 @@ import path from 'node:path'; import fs from 'node:fs'; +import type { RmOptions } from 'node:fs'; import fsPromises from 'node:fs/promises'; const filePathToOsPath = (filePath: string) => @@ -30,3 +31,6 @@ export const writeFile = (filePath: string, content: string) => export const stat = (filePath: string) => fsPromises.stat(filePathToOsPath(filePath)); + +export const rm = (path: string, options?: RmOptions) => + fsPromises.rm(filePathToOsPath(path), options); diff --git a/packages/waku/src/lib/utils/stream.ts b/packages/waku/src/lib/utils/stream.ts index a72b5b130..ac07c3843 100644 --- a/packages/waku/src/lib/utils/stream.ts +++ b/packages/waku/src/lib/utils/stream.ts @@ -17,23 +17,3 @@ export const concatUint8Arrays = (arrs: Uint8Array[]): Uint8Array => { } return array; }; - -export const streamToString = async ( - stream: ReadableStream, -): Promise => { - const decoder = new TextDecoder(); - const reader = stream.getReader(); - const outs: string[] = []; - let result: ReadableStreamReadResult; - do { - result = await reader.read(); - if (result.value) { - if (!(result.value instanceof Uint8Array)) { - throw new Error('Unexepected buffer type'); - } - outs.push(decoder.decode(result.value, { stream: true })); - } - } while (!result.done); - outs.push(decoder.decode()); - return outs.join(''); -}; diff --git a/packages/waku/src/router/server.ts b/packages/waku/src/router/server.ts index 52d96c02c..31a99d136 100644 --- a/packages/waku/src/router/server.ts +++ b/packages/waku/src/router/server.ts @@ -20,10 +20,10 @@ const prefetcher = (pathname: string) => { const Default = ({ children }: { children: ReactNode }) => children; -export function defineRouter

( +export function defineRouter( getComponent: ( componentId: string, - ) => Promise | { default: FunctionComponent

} | null>, + ) => Promise, getPathsForBuild: () => Promise, ): ReturnType { const renderEntries: RenderEntries = async (input) => { diff --git a/packages/website/src/index.html b/packages/website/src/index.html index b94919fc8..c84487e62 100644 --- a/packages/website/src/index.html +++ b/packages/website/src/index.html @@ -56,7 +56,9 @@

- + + + diff --git a/packages/website/src/main.tsx b/packages/website/src/main.tsx index 87e03a85e..248a8891c 100644 --- a/packages/website/src/main.tsx +++ b/packages/website/src/main.tsx @@ -10,17 +10,8 @@ const rootElement = ( ); -// FIXME temporary fix, doesn't feel ideal. -function init() { - const root = document.getElementById('root'); - if (!root) { - setTimeout(init); - return; - } - if ((globalThis as any).__WAKU_SSR_ENABLED__) { - hydrateRoot(root, rootElement); - } else { - createRoot(root).render(rootElement); - } +if ((globalThis as any).__WAKU_SSR_ENABLED__) { + hydrateRoot(document.getElementById('root')!, rootElement); +} else { + createRoot(document.getElementById('root')!).render(rootElement); } -init(); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6a4800d7e..87167ad0b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -112,46 +112,6 @@ importers: specifier: 5.0.4 version: 5.0.4(@types/node@20.9.0) - e2e/fixtures/rsc-router: - dependencies: - '@hono/node-server': - specifier: ^1.2.2 - version: 1.2.3 - hono: - specifier: ^3.10.0 - version: 3.10.2 - react: - specifier: 18.3.0-canary-6c7b41da3-20231123 - version: 18.3.0-canary-6c7b41da3-20231123 - react-dom: - specifier: 18.3.0-canary-6c7b41da3-20231123 - version: 18.3.0-canary-6c7b41da3-20231123(react@18.3.0-canary-6c7b41da3-20231123) - react-server-dom-webpack: - specifier: 18.3.0-canary-6c7b41da3-20231123 - version: 18.3.0-canary-6c7b41da3-20231123(react-dom@18.3.0-canary-6c7b41da3-20231123)(react@18.3.0-canary-6c7b41da3-20231123)(webpack@5.89.0) - waku: - specifier: 0.17.1 - version: link:../../../packages/waku - devDependencies: - '@swc/core': - specifier: 1.3.96 - version: 1.3.96 - '@types/react': - specifier: ^18.2.37 - version: 18.2.37 - '@types/react-dom': - specifier: ^18.2.15 - version: 18.2.15 - '@vitejs/plugin-react': - specifier: 4.2.0 - version: 4.2.0(vite@5.0.4) - typescript: - specifier: ^5.2.2 - version: 5.2.2 - vite: - specifier: 5.0.4 - version: 5.0.4(@types/node@20.9.0) - e2e/fixtures/ssr-basic: dependencies: '@hono/node-server': diff --git a/tsconfig.e2e.json b/tsconfig.e2e.json index 882b10f7e..8966ab450 100644 --- a/tsconfig.e2e.json +++ b/tsconfig.e2e.json @@ -11,9 +11,6 @@ }, { "path": "./e2e/fixtures/ssr-basic/tsconfig.json" - }, - { - "path": "./e2e/fixtures/rsc-router/tsconfig.json" } ] }