From d0d0fc8bc7f6faa07ecc752b7b70e0bc7800ac8f Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Wed, 12 Jul 2023 23:56:35 +0200 Subject: [PATCH 1/2] RSC: Build using rw build --- packages/vite/src/buildFeServer.ts | 36 +++- packages/vite/src/buildRscFeServer.ts | 184 ++++---------------- packages/vite/src/buildStreamingFeServer.ts | 164 +++++++++++++++++ packages/vite/src/rscBuild.ts | 71 ++++++++ 4 files changed, 298 insertions(+), 157 deletions(-) create mode 100644 packages/vite/src/buildStreamingFeServer.ts create mode 100644 packages/vite/src/rscBuild.ts diff --git a/packages/vite/src/buildFeServer.ts b/packages/vite/src/buildFeServer.ts index 7ad32d00a1f8..2e070f0bdcd5 100644 --- a/packages/vite/src/buildFeServer.ts +++ b/packages/vite/src/buildFeServer.ts @@ -9,21 +9,24 @@ import { transformWithBabel } from '@redwoodjs/internal/dist/build/babel/api' import { buildWeb } from '@redwoodjs/internal/dist/build/web' import { findRouteHooksSrc } from '@redwoodjs/internal/dist/files' import { getProjectRoutes } from '@redwoodjs/internal/dist/routes' -import { getAppRouteHook, getPaths } from '@redwoodjs/project-config' +import { getAppRouteHook, getConfig, getPaths } from '@redwoodjs/project-config' +import { buildRscFeServer } from './buildRscFeServer' import { RWRouteManifest } from './types' -interface BuildOptions { +export interface BuildOptions { verbose?: boolean } export const buildFeServer = async ({ verbose }: BuildOptions) => { const rwPaths = getPaths() - const viteConfig = rwPaths.web.viteConfig + const rwConfig = getConfig() + const viteConfigPath = rwPaths.web.viteConfig - if (!viteConfig) { + if (!viteConfigPath) { throw new Error( - 'Vite config not found. You need to setup your project with Vite using `yarn rw setup vite`' + 'Vite config not found. You need to setup your project with Vite ' + + 'using `yarn rw setup vite`' ) } @@ -35,17 +38,34 @@ export const buildFeServer = async ({ verbose }: BuildOptions) => { ) } + if (rwConfig.experimental?.rsc?.enabled) { + if (!rwPaths.web.entries) { + throw new Error('RSC entries file not found') + } + + return await buildRscFeServer({ + viteConfigPath, + webSrc: rwPaths.web.src, + webHtml: rwPaths.web.html, + entries: rwPaths.web.entries, + webDist: rwPaths.web.dist, + webDistServer: rwPaths.web.distServer, + webDistEntries: rwPaths.web.distServerEntries, + webRouteManifest: rwPaths.web.routeManifest, + }) + } + // Step 1A: Generate the client bundle await buildWeb({ verbose }) // TODO (STREAMING) When Streaming is released Vite will be the only bundler, // so we can switch to a regular import // @NOTE: Using dynamic import, because vite is still opt-in - const { build } = await import('vite') + const { build: viteBuild } = await import('vite') // Step 1B: Generate the server output - await build({ - configFile: viteConfig, + await viteBuild({ + configFile: viteConfigPath, build: { // Because we configure the root to be web/src, we need to go up one level outDir: rwPaths.web.distServer, diff --git a/packages/vite/src/buildRscFeServer.ts b/packages/vite/src/buildRscFeServer.ts index 266d2a264ae0..a34379c5599f 100644 --- a/packages/vite/src/buildRscFeServer.ts +++ b/packages/vite/src/buildRscFeServer.ts @@ -6,135 +6,47 @@ import { build as viteBuild } from 'vite' import type { Manifest as ViteBuildManifest } from 'vite' import { RouteSpec } from '@redwoodjs/internal/dist/routes' -import { getAppRouteHook, getPaths } from '@redwoodjs/project-config' +import { rscBuild } from './rscBuild' import { RWRouteManifest } from './types' import { serverBuild } from './waku-lib/build-server' -import { rscAnalyzePlugin, rscIndexPlugin } from './waku-lib/vite-plugin-rsc' - -interface BuildOptions { - verbose?: boolean +import { rscIndexPlugin } from './waku-lib/vite-plugin-rsc' + +interface Args { + viteConfigPath: string + webSrc: string + webHtml: string + entries: string + webDist: string + webDistServer: string + webDistEntries: string + webRouteManifest: string } -export const buildFeServer = async ({ verbose: _verbose }: BuildOptions) => { - const rwPaths = getPaths() - const viteConfig = rwPaths.web.viteConfig - - if (!viteConfig) { - throw new Error('Vite config not found') - } - - if (!rwPaths.web.entries) { - throw new Error('RSC entries file not found') - } - - const clientEntryFileSet = new Set() - const serverEntryFileSet = new Set() - - /** - * RSC build - * Uses rscAnalyzePlugin to collect client and server entry points - * Starts building the AST in entries.ts - * Doesn't output any files, only collects a list of RSCs and RSFs - */ - await viteBuild({ - configFile: viteConfig, - root: rwPaths.base, - plugins: [ - react(), - { - name: 'rsc-test-plugin', - transform(_code, id) { - console.log('rsc-test-plugin id', id) - }, - }, - rscAnalyzePlugin( - (id) => clientEntryFileSet.add(id), - (id) => serverEntryFileSet.add(id) - ), - ], - // ssr: { - // // FIXME Without this, waku/router isn't considered to have client - // // entries, and "No client entry" error occurs. - // // Unless we fix this, RSC-capable packages aren't supported. - // // This also seems to cause problems with pnpm. - // // noExternal: ['@redwoodjs/web', '@redwoodjs/router'], - // }, - build: { - manifest: 'rsc-build-manifest.json', - write: false, - ssr: true, - rollupOptions: { - input: { - entries: rwPaths.web.entries, - }, - }, - }, - }) - - const clientEntryFiles = Object.fromEntries( - Array.from(clientEntryFileSet).map((filename, i) => [`rsc${i}`, filename]) - ) - const serverEntryFiles = Object.fromEntries( - Array.from(serverEntryFileSet).map((filename, i) => [`rsf${i}`, filename]) - ) - - console.log('clientEntryFileSet', Array.from(clientEntryFileSet)) - console.log('serverEntryFileSet', Array.from(serverEntryFileSet)) - console.log('clientEntryFiles', clientEntryFiles) - console.log('serverEntryFiles', serverEntryFiles) - - const clientEntryPath = rwPaths.web.entryClient - - if (!clientEntryPath) { - throw new Error( - 'Vite client entry point not found. Please check that your project ' + - 'has an entry.client.{jsx,tsx} file in the web/src directory.' - ) - } +export const buildRscFeServer = async ({ + viteConfigPath, + webSrc, + webHtml, + entries, + webDist, + webDistServer, + webDistEntries, + webRouteManifest, +}: Args) => { + const { clientEntryFiles, serverEntryFiles } = await rscBuild(viteConfigPath) const clientBuildOutput = await viteBuild({ - configFile: viteConfig, - root: rwPaths.web.src, - plugins: [ - // TODO (RSC) Update index.html to include the entry.client.js script - // TODO (RSC) Do the above in the exp-rsc setup command - // { - // name: 'redwood-plugin-vite', - - // // ---------- Bundle injection ---------- - // // Used by rollup during build to inject the entrypoint - // // but note index.html does not come through as an id during dev - // transform: (code: string, id: string) => { - // if ( - // existsSync(clientEntryPath) && - // // TODO (RSC) Is this even needed? We throw if we can't find it above - // // TODO (RSC) Consider making this async (if we do need it) - // normalizePath(id) === normalizePath(rwPaths.web.html) - // ) { - // const newCode = code.replace( - // '', - // '' - // ) - // - // return { code: newCode, map: null } - // } else { - // // Returning null as the map preserves the original sourcemap - // return { code, map: null } - // } - // }, - // }, - react(), - rscIndexPlugin(), - ], + configFile: viteConfigPath, + root: webSrc, + plugins: [react(), rscIndexPlugin()], build: { - outDir: rwPaths.web.dist, + outDir: webDist, emptyOutDir: true, // Needed because `outDir` is not inside `root` // TODO (RSC) Enable this when we switch to a server-first approach // emptyOutDir: false, // Already done when building server rollupOptions: { input: { - main: rwPaths.web.html, + main: webHtml, ...clientEntryFiles, }, preserveEntrySignatures: 'exports-only', @@ -151,7 +63,7 @@ export const buildFeServer = async ({ verbose: _verbose }: BuildOptions) => { } const serverBuildOutput = await serverBuild( - rwPaths.web.entries, + entries, clientEntryFiles, serverEntryFiles, {} @@ -168,8 +80,8 @@ export const buildFeServer = async ({ verbose: _verbose }: BuildOptions) => { }) .map((cssAsset) => { return fs.copyFile( - path.join(rwPaths.web.distServer, cssAsset.fileName), - path.join(rwPaths.web.dist, cssAsset.fileName) + path.join(webDistServer, cssAsset.fileName), + path.join(webDist, cssAsset.fileName) ) }) ) @@ -193,7 +105,7 @@ export const buildFeServer = async ({ verbose: _verbose }: BuildOptions) => { console.log('clientEntries', clientEntries) await fs.appendFile( - path.join(rwPaths.web.distServer, 'entries.js'), + webDistEntries, `export const clientEntries=${JSON.stringify(clientEntries)};` ) @@ -294,10 +206,7 @@ export const buildFeServer = async ({ verbose: _verbose }: BuildOptions) => { // * With `assert` and `@babel/plugin-syntax-import-assertions` the // code compiled and ran properly, but Jest tests failed, complaining // about the syntax. - const manifestPath = path.join( - getPaths().web.dist, - 'client-build-manifest.json' - ) + const manifestPath = path.join(webDist, 'client-build-manifest.json') const manifestStr = await fs.readFile(manifestPath, 'utf-8') const clientBuildManifest: ViteBuildManifest = JSON.parse(manifestStr) @@ -316,7 +225,7 @@ export const buildFeServer = async ({ verbose: _verbose }: BuildOptions) => { // E.g. /blog/post/{id:Int} pathDefinition: route.path, hasParams: route.hasParams, - routeHooks: FIXME_constructRouteHookPath(route.routeHooks), + routeHooks: null, redirect: route.redirect ? { to: route.redirect?.to, @@ -329,28 +238,5 @@ export const buildFeServer = async ({ verbose: _verbose }: BuildOptions) => { return acc }, {}) - await fs.writeFile(rwPaths.web.routeManifest, JSON.stringify(routeManifest)) -} - -// TODO (STREAMING) Hacky work around because when you don't have a App.routeHook, esbuild doesn't create -// the pages folder in the dist/server/routeHooks directory. -// @MARK need to change to .mjs here if we use esm -const FIXME_constructRouteHookPath = (rhSrcPath: string | null | undefined) => { - const rwPaths = getPaths() - if (!rhSrcPath) { - return null - } - - if (getAppRouteHook()) { - return path.relative(rwPaths.web.src, rhSrcPath).replace('.ts', '.js') - } else { - return path - .relative(path.join(rwPaths.web.src, 'pages'), rhSrcPath) - .replace('.ts', '.js') - } -} - -if (require.main === module) { - const verbose = process.argv.includes('--verbose') - buildFeServer({ verbose }) + await fs.writeFile(webRouteManifest, JSON.stringify(routeManifest)) } diff --git a/packages/vite/src/buildStreamingFeServer.ts b/packages/vite/src/buildStreamingFeServer.ts new file mode 100644 index 000000000000..7ad32d00a1f8 --- /dev/null +++ b/packages/vite/src/buildStreamingFeServer.ts @@ -0,0 +1,164 @@ +import fs from 'fs/promises' +import path from 'path' + +import { build as esbuildBuild, PluginBuild } from 'esbuild' +import type { Manifest as ViteBuildManifest } from 'vite' + +import { getRouteHookBabelPlugins } from '@redwoodjs/internal' +import { transformWithBabel } from '@redwoodjs/internal/dist/build/babel/api' +import { buildWeb } from '@redwoodjs/internal/dist/build/web' +import { findRouteHooksSrc } from '@redwoodjs/internal/dist/files' +import { getProjectRoutes } from '@redwoodjs/internal/dist/routes' +import { getAppRouteHook, getPaths } from '@redwoodjs/project-config' + +import { RWRouteManifest } from './types' + +interface BuildOptions { + verbose?: boolean +} + +export const buildFeServer = async ({ verbose }: BuildOptions) => { + const rwPaths = getPaths() + const viteConfig = rwPaths.web.viteConfig + + if (!viteConfig) { + throw new Error( + 'Vite config not found. You need to setup your project with Vite using `yarn rw setup vite`' + ) + } + + if (!rwPaths.web.entryServer || !rwPaths.web.entryClient) { + throw new Error( + 'Vite entry points not found. Please check that your project has an ' + + 'entry.client.{jsx,tsx} and entry.server.{jsx,tsx} file in the ' + + 'web/src directory.' + ) + } + + // Step 1A: Generate the client bundle + await buildWeb({ verbose }) + + // TODO (STREAMING) When Streaming is released Vite will be the only bundler, + // so we can switch to a regular import + // @NOTE: Using dynamic import, because vite is still opt-in + const { build } = await import('vite') + + // Step 1B: Generate the server output + await build({ + configFile: viteConfig, + build: { + // Because we configure the root to be web/src, we need to go up one level + outDir: rwPaths.web.distServer, + ssr: rwPaths.web.entryServer, + }, + envFile: false, + logLevel: verbose ? 'info' : 'warn', + }) + + const allRouteHooks = findRouteHooksSrc() + + const runRwBabelTransformsPlugin = { + name: 'rw-esbuild-babel-transform', + setup(build: PluginBuild) { + build.onLoad({ filter: /\.(js|ts|tsx|jsx)$/ }, async (args) => { + // Remove RedwoodJS "magic" from a user's code leaving JavaScript behind. + // TODO (STREAMING) We need the new transformWithBabel function in https://github.com/redwoodjs/redwood/pull/7672/files + const transformedCode = transformWithBabel(args.path, [ + ...getRouteHookBabelPlugins(), + ]) + + if (transformedCode?.code) { + return { + contents: transformedCode.code, + loader: 'js', + } + } + + throw new Error(`Could not transform file: ${args.path}`) + }) + }, + } + + await esbuildBuild({ + absWorkingDir: getPaths().web.base, + entryPoints: allRouteHooks, + platform: 'node', + target: 'node16', + // @MARK Disable splitting and esm, because Redwood web modules don't support esm yet + // outExtension: { '.js': '.mjs' }, + // format: 'esm', + // splitting: true, + bundle: true, + plugins: [runRwBabelTransformsPlugin], + packages: 'external', + logLevel: verbose ? 'info' : 'error', + outdir: rwPaths.web.distRouteHooks, + }) + + // Step 3: Generate route-manifest.json + + // TODO When https://github.com/tc39/proposal-import-attributes and + // https://github.com/microsoft/TypeScript/issues/53656 have both landed we + // should try to do this instead: + // const clientBuildManifest: ViteBuildManifest = await import( + // path.join(getPaths().web.dist, 'build-manifest.json'), + // { with: { type: 'json' } } + // ) + // NOTES: + // * There's a related babel plugin here + // https://babeljs.io/docs/babel-plugin-syntax-import-attributes + // * Included in `preset-env` if you set `shippedProposals: true` + // * We had this before, but with `assert` instead of `with`. We really + // should be using `with`. See motivation in issues linked above. + // * With `assert` and `@babel/plugin-syntax-import-assertions` the + // code compiled and ran properly, but Jest tests failed, complaining + // about the syntax. + const manifestPath = path.join(getPaths().web.dist, 'build-manifest.json') + const buildManifestStr = await fs.readFile(manifestPath, 'utf-8') + const clientBuildManifest: ViteBuildManifest = JSON.parse(buildManifestStr) + + const routesList = getProjectRoutes() + + const routeManifest = routesList.reduce((acc, route) => { + acc[route.path] = { + name: route.name, + bundle: route.relativeFilePath + ? clientBuildManifest[route.relativeFilePath].file + : null, + matchRegexString: route.matchRegexString, + // @NOTE this is the path definition, not the actual path + // E.g. /blog/post/{id:Int} + pathDefinition: route.path, + hasParams: route.hasParams, + routeHooks: FIXME_constructRouteHookPath(route.routeHooks), + redirect: route.redirect + ? { + to: route.redirect?.to, + permanent: false, + } + : null, + renderMode: route.renderMode, + } + return acc + }, {}) + + await fs.writeFile(rwPaths.web.routeManifest, JSON.stringify(routeManifest)) +} + +// TODO (STREAMING) Hacky work around because when you don't have a App.routeHook, esbuild doesn't create +// the pages folder in the dist/server/routeHooks directory. +// @MARK need to change to .mjs here if we use esm +const FIXME_constructRouteHookPath = (rhSrcPath: string | null | undefined) => { + const rwPaths = getPaths() + if (!rhSrcPath) { + return null + } + + if (getAppRouteHook()) { + return path.relative(rwPaths.web.src, rhSrcPath).replace('.ts', '.js') + } else { + return path + .relative(path.join(rwPaths.web.src, 'pages'), rhSrcPath) + .replace('.ts', '.js') + } +} diff --git a/packages/vite/src/rscBuild.ts b/packages/vite/src/rscBuild.ts new file mode 100644 index 000000000000..d1cd6f4f7002 --- /dev/null +++ b/packages/vite/src/rscBuild.ts @@ -0,0 +1,71 @@ +import react from '@vitejs/plugin-react' +import { build as viteBuild } from 'vite' + +import { getPaths } from '@redwoodjs/project-config' + +import { rscAnalyzePlugin } from './waku-lib/vite-plugin-rsc' + +/** + * RSC build + * Uses rscAnalyzePlugin to collect client and server entry points + * Starts building the AST in entries.ts + * Doesn't output any files, only collects a list of RSCs and RSFs + */ +export async function rscBuild(viteConfigPath: string) { + const rwPaths = getPaths() + const clientEntryFileSet = new Set() + const serverEntryFileSet = new Set() + + if (!rwPaths.web.entries) { + throw new Error('RSC entries file not found') + } + + await viteBuild({ + configFile: viteConfigPath, + root: rwPaths.base, + plugins: [ + react(), + { + name: 'rsc-test-plugin', + transform(_code, id) { + console.log('rsc-test-plugin id', id) + }, + }, + rscAnalyzePlugin( + (id) => clientEntryFileSet.add(id), + (id) => serverEntryFileSet.add(id) + ), + ], + // ssr: { + // // FIXME Without this, waku/router isn't considered to have client + // // entries, and "No client entry" error occurs. + // // Unless we fix this, RSC-capable packages aren't supported. + // // This also seems to cause problems with pnpm. + // // noExternal: ['@redwoodjs/web', '@redwoodjs/router'], + // }, + build: { + manifest: 'rsc-build-manifest.json', + write: false, + ssr: true, + rollupOptions: { + input: { + entries: rwPaths.web.entries, + }, + }, + }, + }) + + const clientEntryFiles = Object.fromEntries( + Array.from(clientEntryFileSet).map((filename, i) => [`rsc${i}`, filename]) + ) + const serverEntryFiles = Object.fromEntries( + Array.from(serverEntryFileSet).map((filename, i) => [`rsf${i}`, filename]) + ) + + console.log('clientEntryFileSet', Array.from(clientEntryFileSet)) + console.log('serverEntryFileSet', Array.from(serverEntryFileSet)) + console.log('clientEntryFiles', clientEntryFiles) + console.log('serverEntryFiles', serverEntryFiles) + + return { clientEntryFiles, serverEntryFiles } +} From 4c39e538720a75e5e3cdcda8842303c452c0369b Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Thu, 13 Jul 2023 10:42:38 +0200 Subject: [PATCH 2/2] Remove unused file --- packages/vite/src/buildStreamingFeServer.ts | 164 -------------------- 1 file changed, 164 deletions(-) delete mode 100644 packages/vite/src/buildStreamingFeServer.ts diff --git a/packages/vite/src/buildStreamingFeServer.ts b/packages/vite/src/buildStreamingFeServer.ts deleted file mode 100644 index 7ad32d00a1f8..000000000000 --- a/packages/vite/src/buildStreamingFeServer.ts +++ /dev/null @@ -1,164 +0,0 @@ -import fs from 'fs/promises' -import path from 'path' - -import { build as esbuildBuild, PluginBuild } from 'esbuild' -import type { Manifest as ViteBuildManifest } from 'vite' - -import { getRouteHookBabelPlugins } from '@redwoodjs/internal' -import { transformWithBabel } from '@redwoodjs/internal/dist/build/babel/api' -import { buildWeb } from '@redwoodjs/internal/dist/build/web' -import { findRouteHooksSrc } from '@redwoodjs/internal/dist/files' -import { getProjectRoutes } from '@redwoodjs/internal/dist/routes' -import { getAppRouteHook, getPaths } from '@redwoodjs/project-config' - -import { RWRouteManifest } from './types' - -interface BuildOptions { - verbose?: boolean -} - -export const buildFeServer = async ({ verbose }: BuildOptions) => { - const rwPaths = getPaths() - const viteConfig = rwPaths.web.viteConfig - - if (!viteConfig) { - throw new Error( - 'Vite config not found. You need to setup your project with Vite using `yarn rw setup vite`' - ) - } - - if (!rwPaths.web.entryServer || !rwPaths.web.entryClient) { - throw new Error( - 'Vite entry points not found. Please check that your project has an ' + - 'entry.client.{jsx,tsx} and entry.server.{jsx,tsx} file in the ' + - 'web/src directory.' - ) - } - - // Step 1A: Generate the client bundle - await buildWeb({ verbose }) - - // TODO (STREAMING) When Streaming is released Vite will be the only bundler, - // so we can switch to a regular import - // @NOTE: Using dynamic import, because vite is still opt-in - const { build } = await import('vite') - - // Step 1B: Generate the server output - await build({ - configFile: viteConfig, - build: { - // Because we configure the root to be web/src, we need to go up one level - outDir: rwPaths.web.distServer, - ssr: rwPaths.web.entryServer, - }, - envFile: false, - logLevel: verbose ? 'info' : 'warn', - }) - - const allRouteHooks = findRouteHooksSrc() - - const runRwBabelTransformsPlugin = { - name: 'rw-esbuild-babel-transform', - setup(build: PluginBuild) { - build.onLoad({ filter: /\.(js|ts|tsx|jsx)$/ }, async (args) => { - // Remove RedwoodJS "magic" from a user's code leaving JavaScript behind. - // TODO (STREAMING) We need the new transformWithBabel function in https://github.com/redwoodjs/redwood/pull/7672/files - const transformedCode = transformWithBabel(args.path, [ - ...getRouteHookBabelPlugins(), - ]) - - if (transformedCode?.code) { - return { - contents: transformedCode.code, - loader: 'js', - } - } - - throw new Error(`Could not transform file: ${args.path}`) - }) - }, - } - - await esbuildBuild({ - absWorkingDir: getPaths().web.base, - entryPoints: allRouteHooks, - platform: 'node', - target: 'node16', - // @MARK Disable splitting and esm, because Redwood web modules don't support esm yet - // outExtension: { '.js': '.mjs' }, - // format: 'esm', - // splitting: true, - bundle: true, - plugins: [runRwBabelTransformsPlugin], - packages: 'external', - logLevel: verbose ? 'info' : 'error', - outdir: rwPaths.web.distRouteHooks, - }) - - // Step 3: Generate route-manifest.json - - // TODO When https://github.com/tc39/proposal-import-attributes and - // https://github.com/microsoft/TypeScript/issues/53656 have both landed we - // should try to do this instead: - // const clientBuildManifest: ViteBuildManifest = await import( - // path.join(getPaths().web.dist, 'build-manifest.json'), - // { with: { type: 'json' } } - // ) - // NOTES: - // * There's a related babel plugin here - // https://babeljs.io/docs/babel-plugin-syntax-import-attributes - // * Included in `preset-env` if you set `shippedProposals: true` - // * We had this before, but with `assert` instead of `with`. We really - // should be using `with`. See motivation in issues linked above. - // * With `assert` and `@babel/plugin-syntax-import-assertions` the - // code compiled and ran properly, but Jest tests failed, complaining - // about the syntax. - const manifestPath = path.join(getPaths().web.dist, 'build-manifest.json') - const buildManifestStr = await fs.readFile(manifestPath, 'utf-8') - const clientBuildManifest: ViteBuildManifest = JSON.parse(buildManifestStr) - - const routesList = getProjectRoutes() - - const routeManifest = routesList.reduce((acc, route) => { - acc[route.path] = { - name: route.name, - bundle: route.relativeFilePath - ? clientBuildManifest[route.relativeFilePath].file - : null, - matchRegexString: route.matchRegexString, - // @NOTE this is the path definition, not the actual path - // E.g. /blog/post/{id:Int} - pathDefinition: route.path, - hasParams: route.hasParams, - routeHooks: FIXME_constructRouteHookPath(route.routeHooks), - redirect: route.redirect - ? { - to: route.redirect?.to, - permanent: false, - } - : null, - renderMode: route.renderMode, - } - return acc - }, {}) - - await fs.writeFile(rwPaths.web.routeManifest, JSON.stringify(routeManifest)) -} - -// TODO (STREAMING) Hacky work around because when you don't have a App.routeHook, esbuild doesn't create -// the pages folder in the dist/server/routeHooks directory. -// @MARK need to change to .mjs here if we use esm -const FIXME_constructRouteHookPath = (rhSrcPath: string | null | undefined) => { - const rwPaths = getPaths() - if (!rhSrcPath) { - return null - } - - if (getAppRouteHook()) { - return path.relative(rwPaths.web.src, rhSrcPath).replace('.ts', '.js') - } else { - return path - .relative(path.join(rwPaths.web.src, 'pages'), rhSrcPath) - .replace('.ts', '.js') - } -}