mirror of
https://github.com/withastro/astro.git
synced 2025-01-20 22:12:38 -05:00
feat(vercel): maxDuration config (#8867)
Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca>
This commit is contained in:
parent
42a83ffcfe
commit
b209e53358
9 changed files with 206 additions and 42 deletions
14
.changeset/lazy-actors-enjoy.md
Normal file
14
.changeset/lazy-actors-enjoy.md
Normal file
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
'@astrojs/vercel': minor
|
||||
---
|
||||
|
||||
You can now configure how long your functions can run before timing out.
|
||||
|
||||
```diff
|
||||
export default defineConfig({
|
||||
output: "server",
|
||||
adapter: vercel({
|
||||
+ maxDuration: 60
|
||||
}),
|
||||
});
|
||||
```
|
|
@ -261,6 +261,26 @@ export default defineConfig({
|
|||
});
|
||||
```
|
||||
|
||||
### maxDuration
|
||||
|
||||
**Type:** `number`<br>
|
||||
**Available for:** Serverless
|
||||
|
||||
Use this property to extend or limit the maximum duration (in seconds) that Serverless Functions can run before timing out. See the [Vercel documentation](https://vercel.com/docs/functions/serverless-functions/runtimes#maxduration) for the default and maximum limit for your account plan.
|
||||
|
||||
```diff lang="js"
|
||||
// astro.config.mjs
|
||||
import { defineConfig } from 'astro/config';
|
||||
import vercel from '@astrojs/vercel/serverless';
|
||||
|
||||
export default defineConfig({
|
||||
output: "server",
|
||||
adapter: vercel({
|
||||
+ maxDuration: 60
|
||||
}),
|
||||
});
|
||||
```
|
||||
|
||||
### Function bundling configuration
|
||||
|
||||
The Vercel adapter combines all of your routes into a single function by default.
|
||||
|
|
|
@ -75,15 +75,36 @@ export interface VercelServerlessConfig {
|
|||
* @deprecated
|
||||
*/
|
||||
analytics?: boolean;
|
||||
|
||||
/** Configuration for [Vercel Web Analytics](https://vercel.com/docs/concepts/analytics). */
|
||||
webAnalytics?: VercelWebAnalyticsConfig;
|
||||
|
||||
/** Configuration for [Vercel Speed Insights](https://vercel.com/docs/concepts/speed-insights). */
|
||||
speedInsights?: VercelSpeedInsightsConfig;
|
||||
|
||||
/** Force files to be bundled with your function. This is helpful when you notice missing files. */
|
||||
includeFiles?: string[];
|
||||
|
||||
/** Exclude any files from the bundling process that would otherwise be included. */
|
||||
excludeFiles?: string[];
|
||||
|
||||
/** When enabled, an Image Service powered by the Vercel Image Optimization API will be automatically configured and used in production. In development, the image service specified by devImageService will be used instead. */
|
||||
imageService?: boolean;
|
||||
|
||||
/** Configuration options for [Vercel’s Image Optimization API](https://vercel.com/docs/concepts/image-optimization). See [Vercel’s image configuration documentation](https://vercel.com/docs/build-output-api/v3/configuration#images) for a complete list of supported parameters. */
|
||||
imagesConfig?: VercelImageConfig;
|
||||
|
||||
/** Allows you to configure which image service to use in development when imageService is enabled. */
|
||||
devImageService?: DevImageService;
|
||||
|
||||
/** Whether to create the Vercel Edge middleware from an Astro middleware in your code base. */
|
||||
edgeMiddleware?: boolean;
|
||||
|
||||
/** Whether to split builds into a separate function for each route. */
|
||||
functionPerRoute?: boolean;
|
||||
|
||||
/** The maximum duration (in seconds) that Serverless Functions can run before timing out. See the [Vercel documentation](https://vercel.com/docs/functions/serverless-functions/runtimes#maxduration) for the default and maximum limit for your account plan. */
|
||||
maxDuration?: number;
|
||||
}
|
||||
|
||||
export default function vercelServerless({
|
||||
|
@ -97,7 +118,18 @@ export default function vercelServerless({
|
|||
devImageService = 'sharp',
|
||||
functionPerRoute = false,
|
||||
edgeMiddleware = false,
|
||||
maxDuration,
|
||||
}: VercelServerlessConfig = {}): AstroIntegration {
|
||||
|
||||
if (maxDuration) {
|
||||
if (typeof maxDuration !== 'number') {
|
||||
throw new TypeError(`maxDuration must be a number`, { cause: maxDuration });
|
||||
}
|
||||
if (maxDuration <= 0) {
|
||||
throw new TypeError(`maxDuration must be a positive number`, { cause: maxDuration });
|
||||
}
|
||||
}
|
||||
|
||||
let _config: AstroConfig;
|
||||
let buildTempFolder: URL;
|
||||
let serverEntry: string;
|
||||
|
@ -107,45 +139,16 @@ export default function vercelServerless({
|
|||
|
||||
const NTF_CACHE = Object.create(null);
|
||||
|
||||
async function createFunctionFolder(
|
||||
funcName: string,
|
||||
entry: URL,
|
||||
inc: URL[],
|
||||
logger: AstroIntegrationLogger
|
||||
) {
|
||||
const functionFolder = new URL(`./functions/${funcName}.func/`, _config.outDir);
|
||||
|
||||
// Copy necessary files (e.g. node_modules/)
|
||||
const { handler } = await copyDependenciesToFunction(
|
||||
{
|
||||
entry,
|
||||
outDir: functionFolder,
|
||||
includeFiles: inc,
|
||||
excludeFiles: excludeFiles?.map((file) => new URL(file, _config.root)) || [],
|
||||
logger,
|
||||
},
|
||||
NTF_CACHE
|
||||
);
|
||||
|
||||
// Enable ESM
|
||||
// https://aws.amazon.com/blogs/compute/using-node-js-es-modules-and-top-level-await-in-aws-lambda/
|
||||
await writeJson(new URL(`./package.json`, functionFolder), {
|
||||
type: 'module',
|
||||
});
|
||||
|
||||
// Serverless function config
|
||||
// https://vercel.com/docs/build-output-api/v3#vercel-primitives/serverless-functions/configuration
|
||||
await writeJson(new URL(`./.vc-config.json`, functionFolder), {
|
||||
runtime: getRuntime(),
|
||||
handler,
|
||||
launcherType: 'Nodejs',
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
name: PACKAGE_NAME,
|
||||
hooks: {
|
||||
'astro:config:setup': async ({ command, config, updateConfig, injectScript, logger }) => {
|
||||
|
||||
if (maxDuration && maxDuration > 900) {
|
||||
logger.warn(`maxDuration is set to ${maxDuration} seconds, which is longer than the maximum allowed duration of 900 seconds.`)
|
||||
logger.warn(`Please make sure that your plan allows for this duration. See https://vercel.com/docs/functions/serverless-functions/runtimes#maxduration for more information.`)
|
||||
}
|
||||
|
||||
if (webAnalytics?.enabled || analytics) {
|
||||
if (analytics) {
|
||||
logger.warn(
|
||||
|
@ -261,19 +264,32 @@ You can set functionPerRoute: false to prevent surpassing the limit.`
|
|||
? getRouteFuncName(route)
|
||||
: getFallbackFuncName(entryFile);
|
||||
|
||||
await createFunctionFolder(func, entryFile, filesToInclude, logger);
|
||||
await createFunctionFolder({
|
||||
functionName: func,
|
||||
entry: entryFile,
|
||||
config: _config,
|
||||
logger,
|
||||
NTF_CACHE,
|
||||
includeFiles: filesToInclude,
|
||||
excludeFiles,
|
||||
maxDuration
|
||||
});
|
||||
routeDefinitions.push({
|
||||
src: route.pattern.source,
|
||||
dest: func,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
await createFunctionFolder(
|
||||
'render',
|
||||
new URL(serverEntry, buildTempFolder),
|
||||
filesToInclude,
|
||||
logger
|
||||
);
|
||||
await createFunctionFolder({
|
||||
functionName: 'render',
|
||||
entry: new URL(serverEntry, buildTempFolder),
|
||||
config: _config,
|
||||
logger,
|
||||
NTF_CACHE,
|
||||
includeFiles: filesToInclude,
|
||||
excludeFiles,
|
||||
maxDuration
|
||||
});
|
||||
routeDefinitions.push({ src: '/.*', dest: 'render' });
|
||||
}
|
||||
|
||||
|
@ -314,6 +330,57 @@ You can set functionPerRoute: false to prevent surpassing the limit.`
|
|||
};
|
||||
}
|
||||
|
||||
interface CreateFunctionFolderArgs {
|
||||
functionName: string
|
||||
entry: URL
|
||||
config: AstroConfig
|
||||
logger: AstroIntegrationLogger
|
||||
NTF_CACHE: any
|
||||
includeFiles: URL[]
|
||||
excludeFiles?: string[]
|
||||
maxDuration?: number
|
||||
}
|
||||
|
||||
async function createFunctionFolder({
|
||||
functionName,
|
||||
entry,
|
||||
config,
|
||||
logger,
|
||||
NTF_CACHE,
|
||||
includeFiles,
|
||||
excludeFiles,
|
||||
maxDuration,
|
||||
}: CreateFunctionFolderArgs) {
|
||||
const functionFolder = new URL(`./functions/${functionName}.func/`, config.outDir);
|
||||
|
||||
// Copy necessary files (e.g. node_modules/)
|
||||
const { handler } = await copyDependenciesToFunction(
|
||||
{
|
||||
entry,
|
||||
outDir: functionFolder,
|
||||
includeFiles,
|
||||
excludeFiles: excludeFiles?.map((file) => new URL(file, config.root)) || [],
|
||||
logger,
|
||||
},
|
||||
NTF_CACHE
|
||||
);
|
||||
|
||||
// Enable ESM
|
||||
// https://aws.amazon.com/blogs/compute/using-node-js-es-modules-and-top-level-await-in-aws-lambda/
|
||||
await writeJson(new URL(`./package.json`, functionFolder), {
|
||||
type: 'module',
|
||||
});
|
||||
|
||||
// Serverless function config
|
||||
// https://vercel.com/docs/build-output-api/v3#vercel-primitives/serverless-functions/configuration
|
||||
await writeJson(new URL(`./.vc-config.json`, functionFolder), {
|
||||
runtime: getRuntime(),
|
||||
handler,
|
||||
launcherType: 'Nodejs',
|
||||
maxDuration,
|
||||
});
|
||||
}
|
||||
|
||||
function validateRuntime() {
|
||||
const version = process.version.slice(1); // 'v16.5.0' --> '16.5.0'
|
||||
const major = version.split('.')[0]; // '16.5.0' --> '16'
|
||||
|
|
9
packages/integrations/vercel/test/fixtures/max-duration/astro.config.mjs
vendored
Normal file
9
packages/integrations/vercel/test/fixtures/max-duration/astro.config.mjs
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
import { defineConfig } from 'astro/config';
|
||||
import vercel from '@astrojs/vercel/serverless';
|
||||
|
||||
export default defineConfig({
|
||||
output: "server",
|
||||
adapter: vercel({
|
||||
maxDuration: 60
|
||||
})
|
||||
});
|
10
packages/integrations/vercel/test/fixtures/max-duration/package.json
vendored
Normal file
10
packages/integrations/vercel/test/fixtures/max-duration/package.json
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"name": "@test/vercel-max-duration",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@astrojs/vercel": "workspace:*",
|
||||
"astro": "workspace:*"
|
||||
}
|
||||
}
|
||||
|
8
packages/integrations/vercel/test/fixtures/max-duration/src/pages/one.astro
vendored
Normal file
8
packages/integrations/vercel/test/fixtures/max-duration/src/pages/one.astro
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>One</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>One</h1>
|
||||
</body>
|
||||
</html>
|
8
packages/integrations/vercel/test/fixtures/max-duration/src/pages/two.astro
vendored
Normal file
8
packages/integrations/vercel/test/fixtures/max-duration/src/pages/two.astro
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>Two</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Two</h1>
|
||||
</body>
|
||||
</html>
|
19
packages/integrations/vercel/test/max-duration.test.js
Normal file
19
packages/integrations/vercel/test/max-duration.test.js
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { loadFixture } from './test-utils.js';
|
||||
import { expect } from 'chai';
|
||||
|
||||
describe('maxDuration', () => {
|
||||
/** @type {import('./test-utils.js').Fixture} */
|
||||
let fixture;
|
||||
|
||||
before(async () => {
|
||||
fixture = await loadFixture({
|
||||
root: './fixtures/max-duration/',
|
||||
});
|
||||
await fixture.build();
|
||||
});
|
||||
|
||||
it('makes it to vercel function configuration', async () => {
|
||||
const vcConfig = JSON.parse(await fixture.readFile('../.vercel/output/functions/render.func/.vc-config.json'));
|
||||
expect(vcConfig).to.deep.include({ maxDuration: 60 });
|
||||
});
|
||||
});
|
9
pnpm-lock.yaml
generated
9
pnpm-lock.yaml
generated
|
@ -4605,6 +4605,15 @@ importers:
|
|||
specifier: workspace:*
|
||||
version: link:../../../../../astro
|
||||
|
||||
packages/integrations/vercel/test/fixtures/max-duration:
|
||||
dependencies:
|
||||
'@astrojs/vercel':
|
||||
specifier: workspace:*
|
||||
version: link:../../..
|
||||
astro:
|
||||
specifier: workspace:*
|
||||
version: link:../../../../../astro
|
||||
|
||||
packages/integrations/vercel/test/fixtures/middleware-with-edge-file:
|
||||
dependencies:
|
||||
'@astrojs/vercel':
|
||||
|
|
Loading…
Add table
Reference in a new issue