Skip to content

Commit

Permalink
chore(vercel): delete request response conversion logic (#9583)
Browse files Browse the repository at this point in the history
* refactor

* add changeset

* bump peer dependencies
  • Loading branch information
lilnasy authored Jan 12, 2024
1 parent 3285dad commit 8363b75
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 274 deletions.
7 changes: 7 additions & 0 deletions .changeset/early-cups-poke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@astrojs/vercel": major
---

**Breaking**: Minimum required Astro version is now 4.2.0.
Reorganizes internals to be more maintainable.
---
2 changes: 1 addition & 1 deletion packages/integrations/node/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
"server-destroy": "^1.0.1"
},
"peerDependencies": {
"astro": "^4.0.0"
"astro": "^4.2.0"
},
"devDependencies": {
"@types/node": "^18.17.8",
Expand Down
2 changes: 1 addition & 1 deletion packages/integrations/vercel/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
"web-vitals": "^3.4.0"
},
"peerDependencies": {
"astro": "^4.0.2"
"astro": "^4.2.0"
},
"devDependencies": {
"@types/set-cookie-parser": "^2.4.6",
Expand Down
95 changes: 50 additions & 45 deletions packages/integrations/vercel/src/serverless/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ export default function vercelServerless({
webAnalytics,
speedInsights,
includeFiles,
excludeFiles,
excludeFiles = [],
imageService,
imagesConfig,
devImageService = 'sharp',
Expand Down Expand Up @@ -189,9 +189,10 @@ export default function vercelServerless({
'astro:config:done': ({ setAdapter, config, logger }) => {
if (functionPerRoute === true) {
logger.warn(
`Vercel's hosting plans might have limits to the number of functions you can create.
Make sure to check your plan carefully to avoid incurring additional costs.
You can set functionPerRoute: false to prevent surpassing the limit.`
`\n` +
`\tVercel's hosting plans might have limits to the number of functions you can create.\n` +
`\tMake sure to check your plan carefully to avoid incurring additional costs.\n` +
`\tYou can set functionPerRoute: false to prevent surpassing the limit.\n`
);
}
setAdapter(getAdapter({ functionPerRoute, edgeMiddleware }));
Expand All @@ -205,7 +206,6 @@ You can set functionPerRoute: false to prevent surpassing the limit.`
);
}
},

'astro:build:ssr': async ({ entryPoints, middlewareEntryPoint }) => {
_entryPoints = entryPoints;
if (middlewareEntryPoint) {
Expand All @@ -223,7 +223,6 @@ You can set functionPerRoute: false to prevent surpassing the limit.`
extraFilesToInclude.push(bundledMiddlewarePath);
}
},

'astro:build:done': async ({ routes, logger }) => {
// Merge any includes from `vite.assetsInclude
if (_config.vite.assetsInclude) {
Expand All @@ -245,7 +244,7 @@ You can set functionPerRoute: false to prevent surpassing the limit.`
const filesToInclude = includeFiles?.map((file) => new URL(file, _config.root)) || [];
filesToInclude.push(...extraFilesToInclude);

validateRuntime();
const runtime = getRuntime(process, logger);

// Multiple entrypoint support
if (_entryPoints.size) {
Expand All @@ -263,6 +262,7 @@ You can set functionPerRoute: false to prevent surpassing the limit.`

await createFunctionFolder({
functionName: func,
runtime,
entry: entryFile,
config: _config,
logger,
Expand All @@ -279,6 +279,7 @@ You can set functionPerRoute: false to prevent surpassing the limit.`
} else {
await createFunctionFolder({
functionName: 'render',
runtime,
entry: new URL(serverEntry, buildTempFolder),
config: _config,
logger,
Expand Down Expand Up @@ -342,19 +343,23 @@ You can set functionPerRoute: false to prevent surpassing the limit.`
};
}

type Runtime = `nodejs${string}.x`;

interface CreateFunctionFolderArgs {
functionName: string;
runtime: Runtime;
entry: URL;
config: AstroConfig;
logger: AstroIntegrationLogger;
NTF_CACHE: any;
includeFiles: URL[];
excludeFiles?: string[];
excludeFiles: string[];
maxDuration: number | undefined;
}

async function createFunctionFolder({
functionName,
runtime,
entry,
config,
logger,
Expand All @@ -363,75 +368,75 @@ async function createFunctionFolder({
excludeFiles,
maxDuration,
}: CreateFunctionFolderArgs) {
// .vercel/output/functions/<name>.func/
const functionFolder = new URL(`./functions/${functionName}.func/`, config.outDir);
const packageJson = new URL(`./functions/${functionName}.func/package.json`, config.outDir);
const vcConfig = new URL(`./functions/${functionName}.func/.vc-config.json`, 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)) || [],
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',
});
await writeJson(packageJson, { 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(),
await writeJson(vcConfig, {
runtime,
handler,
launcherType: 'Nodejs',
maxDuration,
supportsResponseStreaming: true,
});
}

function validateRuntime() {
const version = process.version.slice(1); // 'v16.5.0' --> '16.5.0'
const major = version.split('.')[0]; // '16.5.0' --> '16'
function getRuntime(process: NodeJS.Process, logger: AstroIntegrationLogger): Runtime {
const version = process.version.slice(1); // 'v18.19.0' --> '18.19.0'
const major = version.split('.')[0]; // '18.19.0' --> '18'
const support = SUPPORTED_NODE_VERSIONS[major];
if (support === undefined) {
console.warn(
`[${PACKAGE_NAME}] The local Node.js version (${major}) is not supported by Vercel Serverless Functions.`
logger.warn(
`\n` +
`\tThe local Node.js version (${major}) is not supported by Vercel Serverless Functions.\n` +
`\tYour project will use Node.js 18 as the runtime instead.\n` +
`\tConsider switching your local version to 18.\n`
);
console.warn(`[${PACKAGE_NAME}] Your project will use Node.js 18 as the runtime instead.`);
console.warn(`[${PACKAGE_NAME}] Consider switching your local version to 18.`);
return;
}
if (support.status === 'beta') {
console.warn(
`[${PACKAGE_NAME}] The local Node.js version (${major}) is currently in beta for Vercel Serverless Functions.`
if (support.status === 'current') {
return `nodejs${major}.x`;
} else if (support?.status === 'beta') {
logger.warn(
`Your project is being built for Node.js ${major} as the runtime, which is currently in beta for Vercel Serverless Functions.`
);
console.warn(`[${PACKAGE_NAME}] Make sure to update your Vercel settings to use ${major}.`);
return;
}
if (support.status === 'deprecated') {
console.warn(
`[${PACKAGE_NAME}] Your project is being built for Node.js ${major} as the runtime.`
return `nodejs${major}.x`;
} else if (support.status === 'deprecated') {
const removeDate = new Intl.DateTimeFormat(undefined, { dateStyle: 'long' }).format(
support.removal
);
console.warn(
`[${PACKAGE_NAME}] This version is deprecated by Vercel Serverless Functions, and scheduled to be disabled on ${new Intl.DateTimeFormat(
undefined,
{ dateStyle: 'long' }
).format(support.removal)}.`
logger.warn(
`\n` +
`\tYour project is being built for Node.js ${major} as the runtime.\n` +
`\tThis version is deprecated by Vercel Serverless Functions, and scheduled to be disabled on ${removeDate}.\n` +
`\tConsider upgrading your local version to 18.\n`
);
return `nodejs${major}.x`;
} else {
logger.warn(
`\n` +
`\tThe local Node.js version (${major}) is not supported by Vercel Serverless Functions.\n` +
`\tYour project will use Node.js 18 as the runtime instead.\n` +
`\tConsider switching your local version to 18.\n`
);
console.warn(`[${PACKAGE_NAME}] Consider upgrading your local version to 18.`);
}
}

function getRuntime() {
const version = process.version.slice(1); // 'v16.5.0' --> '16.5.0'
const major = version.split('.')[0]; // '16.5.0' --> '16'
const support = SUPPORTED_NODE_VERSIONS[major];
if (support === undefined) {
return 'nodejs18.x';
}
return `nodejs${major}.x`;
Expand Down
34 changes: 10 additions & 24 deletions packages/integrations/vercel/src/serverless/entrypoint.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,21 @@
import type { SSRManifest } from 'astro';
import { App } from 'astro/app';
import { applyPolyfills } from 'astro/app/node';
import { applyPolyfills, NodeApp } from 'astro/app/node';
import type { IncomingMessage, ServerResponse } from 'node:http';

import { ASTRO_LOCALS_HEADER } from './adapter.js';
import { getRequest, setResponse } from './request-transform.js';

applyPolyfills();

export const createExports = (manifest: SSRManifest) => {
const app = new App(manifest);

const app = new NodeApp(manifest);
const handler = async (req: IncomingMessage, res: ServerResponse) => {
let request: Request;

try {
request = await getRequest(`https://${req.headers.host}`, req);
} catch (err: any) {
res.statusCode = err.status || 400;
return res.end(err.reason || 'Invalid request body');
}

let routeData = app.match(request);
let locals = {};
if (request.headers.has(ASTRO_LOCALS_HEADER)) {
let localsAsString = request.headers.get(ASTRO_LOCALS_HEADER);
if (localsAsString) {
locals = JSON.parse(localsAsString);
}
}
await setResponse(app, res, await app.render(request, { routeData, locals }));
const clientAddress = req.headers['x-forwarded-for'] as string | undefined;
const localsHeader = req.headers[ASTRO_LOCALS_HEADER]
const locals =
typeof localsHeader === "string" ? JSON.parse(localsHeader)
: Array.isArray(localsHeader) ? JSON.parse(localsHeader[0])
: {};
const webResponse = await app.render(req, { locals, clientAddress })
await NodeApp.writeResponse(webResponse, res);
};

return { default: handler };
Expand Down
Loading

0 comments on commit 8363b75

Please sign in to comment.