Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: vercel edge middleware support #7532

Merged
merged 23 commits into from
Jul 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
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
11 changes: 11 additions & 0 deletions .changeset/brown-shrimps-hug.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
'astro': minor
---

The `astro/middleware` module exports a new utility called `trySerializeLocals`.

This utility can be used by adapters to validate their `locals` before sending it
to the Astro middleware.

This function will throw a runtime error if the value passed is not serializable, so
consumers will need to handle that error.
24 changes: 24 additions & 0 deletions .changeset/chilly-pants-fix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
'astro': minor
---

Astro exposes the middleware file path to the integrations in the hook `astro:build:ssr`

```ts
// myIntegration.js
import type { AstroIntegration } from 'astro';
function integration(): AstroIntegration {
return {
name: "fancy-astro-integration",
hooks: {
'astro:build:ssr': ({ middlewareEntryPoint }) => {
if (middlewareEntryPoint) {
// do some operations
}
}
}
}
}
```

The `middlewareEntryPoint` is only defined if the user has created an Astro middleware.
5 changes: 5 additions & 0 deletions .changeset/cool-kids-grin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': patch
---

Correctly track the middleware during the SSR build.
11 changes: 11 additions & 0 deletions .changeset/good-pigs-fetch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
'@astrojs/vercel': minor
---

Support for Vercel Edge Middleware via Astro middleware.

When a project uses the new option Astro `build.excludeMiddleware`, the
`@astrojs/vercel/serverless` adapter will automatically create a Vercel Edge Middleware
that will automatically communicate with the Astro Middleware.

