Skip to content

Instantly share code, notes, and snippets.

@dac09
Created March 17, 2024 14:04

Revisions

  1. dac09 created this gist Mar 17, 2024.
    101 changes: 101 additions & 0 deletions buildForStreamingServer.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,101 @@
    import fs from 'node:fs'
    import path from 'node:path'

    import { build as viteBuild } from 'vite'
    import { cjsInterop } from 'vite-plugin-cjs-interop'

    import { getPaths } from '@redwoodjs/project-config'

    export async function buildForStreamingServer({
    verbose = false,
    clientEntryFiles,
    serverEntryFiles,
    }: {
    verbose?: boolean
    clientEntryFiles: Record<string, string>
    serverEntryFiles: Record<string, string>
    }) {
    console.log('Starting streaming server build...\n')

    const rwPaths = getPaths()

    const rscBuildManifest: Record<string, any> = JSON.parse(
    fs.readFileSync(
    path.join(rwPaths.web.distRsc, 'server-build-manifest.json'),
    'utf-8',
    ),
    )

    const serverEntriesPaths = Object.values(serverEntryFiles)

    if (!rwPaths.web.viteConfig) {
    throw new Error('Vite config not found')
    }

    await viteBuild({
    configFile: rwPaths.web.viteConfig,
    plugins: [
    cjsInterop({
    dependencies: ['@redwoodjs/**'],
    }),
    {
    name: 'rw-resolve-redirect',
    enforce: 'pre',
    async resolveId(source, importer, _options) {
    // Not sure how this is possible but TS says so
    if (!importer) {
    return null
    }

    // @TODO: ignore node_modules, this seems like a lot of processing otherwise

    // Step 1: create path for lookup
    const absPath = path.join(path.parse(importer).dir, source)

    // Only do it for files with "use server" in src, ignore node_modules
    // @MARK @TODO Temporary!!!!
    const absPathWithHackedExtension = absPath + '.ts'
    if (
    !serverEntriesPaths.includes(absPathWithHackedExtension) ||
    absPath.includes('node_modules')
    ) {
    return null
    }

    const relToSrc = path.relative(
    rwPaths.web.src,
    absPathWithHackedExtension,
    )

    // Step 2: lookup in the manifest
    // const manifest = await getManifest()
    const fileId = rscBuildManifest[relToSrc].file

    // Step 3, return fileId and external: true
    return {
    id: path.join('../../rsc', fileId),
    external: true,
    }
    },
    },
    ],
    build: {
    rollupOptions: {
    input: {
    ...clientEntryFiles,
    entries: rwPaths.web.entries as string,
    },
    },
    outDir: rwPaths.web.distServer,
    ssr: true,
    emptyOutDir: true,
    },
    ssr: {
    noExternal: /^(?!node:)/,
    // Can't inline prisma client
    external: ['@prisma/client'],
    },
    envFile: false,
    logLevel: verbose ? 'info' : 'warn',
    })
    }