From a0b5617d68f1d6b8f5343fb9d88a3750e0b36459 Mon Sep 17 00:00:00 2001 From: daishi Date: Sun, 28 Apr 2024 11:12:05 +0900 Subject: [PATCH 01/12] wip: remove mainJs and entriesJs from config --- packages/waku/src/cli.ts | 5 ++--- packages/waku/src/config.ts | 14 -------------- packages/waku/src/lib/builder/build.ts | 8 +++++--- packages/waku/src/lib/config.ts | 2 -- 4 files changed, 7 insertions(+), 22 deletions(-) diff --git a/packages/waku/src/cli.ts b/packages/waku/src/cli.ts index 79febe71a..a881adb6f 100644 --- a/packages/waku/src/cli.ts +++ b/packages/waku/src/cli.ts @@ -10,7 +10,7 @@ import * as dotenv from 'dotenv'; import type { Config } from './config.js'; import { runner } from './lib/hono/runner.js'; -import { build } from './lib/builder/build.js'; +import { ENTRIES_JS, build } from './lib/builder/build.js'; const require = createRequire(new URL('.', import.meta.url)); @@ -122,11 +122,10 @@ async function runBuild() { async function runStart({ distDir = 'dist', - entriesJs = 'entries.js', publicDir = 'public', }) { const loadEntries = () => - import(pathToFileURL(path.resolve(distDir, entriesJs)).toString()); + import(pathToFileURL(path.resolve(distDir, ENTRIES_JS)).toString()); const app = new Hono(); app.use('*', serveStatic({ root: path.join(distDir, publicDir) })); app.use('*', runner({ cmd: 'start', loadEntries, env: process.env as any })); diff --git a/packages/waku/src/config.ts b/packages/waku/src/config.ts index 8c08600ac..b0c8c6bc6 100644 --- a/packages/waku/src/config.ts +++ b/packages/waku/src/config.ts @@ -41,20 +41,6 @@ export interface Config { * Defaults to "index.html". */ indexHtml?: string; - /** - * The client main file relative to srcDir. - * The extension should be `.js`, - * but resolved with other extensions in the development mode. - * Defaults to "main.js". - */ - mainJs?: string; - /** - * The entries.js file relative to srcDir or distDir. - * The extension should be `.js`, - * but resolved with other extensions in the development mode. - * Defaults to "entries.js". - */ - entriesJs?: string; /** * The list of directries to preserve server module structure. * Relative to srcDir. diff --git a/packages/waku/src/lib/builder/build.ts b/packages/waku/src/lib/builder/build.ts index 7a262b9d9..893524cc5 100644 --- a/packages/waku/src/lib/builder/build.ts +++ b/packages/waku/src/lib/builder/build.ts @@ -75,6 +75,10 @@ const onwarn = (warning: RollupLog, defaultHandler: LoggingFunction) => { defaultHandler(warning); }; +// entries file name for dist +// We may change this in the future +export const ENTRIES_JS = 'entries.js'; + const analyzeEntries = async ( rootDir: string, config: ResolvedConfig, @@ -641,9 +645,7 @@ export async function build(options: { const entriesFile = resolveFileName( joinPath(rootDir, config.srcDir, config.entriesJs), ); - const distEntriesFile = resolveFileName( - joinPath(rootDir, config.distDir, config.entriesJs), - ); + const distEntriesFile = joinPath(rootDir, config.distDir, ENTRIES_JS); const mainJsFile = resolveFileName( joinPath(rootDir, config.srcDir, config.mainJs), ); diff --git a/packages/waku/src/lib/config.ts b/packages/waku/src/lib/config.ts index ee4632805..29d04dc14 100644 --- a/packages/waku/src/lib/config.ts +++ b/packages/waku/src/lib/config.ts @@ -36,8 +36,6 @@ export async function resolveConfig(config: Config) { assetsDir: 'assets', ssrDir: 'ssr', indexHtml: 'index.html', - mainJs: 'main.js', - entriesJs: 'entries.js', preserveModuleDirs: ['pages', 'templates', 'routes', 'components'], privateDir: 'private', serveJs: 'serve.js', From ce1f1d03aadc6c3187869ccda011c717dc3173b5 Mon Sep 17 00:00:00 2001 From: daishi Date: Sun, 28 Apr 2024 17:27:22 +0900 Subject: [PATCH 02/12] do not expose serveJs --- packages/waku/src/config.ts | 7 ------- packages/waku/src/lib/builder/build.ts | 10 +++++++--- packages/waku/src/lib/builder/output-cloudflare.ts | 3 ++- packages/waku/src/lib/builder/output-netlify.ts | 3 ++- packages/waku/src/lib/builder/output-partykit.ts | 3 ++- packages/waku/src/lib/builder/output-vercel.ts | 3 ++- packages/waku/src/lib/config.ts | 1 - 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/packages/waku/src/config.ts b/packages/waku/src/config.ts index 15bf2b262..d8805489d 100644 --- a/packages/waku/src/config.ts +++ b/packages/waku/src/config.ts @@ -10,7 +10,6 @@ export interface Config { basePath?: string; /** * The source directory relative to root. - * This will be the actual root in the development mode. * Defaults to "src". */ srcDir?: string; @@ -49,12 +48,6 @@ export interface Config { * Defaults to "private". */ privateDir?: string; - /** - * The serve.js file relative distDir. - * This file is used for deployment. - * Defaults to "serve.js". - */ - serveJs?: string; /** * Prefix for HTTP requests to indicate RSC requests. * Defaults to "RSC". diff --git a/packages/waku/src/lib/builder/build.ts b/packages/waku/src/lib/builder/build.ts index 18c324261..33c566eb9 100644 --- a/packages/waku/src/lib/builder/build.ts +++ b/packages/waku/src/lib/builder/build.ts @@ -75,9 +75,10 @@ const onwarn = (warning: RollupLog, defaultHandler: LoggingFunction) => { defaultHandler(warning); }; -// entries file name for dist +// Some file and dir names for dist // We may change this in the future export const ENTRIES_JS = 'entries.js'; +export const SERVE_JS = 'serve.js'; const analyzeEntries = async ( rootDir: string, @@ -212,6 +213,7 @@ const buildServerBundle = async ( ? [ rscServePlugin({ ...config, + serveJs: SERVE_JS, entriesFile, srcServeFile: decodeFilePathFromAbsolute( joinPath( @@ -713,18 +715,20 @@ export async function build(options: { await emitVercelOutput( rootDir, config, + SERVE_JS, options.deploy.slice('vercel-'.length) as 'static' | 'serverless', ); } else if (options.deploy?.startsWith('netlify-')) { await emitNetlifyOutput( rootDir, config, + SERVE_JS, options.deploy.slice('netlify-'.length) as 'static' | 'functions', ); } else if (options.deploy === 'cloudflare') { - await emitCloudflareOutput(rootDir, config); + await emitCloudflareOutput(rootDir, config, SERVE_JS); } else if (options.deploy === 'partykit') { - await emitPartyKitOutput(rootDir, config); + await emitPartyKitOutput(rootDir, config, SERVE_JS); } else if (options.deploy === 'aws-lambda') { await emitAwsLambdaOutput(config); } diff --git a/packages/waku/src/lib/builder/output-cloudflare.ts b/packages/waku/src/lib/builder/output-cloudflare.ts index d6546299e..e7b90d9a3 100644 --- a/packages/waku/src/lib/builder/output-cloudflare.ts +++ b/packages/waku/src/lib/builder/output-cloudflare.ts @@ -7,6 +7,7 @@ import type { ResolvedConfig } from '../config.js'; export const emitCloudflareOutput = async ( rootDir: string, config: ResolvedConfig, + serveJs: string, ) => { const wranglerTomlFile = path.join(rootDir, 'wrangler.toml'); if (!existsSync(wranglerTomlFile)) { @@ -14,7 +15,7 @@ export const emitCloudflareOutput = async ( wranglerTomlFile, ` name = "waku-project" -main = "${config.distDir}/${config.serveJs}" +main = "${config.distDir}/${serveJs}" compatibility_date = "2023-12-06" compatibility_flags = [ "nodejs_als" ] diff --git a/packages/waku/src/lib/builder/output-netlify.ts b/packages/waku/src/lib/builder/output-netlify.ts index bdb5fb1ee..03d9b8d30 100644 --- a/packages/waku/src/lib/builder/output-netlify.ts +++ b/packages/waku/src/lib/builder/output-netlify.ts @@ -6,6 +6,7 @@ import type { ResolvedConfig } from '../config.js'; export const emitNetlifyOutput = async ( rootDir: string, config: ResolvedConfig, + serveJs: string, type: 'static' | 'functions', ) => { if (type === 'functions') { @@ -26,7 +27,7 @@ export const emitNetlifyOutput = async ( path.join(functionsDir, 'serve.js'), ` globalThis.__WAKU_NOT_FOUND_HTML__ = ${JSON.stringify(notFoundHtml)}; -export { default } from '../../${config.distDir}/${config.serveJs}'; +export { default } from '../../${config.distDir}/${serveJs}'; export const config = { preferStatic: true, path: ['/', '/*'], diff --git a/packages/waku/src/lib/builder/output-partykit.ts b/packages/waku/src/lib/builder/output-partykit.ts index fb41247da..869472ddc 100644 --- a/packages/waku/src/lib/builder/output-partykit.ts +++ b/packages/waku/src/lib/builder/output-partykit.ts @@ -7,6 +7,7 @@ import type { ResolvedConfig } from '../config.js'; export const emitPartyKitOutput = async ( rootDir: string, config: ResolvedConfig, + serveJs: string, ) => { const partykitJsonFile = path.join(rootDir, 'partykit.json'); if (!existsSync(partykitJsonFile)) { @@ -15,7 +16,7 @@ export const emitPartyKitOutput = async ( JSON.stringify( { name: 'waku-project', - main: `${config.distDir}/${config.serveJs}`, + main: `${config.distDir}/${serveJs}`, compatibilityDate: '2023-02-16', serve: `./${config.distDir}/${config.publicDir}`, }, diff --git a/packages/waku/src/lib/builder/output-vercel.ts b/packages/waku/src/lib/builder/output-vercel.ts index 3ba0595db..93f271de2 100644 --- a/packages/waku/src/lib/builder/output-vercel.ts +++ b/packages/waku/src/lib/builder/output-vercel.ts @@ -7,6 +7,7 @@ import type { ResolvedConfig } from '../config.js'; export const emitVercelOutput = async ( rootDir: string, config: ResolvedConfig, + serveJs: string, type: 'static' | 'serverless', ) => { const publicDir = path.join(rootDir, config.distDir, config.publicDir); @@ -37,7 +38,7 @@ export const emitVercelOutput = async ( } const vcConfigJson = { runtime: 'nodejs18.x', - handler: `${config.distDir}/${config.serveJs}`, + handler: `${config.distDir}/${serveJs}`, launcherType: 'Nodejs', }; writeFileSync( diff --git a/packages/waku/src/lib/config.ts b/packages/waku/src/lib/config.ts index 048c11db0..be8e040ab 100644 --- a/packages/waku/src/lib/config.ts +++ b/packages/waku/src/lib/config.ts @@ -37,7 +37,6 @@ export async function resolveConfig(config: Config) { ssrDir: 'ssr', preserveModuleDirs: ['pages', 'templates', 'routes', 'components'], privateDir: 'private', - serveJs: 'serve.js', rscPath: 'RSC', htmlAttrs: '', htmlHead: DEFAULT_HTML_HEAD, From 947d9e31b7fe8d8035a3dc8991fa238e7f548018 Mon Sep 17 00:00:00 2001 From: daishi Date: Sun, 28 Apr 2024 22:06:41 +0900 Subject: [PATCH 03/12] internalize entriesJs and mainJs --- packages/waku/src/cli.ts | 9 +- packages/waku/src/lib/builder/build.ts | 85 +++++++------------ .../waku/src/lib/middleware/dev-server.ts | 11 ++- .../lib/plugins/vite-plugin-rsc-entries.ts | 37 +++++--- .../src/lib/plugins/vite-plugin-rsc-index.ts | 9 +- .../lib/plugins/vite-plugin-rsc-managed.ts | 48 +++++++++-- .../src/lib/plugins/vite-plugin-rsc-serve.ts | 30 ++++++- .../waku/src/lib/renderers/dev-worker-api.ts | 5 +- .../waku/src/lib/renderers/dev-worker-impl.ts | 8 +- .../waku/src/lib/renderers/html-renderer.ts | 5 +- 10 files changed, 151 insertions(+), 96 deletions(-) diff --git a/packages/waku/src/cli.ts b/packages/waku/src/cli.ts index a881adb6f..c9e3f9018 100644 --- a/packages/waku/src/cli.ts +++ b/packages/waku/src/cli.ts @@ -10,7 +10,7 @@ import * as dotenv from 'dotenv'; import type { Config } from './config.js'; import { runner } from './lib/hono/runner.js'; -import { ENTRIES_JS, build } from './lib/builder/build.js'; +import { DIST_ENTRIES_JS, build } from './lib/builder/build.js'; const require = createRequire(new URL('.', import.meta.url)); @@ -120,12 +120,9 @@ async function runBuild() { }); } -async function runStart({ - distDir = 'dist', - publicDir = 'public', -}) { +async function runStart({ distDir = 'dist', publicDir = 'public' }) { const loadEntries = () => - import(pathToFileURL(path.resolve(distDir, ENTRIES_JS)).toString()); + import(pathToFileURL(path.resolve(distDir, DIST_ENTRIES_JS)).toString()); const app = new Hono(); app.use('*', serveStatic({ root: path.join(distDir, publicDir) })); app.use('*', runner({ cmd: 'start', loadEntries, env: process.env as any })); diff --git a/packages/waku/src/lib/builder/build.ts b/packages/waku/src/lib/builder/build.ts index 33c566eb9..9fa5848e9 100644 --- a/packages/waku/src/lib/builder/build.ts +++ b/packages/waku/src/lib/builder/build.ts @@ -77,14 +77,10 @@ const onwarn = (warning: RollupLog, defaultHandler: LoggingFunction) => { // Some file and dir names for dist // We may change this in the future -export const ENTRIES_JS = 'entries.js'; -export const SERVE_JS = 'serve.js'; +export const DIST_ENTRIES_JS = 'entries.js'; +export const DIST_SERVE_JS = 'serve.js'; -const analyzeEntries = async ( - rootDir: string, - config: ResolvedConfig, - entriesFile: string, -) => { +const analyzeEntries = async (rootDir: string, config: ResolvedConfig) => { const wakuClientDist = decodeFilePathFromAbsolute( joinPath(fileURLToFilePath(import.meta.url), '../../../client.js'), ); @@ -111,7 +107,10 @@ const analyzeEntries = async ( await buildVite({ plugins: [ rscAnalyzePlugin(clientFileSet, serverFileSet, fileHashMap), - rscManagedPlugin(config), + rscManagedPlugin({ + srcDir: config.srcDir, + addEntriesJsToInput: true, + }), ], ssr: { target: 'webworker', @@ -127,10 +126,7 @@ const analyzeEntries = async ( target: 'node18', rollupOptions: { onwarn, - input: { - ...Object.fromEntries(moduleFileMap), - entries: entriesFile, - }, + input: Object.fromEntries(moduleFileMap), }, }, }); @@ -158,7 +154,6 @@ const analyzeEntries = async ( const buildServerBundle = async ( rootDir: string, config: ResolvedConfig, - entriesFile: string, clientEntryFiles: Record, serverEntryFiles: Record, serverModuleFiles: Record, @@ -182,9 +177,12 @@ const buildServerBundle = async ( }), rscEnvPlugin({ config }), rscPrivatePlugin(config), - rscManagedPlugin(config), + rscManagedPlugin({ + srcDir: config.srcDir, + addEntriesJsToInput: true, + }), rscEntriesPlugin({ - entriesFile, + srcDir: config.srcDir, moduleMap: { ...Object.fromEntries( Object.keys(SERVER_MODULE_MAP).map((key) => [key, `./${key}.js`]), @@ -213,8 +211,7 @@ const buildServerBundle = async ( ? [ rscServePlugin({ ...config, - serveJs: SERVE_JS, - entriesFile, + distServeJs: DIST_SERVE_JS, srcServeFile: decodeFilePathFromAbsolute( joinPath( fileURLToFilePath(import.meta.url), @@ -257,7 +254,6 @@ const buildServerBundle = async ( rollupOptions: { onwarn, input: { - entries: entriesFile, ...SERVER_MODULE_MAP, ...serverModuleFiles, ...clientEntryFiles, @@ -276,7 +272,6 @@ const buildServerBundle = async ( const buildSsrBundle = async ( rootDir: string, config: ResolvedConfig, - mainJsFile: string, clientEntryFiles: Record, serverBuildOutput: Awaited>, isNodeCompatible: boolean, @@ -290,11 +285,13 @@ const buildSsrBundle = async ( rscIndexPlugin({ ...config, cssAssets, - mainJs: mainJsFile.split('/').pop()!, }), rscEnvPlugin({ config }), rscPrivatePlugin(config), - rscManagedPlugin(config), + rscManagedPlugin({ + srcDir: config.srcDir, + addMainJsToInput: true, + }), ], ssr: isNodeCompatible ? { @@ -322,7 +319,6 @@ const buildSsrBundle = async ( rollupOptions: { onwarn, input: { - main: mainJsFile, ...clientEntryFiles, ...CLIENT_MODULE_MAP, }, @@ -348,7 +344,6 @@ const buildSsrBundle = async ( const buildClientBundle = async ( rootDir: string, config: ResolvedConfig, - mainJsFile: string, clientEntryFiles: Record, serverBuildOutput: Awaited>, ) => { @@ -363,21 +358,20 @@ const buildClientBundle = async ( rscIndexPlugin({ ...config, cssAssets, - mainJs: mainJsFile.split('/').pop()!, }), rscEnvPlugin({ config }), rscPrivatePlugin(config), - rscManagedPlugin(config), + rscManagedPlugin({ + srcDir: config.srcDir, + addMainJsToInput: true, + }), ], build: { outDir: joinPath(rootDir, config.distDir, config.publicDir), rollupOptions: { onwarn, - input: { - main: mainJsFile, - // rollup will ouput the style files related to clientEntryFiles, but since it does not find any link to them in the index.html file, it will not inject them. They are only mentioned by the standalone `clientEntryFiles` - ...clientEntryFiles, - }, + // rollup will ouput the style files related to clientEntryFiles, but since it does not find any link to them in the index.html file, it will not inject them. They are only mentioned by the standalone `clientEntryFiles` + input: clientEntryFiles, preserveEntrySignatures: 'exports-only', output: { entryFileNames: (chunkInfo) => { @@ -615,16 +609,6 @@ export const publicIndexHtml = ${JSON.stringify(publicIndexHtml)}; await appendFile(distEntriesFile, code); }; -const resolveFileName = (fname: string) => { - for (const ext of EXTENSIONS) { - const resolvedName = fname.slice(0, -extname(fname).length) + ext; - if (existsSync(resolvedName)) { - return resolvedName; - } - } - return fname; // returning the default one -}; - export async function build(options: { config: Config; env?: Record; @@ -644,24 +628,17 @@ export async function build(options: { const rootDir = ( await resolveViteConfig({}, 'build', 'production', 'production') ).root; - const entriesFile = resolveFileName( - joinPath(rootDir, config.srcDir, config.entriesJs), - ); - const distEntriesFile = joinPath(rootDir, config.distDir, ENTRIES_JS); - const mainJsFile = resolveFileName( - joinPath(rootDir, config.srcDir, config.mainJs), - ); + const distEntriesFile = joinPath(rootDir, config.distDir, DIST_ENTRIES_JS); const isNodeCompatible = options.deploy !== 'cloudflare' && options.deploy !== 'partykit' && options.deploy !== 'deno'; const { clientEntryFiles, serverEntryFiles, serverModuleFiles } = - await analyzeEntries(rootDir, config, entriesFile); + await analyzeEntries(rootDir, config); const serverBuildOutput = await buildServerBundle( rootDir, config, - entriesFile, clientEntryFiles, serverEntryFiles, serverModuleFiles, @@ -676,7 +653,6 @@ export async function build(options: { await buildSsrBundle( rootDir, config, - mainJsFile, clientEntryFiles, serverBuildOutput, isNodeCompatible, @@ -684,7 +660,6 @@ export async function build(options: { const clientBuildOutput = await buildClientBundle( rootDir, config, - mainJsFile, clientEntryFiles, serverBuildOutput, ); @@ -715,20 +690,20 @@ export async function build(options: { await emitVercelOutput( rootDir, config, - SERVE_JS, + DIST_SERVE_JS, options.deploy.slice('vercel-'.length) as 'static' | 'serverless', ); } else if (options.deploy?.startsWith('netlify-')) { await emitNetlifyOutput( rootDir, config, - SERVE_JS, + DIST_SERVE_JS, options.deploy.slice('netlify-'.length) as 'static' | 'functions', ); } else if (options.deploy === 'cloudflare') { - await emitCloudflareOutput(rootDir, config, SERVE_JS); + await emitCloudflareOutput(rootDir, config, DIST_SERVE_JS); } else if (options.deploy === 'partykit') { - await emitPartyKitOutput(rootDir, config, SERVE_JS); + await emitPartyKitOutput(rootDir, config, DIST_SERVE_JS); } else if (options.deploy === 'aws-lambda') { await emitAwsLambdaOutput(config); } diff --git a/packages/waku/src/lib/middleware/dev-server.ts b/packages/waku/src/lib/middleware/dev-server.ts index da35a54eb..9681db1ea 100644 --- a/packages/waku/src/lib/middleware/dev-server.ts +++ b/packages/waku/src/lib/middleware/dev-server.ts @@ -16,7 +16,12 @@ import { rscIndexPlugin } from '../plugins/vite-plugin-rsc-index.js'; import { rscHmrPlugin, hotUpdate } from '../plugins/vite-plugin-rsc-hmr.js'; import { rscEnvPlugin } from '../plugins/vite-plugin-rsc-env.js'; import { rscPrivatePlugin } from '../plugins/vite-plugin-rsc-private.js'; -import { rscManagedPlugin } from '../plugins/vite-plugin-rsc-managed.js'; +import { + // HACK depending on these constants is not ideal + SRC_ENTRIES_JS, + SRC_MAIN_JS, + rscManagedPlugin, +} from '../plugins/vite-plugin-rsc-managed.js'; import { mergeUserViteConfig } from '../utils/merge-vite-config.js'; import type { ClonableModuleNode, Middleware } from './types.js'; @@ -86,7 +91,7 @@ export const devServer: Middleware = (options) => { include: ['react-server-dom-webpack/client', 'react-dom'], exclude: ['waku'], entries: [ - `${config.srcDir}/${config.entriesJs}`.replace(/\.js$/, '.*'), + `${config.srcDir}/${SRC_ENTRIES_JS}`.replace(/\.js$/, '.*'), // HACK hard-coded "pages" `${config.srcDir}/pages/**/*.*`, ], @@ -170,7 +175,7 @@ export const devServer: Middleware = (options) => { if (!initialModules) { // pre-process the mainJs file to see which modules are being sent to the browser by vite // and using the same modules if possible in the bundlerConfig in the stream - const mainJs = `${config.basePath}${config.srcDir}/${config.mainJs}`; + const mainJs = `${config.basePath}${config.srcDir}/${SRC_MAIN_JS}`; await vite.transformRequest(mainJs); const resolved = await vite.pluginContainer.resolveId(mainJs); const resolvedModule = vite.moduleGraph.idToModuleMap.get(resolved!.id)!; diff --git a/packages/waku/src/lib/plugins/vite-plugin-rsc-entries.ts b/packages/waku/src/lib/plugins/vite-plugin-rsc-entries.ts index b1f9d3221..6a8bfa9e1 100644 --- a/packages/waku/src/lib/plugins/vite-plugin-rsc-entries.ts +++ b/packages/waku/src/lib/plugins/vite-plugin-rsc-entries.ts @@ -3,6 +3,10 @@ import path from 'node:path'; import { normalizePath } from 'vite'; import type { Plugin } from 'vite'; +// HACK Depending on a different plugin isn't ideal. +// Maybe we could put in vite config object? +import { SRC_ENTRIES_JS } from './vite-plugin-rsc-managed.js'; + import { extname } from '../utils/path.js'; const stripExt = (fname: string) => { @@ -13,7 +17,7 @@ const stripExt = (fname: string) => { const CONFIG_FILE = 'waku.config.ts'; // XXX only ts extension export function rscEntriesPlugin(opts: { - entriesFile: string; + srcDir: string; moduleMap: Record; }): Plugin { const codeToPrepend = ` @@ -31,20 +35,29 @@ export function loadModule(id) { } } `; - if (existsSync(CONFIG_FILE)) { - const file = normalizePath( - path.relative(path.dirname(opts.entriesFile), path.resolve(CONFIG_FILE)), - ); - codeToAppend += ` + let entriesFileWithoutExt = ''; + return { + name: 'rsc-entries-plugin', + configResolved(config) { + entriesFileWithoutExt = stripExt( + path.resolve(config.root, opts.srcDir, SRC_ENTRIES_JS), + ); + if (existsSync(CONFIG_FILE)) { + const file = normalizePath( + path.relative( + path.dirname(entriesFileWithoutExt), + path.resolve(CONFIG_FILE), + ), + ); + codeToAppend += ` export const loadConfig = async () => (await import('${file}')).default; `; - } else { - codeToAppend += ` + } else { + codeToAppend += ` export const loadConfig = async () => ({}); `; - } - return { - name: 'rsc-entries-plugin', + } + }, transform(code, id) { if ( // FIXME this is too hacky and not the right place to patch @@ -52,7 +65,7 @@ export const loadConfig = async () => ({}); ) { return codeToPrepend + code; } - if (stripExt(id) === stripExt(opts.entriesFile)) { + if (stripExt(id) === entriesFileWithoutExt) { return code + codeToAppend; } }, diff --git a/packages/waku/src/lib/plugins/vite-plugin-rsc-index.ts b/packages/waku/src/lib/plugins/vite-plugin-rsc-index.ts index 33fdac627..1136ee5b9 100644 --- a/packages/waku/src/lib/plugins/vite-plugin-rsc-index.ts +++ b/packages/waku/src/lib/plugins/vite-plugin-rsc-index.ts @@ -1,11 +1,14 @@ import type { Plugin } from 'vite'; +// HACK Depending on a different plugin isn't ideal. +// Maybe we could put in vite config object? +import { SRC_MAIN_JS } from './vite-plugin-rsc-managed.js'; + import { codeToInject } from '../renderers/utils.js'; export function rscIndexPlugin(opts: { basePath: string; srcDir: string; - mainJs: string; htmlAttrs: string; htmlHead: string; cssAssets?: string[]; @@ -18,7 +21,7 @@ export function rscIndexPlugin(opts: { ${opts.htmlHead} - + `; @@ -27,7 +30,7 @@ ${opts.htmlHead} config() { return { optimizeDeps: { - entries: [`${opts.srcDir}/${opts.mainJs}`.replace(/\.js$/, '.*')], + entries: [`${opts.srcDir}/${SRC_MAIN_JS}`.replace(/\.js$/, '.*')], }, }; }, diff --git a/packages/waku/src/lib/plugins/vite-plugin-rsc-managed.ts b/packages/waku/src/lib/plugins/vite-plugin-rsc-managed.ts index 00033089c..fc4db155e 100644 --- a/packages/waku/src/lib/plugins/vite-plugin-rsc-managed.ts +++ b/packages/waku/src/lib/plugins/vite-plugin-rsc-managed.ts @@ -1,7 +1,22 @@ +import { existsSync } from 'node:fs'; +import path from 'node:path'; import type { Plugin } from 'vite'; import { EXTENSIONS } from '../config.js'; -import { joinPath } from '../utils/path.js'; +import { extname, joinPath } from '../utils/path.js'; + +export const SRC_MAIN_JS = 'main.js'; +export const SRC_ENTRIES_JS = 'entries.js'; + +const resolveFileName = (fname: string) => { + for (const ext of EXTENSIONS) { + const resolvedName = fname.slice(0, -extname(fname).length) + ext; + if (existsSync(resolvedName)) { + return resolvedName; + } + } + return fname; // returning the default one +}; const getManagedMain = () => ` import { Component, StrictMode } from 'react'; @@ -44,22 +59,41 @@ const addSuffixX = (fname: string | undefined) => { export function rscManagedPlugin(opts: { srcDir: string; - entriesJs: string; - mainJs?: string; + addEntriesJsToInput?: boolean; + addMainJsToInput?: boolean; }): Plugin { let entriesFile: string | undefined; let mainFile: string | undefined; - const mainJsPath = opts.mainJs && '/' + joinPath(opts.srcDir, opts.mainJs); + const mainJsPath = '/' + joinPath(opts.srcDir, SRC_MAIN_JS); let managedEntries = false; let managedMain = false; return { name: 'rsc-managed-plugin', enforce: 'pre', configResolved(config) { - entriesFile = joinPath(config.root, opts.srcDir, opts.entriesJs); - if (opts.mainJs) { - mainFile = joinPath(config.root, opts.srcDir, opts.mainJs); + entriesFile = resolveFileName( + path.resolve(config.root, opts.srcDir, SRC_ENTRIES_JS), + ); + mainFile = resolveFileName( + joinPath(config.root, opts.srcDir, SRC_MAIN_JS), + ); + }, + options(options) { + if (typeof options.input === 'string') { + throw new Error('string input is unsupported'); + } + if (Array.isArray(options.input)) { + throw new Error('array input is unsupported'); } + return { + ...options, + input: { + ...(opts.addEntriesJsToInput && + entriesFile && { entries: entriesFile }), + ...(opts.addMainJsToInput && mainFile && { main: mainFile }), + ...options.input, + }, + }; }, async resolveId(id, importer, options) { const resolved = await this.resolve(id, importer, options); diff --git a/packages/waku/src/lib/plugins/vite-plugin-rsc-serve.ts b/packages/waku/src/lib/plugins/vite-plugin-rsc-serve.ts index 360244d7e..23771dbda 100644 --- a/packages/waku/src/lib/plugins/vite-plugin-rsc-serve.ts +++ b/packages/waku/src/lib/plugins/vite-plugin-rsc-serve.ts @@ -1,10 +1,29 @@ +import { existsSync } from 'node:fs'; +import path from 'node:path'; import type { Plugin } from 'vite'; +// HACK: Depending on a different plugin isn't ideal. +// Maybe we could put in vite config object? +import { SRC_ENTRIES_JS } from './vite-plugin-rsc-managed.js'; + +import { EXTENSIONS } from '../config.js'; +import { extname } from '../utils/path.js'; + +const resolveFileName = (fname: string) => { + for (const ext of EXTENSIONS) { + const resolvedName = fname.slice(0, -extname(fname).length) + ext; + if (existsSync(resolvedName)) { + return resolvedName; + } + } + return fname; // returning the default one +}; + export function rscServePlugin(opts: { - serveJs: string; + srcDir: string; + distServeJs: string; distDir: string; publicDir: string; - entriesFile: string; srcServeFile: string; serve: | 'vercel' @@ -17,13 +36,16 @@ export function rscServePlugin(opts: { return { name: 'rsc-serve-plugin', config(viteConfig) { + const entriesFile = resolveFileName( + path.resolve(viteConfig.root || '.', opts.srcDir, SRC_ENTRIES_JS), + ); const { input } = viteConfig.build?.rollupOptions ?? {}; if (input && !(typeof input === 'string') && !(input instanceof Array)) { - input[opts.serveJs.replace(/\.js$/, '')] = opts.srcServeFile; + input[opts.distServeJs.replace(/\.js$/, '')] = opts.srcServeFile; } viteConfig.define = { ...viteConfig.define, - 'import.meta.env.WAKU_ENTRIES_FILE': JSON.stringify(opts.entriesFile), + 'import.meta.env.WAKU_ENTRIES_FILE': JSON.stringify(entriesFile), 'import.meta.env.WAKU_CONFIG_DIST_DIR': JSON.stringify(opts.distDir), 'import.meta.env.WAKU_CONFIG_PUBLIC_DIR': JSON.stringify( opts.publicDir, diff --git a/packages/waku/src/lib/renderers/dev-worker-api.ts b/packages/waku/src/lib/renderers/dev-worker-api.ts index b8d4652d3..1732bab56 100644 --- a/packages/waku/src/lib/renderers/dev-worker-api.ts +++ b/packages/waku/src/lib/renderers/dev-worker-api.ts @@ -8,6 +8,9 @@ import type { HotUpdatePayload } from '../plugins/vite-plugin-rsc-hmr.js'; import type { RenderRscArgs, GetSsrConfigArgs } from './rsc-renderer.js'; import type { ClonableModuleNode } from '../middleware/types.js'; +// HACK depending on these constants is not ideal +import { SRC_ENTRIES_JS } from '../plugins/vite-plugin-rsc-managed.js'; + export type BuildOutput = { rscFiles: string[]; htmlFiles: string[]; @@ -75,7 +78,7 @@ export function initializeWorker(config: ResolvedConfig) { (globalThis as any).__WAKU_PRIVATE_ENV__, ); setEnvironmentData('CONFIG_SRC_DIR', config.srcDir); - setEnvironmentData('CONFIG_ENTRIES_JS', config.entriesJs); + setEnvironmentData('CONFIG_ENTRIES_JS', SRC_ENTRIES_JS); setEnvironmentData('CONFIG_PRIVATE_DIR', config.privateDir); const worker = new Worker( new URL('dev-worker-impl.js', import.meta.url), diff --git a/packages/waku/src/lib/renderers/dev-worker-impl.ts b/packages/waku/src/lib/renderers/dev-worker-impl.ts index e56d55ca2..2b28790b9 100644 --- a/packages/waku/src/lib/renderers/dev-worker-impl.ts +++ b/packages/waku/src/lib/renderers/dev-worker-impl.ts @@ -182,7 +182,7 @@ const mergedViteConfig = await mergeUserViteConfig({ nonjsResolvePlugin(), rscEnvPlugin({}), rscPrivatePlugin({ privateDir: configPrivateDir, hotUpdateCallback }), - rscManagedPlugin({ srcDir: configSrcDir, entriesJs: configEntriesJs }), + rscManagedPlugin({ srcDir: configSrcDir }), rscTransformPlugin({ isBuild: false }), rscDelegatePlugin(hotUpdateCallback), ], @@ -231,14 +231,14 @@ const loadServerModule = async (id: string) => { return vite.ssrLoadModule(id); }; -const loadEntries = async (config: { srcDir: string; entriesJs: string }) => { +const loadEntries = async (config: { srcDir: string }) => { const vite = await vitePromise; - const filePath = joinPath(vite.config.root, config.srcDir, config.entriesJs); + const filePath = joinPath(vite.config.root, config.srcDir, configEntriesJs); return vite.ssrLoadModule(filePath) as Promise; }; // load entries eagerly -loadEntries({ srcDir: configSrcDir, entriesJs: configEntriesJs }).catch(() => { +loadEntries({ srcDir: configSrcDir }).catch(() => { // ignore }); diff --git a/packages/waku/src/lib/renderers/html-renderer.ts b/packages/waku/src/lib/renderers/html-renderer.ts index b1da9b810..5d6f0ff89 100644 --- a/packages/waku/src/lib/renderers/html-renderer.ts +++ b/packages/waku/src/lib/renderers/html-renderer.ts @@ -21,6 +21,9 @@ import { } from '../utils/path.js'; import { encodeInput, hasStatusCode } from './utils.js'; +// HACK depending on these constants is not ideal +import { SRC_MAIN_JS } from '../plugins/vite-plugin-rsc-managed.js'; + export const CLIENT_MODULE_MAP = { react: 'react', 'rd-server': 'react-dom/server.edge', @@ -368,7 +371,7 @@ export const renderHtml = async ( .pipeThrough( injectScript( config.basePath + config.rscPath + '/' + encodeInput(ssrConfig.input), - isDev ? `${config.basePath}${config.srcDir}/${config.mainJs}` : '', + isDev ? `${config.basePath}${config.srcDir}/${SRC_MAIN_JS}` : '', ), ) .pipeThrough(injectRSCPayload(stream2)); From 533d30ee531b49155fadeea4a157a32660382acc Mon Sep 17 00:00:00 2001 From: daishi Date: Sun, 28 Apr 2024 22:20:56 +0900 Subject: [PATCH 04/12] avoid path.resolve --- packages/waku/src/lib/plugins/vite-plugin-rsc-entries.ts | 2 +- packages/waku/src/lib/plugins/vite-plugin-rsc-managed.ts | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/waku/src/lib/plugins/vite-plugin-rsc-entries.ts b/packages/waku/src/lib/plugins/vite-plugin-rsc-entries.ts index 6a8bfa9e1..69b1e5460 100644 --- a/packages/waku/src/lib/plugins/vite-plugin-rsc-entries.ts +++ b/packages/waku/src/lib/plugins/vite-plugin-rsc-entries.ts @@ -40,7 +40,7 @@ export function loadModule(id) { name: 'rsc-entries-plugin', configResolved(config) { entriesFileWithoutExt = stripExt( - path.resolve(config.root, opts.srcDir, SRC_ENTRIES_JS), + path.join(config.root, opts.srcDir, SRC_ENTRIES_JS), ); if (existsSync(CONFIG_FILE)) { const file = normalizePath( diff --git a/packages/waku/src/lib/plugins/vite-plugin-rsc-managed.ts b/packages/waku/src/lib/plugins/vite-plugin-rsc-managed.ts index fc4db155e..dde7f8172 100644 --- a/packages/waku/src/lib/plugins/vite-plugin-rsc-managed.ts +++ b/packages/waku/src/lib/plugins/vite-plugin-rsc-managed.ts @@ -1,5 +1,4 @@ import { existsSync } from 'node:fs'; -import path from 'node:path'; import type { Plugin } from 'vite'; import { EXTENSIONS } from '../config.js'; @@ -72,7 +71,7 @@ export function rscManagedPlugin(opts: { enforce: 'pre', configResolved(config) { entriesFile = resolveFileName( - path.resolve(config.root, opts.srcDir, SRC_ENTRIES_JS), + joinPath(config.root, opts.srcDir, SRC_ENTRIES_JS), ); mainFile = resolveFileName( joinPath(config.root, opts.srcDir, SRC_MAIN_JS), From 66d52e4b17dacfe673f1eef8e2b5a7cab0808378 Mon Sep 17 00:00:00 2001 From: daishi Date: Sun, 28 Apr 2024 23:05:40 +0900 Subject: [PATCH 05/12] refactor --- packages/waku/src/lib/builder/build.ts | 8 ++++---- .../waku/src/lib/middleware/dev-server.ts | 2 +- .../lib/plugins/vite-plugin-rsc-managed.ts | 19 ++++++++----------- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/packages/waku/src/lib/builder/build.ts b/packages/waku/src/lib/builder/build.ts index 9fa5848e9..9638d1f36 100644 --- a/packages/waku/src/lib/builder/build.ts +++ b/packages/waku/src/lib/builder/build.ts @@ -109,7 +109,7 @@ const analyzeEntries = async (rootDir: string, config: ResolvedConfig) => { rscAnalyzePlugin(clientFileSet, serverFileSet, fileHashMap), rscManagedPlugin({ srcDir: config.srcDir, - addEntriesJsToInput: true, + addEntriesToInput: true, }), ], ssr: { @@ -179,7 +179,7 @@ const buildServerBundle = async ( rscPrivatePlugin(config), rscManagedPlugin({ srcDir: config.srcDir, - addEntriesJsToInput: true, + addEntriesToInput: true, }), rscEntriesPlugin({ srcDir: config.srcDir, @@ -290,7 +290,7 @@ const buildSsrBundle = async ( rscPrivatePlugin(config), rscManagedPlugin({ srcDir: config.srcDir, - addMainJsToInput: true, + addMainToInput: true, }), ], ssr: isNodeCompatible @@ -363,7 +363,7 @@ const buildClientBundle = async ( rscPrivatePlugin(config), rscManagedPlugin({ srcDir: config.srcDir, - addMainJsToInput: true, + addMainToInput: true, }), ], build: { diff --git a/packages/waku/src/lib/middleware/dev-server.ts b/packages/waku/src/lib/middleware/dev-server.ts index 9681db1ea..c7b176bb3 100644 --- a/packages/waku/src/lib/middleware/dev-server.ts +++ b/packages/waku/src/lib/middleware/dev-server.ts @@ -83,7 +83,7 @@ export const devServer: Middleware = (options) => { nonjsResolvePlugin(), rscEnvPlugin({ config }), rscPrivatePlugin(config), - rscManagedPlugin(config), + rscManagedPlugin({ srcDir: config.srcDir }), rscIndexPlugin(config), rscHmrPlugin(), ], diff --git a/packages/waku/src/lib/plugins/vite-plugin-rsc-managed.ts b/packages/waku/src/lib/plugins/vite-plugin-rsc-managed.ts index dde7f8172..0698e0cd1 100644 --- a/packages/waku/src/lib/plugins/vite-plugin-rsc-managed.ts +++ b/packages/waku/src/lib/plugins/vite-plugin-rsc-managed.ts @@ -58,8 +58,8 @@ const addSuffixX = (fname: string | undefined) => { export function rscManagedPlugin(opts: { srcDir: string; - addEntriesJsToInput?: boolean; - addMainJsToInput?: boolean; + addEntriesToInput?: boolean; + addMainToInput?: boolean; }): Plugin { let entriesFile: string | undefined; let mainFile: string | undefined; @@ -70,12 +70,8 @@ export function rscManagedPlugin(opts: { name: 'rsc-managed-plugin', enforce: 'pre', configResolved(config) { - entriesFile = resolveFileName( - joinPath(config.root, opts.srcDir, SRC_ENTRIES_JS), - ); - mainFile = resolveFileName( - joinPath(config.root, opts.srcDir, SRC_MAIN_JS), - ); + entriesFile = joinPath(config.root, opts.srcDir, SRC_ENTRIES_JS); + mainFile = joinPath(config.root, opts.srcDir, SRC_MAIN_JS); }, options(options) { if (typeof options.input === 'string') { @@ -87,9 +83,10 @@ export function rscManagedPlugin(opts: { return { ...options, input: { - ...(opts.addEntriesJsToInput && - entriesFile && { entries: entriesFile }), - ...(opts.addMainJsToInput && mainFile && { main: mainFile }), + ...(opts.addEntriesToInput && { + entries: resolveFileName(entriesFile!), + }), + ...(opts.addMainToInput && { main: resolveFileName(mainFile!) }), ...options.input, }, }; From 68ae22b4d58bfbfed84f3413e4c0d0361cd0c377 Mon Sep 17 00:00:00 2001 From: daishi Date: Sun, 28 Apr 2024 23:55:30 +0900 Subject: [PATCH 06/12] wip: possible fix --- packages/waku/src/lib/builder/build.ts | 17 ++++----------- .../waku/src/lib/middleware/dev-server.ts | 2 +- .../src/lib/plugins/vite-plugin-rsc-index.ts | 5 +++-- .../lib/plugins/vite-plugin-rsc-managed.ts | 21 ++++++++++++++----- .../waku/src/lib/renderers/dev-worker-api.ts | 1 + .../waku/src/lib/renderers/dev-worker-impl.ts | 3 ++- 6 files changed, 27 insertions(+), 22 deletions(-) diff --git a/packages/waku/src/lib/builder/build.ts b/packages/waku/src/lib/builder/build.ts index 9638d1f36..106d0d5d9 100644 --- a/packages/waku/src/lib/builder/build.ts +++ b/packages/waku/src/lib/builder/build.ts @@ -107,10 +107,7 @@ const analyzeEntries = async (rootDir: string, config: ResolvedConfig) => { await buildVite({ plugins: [ rscAnalyzePlugin(clientFileSet, serverFileSet, fileHashMap), - rscManagedPlugin({ - srcDir: config.srcDir, - addEntriesToInput: true, - }), + rscManagedPlugin({ ...config, addEntriesToInput: true }), ], ssr: { target: 'webworker', @@ -178,7 +175,7 @@ const buildServerBundle = async ( rscEnvPlugin({ config }), rscPrivatePlugin(config), rscManagedPlugin({ - srcDir: config.srcDir, + ...config, addEntriesToInput: true, }), rscEntriesPlugin({ @@ -288,10 +285,7 @@ const buildSsrBundle = async ( }), rscEnvPlugin({ config }), rscPrivatePlugin(config), - rscManagedPlugin({ - srcDir: config.srcDir, - addMainToInput: true, - }), + rscManagedPlugin({ ...config, addMainToInput: true }), ], ssr: isNodeCompatible ? { @@ -361,10 +355,7 @@ const buildClientBundle = async ( }), rscEnvPlugin({ config }), rscPrivatePlugin(config), - rscManagedPlugin({ - srcDir: config.srcDir, - addMainToInput: true, - }), + rscManagedPlugin({ ...config, addMainToInput: true }), ], build: { outDir: joinPath(rootDir, config.distDir, config.publicDir), diff --git a/packages/waku/src/lib/middleware/dev-server.ts b/packages/waku/src/lib/middleware/dev-server.ts index c7b176bb3..9681db1ea 100644 --- a/packages/waku/src/lib/middleware/dev-server.ts +++ b/packages/waku/src/lib/middleware/dev-server.ts @@ -83,7 +83,7 @@ export const devServer: Middleware = (options) => { nonjsResolvePlugin(), rscEnvPlugin({ config }), rscPrivatePlugin(config), - rscManagedPlugin({ srcDir: config.srcDir }), + rscManagedPlugin(config), rscIndexPlugin(config), rscHmrPlugin(), ], diff --git a/packages/waku/src/lib/plugins/vite-plugin-rsc-index.ts b/packages/waku/src/lib/plugins/vite-plugin-rsc-index.ts index 1136ee5b9..aa0552751 100644 --- a/packages/waku/src/lib/plugins/vite-plugin-rsc-index.ts +++ b/packages/waku/src/lib/plugins/vite-plugin-rsc-index.ts @@ -14,6 +14,7 @@ export function rscIndexPlugin(opts: { cssAssets?: string[]; }): Plugin { const indexHtml = 'index.html'; + const mainJsWithoutExt = SRC_MAIN_JS.replace(/\.js$/, ''); const html = ` @@ -21,7 +22,7 @@ export function rscIndexPlugin(opts: { ${opts.htmlHead} - + `; @@ -30,7 +31,7 @@ ${opts.htmlHead} config() { return { optimizeDeps: { - entries: [`${opts.srcDir}/${SRC_MAIN_JS}`.replace(/\.js$/, '.*')], + entries: [`${opts.srcDir}/${mainJsWithoutExt}.*`], }, }; }, diff --git a/packages/waku/src/lib/plugins/vite-plugin-rsc-managed.ts b/packages/waku/src/lib/plugins/vite-plugin-rsc-managed.ts index 0698e0cd1..a84789678 100644 --- a/packages/waku/src/lib/plugins/vite-plugin-rsc-managed.ts +++ b/packages/waku/src/lib/plugins/vite-plugin-rsc-managed.ts @@ -17,6 +17,11 @@ const resolveFileName = (fname: string) => { return fname; // returning the default one }; +const stripExt = (fname: string) => { + const ext = extname(fname); + return ext ? fname.slice(0, -ext.length) : fname; +}; + const getManagedMain = () => ` import { Component, StrictMode } from 'react'; import { createRoot, hydrateRoot } from 'react-dom/client'; @@ -57,13 +62,15 @@ const addSuffixX = (fname: string | undefined) => { }; export function rscManagedPlugin(opts: { + basePath: string; srcDir: string; addEntriesToInput?: boolean; addMainToInput?: boolean; }): Plugin { let entriesFile: string | undefined; let mainFile: string | undefined; - const mainJsPath = '/' + joinPath(opts.srcDir, SRC_MAIN_JS); + const mainJsWithoutExt = SRC_MAIN_JS.replace(/\.js$/, ''); + const mainPath = `${opts.basePath}${opts.srcDir}/${mainJsWithoutExt}`; let managedEntries = false; let managedMain = false; return { @@ -95,11 +102,15 @@ export function rscManagedPlugin(opts: { const resolved = await this.resolve(id, importer, options); if (!resolved && id === entriesFile) { managedEntries = true; - return addSuffixX(id); + return addSuffixX(entriesFile); + } + if (!resolved && id === mainFile) { + managedMain = true; + return addSuffixX(mainFile); } - if (!resolved && (id === mainFile || id === mainJsPath)) { + if (!resolved && stripExt(id) === mainPath) { managedMain = true; - return addSuffixX(id); + return mainPath + '.jsx'; } return resolved; }, @@ -109,7 +120,7 @@ export function rscManagedPlugin(opts: { } if ( managedMain && - (id === addSuffixX(mainFile) || id === addSuffixX(mainJsPath)) + (id === addSuffixX(mainFile) || id === mainPath + '.jsx') ) { return getManagedMain(); } diff --git a/packages/waku/src/lib/renderers/dev-worker-api.ts b/packages/waku/src/lib/renderers/dev-worker-api.ts index 1732bab56..6be8a296a 100644 --- a/packages/waku/src/lib/renderers/dev-worker-api.ts +++ b/packages/waku/src/lib/renderers/dev-worker-api.ts @@ -77,6 +77,7 @@ export function initializeWorker(config: ResolvedConfig) { '__WAKU_PRIVATE_ENV__', (globalThis as any).__WAKU_PRIVATE_ENV__, ); + setEnvironmentData('CONFIG_BASE_PATH', config.basePath); setEnvironmentData('CONFIG_SRC_DIR', config.srcDir); setEnvironmentData('CONFIG_ENTRIES_JS', SRC_ENTRIES_JS); setEnvironmentData('CONFIG_PRIVATE_DIR', config.privateDir); diff --git a/packages/waku/src/lib/renderers/dev-worker-impl.ts b/packages/waku/src/lib/renderers/dev-worker-impl.ts index 2b28790b9..83275b364 100644 --- a/packages/waku/src/lib/renderers/dev-worker-impl.ts +++ b/packages/waku/src/lib/renderers/dev-worker-impl.ts @@ -40,6 +40,7 @@ if (HAS_MODULE_REGISTER) { (globalThis as any).__WAKU_PRIVATE_ENV__ = getEnvironmentData( '__WAKU_PRIVATE_ENV__', ); +const configBasePath = getEnvironmentData('CONFIG_BASE_PATH') as string; const configSrcDir = getEnvironmentData('CONFIG_SRC_DIR') as string; const configEntriesJs = getEnvironmentData('CONFIG_ENTRIES_JS') as string; const configPrivateDir = getEnvironmentData('CONFIG_PRIVATE_DIR') as string; @@ -182,7 +183,7 @@ const mergedViteConfig = await mergeUserViteConfig({ nonjsResolvePlugin(), rscEnvPlugin({}), rscPrivatePlugin({ privateDir: configPrivateDir, hotUpdateCallback }), - rscManagedPlugin({ srcDir: configSrcDir }), + rscManagedPlugin({ basePath: configBasePath, srcDir: configSrcDir }), rscTransformPlugin({ isBuild: false }), rscDelegatePlugin(hotUpdateCallback), ], From 579deb7996c8f226782d8e921bd5c7dd1fb257a4 Mon Sep 17 00:00:00 2001 From: daishi Date: Mon, 29 Apr 2024 15:30:07 +0900 Subject: [PATCH 07/12] wip: SRC_ENTRIES without ext --- .../waku/src/lib/middleware/dev-server.ts | 4 +-- .../lib/plugins/vite-plugin-rsc-entries.ts | 15 +++----- .../lib/plugins/vite-plugin-rsc-managed.ts | 36 +++++++++---------- .../src/lib/plugins/vite-plugin-rsc-serve.ts | 5 +-- .../waku/src/lib/renderers/dev-worker-api.ts | 4 +-- .../waku/src/lib/renderers/dev-worker-impl.ts | 6 ++-- 6 files changed, 32 insertions(+), 38 deletions(-) diff --git a/packages/waku/src/lib/middleware/dev-server.ts b/packages/waku/src/lib/middleware/dev-server.ts index 9681db1ea..0a77012fd 100644 --- a/packages/waku/src/lib/middleware/dev-server.ts +++ b/packages/waku/src/lib/middleware/dev-server.ts @@ -18,7 +18,7 @@ import { rscEnvPlugin } from '../plugins/vite-plugin-rsc-env.js'; import { rscPrivatePlugin } from '../plugins/vite-plugin-rsc-private.js'; import { // HACK depending on these constants is not ideal - SRC_ENTRIES_JS, + SRC_ENTRIES, SRC_MAIN_JS, rscManagedPlugin, } from '../plugins/vite-plugin-rsc-managed.js'; @@ -91,7 +91,7 @@ export const devServer: Middleware = (options) => { include: ['react-server-dom-webpack/client', 'react-dom'], exclude: ['waku'], entries: [ - `${config.srcDir}/${SRC_ENTRIES_JS}`.replace(/\.js$/, '.*'), + `${config.srcDir}/${SRC_ENTRIES}.*`, // HACK hard-coded "pages" `${config.srcDir}/pages/**/*.*`, ], diff --git a/packages/waku/src/lib/plugins/vite-plugin-rsc-entries.ts b/packages/waku/src/lib/plugins/vite-plugin-rsc-entries.ts index 69b1e5460..0f3b5607a 100644 --- a/packages/waku/src/lib/plugins/vite-plugin-rsc-entries.ts +++ b/packages/waku/src/lib/plugins/vite-plugin-rsc-entries.ts @@ -5,7 +5,7 @@ import type { Plugin } from 'vite'; // HACK Depending on a different plugin isn't ideal. // Maybe we could put in vite config object? -import { SRC_ENTRIES_JS } from './vite-plugin-rsc-managed.js'; +import { SRC_ENTRIES } from './vite-plugin-rsc-managed.js'; import { extname } from '../utils/path.js'; @@ -35,19 +35,14 @@ export function loadModule(id) { } } `; - let entriesFileWithoutExt = ''; + let entriesFile = ''; return { name: 'rsc-entries-plugin', configResolved(config) { - entriesFileWithoutExt = stripExt( - path.join(config.root, opts.srcDir, SRC_ENTRIES_JS), - ); + entriesFile = path.join(config.root, opts.srcDir, SRC_ENTRIES); if (existsSync(CONFIG_FILE)) { const file = normalizePath( - path.relative( - path.dirname(entriesFileWithoutExt), - path.resolve(CONFIG_FILE), - ), + path.relative(path.dirname(entriesFile), path.resolve(CONFIG_FILE)), ); codeToAppend += ` export const loadConfig = async () => (await import('${file}')).default; @@ -65,7 +60,7 @@ export const loadConfig = async () => ({}); ) { return codeToPrepend + code; } - if (stripExt(id) === entriesFileWithoutExt) { + if (stripExt(id) === entriesFile) { return code + codeToAppend; } }, diff --git a/packages/waku/src/lib/plugins/vite-plugin-rsc-managed.ts b/packages/waku/src/lib/plugins/vite-plugin-rsc-managed.ts index a84789678..d7fa18997 100644 --- a/packages/waku/src/lib/plugins/vite-plugin-rsc-managed.ts +++ b/packages/waku/src/lib/plugins/vite-plugin-rsc-managed.ts @@ -5,7 +5,7 @@ import { EXTENSIONS } from '../config.js'; import { extname, joinPath } from '../utils/path.js'; export const SRC_MAIN_JS = 'main.js'; -export const SRC_ENTRIES_JS = 'entries.js'; +export const SRC_ENTRIES = 'entries'; const resolveFileName = (fname: string) => { for (const ext of EXTENSIONS) { @@ -22,6 +22,17 @@ const stripExt = (fname: string) => { return ext ? fname.slice(0, -ext.length) : fname; }; +const getManagedEntries = () => ` +import { fsRouter } from 'waku/router/server'; + +export default fsRouter( + import.meta.url, + (file) => import.meta.glob('./pages/**/*.{${EXTENSIONS.map((ext) => + ext.replace(/^\./, ''), + ).join(',')}}')[\`./pages/\${file}\`]?.(), +); +`; + const getManagedMain = () => ` import { Component, StrictMode } from 'react'; import { createRoot, hydrateRoot } from 'react-dom/client'; @@ -40,17 +51,6 @@ if (document.body.dataset.hydrate) { } `; -const getManagedEntries = () => ` -import { fsRouter } from 'waku/router/server'; - -export default fsRouter( - import.meta.url, - (file) => import.meta.glob('./pages/**/*.{${EXTENSIONS.map((ext) => - ext.replace(/^\./, ''), - ).join(',')}}')[\`./pages/\${file}\`]?.(), -); -`; - const addSuffixX = (fname: string | undefined) => { if (!fname) { return fname; @@ -77,7 +77,7 @@ export function rscManagedPlugin(opts: { name: 'rsc-managed-plugin', enforce: 'pre', configResolved(config) { - entriesFile = joinPath(config.root, opts.srcDir, SRC_ENTRIES_JS); + entriesFile = joinPath(config.root, opts.srcDir, SRC_ENTRIES); mainFile = joinPath(config.root, opts.srcDir, SRC_MAIN_JS); }, options(options) { @@ -90,9 +90,7 @@ export function rscManagedPlugin(opts: { return { ...options, input: { - ...(opts.addEntriesToInput && { - entries: resolveFileName(entriesFile!), - }), + ...(opts.addEntriesToInput && { entries: entriesFile! }), ...(opts.addMainToInput && { main: resolveFileName(mainFile!) }), ...options.input, }, @@ -100,9 +98,9 @@ export function rscManagedPlugin(opts: { }, async resolveId(id, importer, options) { const resolved = await this.resolve(id, importer, options); - if (!resolved && id === entriesFile) { + if ((!resolved || resolved.id === id) && id === entriesFile) { managedEntries = true; - return addSuffixX(entriesFile); + return entriesFile + '.jsx'; } if (!resolved && id === mainFile) { managedMain = true; @@ -115,7 +113,7 @@ export function rscManagedPlugin(opts: { return resolved; }, load(id) { - if (managedEntries && id === addSuffixX(entriesFile)) { + if (managedEntries && id === entriesFile + '.jsx') { return getManagedEntries(); } if ( diff --git a/packages/waku/src/lib/plugins/vite-plugin-rsc-serve.ts b/packages/waku/src/lib/plugins/vite-plugin-rsc-serve.ts index 23771dbda..bcec4028b 100644 --- a/packages/waku/src/lib/plugins/vite-plugin-rsc-serve.ts +++ b/packages/waku/src/lib/plugins/vite-plugin-rsc-serve.ts @@ -4,7 +4,7 @@ import type { Plugin } from 'vite'; // HACK: Depending on a different plugin isn't ideal. // Maybe we could put in vite config object? -import { SRC_ENTRIES_JS } from './vite-plugin-rsc-managed.js'; +import { SRC_ENTRIES } from './vite-plugin-rsc-managed.js'; import { EXTENSIONS } from '../config.js'; import { extname } from '../utils/path.js'; @@ -36,8 +36,9 @@ export function rscServePlugin(opts: { return { name: 'rsc-serve-plugin', config(viteConfig) { + // FIXME This seems too hacky (The use of viteConfig.root, '.', path.resolve and resolveFileName) const entriesFile = resolveFileName( - path.resolve(viteConfig.root || '.', opts.srcDir, SRC_ENTRIES_JS), + path.resolve(viteConfig.root || '.', opts.srcDir, SRC_ENTRIES + '.js'), ); const { input } = viteConfig.build?.rollupOptions ?? {}; if (input && !(typeof input === 'string') && !(input instanceof Array)) { diff --git a/packages/waku/src/lib/renderers/dev-worker-api.ts b/packages/waku/src/lib/renderers/dev-worker-api.ts index 6be8a296a..e61d0c664 100644 --- a/packages/waku/src/lib/renderers/dev-worker-api.ts +++ b/packages/waku/src/lib/renderers/dev-worker-api.ts @@ -9,7 +9,7 @@ import type { RenderRscArgs, GetSsrConfigArgs } from './rsc-renderer.js'; import type { ClonableModuleNode } from '../middleware/types.js'; // HACK depending on these constants is not ideal -import { SRC_ENTRIES_JS } from '../plugins/vite-plugin-rsc-managed.js'; +import { SRC_ENTRIES } from '../plugins/vite-plugin-rsc-managed.js'; export type BuildOutput = { rscFiles: string[]; @@ -79,7 +79,7 @@ export function initializeWorker(config: ResolvedConfig) { ); setEnvironmentData('CONFIG_BASE_PATH', config.basePath); setEnvironmentData('CONFIG_SRC_DIR', config.srcDir); - setEnvironmentData('CONFIG_ENTRIES_JS', SRC_ENTRIES_JS); + setEnvironmentData('CONFIG_ENTRIES', SRC_ENTRIES); setEnvironmentData('CONFIG_PRIVATE_DIR', config.privateDir); const worker = new Worker( new URL('dev-worker-impl.js', import.meta.url), diff --git a/packages/waku/src/lib/renderers/dev-worker-impl.ts b/packages/waku/src/lib/renderers/dev-worker-impl.ts index 83275b364..291f867e6 100644 --- a/packages/waku/src/lib/renderers/dev-worker-impl.ts +++ b/packages/waku/src/lib/renderers/dev-worker-impl.ts @@ -42,7 +42,7 @@ if (HAS_MODULE_REGISTER) { ); const configBasePath = getEnvironmentData('CONFIG_BASE_PATH') as string; const configSrcDir = getEnvironmentData('CONFIG_SRC_DIR') as string; -const configEntriesJs = getEnvironmentData('CONFIG_ENTRIES_JS') as string; +const configEntries = getEnvironmentData('CONFIG_ENTRIES') as string; const configPrivateDir = getEnvironmentData('CONFIG_PRIVATE_DIR') as string; const resolveClientEntryForDev = ( @@ -191,7 +191,7 @@ const mergedViteConfig = await mergeUserViteConfig({ include: ['react-server-dom-webpack/client', 'react-dom'], exclude: ['waku'], entries: [ - `${configSrcDir}/${configEntriesJs}`.replace(/\.js$/, '.*'), + `${configSrcDir}/${configEntries}.*`, // HACK hard-coded "pages" `${configSrcDir}/pages/**/*.*`, ], @@ -234,7 +234,7 @@ const loadServerModule = async (id: string) => { const loadEntries = async (config: { srcDir: string }) => { const vite = await vitePromise; - const filePath = joinPath(vite.config.root, config.srcDir, configEntriesJs); + const filePath = joinPath(vite.config.root, config.srcDir, configEntries); return vite.ssrLoadModule(filePath) as Promise; }; From b6e0907544830e3fd15aac0f99794c8121b17bee Mon Sep 17 00:00:00 2001 From: daishi Date: Mon, 29 Apr 2024 21:07:28 +0900 Subject: [PATCH 08/12] wip: SRC_MAIN without ext --- .../waku/src/lib/middleware/dev-server.ts | 4 +- .../src/lib/plugins/vite-plugin-rsc-index.ts | 7 ++-- .../lib/plugins/vite-plugin-rsc-managed.ts | 38 ++++--------------- .../waku/src/lib/renderers/html-renderer.ts | 4 +- 4 files changed, 15 insertions(+), 38 deletions(-) diff --git a/packages/waku/src/lib/middleware/dev-server.ts b/packages/waku/src/lib/middleware/dev-server.ts index 0a77012fd..71480847e 100644 --- a/packages/waku/src/lib/middleware/dev-server.ts +++ b/packages/waku/src/lib/middleware/dev-server.ts @@ -19,7 +19,7 @@ import { rscPrivatePlugin } from '../plugins/vite-plugin-rsc-private.js'; import { // HACK depending on these constants is not ideal SRC_ENTRIES, - SRC_MAIN_JS, + SRC_MAIN, rscManagedPlugin, } from '../plugins/vite-plugin-rsc-managed.js'; import { mergeUserViteConfig } from '../utils/merge-vite-config.js'; @@ -175,7 +175,7 @@ export const devServer: Middleware = (options) => { if (!initialModules) { // pre-process the mainJs file to see which modules are being sent to the browser by vite // and using the same modules if possible in the bundlerConfig in the stream - const mainJs = `${config.basePath}${config.srcDir}/${SRC_MAIN_JS}`; + const mainJs = `${config.basePath}${config.srcDir}/${SRC_MAIN}`; await vite.transformRequest(mainJs); const resolved = await vite.pluginContainer.resolveId(mainJs); const resolvedModule = vite.moduleGraph.idToModuleMap.get(resolved!.id)!; diff --git a/packages/waku/src/lib/plugins/vite-plugin-rsc-index.ts b/packages/waku/src/lib/plugins/vite-plugin-rsc-index.ts index aa0552751..8ab0169e7 100644 --- a/packages/waku/src/lib/plugins/vite-plugin-rsc-index.ts +++ b/packages/waku/src/lib/plugins/vite-plugin-rsc-index.ts @@ -2,7 +2,7 @@ import type { Plugin } from 'vite'; // HACK Depending on a different plugin isn't ideal. // Maybe we could put in vite config object? -import { SRC_MAIN_JS } from './vite-plugin-rsc-managed.js'; +import { SRC_MAIN } from './vite-plugin-rsc-managed.js'; import { codeToInject } from '../renderers/utils.js'; @@ -14,7 +14,6 @@ export function rscIndexPlugin(opts: { cssAssets?: string[]; }): Plugin { const indexHtml = 'index.html'; - const mainJsWithoutExt = SRC_MAIN_JS.replace(/\.js$/, ''); const html = ` @@ -22,7 +21,7 @@ export function rscIndexPlugin(opts: { ${opts.htmlHead} - + `; @@ -31,7 +30,7 @@ ${opts.htmlHead} config() { return { optimizeDeps: { - entries: [`${opts.srcDir}/${mainJsWithoutExt}.*`], + entries: [`${opts.srcDir}/${SRC_MAIN}.*`], }, }; }, diff --git a/packages/waku/src/lib/plugins/vite-plugin-rsc-managed.ts b/packages/waku/src/lib/plugins/vite-plugin-rsc-managed.ts index d7fa18997..7ed78cd93 100644 --- a/packages/waku/src/lib/plugins/vite-plugin-rsc-managed.ts +++ b/packages/waku/src/lib/plugins/vite-plugin-rsc-managed.ts @@ -1,22 +1,11 @@ -import { existsSync } from 'node:fs'; import type { Plugin } from 'vite'; import { EXTENSIONS } from '../config.js'; import { extname, joinPath } from '../utils/path.js'; -export const SRC_MAIN_JS = 'main.js'; +export const SRC_MAIN = 'main'; export const SRC_ENTRIES = 'entries'; -const resolveFileName = (fname: string) => { - for (const ext of EXTENSIONS) { - const resolvedName = fname.slice(0, -extname(fname).length) + ext; - if (existsSync(resolvedName)) { - return resolvedName; - } - } - return fname; // returning the default one -}; - const stripExt = (fname: string) => { const ext = extname(fname); return ext ? fname.slice(0, -ext.length) : fname; @@ -51,16 +40,6 @@ if (document.body.dataset.hydrate) { } `; -const addSuffixX = (fname: string | undefined) => { - if (!fname) { - return fname; - } - if (fname.endsWith('x')) { - return fname; - } - return fname + 'x'; -}; - export function rscManagedPlugin(opts: { basePath: string; srcDir: string; @@ -69,8 +48,7 @@ export function rscManagedPlugin(opts: { }): Plugin { let entriesFile: string | undefined; let mainFile: string | undefined; - const mainJsWithoutExt = SRC_MAIN_JS.replace(/\.js$/, ''); - const mainPath = `${opts.basePath}${opts.srcDir}/${mainJsWithoutExt}`; + const mainPath = `${opts.basePath}${opts.srcDir}/${SRC_MAIN}`; let managedEntries = false; let managedMain = false; return { @@ -78,7 +56,7 @@ export function rscManagedPlugin(opts: { enforce: 'pre', configResolved(config) { entriesFile = joinPath(config.root, opts.srcDir, SRC_ENTRIES); - mainFile = joinPath(config.root, opts.srcDir, SRC_MAIN_JS); + mainFile = joinPath(config.root, opts.srcDir, SRC_MAIN); }, options(options) { if (typeof options.input === 'string') { @@ -91,7 +69,7 @@ export function rscManagedPlugin(opts: { ...options, input: { ...(opts.addEntriesToInput && { entries: entriesFile! }), - ...(opts.addMainToInput && { main: resolveFileName(mainFile!) }), + ...(opts.addMainToInput && { main: mainFile! }), ...options.input, }, }; @@ -102,11 +80,11 @@ export function rscManagedPlugin(opts: { managedEntries = true; return entriesFile + '.jsx'; } - if (!resolved && id === mainFile) { + if ((!resolved || resolved.id === id) && id === mainFile) { managedMain = true; - return addSuffixX(mainFile); + return mainFile + '.jsx'; } - if (!resolved && stripExt(id) === mainPath) { + if ((!resolved || resolved.id === id) && stripExt(id) === mainPath) { managedMain = true; return mainPath + '.jsx'; } @@ -118,7 +96,7 @@ export function rscManagedPlugin(opts: { } if ( managedMain && - (id === addSuffixX(mainFile) || id === mainPath + '.jsx') + (id === mainFile + '.jsx' || id === mainPath + '.jsx') ) { return getManagedMain(); } diff --git a/packages/waku/src/lib/renderers/html-renderer.ts b/packages/waku/src/lib/renderers/html-renderer.ts index 5d6f0ff89..7c7ee3b93 100644 --- a/packages/waku/src/lib/renderers/html-renderer.ts +++ b/packages/waku/src/lib/renderers/html-renderer.ts @@ -22,7 +22,7 @@ import { import { encodeInput, hasStatusCode } from './utils.js'; // HACK depending on these constants is not ideal -import { SRC_MAIN_JS } from '../plugins/vite-plugin-rsc-managed.js'; +import { SRC_MAIN } from '../plugins/vite-plugin-rsc-managed.js'; export const CLIENT_MODULE_MAP = { react: 'react', @@ -371,7 +371,7 @@ export const renderHtml = async ( .pipeThrough( injectScript( config.basePath + config.rscPath + '/' + encodeInput(ssrConfig.input), - isDev ? `${config.basePath}${config.srcDir}/${SRC_MAIN_JS}` : '', + isDev ? `${config.basePath}${config.srcDir}/${SRC_MAIN}` : '', ), ) .pipeThrough(injectRSCPayload(stream2)); From e94f5156b9c2ba56681f35f6e089be996a7c5d0e Mon Sep 17 00:00:00 2001 From: daishi Date: Thu, 2 May 2024 20:47:56 +0900 Subject: [PATCH 09/12] use posix path --- packages/waku/src/lib/plugins/vite-plugin-rsc-entries.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/waku/src/lib/plugins/vite-plugin-rsc-entries.ts b/packages/waku/src/lib/plugins/vite-plugin-rsc-entries.ts index 7aa40bb3b..38c538733 100644 --- a/packages/waku/src/lib/plugins/vite-plugin-rsc-entries.ts +++ b/packages/waku/src/lib/plugins/vite-plugin-rsc-entries.ts @@ -7,7 +7,7 @@ import type { Plugin } from 'vite'; // Maybe we could put in vite config object? import { SRC_ENTRIES } from './vite-plugin-rsc-managed.js'; -import { extname } from '../utils/path.js'; +import { extname, joinPath } from '../utils/path.js'; const stripExt = (fname: string) => { const ext = extname(fname); @@ -39,7 +39,7 @@ export function loadModule(id) { return { name: 'rsc-entries-plugin', configResolved(config) { - entriesFile = path.join(config.root, opts.srcDir, SRC_ENTRIES); + entriesFile = joinPath(config.root, opts.srcDir, SRC_ENTRIES); if (existsSync(CONFIG_FILE)) { const file = normalizePath( path.relative(path.dirname(entriesFile), path.resolve(CONFIG_FILE)), From 800a7d6a37132c7ef27c7151b4ee3d7015093d3f Mon Sep 17 00:00:00 2001 From: daishi Date: Thu, 2 May 2024 22:42:12 +0900 Subject: [PATCH 10/12] internalize dist subfolder names --- packages/waku/src/cli.ts | 8 ++--- packages/waku/src/config.ts | 16 ---------- packages/waku/src/lib/builder/build.ts | 30 +++++++++++-------- .../waku/src/lib/builder/output-cloudflare.ts | 3 +- .../waku/src/lib/builder/output-netlify.ts | 5 ++-- .../waku/src/lib/builder/output-partykit.ts | 3 +- .../waku/src/lib/builder/output-vercel.ts | 3 +- packages/waku/src/lib/config.ts | 3 -- .../src/lib/plugins/vite-plugin-rsc-serve.ts | 4 +-- .../waku/src/lib/renderers/html-renderer.ts | 3 +- 10 files changed, 34 insertions(+), 44 deletions(-) diff --git a/packages/waku/src/cli.ts b/packages/waku/src/cli.ts index c9e3f9018..0ddd77f50 100644 --- a/packages/waku/src/cli.ts +++ b/packages/waku/src/cli.ts @@ -10,7 +10,7 @@ import * as dotenv from 'dotenv'; import type { Config } from './config.js'; import { runner } from './lib/hono/runner.js'; -import { DIST_ENTRIES_JS, build } from './lib/builder/build.js'; +import { DIST_ENTRIES_JS, DIST_PUBLIC, build } from './lib/builder/build.js'; const require = createRequire(new URL('.', import.meta.url)); @@ -120,15 +120,15 @@ async function runBuild() { }); } -async function runStart({ distDir = 'dist', publicDir = 'public' }) { +async function runStart({ distDir = 'dist' }) { const loadEntries = () => import(pathToFileURL(path.resolve(distDir, DIST_ENTRIES_JS)).toString()); const app = new Hono(); - app.use('*', serveStatic({ root: path.join(distDir, publicDir) })); + app.use('*', serveStatic({ root: path.join(distDir, DIST_PUBLIC) })); app.use('*', runner({ cmd: 'start', loadEntries, env: process.env as any })); app.notFound((c) => { // FIXME better implementation using node stream? - const file = path.join(distDir, publicDir, '404.html'); + const file = path.join(distDir, DIST_PUBLIC, '404.html'); if (existsSync(file)) { return c.html(readFileSync(file, 'utf8'), 404); } diff --git a/packages/waku/src/config.ts b/packages/waku/src/config.ts index d8805489d..cf932c3b9 100644 --- a/packages/waku/src/config.ts +++ b/packages/waku/src/config.ts @@ -19,22 +19,6 @@ export interface Config { * Defaults to "dist". */ distDir?: string; - /** - * The public directory relative to distDir. - * It's different from Vite's build.publicDir config. - * Defaults to "public". - */ - publicDir?: string; - /** - * The assets directory relative to distDir and publicDir. - * Defaults to "assets". - */ - assetsDir?: string; - /** - * The SSR directory relative to distDir. - * Defaults to "ssr". - */ - ssrDir?: string; /** /** * The list of directries to preserve server module structure. diff --git a/packages/waku/src/lib/builder/build.ts b/packages/waku/src/lib/builder/build.ts index a216d551b..0aa47b263 100644 --- a/packages/waku/src/lib/builder/build.ts +++ b/packages/waku/src/lib/builder/build.ts @@ -79,6 +79,9 @@ const onwarn = (warning: RollupLog, defaultHandler: LoggingFunction) => { // We may change this in the future export const DIST_ENTRIES_JS = 'entries.js'; export const DIST_SERVE_JS = 'serve.js'; +export const DIST_PUBLIC = 'public'; +export const DIST_ASSETS = 'assets'; +export const DIST_SSR = 'ssr'; const analyzeEntries = async (rootDir: string, config: ResolvedConfig) => { const wakuClientDist = decodeFilePathFromAbsolute( @@ -129,13 +132,13 @@ const analyzeEntries = async (rootDir: string, config: ResolvedConfig) => { }); const clientEntryFiles = Object.fromEntries( Array.from(clientFileSet).map((fname, i) => [ - `${config.assetsDir}/rsc${i}-${fileHashMap.get(fname)}`, + `${DIST_ASSETS}/rsc${i}-${fileHashMap.get(fname)}`, fname, ]), ); const serverEntryFiles = Object.fromEntries( Array.from(serverFileSet).map((fname, i) => [ - `${config.assetsDir}/rsf${i}`, + `${DIST_ASSETS}/rsf${i}`, fname, ]), ); @@ -187,13 +190,13 @@ const buildServerBundle = async ( ...Object.fromEntries( Object.keys(CLIENT_MODULE_MAP).map((key) => [ `${CLIENT_PREFIX}${key}`, - `./${config.ssrDir}/${key}.js`, + `./${DIST_SSR}/${key}.js`, ]), ), ...Object.fromEntries( Object.keys(clientEntryFiles || {}).map((key) => [ - `${config.ssrDir}/${key}.js`, - `./${config.ssrDir}/${key}.js`, + `${DIST_SSR}/${key}.js`, + `./${DIST_SSR}/${key}.js`, ]), ), ...Object.fromEntries( @@ -209,6 +212,7 @@ const buildServerBundle = async ( rscServePlugin({ ...config, distServeJs: DIST_SERVE_JS, + distPublic: DIST_PUBLIC, srcServeFile: decodeFilePathFromAbsolute( joinPath( fileURLToFilePath(import.meta.url), @@ -309,7 +313,7 @@ const buildSsrBundle = async ( build: { ssr: true, target: 'node18', - outDir: joinPath(rootDir, config.distDir, config.ssrDir), + outDir: joinPath(rootDir, config.distDir, DIST_SSR), rollupOptions: { onwarn, input: { @@ -326,7 +330,7 @@ const buildSsrBundle = async ( ) { return '[name].js'; } - return config.assetsDir + '/[name]-[hash].js'; + return DIST_ASSETS + '/[name]-[hash].js'; }, }, }, @@ -358,7 +362,7 @@ const buildClientBundle = async ( rscManagedPlugin({ ...config, addMainToInput: true }), ], build: { - outDir: joinPath(rootDir, config.distDir, config.publicDir), + outDir: joinPath(rootDir, config.distDir, DIST_PUBLIC), rollupOptions: { onwarn, // rollup will ouput the style files related to clientEntryFiles, but since it does not find any link to them in the index.html file, it will not inject them. They are only mentioned by the standalone `clientEntryFiles` @@ -369,7 +373,7 @@ const buildClientBundle = async ( if (clientEntryFiles[chunkInfo.name]) { return '[name].js'; } - return config.assetsDir + '/[name]-[hash].js'; + return DIST_ASSETS + '/[name]-[hash].js'; }, }, }, @@ -380,7 +384,7 @@ const buildClientBundle = async ( } for (const nonJsAsset of nonJsAssets) { const from = joinPath(rootDir, config.distDir, nonJsAsset); - const to = joinPath(rootDir, config.distDir, config.publicDir, nonJsAsset); + const to = joinPath(rootDir, config.distDir, DIST_PUBLIC, nonJsAsset); await rename(from, to); } return clientBuildOutput; @@ -419,7 +423,7 @@ const emitRscFiles = async ( const destRscFile = joinPath( rootDir, config.distDir, - config.publicDir, + DIST_PUBLIC, config.rscPath, encodeInput(input), ); @@ -480,7 +484,7 @@ const emitHtmlFiles = async ( const publicIndexHtmlFile = joinPath( rootDir, config.distDir, - config.publicDir, + DIST_PUBLIC, 'index.html', ); const publicIndexHtml = await readFile(publicIndexHtmlFile, { @@ -539,7 +543,7 @@ const emitHtmlFiles = async ( const destHtmlFile = joinPath( rootDir, config.distDir, - config.publicDir, + DIST_PUBLIC, extname(pathname) ? pathname : pathname === '/404' diff --git a/packages/waku/src/lib/builder/output-cloudflare.ts b/packages/waku/src/lib/builder/output-cloudflare.ts index e7b90d9a3..6f761a2e2 100644 --- a/packages/waku/src/lib/builder/output-cloudflare.ts +++ b/packages/waku/src/lib/builder/output-cloudflare.ts @@ -2,6 +2,7 @@ import path from 'node:path'; import { existsSync, writeFileSync } from 'node:fs'; import type { ResolvedConfig } from '../config.js'; +import { DIST_PUBLIC } from './build.js'; // XXX this can be very limited. FIXME if anyone has better knowledge. export const emitCloudflareOutput = async ( @@ -20,7 +21,7 @@ compatibility_date = "2023-12-06" compatibility_flags = [ "nodejs_als" ] [site] -bucket = "./${config.distDir}/${config.publicDir}" +bucket = "./${config.distDir}/${DIST_PUBLIC}" `, ); } diff --git a/packages/waku/src/lib/builder/output-netlify.ts b/packages/waku/src/lib/builder/output-netlify.ts index 03d9b8d30..4f7705b3c 100644 --- a/packages/waku/src/lib/builder/output-netlify.ts +++ b/packages/waku/src/lib/builder/output-netlify.ts @@ -2,6 +2,7 @@ import path from 'node:path'; import { mkdirSync, writeFileSync, existsSync, readFileSync } from 'node:fs'; import type { ResolvedConfig } from '../config.js'; +import { DIST_PUBLIC } from './build.js'; export const emitNetlifyOutput = async ( rootDir: string, @@ -17,7 +18,7 @@ export const emitNetlifyOutput = async ( const notFoundFile = path.join( rootDir, config.distDir, - config.publicDir, + DIST_PUBLIC, '404.html', ); const notFoundHtml = existsSync(notFoundFile) @@ -42,7 +43,7 @@ export const config = { ` [build] command = "npm run build -- --with-netlify" - publish = "${config.distDir}/${config.publicDir}" + publish = "${config.distDir}/${DIST_PUBLIC}" [functions] included_files = ["${config.privateDir}/**"] `, diff --git a/packages/waku/src/lib/builder/output-partykit.ts b/packages/waku/src/lib/builder/output-partykit.ts index 869472ddc..5d7b3e165 100644 --- a/packages/waku/src/lib/builder/output-partykit.ts +++ b/packages/waku/src/lib/builder/output-partykit.ts @@ -2,6 +2,7 @@ import path from 'node:path'; import { existsSync, writeFileSync } from 'node:fs'; import type { ResolvedConfig } from '../config.js'; +import { DIST_PUBLIC } from './build.js'; // XXX this can be very limited. FIXME if anyone has better knowledge. export const emitPartyKitOutput = async ( @@ -18,7 +19,7 @@ export const emitPartyKitOutput = async ( name: 'waku-project', main: `${config.distDir}/${serveJs}`, compatibilityDate: '2023-02-16', - serve: `./${config.distDir}/${config.publicDir}`, + serve: `./${config.distDir}/${DIST_PUBLIC}`, }, null, 2, diff --git a/packages/waku/src/lib/builder/output-vercel.ts b/packages/waku/src/lib/builder/output-vercel.ts index 6f051a827..60e02ee1d 100644 --- a/packages/waku/src/lib/builder/output-vercel.ts +++ b/packages/waku/src/lib/builder/output-vercel.ts @@ -2,6 +2,7 @@ import path from 'node:path'; import { cpSync, existsSync, mkdirSync, writeFileSync } from 'node:fs'; import type { ResolvedConfig } from '../config.js'; +import { DIST_PUBLIC } from './build.js'; // https://vercel.com/docs/build-output-api/v3 export const emitVercelOutput = async ( @@ -10,7 +11,7 @@ export const emitVercelOutput = async ( serveJs: string, type: 'static' | 'serverless', ) => { - const publicDir = path.join(rootDir, config.distDir, config.publicDir); + const publicDir = path.join(rootDir, config.distDir, DIST_PUBLIC); const outputDir = path.resolve('.vercel', 'output'); cpSync(publicDir, path.join(outputDir, 'static'), { recursive: true }); diff --git a/packages/waku/src/lib/config.ts b/packages/waku/src/lib/config.ts index be8e040ab..66137b52f 100644 --- a/packages/waku/src/lib/config.ts +++ b/packages/waku/src/lib/config.ts @@ -32,9 +32,6 @@ export async function resolveConfig(config: Config) { basePath: '/', srcDir: 'src', distDir: 'dist', - publicDir: 'public', - assetsDir: 'assets', - ssrDir: 'ssr', preserveModuleDirs: ['pages', 'templates', 'routes', 'components'], privateDir: 'private', rscPath: 'RSC', diff --git a/packages/waku/src/lib/plugins/vite-plugin-rsc-serve.ts b/packages/waku/src/lib/plugins/vite-plugin-rsc-serve.ts index bcec4028b..5f4b50055 100644 --- a/packages/waku/src/lib/plugins/vite-plugin-rsc-serve.ts +++ b/packages/waku/src/lib/plugins/vite-plugin-rsc-serve.ts @@ -23,7 +23,7 @@ export function rscServePlugin(opts: { srcDir: string; distServeJs: string; distDir: string; - publicDir: string; + distPublic: string; srcServeFile: string; serve: | 'vercel' @@ -49,7 +49,7 @@ export function rscServePlugin(opts: { 'import.meta.env.WAKU_ENTRIES_FILE': JSON.stringify(entriesFile), 'import.meta.env.WAKU_CONFIG_DIST_DIR': JSON.stringify(opts.distDir), 'import.meta.env.WAKU_CONFIG_PUBLIC_DIR': JSON.stringify( - opts.publicDir, + opts.distPublic, ), }; if (opts.serve === 'cloudflare' || opts.serve === 'partykit') { diff --git a/packages/waku/src/lib/renderers/html-renderer.ts b/packages/waku/src/lib/renderers/html-renderer.ts index 7c7ee3b93..8859e4d37 100644 --- a/packages/waku/src/lib/renderers/html-renderer.ts +++ b/packages/waku/src/lib/renderers/html-renderer.ts @@ -23,6 +23,7 @@ import { encodeInput, hasStatusCode } from './utils.js'; // HACK depending on these constants is not ideal import { SRC_MAIN } from '../plugins/vite-plugin-rsc-managed.js'; +import { DIST_SSR } from '../builder/build.js'; export const CLIENT_MODULE_MAP = { react: 'react', @@ -323,7 +324,7 @@ export const renderHtml = async ( moduleLoading.set( id, opts - .loadModule(joinPath(config.ssrDir, id)) + .loadModule(joinPath(DIST_SSR, id)) .then((m: any) => { moduleCache.set(id, m); }), From bf88b9bb8db8f5d566398cb429ec69e397bb6e30 Mon Sep 17 00:00:00 2001 From: daishi Date: Thu, 2 May 2024 22:59:15 +0900 Subject: [PATCH 11/12] run prettier --- packages/waku/src/lib/renderers/html-renderer.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/waku/src/lib/renderers/html-renderer.ts b/packages/waku/src/lib/renderers/html-renderer.ts index 8859e4d37..7c8b36378 100644 --- a/packages/waku/src/lib/renderers/html-renderer.ts +++ b/packages/waku/src/lib/renderers/html-renderer.ts @@ -323,11 +323,9 @@ export const renderHtml = async ( if (!moduleLoading.has(id)) { moduleLoading.set( id, - opts - .loadModule(joinPath(DIST_SSR, id)) - .then((m: any) => { - moduleCache.set(id, m); - }), + opts.loadModule(joinPath(DIST_SSR, id)).then((m: any) => { + moduleCache.set(id, m); + }), ); } return { id, chunks: [id], name }; From 17bbafb84db7e550b7d5bf290aa88c9c48cf58eb Mon Sep 17 00:00:00 2001 From: daishi Date: Thu, 2 May 2024 23:35:52 +0900 Subject: [PATCH 12/12] build constants --- packages/waku/src/cli.ts | 3 ++- packages/waku/src/lib/builder/build.ts | 15 +++++++-------- packages/waku/src/lib/builder/constants.ts | 7 +++++++ .../waku/src/lib/builder/output-cloudflare.ts | 2 +- packages/waku/src/lib/builder/output-netlify.ts | 2 +- packages/waku/src/lib/builder/output-partykit.ts | 2 +- packages/waku/src/lib/builder/output-vercel.ts | 2 +- packages/waku/src/lib/renderers/html-renderer.ts | 2 +- 8 files changed, 21 insertions(+), 14 deletions(-) create mode 100644 packages/waku/src/lib/builder/constants.ts diff --git a/packages/waku/src/cli.ts b/packages/waku/src/cli.ts index 0ddd77f50..bed77bd55 100644 --- a/packages/waku/src/cli.ts +++ b/packages/waku/src/cli.ts @@ -10,7 +10,8 @@ import * as dotenv from 'dotenv'; import type { Config } from './config.js'; import { runner } from './lib/hono/runner.js'; -import { DIST_ENTRIES_JS, DIST_PUBLIC, build } from './lib/builder/build.js'; +import { build } from './lib/builder/build.js'; +import { DIST_ENTRIES_JS, DIST_PUBLIC } from './lib/builder/constants.js'; const require = createRequire(new URL('.', import.meta.url)); diff --git a/packages/waku/src/lib/builder/build.ts b/packages/waku/src/lib/builder/build.ts index 0aa47b263..0e5585958 100644 --- a/packages/waku/src/lib/builder/build.ts +++ b/packages/waku/src/lib/builder/build.ts @@ -54,6 +54,13 @@ import { emitNetlifyOutput } from './output-netlify.js'; import { emitCloudflareOutput } from './output-cloudflare.js'; import { emitPartyKitOutput } from './output-partykit.js'; import { emitAwsLambdaOutput } from './output-aws-lambda.js'; +import { + DIST_ENTRIES_JS, + DIST_SERVE_JS, + DIST_PUBLIC, + DIST_ASSETS, + DIST_SSR, +} from './constants.js'; // TODO this file and functions in it are too long. will fix. @@ -75,14 +82,6 @@ const onwarn = (warning: RollupLog, defaultHandler: LoggingFunction) => { defaultHandler(warning); }; -// Some file and dir names for dist -// We may change this in the future -export const DIST_ENTRIES_JS = 'entries.js'; -export const DIST_SERVE_JS = 'serve.js'; -export const DIST_PUBLIC = 'public'; -export const DIST_ASSETS = 'assets'; -export const DIST_SSR = 'ssr'; - const analyzeEntries = async (rootDir: string, config: ResolvedConfig) => { const wakuClientDist = decodeFilePathFromAbsolute( joinPath(fileURLToFilePath(import.meta.url), '../../../client.js'), diff --git a/packages/waku/src/lib/builder/constants.ts b/packages/waku/src/lib/builder/constants.ts new file mode 100644 index 000000000..6edf165f9 --- /dev/null +++ b/packages/waku/src/lib/builder/constants.ts @@ -0,0 +1,7 @@ +// Some file and dir names for dist +// We may change this in the future +export const DIST_ENTRIES_JS = 'entries.js'; +export const DIST_SERVE_JS = 'serve.js'; +export const DIST_PUBLIC = 'public'; +export const DIST_ASSETS = 'assets'; +export const DIST_SSR = 'ssr'; diff --git a/packages/waku/src/lib/builder/output-cloudflare.ts b/packages/waku/src/lib/builder/output-cloudflare.ts index 6f761a2e2..b27dafc5d 100644 --- a/packages/waku/src/lib/builder/output-cloudflare.ts +++ b/packages/waku/src/lib/builder/output-cloudflare.ts @@ -2,7 +2,7 @@ import path from 'node:path'; import { existsSync, writeFileSync } from 'node:fs'; import type { ResolvedConfig } from '../config.js'; -import { DIST_PUBLIC } from './build.js'; +import { DIST_PUBLIC } from './constants.js'; // XXX this can be very limited. FIXME if anyone has better knowledge. export const emitCloudflareOutput = async ( diff --git a/packages/waku/src/lib/builder/output-netlify.ts b/packages/waku/src/lib/builder/output-netlify.ts index 4f7705b3c..c37e210cc 100644 --- a/packages/waku/src/lib/builder/output-netlify.ts +++ b/packages/waku/src/lib/builder/output-netlify.ts @@ -2,7 +2,7 @@ import path from 'node:path'; import { mkdirSync, writeFileSync, existsSync, readFileSync } from 'node:fs'; import type { ResolvedConfig } from '../config.js'; -import { DIST_PUBLIC } from './build.js'; +import { DIST_PUBLIC } from './constants.js'; export const emitNetlifyOutput = async ( rootDir: string, diff --git a/packages/waku/src/lib/builder/output-partykit.ts b/packages/waku/src/lib/builder/output-partykit.ts index 5d7b3e165..8a93b4573 100644 --- a/packages/waku/src/lib/builder/output-partykit.ts +++ b/packages/waku/src/lib/builder/output-partykit.ts @@ -2,7 +2,7 @@ import path from 'node:path'; import { existsSync, writeFileSync } from 'node:fs'; import type { ResolvedConfig } from '../config.js'; -import { DIST_PUBLIC } from './build.js'; +import { DIST_PUBLIC } from './constants.js'; // XXX this can be very limited. FIXME if anyone has better knowledge. export const emitPartyKitOutput = async ( diff --git a/packages/waku/src/lib/builder/output-vercel.ts b/packages/waku/src/lib/builder/output-vercel.ts index 60e02ee1d..eafdcc217 100644 --- a/packages/waku/src/lib/builder/output-vercel.ts +++ b/packages/waku/src/lib/builder/output-vercel.ts @@ -2,7 +2,7 @@ import path from 'node:path'; import { cpSync, existsSync, mkdirSync, writeFileSync } from 'node:fs'; import type { ResolvedConfig } from '../config.js'; -import { DIST_PUBLIC } from './build.js'; +import { DIST_PUBLIC } from './constants.js'; // https://vercel.com/docs/build-output-api/v3 export const emitVercelOutput = async ( diff --git a/packages/waku/src/lib/renderers/html-renderer.ts b/packages/waku/src/lib/renderers/html-renderer.ts index 7c8b36378..87f13dc2e 100644 --- a/packages/waku/src/lib/renderers/html-renderer.ts +++ b/packages/waku/src/lib/renderers/html-renderer.ts @@ -23,7 +23,7 @@ import { encodeInput, hasStatusCode } from './utils.js'; // HACK depending on these constants is not ideal import { SRC_MAIN } from '../plugins/vite-plugin-rsc-managed.js'; -import { DIST_SSR } from '../builder/build.js'; +import { DIST_SSR } from '../builder/constants.js'; export const CLIENT_MODULE_MAP = { react: 'react',