Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions adex/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@
"types": "./src/ssr.d.ts",
"import": "./src/ssr.js"
},
"./static": {
"types": "./src/static.d.ts",
"import": "./src/static.js"
},
"./head": {
"types": "./src/head.d.ts",
"import": "./src/head.js"
Expand Down
43 changes: 43 additions & 0 deletions adex/src/static.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import type { IncomingMessage, ServerResponse } from 'node:http'
import type sirv from 'sirv'

export type StaticPaths = {
assets?: string
islands?: string
client?: string
}

export type ServeStatic = typeof sirv

export type StaticMiddleware = (
req: IncomingMessage,
res: ServerResponse,
next?: (err?: unknown) => void
) => unknown

/**
* Static plugin contract — default export of `adex/static` and of any module
* passed as `kernel.staticServer`.
*/
export type StaticServer = (options: {
paths?: StaticPaths
}) => StaticMiddleware | StaticMiddleware[]

export type CreateStaticMiddlewaresOptions = {
paths?: StaticPaths
serve?: ServeStatic
options?: Parameters<ServeStatic>[1]
}

/** Adex default static plugin (sirv + URL rewrites). */
export function createStaticMiddlewares(
options?: CreateStaticMiddlewaresOptions
): StaticMiddleware[]

declare const defaultStaticPlugin: typeof createStaticMiddlewares
export default defaultStaticPlugin

/** Virtual module source for `kernel.staticServer` (defaults to `adex/static`). */
export function resolveStaticServerModuleSource(
staticServer?: false | string
): string
101 changes: 101 additions & 0 deletions adex/src/static.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { existsSync } from 'node:fs'
import sirv from 'sirv'

const DEFAULT_SERVE_OPTIONS = {
maxAge: 31536000,
immutable: true,
}

/**
* Adex's default production static plugin.
*
* Includes sirv plus the `/assets` and `/islands` URL-prefix rewrites that
* sirv needs when rooted at a build output directory. This is what runs when
* `kernel.staticServer` is omitted.
*
* Switch away with `adex({ kernel: { staticServer: './my-static.js' } })` or
* disable with `staticServer: false`. Custom factories do not inherit these
* rewrites — they receive the original `req.url`.
*
* @param {object} [options]
* @param {{ assets?: string, islands?: string, client?: string }} [options.paths]
* @param {typeof sirv} [options.serve]
* @param {Parameters<typeof sirv>[1]} [options.options]
* @returns {Array<(req: import('node:http').IncomingMessage, res: import('node:http').ServerResponse, next?: Function) => any>}
*/
export function createStaticMiddlewares({
paths = {},
serve = sirv,
options = {},
} = {}) {
const serveOptions = {
...DEFAULT_SERVE_OPTIONS,
...options,
}

const serverAssets = paths.assets
? serve(paths.assets, serveOptions)
: passthrough

const islandAssets =
paths.islands && existsSync(paths.islands)
? serve(paths.islands, serveOptions)
: passthrough

const clientAssets =
paths.client && existsSync(paths.client)
? serve(paths.client, serveOptions)
: passthrough

return [
async (req, res, next) => {
// sirv is rooted at the directory; strip public URL prefixes for lookup
// @ts-expect-error shared-state between the middlewares
req.__originalUrl = req.url
// @ts-expect-error shared-state between the middlewares
req.url = req.__originalUrl.replace(/(\/?assets\/?)/, '/')
return serverAssets(req, res, next)
},
async (req, res, next) => {
// @ts-expect-error shared-state between the middlewares
req.url = req.__originalUrl.replace(/(\/?islands\/?)/, '/')
return islandAssets(req, res, next)
},
async (req, res, next) => {
// @ts-expect-error shared-state between the middlewares
req.url = req.__originalUrl
return clientAssets(req, res, next)
},
]
}

/** Default static plugin entry (same as `createStaticMiddlewares`). */
export default createStaticMiddlewares

function passthrough(_req, _res, next) {
next()
}

/**
* Virtual module source for `virtual:adex:static-server`.
* Resolves which static plugin to use (`adex/static` by default).
*
* @param {false | string | undefined} staticServer
* @returns {string}
*/
export function resolveStaticServerModuleSource(staticServer) {
if (staticServer === false) {
return `export default function staticServer() {
return []
}
`
}

if (typeof staticServer === 'string' && staticServer.length > 0) {
return `export { default } from ${JSON.stringify(staticServer)}
`
}

return `export { default } from 'adex/static'
`
}
13 changes: 13 additions & 0 deletions adex/src/vite.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,24 @@ import type { Options as FontOptions } from './fonts.js'

export type Adapters = 'node'

export interface AdexKernelOptions {
/**
* Production static plugin.
* - omitted: adex default (`adex/static` — sirv + `/assets`/`/islands` rewrites)
* - `false`: no static middlewares
* - string: module id whose default export is `({ paths }) => middleware | middleware[]`
*
* Custom plugins own mounting and see the original `req.url` (no adex rewrites).
*/
staticServer?: false | string
}

export interface AdexOptions {
fonts?: FontOptions
islands?: boolean
adapter?: Adapters
ssr?: boolean
kernel?: AdexKernelOptions
__clientConfig?: UserConfig
}

Expand Down
Loading