Check the [documentation](https://github.com/withastro/astro/blob/main/packages/integrations/vercel/README.md##vercel-edge-middleware-with-astro-middleware) for more details.
7 changes: 7 additions & 0 deletions .changeset/long-geckos-battle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'astro': minor
---

The `astro/middleware` module exports a new API called `createContext`.

This a low-level API that adapters can use to create a context that can be consumed by middleware functions.
20 changes: 20 additions & 0 deletions .changeset/strong-years-travel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
'astro': minor
---

Introduced a new build option for SSR, called `build.excludeMiddleware`.

```js
// astro.config.mjs
import {defineConfig} from "astro/config";

export default defineConfig({
build: {
excludeMiddleware: true
}
})
```

When enabled, the code that belongs to be middleware **won't** be imported
by the final pages/entry points. The user is responsible for importing it and
calling it manually.
25 changes: 25 additions & 0 deletions packages/astro/src/@types/astro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -849,6 +849,27 @@ export interface AstroUserConfig {
* ```
*/
split?: boolean;

/**
* @docs
* @name build.excludeMiddleware
* @type {boolean}
* @default {false}
* @version 2.8.0
* @description
* Defines whether or not any SSR middleware code will be bundled when built.
*
* When enabled, middleware code is not bundled and imported by all pages during the build. To instead execute and import middleware code manually, set `build.excludeMiddleware: true`:
*
* ```js
* {
* build: {
* excludeMiddleware: true
* }
* }
* ```
*/
excludeMiddleware?: boolean;
};

/**
Expand Down Expand Up @@ -1842,6 +1863,10 @@ export interface AstroIntegration {
* the physical file you should import.
*/
entryPoints: Map<RouteData, URL>;
/**
* File path of the emitted middleware
*/
middlewareEntryPoint: URL | undefined;
}) => void | Promise<void>;
'astro:build:start'?: () => void | Promise<void>;
'astro:build:setup'?: (options: {
Expand Down
22 changes: 20 additions & 2 deletions packages/astro/src/core/build/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { AstroConfig, AstroSettings, ManifestData, RuntimeMode } from '../../@types/astro';

import fs from 'fs';
import * as colors from 'kleur/colors';
import { performance } from 'perf_hooks';
Expand All @@ -12,7 +11,7 @@ import {
runHookConfigSetup,
} from '../../integrations/index.js';
import { createVite } from '../create-vite.js';
import { debug, info, levels, timerMessage, type LogOptions } from '../logger/core.js';
import { debug, info, warn, levels, timerMessage, type LogOptions } from '../logger/core.js';
import { printHelp } from '../messages.js';
import { apply as applyPolyfill } from '../polyfill.js';
import { RouteCache } from '../render/route-cache.js';
Expand Down Expand Up @@ -211,6 +210,25 @@ class AstroBuilder {
`the outDir cannot be the root folder. Please build to a folder such as dist.`
);
}

if (config.build.split === true) {
if (config.output === 'static') {
warn(
this.logging,
'configuration',
'The option `build.split` won\'t take effect, because `output` is not `"server"` or `"hybrid"`.'
);
}
}
if (config.build.excludeMiddleware === true) {
if (config.output === 'static') {
warn(
this.logging,
'configuration',
'The option `build.excludeMiddleware` won\'t take effect, because `output` is not `"server"` or `"hybrid"`.'
);
}
}
}

/** Stats */
Expand Down
1 change: 1 addition & 0 deletions packages/astro/src/core/build/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ export interface BuildInternals {
entryPoints: Map<RouteData, URL>;
ssrSplitEntryChunks: Map<string, Rollup.OutputChunk>;
componentMetadata: SSRResult['componentMetadata'];
middlewareEntryPoint?: URL;
}

/**
Expand Down
2 changes: 1 addition & 1 deletion packages/astro/src/core/build/plugins/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export function registerAllPlugins({ internals, options, register }: AstroBuildP
register(pluginAnalyzer(internals));
register(pluginInternals(internals));
register(pluginRenderers(options));
register(pluginMiddleware(options));
register(pluginMiddleware(options, internals));
register(pluginPages(options, internals));
register(pluginCSS(options, internals));
register(astroHeadBuildPlugin(internals));
Expand Down
33 changes: 30 additions & 3 deletions packages/astro/src/core/build/plugins/plugin-middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,17 @@ import { MIDDLEWARE_PATH_SEGMENT_NAME } from '../../constants.js';
import { addRollupInput } from '../add-rollup-input.js';
import type { AstroBuildPlugin } from '../plugin';
import type { StaticBuildOptions } from '../types';
import type { BuildInternals } from '../internal';

export const MIDDLEWARE_MODULE_ID = '@astro-middleware';

const EMPTY_MIDDLEWARE = '\0empty-middleware';

export function vitePluginMiddleware(opts: StaticBuildOptions): VitePlugin {
export function vitePluginMiddleware(
opts: StaticBuildOptions,
internals: BuildInternals
): VitePlugin {
let resolvedMiddlewareId: string;
return {
name: '@astro/plugin-middleware',

Expand All @@ -22,6 +27,7 @@ export function vitePluginMiddleware(opts: StaticBuildOptions): VitePlugin {
`${opts.settings.config.srcDir.pathname}/${MIDDLEWARE_PATH_SEGMENT_NAME}`
);
if (middlewareId) {
resolvedMiddlewareId = middlewareId.id;
return middlewareId.id;
} else {
return EMPTY_MIDDLEWARE;
Expand All @@ -35,18 +41,39 @@ export function vitePluginMiddleware(opts: StaticBuildOptions): VitePlugin {
load(id) {
if (id === EMPTY_MIDDLEWARE) {
return 'export const onRequest = undefined';
} else if (id === resolvedMiddlewareId) {
this.emitFile({
type: 'chunk',
preserveSignature: 'strict',
fileName: 'middleware.mjs',
id,
});
}
},

writeBundle(_, bundle) {
for (const [chunkName, chunk] of Object.entries(bundle)) {
if (chunk.type === 'asset') {
continue;
}
if (chunk.fileName === 'middleware.mjs') {
internals.middlewareEntryPoint = new URL(chunkName, opts.settings.config.build.server);
}
}
},
};
}

export function pluginMiddleware(opts: StaticBuildOptions): AstroBuildPlugin {
export function pluginMiddleware(
opts: StaticBuildOptions,
internals: BuildInternals
): AstroBuildPlugin {
return {
build: 'ssr',
hooks: {
'build:before': () => {
return {
vitePlugin: vitePluginMiddleware(opts),
vitePlugin: vitePluginMiddleware(opts, internals),
};
},
},
Expand Down
11 changes: 7 additions & 4 deletions packages/astro/src/core/build/plugins/plugin-pages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,13 @@ function vitePluginPages(opts: StaticBuildOptions, internals: BuildInternals): V
imports.push(`import { renderers } from "${RENDERERS_MODULE_ID}";`);
exports.push(`export { renderers };`);

const middlewareModule = await this.resolve(MIDDLEWARE_MODULE_ID);
if (middlewareModule) {
imports.push(`import { onRequest } from "${middlewareModule.id}";`);
exports.push(`export { onRequest };`);
// The middleware should not be imported by the pages
if (!opts.settings.config.build.excludeMiddleware) {
const middlewareModule = await this.resolve(MIDDLEWARE_MODULE_ID);
if (middlewareModule) {
imports.push(`import { onRequest } from "${middlewareModule.id}";`);
exports.push(`export { onRequest };`);
}
}

return `${imports.join('\n')}${exports.join('\n')}`;
Expand Down
2 changes: 2 additions & 0 deletions packages/astro/src/core/build/plugins/plugin-ssr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ export function pluginSSR(
manifest,
logging: options.logging,
entryPoints: internals.entryPoints,
middlewareEntryPoint: internals.middlewareEntryPoint,
});
const code = injectManifest(manifest, internals.ssrEntryChunk);
mutate(internals.ssrEntryChunk, 'server', code);
Expand Down Expand Up @@ -260,6 +261,7 @@ export function pluginSSRSplit(
manifest,
logging: options.logging,
entryPoints: internals.entryPoints,
middlewareEntryPoint: internals.middlewareEntryPoint,
});
for (const [, chunk] of internals.ssrSplitEntryChunks) {
const code = injectManifest(manifest, chunk);
Expand Down
3 changes: 0 additions & 3 deletions packages/astro/src/core/build/static-build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import { generatePages } from './generate.js';
import { trackPageData } from './internal.js';
import { createPluginContainer, type AstroBuildPluginContainer } from './plugin.js';
import { registerAllPlugins } from './plugins/index.js';
import { MIDDLEWARE_MODULE_ID } from './plugins/plugin-middleware.js';
import { ASTRO_PAGE_RESOLVED_MODULE_ID } from './plugins/plugin-pages.js';
import { RESOLVED_RENDERERS_MODULE_ID } from './plugins/plugin-renderers.js';
import { RESOLVED_SPLIT_MODULE_ID, SSR_VIRTUAL_MODULE_ID } from './plugins/plugin-ssr.js';
Expand Down Expand Up @@ -183,8 +182,6 @@ async function ssrBuild(
);
} else if (chunkInfo.facadeModuleId?.startsWith(RESOLVED_SPLIT_MODULE_ID)) {
return makeSplitEntryPointFileName(chunkInfo.facadeModuleId, routes);
} else if (chunkInfo.facadeModuleId === MIDDLEWARE_MODULE_ID) {
return 'middleware.mjs';
} else if (chunkInfo.facadeModuleId === SSR_VIRTUAL_MODULE_ID) {
return opts.settings.config.build.serverEntry;
} else if (chunkInfo.facadeModuleId === RESOLVED_RENDERERS_MODULE_ID) {
Expand Down
9 changes: 9 additions & 0 deletions packages/astro/src/core/config/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const ASTRO_CONFIG_DEFAULTS = {
redirects: true,
inlineStylesheets: 'never',
split: false,
excludeMiddleware: false,
},
compressHTML: false,
server: {
Expand Down Expand Up @@ -122,6 +123,10 @@ export const AstroConfigSchema = z.object({
.default(ASTRO_CONFIG_DEFAULTS.build.inlineStylesheets),

split: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.build.split),
excludeMiddleware: z
.boolean()
.optional()
.default(ASTRO_CONFIG_DEFAULTS.build.excludeMiddleware),
})
.optional()
.default({}),
Expand Down Expand Up @@ -283,6 +288,10 @@ export function createRelativeSchema(cmd: string, fileProtocolRoot: URL) {
.default(ASTRO_CONFIG_DEFAULTS.build.inlineStylesheets),

split: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.build.split),
excludeMiddleware: z
.boolean()
.optional()
.default(ASTRO_CONFIG_DEFAULTS.build.excludeMiddleware),
})
.optional()
.default({}),
Expand Down
21 changes: 14 additions & 7 deletions packages/astro/src/core/endpoint/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,26 @@ type EndpointCallResult =
response: Response;
};

type CreateAPIContext = {
request: Request;
params: Params;
site?: string;
props: Record<string, any>;
adapterName?: string;
};

/**
* Creates a context that holds all the information needed to handle an Astro endpoint.
*
* @param {CreateAPIContext} payload
*/
export function createAPIContext({
request,
params,
site,
props,
adapterName,
}: {
request: Request;
params: Params;
site?: string;
props: Record<string, any>;
adapterName?: string;
}): APIContext {
}: CreateAPIContext): APIContext {
const context = {
cookies: new AstroCookies(request),
request,
Expand Down
Loading