one
Version:
One is a new React Framework that makes Vite serve both native and web.
154 lines (137 loc) • 4.44 kB
text/typescript
import { afterEach, describe, expect, it, vi } from 'vitest'
import type { One } from '../vite/types'
import { createWorkerHandler, type LazyRoutes } from './workerHandler'
// route files are router-root relative, the way createRoutesManifest emits them
const pageRoute = {
file: './some-page.tsx',
page: '/some-page',
namedRegex: '^/some-page(?:/)?$',
urlPath: '/some-page',
urlCleanPath: '/some-page',
routeKeys: {},
type: 'ssr',
middlewares: [],
} satisfies One.BuildInfo['manifest']['pageRoutes'][number]
const apiRoute = {
file: './api/status+api.ts',
page: '/api/status',
namedRegex: '^/api/status(?:/)?$',
urlPath: '/api/status',
urlCleanPath: '/api/status',
routeKeys: {},
type: 'api',
middlewares: [],
} satisfies One.BuildInfo['manifest']['apiRoutes'][number]
const buildInfo = {
constants: { CACHE_KEY: 'test' },
routeToBuildInfo: {
[pageRoute.file]: {
type: 'ssr',
path: pageRoute.page,
routeFile: pageRoute.file,
middlewares: [],
preloadPath: '',
cssPreloadPath: '',
loaderPath: '',
cleanPath: pageRoute.page,
htmlPath: '',
clientJsPath: '',
serverJsPath: '',
params: {},
preloads: [],
css: [],
layoutCSS: [],
},
},
pathToRoute: {},
routeMap: {},
manifest: {
pageRoutes: [pageRoute],
apiRoutes: [apiRoute],
allRoutes: [pageRoute, apiRoute],
},
preloads: {},
cssPreloads: {},
loaders: {},
} satisfies One.BuildInfo
// route modules are reached through the server entry's own route map, keyed by
// the router-root prefixed path its `import.meta.glob` produces
const routeKey = `/app/${pageRoute.file.slice(2)}`
const makeServerEntry =
(importPage: () => Promise<any> = async () => ({})) =>
async () => ({
default: {
render: () => '<html></html>',
options: { routes: { [routeKey]: importPage } },
},
})
const lazyRoutes = {
serverEntry: makeServerEntry(),
api: {
[apiRoute.page]: async () => ({
HEAD: () => new Response(null, { status: 204 }),
}),
},
middlewares: {},
} satisfies LazyRoutes
function createHandler() {
vi.stubEnv('ONE_BUFFERED_SSR', '1')
return createWorkerHandler({
oneOptions: { router: { root: 'app' }, web: { defaultRenderMode: 'ssr' } },
buildInfo,
lazyRoutes,
}).handleRequest
}
afterEach(() => {
vi.unstubAllEnvs()
})
describe('createWorkerHandler', () => {
it('answers HEAD page requests exactly like GET', async () => {
const handleRequest = createHandler()
const get = await handleRequest(new Request('https://example.com/some-page'))
const head = await handleRequest(
new Request('https://example.com/some-page', { method: 'HEAD' })
)
if (!(get instanceof Response) || !(head instanceof Response)) {
throw new Error('expected both GET and HEAD to return a Response')
}
// workerd drops the body for HEAD, so the handler must agree with GET on
// status and content type. asserting an empty body here would only be
// asserting the transport's job.
expect(get.status).toBe(200)
expect(head.status).toBe(get.status)
expect(head.headers.get('content-type')).toBe(get.headers.get('content-type'))
})
it('does not route POST requests to page handlers', async () => {
const response = await createHandler()(
new Request('https://example.com/some-page', { method: 'POST' })
)
expect(response).toBeNull()
})
it('continues routing HEAD requests to API handlers', async () => {
const response = await createHandler()(
new Request('https://example.com/api/status', { method: 'HEAD' })
)
expect(response).toBeInstanceOf(Response)
expect(response?.status).toBe(204)
})
it('disableModuleCache re-imports the page module on each request', async () => {
vi.stubEnv('ONE_BUFFERED_SSR', '1')
let imports = 0
const handleRequest = createWorkerHandler({
oneOptions: { router: { root: 'app' }, web: { defaultRenderMode: 'ssr' } },
buildInfo,
disableModuleCache: true,
lazyRoutes: {
...lazyRoutes,
serverEntry: makeServerEntry(async () => {
imports += 1
return {}
}),
},
}).handleRequest
await handleRequest(new Request('https://example.com/some-page'))
await handleRequest(new Request('https://example.com/some-page'))
expect(imports).toBeGreaterThan(1)
})
})