-
Notifications
You must be signed in to change notification settings - Fork 1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
422 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import { relative } from 'node:path' | ||
|
||
import { lazy } from 'react' | ||
|
||
import { getPaths } from '@redwoodjs/project-config' | ||
|
||
import { collectStyles } from './find-styles' | ||
import { RwRscServerGlobal } from './RwRscServerGlobal' | ||
|
||
// import viteDevServer from '../dev-server' | ||
const viteDevServer: any = {} | ||
|
||
export class DevRwRscServerGlobal extends RwRscServerGlobal { | ||
/** @type {import('vite').ViteDevServer} */ | ||
viteServer | ||
|
||
constructor() { | ||
super() | ||
this.viteServer = viteDevServer | ||
// this.routeManifest = viteDevServer.routesManifest | ||
} | ||
|
||
bootstrapModules() { | ||
// return [`/@fs${import.meta.env.CLIENT_ENTRY}`] | ||
// TODO (RSC) No idea if this is correct or even what format CLIENT_ENTRY has. | ||
return [`/@fs${getPaths().web.entryClient}`] | ||
} | ||
|
||
bootstrapScriptContent() { | ||
return undefined | ||
} | ||
|
||
async loadModule(id: string) { | ||
return await viteDevServer.ssrLoadModule(id) | ||
} | ||
|
||
lazyComponent(id: string) { | ||
const importPath = `/@fs${id}` | ||
return lazy( | ||
async () => | ||
await this.viteServer.ssrLoadModule(/* @vite-ignore */ importPath) | ||
) | ||
} | ||
|
||
chunkId(chunk: string) { | ||
// return relative(this.srcAppRoot, chunk) | ||
return relative(getPaths().web.src, chunk) | ||
} | ||
|
||
async findAssetsForModules(modules: string[]) { | ||
const styles = await collectStyles( | ||
this.viteServer, | ||
modules.filter((i) => !!i) | ||
) | ||
|
||
return [...Object.entries(styles ?? {}).map(([key, _value]) => key)] | ||
} | ||
|
||
async findAssets() { | ||
const deps = this.getDependenciesForURL('/') | ||
return await this.findAssetsForModules(deps) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import { readFileSync } from 'node:fs' | ||
import { join, relative } from 'node:path' | ||
|
||
import type { Manifest as BuildManifest } from 'vite' | ||
|
||
import { getPaths } from '@redwoodjs/project-config' | ||
|
||
import { findAssetsInManifest } from './findAssetsInManifest' | ||
import { RwRscServerGlobal } from './RwRscServerGlobal' | ||
|
||
function readJSON(path: string) { | ||
return JSON.parse(readFileSync(path, 'utf-8')) | ||
} | ||
|
||
export class ProdRwRscServerGlobal extends RwRscServerGlobal { | ||
serverManifest: BuildManifest | ||
|
||
constructor() { | ||
super() | ||
|
||
const rwPaths = getPaths() | ||
|
||
this.serverManifest = readJSON( | ||
join(rwPaths.web.distServer, 'server-build-manifest.json') | ||
) | ||
} | ||
|
||
chunkId(chunk: string) { | ||
return relative(getPaths().web.src, chunk) | ||
} | ||
|
||
async findAssetsForModules(modules: string[]) { | ||
return modules?.map((i) => this.findAssetsForModule(i)).flat() ?? [] | ||
} | ||
|
||
findAssetsForModule(module: string) { | ||
return [ | ||
...findAssetsInManifest(this.serverManifest, module).filter( | ||
(asset) => !asset.endsWith('.js') && !asset.endsWith('.mjs') | ||
), | ||
] | ||
} | ||
|
||
async findAssets(): Promise<string[]> { | ||
// TODO (RSC) This is a hack. We need to figure out how to get the | ||
// dependencies for the current page. | ||
const deps = Object.keys(this.serverManifest).filter((name) => | ||
/\.(tsx|jsx|js)$/.test(name) | ||
) | ||
|
||
return await this.findAssetsForModules(deps) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { lazy } from 'react' | ||
|
||
export class RwRscServerGlobal { | ||
async loadModule(id: string) { | ||
return await import(/* @vite-ignore */ id) | ||
} | ||
|
||
lazyComponent(id: string) { | ||
return lazy(() => this.loadModule(id)) | ||
} | ||
|
||
// Will be implemented by subclasses | ||
async findAssets(_id: string): Promise<any[]> { | ||
return [] | ||
} | ||
|
||
getDependenciesForURL(_route: string): string[] { | ||
return [] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
// Copied from | ||
// https://github.com/nksaraf/fully-react/blob/4f738132a17d94486c8da19d8729044c3998fc54/packages/fully-react/src/shared/assets.tsx | ||
// And then modified to work with our codebase | ||
|
||
import React, { use } from 'react' | ||
|
||
const linkProps = [ | ||
['js', { rel: 'modulepreload', crossOrigin: '' }], | ||
['jsx', { rel: 'modulepreload', crossOrigin: '' }], | ||
['ts', { rel: 'modulepreload', crossOrigin: '' }], | ||
['tsx', { rel: 'modulepreload', crossOrigin: '' }], | ||
['css', { rel: 'stylesheet', precedence: 'high' }], | ||
['woff', { rel: 'preload', as: 'font', type: 'font/woff', crossOrigin: '' }], | ||
[ | ||
'woff2', | ||
{ rel: 'preload', as: 'font', type: 'font/woff2', crossOrigin: '' }, | ||
], | ||
['gif', { rel: 'preload', as: 'image', type: 'image/gif' }], | ||
['jpg', { rel: 'preload', as: 'image', type: 'image/jpeg' }], | ||
['jpeg', { rel: 'preload', as: 'image', type: 'image/jpeg' }], | ||
['png', { rel: 'preload', as: 'image', type: 'image/png' }], | ||
['webp', { rel: 'preload', as: 'image', type: 'image/webp' }], | ||
['svg', { rel: 'preload', as: 'image', type: 'image/svg+xml' }], | ||
['ico', { rel: 'preload', as: 'image', type: 'image/x-icon' }], | ||
['avif', { rel: 'preload', as: 'image', type: 'image/avif' }], | ||
['mp4', { rel: 'preload', as: 'video', type: 'video/mp4' }], | ||
['webm', { rel: 'preload', as: 'video', type: 'video/webm' }], | ||
] as const | ||
|
||
type Linkprop = (typeof linkProps)[number][1] | ||
|
||
const linkPropsMap = new Map<string, Linkprop>(linkProps) | ||
|
||
/** | ||
* Generates a link tag for a given file. This will load stylesheets and preload | ||
* everything else. It uses the file extension to determine the type. | ||
*/ | ||
export const Asset = ({ file }: { file: string }) => { | ||
const ext = file.split('.').pop() | ||
const props = ext ? linkPropsMap.get(ext) : null | ||
|
||
if (!props) { | ||
return null | ||
} | ||
|
||
return <link href={file} {...props} /> | ||
} | ||
|
||
export function Assets() { | ||
// TODO (RSC) Currently we only handle server assets. | ||
// Will probably need to handle client assets as well. | ||
// Do we also need special code for SSR? | ||
// if (isClient) return <ClientAssets /> | ||
|
||
// @ts-expect-error Need experimental types here for this to work | ||
return <ServerAssets /> | ||
} | ||
|
||
const findAssets = async () => { | ||
return [...new Set([...(await rwRscGlobal.findAssets(''))]).values()] | ||
} | ||
|
||
const AssetList = ({ assets }: { assets: string[] }) => { | ||
return ( | ||
<> | ||
{assets.map((asset) => { | ||
return <Asset file={asset} key={asset} /> | ||
})} | ||
</> | ||
) | ||
} | ||
|
||
async function ServerAssets() { | ||
const allAssets = await findAssets() | ||
|
||
return <AssetList assets={allAssets} /> | ||
} | ||
|
||
export function ClientAssets() { | ||
const allAssets = use(findAssets()) | ||
|
||
return <AssetList assets={allAssets} /> | ||
} |
Oops, something went wrong.