fix(@astrojs/vercel): slowness and symbolic link (#8348)

This commit is contained in:
Emanuele Stoppa 2023-09-01 14:33:04 +01:00 committed by GitHub
parent f21599671a
commit 5f2c55bb54
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 64 additions and 27 deletions

View file

@ -0,0 +1,6 @@
---
'@astrojs/vercel': patch
---
- Cache result during bundling, to speed up the process of multiple functions;
- Avoid creating multiple symbolic links of the dependencies when building the project with `funcitonPerRoute` enabled;

View file

@ -1,5 +1,6 @@
import type { PathLike } from 'node:fs';
import * as fs from 'node:fs/promises';
import { existsSync } from 'node:fs';
import nodePath from 'node:path';
import { fileURLToPath } from 'node:url';
@ -74,11 +75,13 @@ export async function copyFilesToFunction(
if (isSymlink) {
const realdest = fileURLToPath(new URL(nodePath.relative(commonAncestor, realpath), outDir));
await fs.symlink(
nodePath.relative(fileURLToPath(new URL('.', dest)), realdest),
dest,
isDir ? 'dir' : 'file'
);
const target = nodePath.relative(fileURLToPath(new URL('.', dest)), realdest);
// NOTE: when building function per route, dependencies are linked at the first run, then there's no need anymore to do that once more.
// So we check if the destination already exists. If it does, move on.
// Symbolic links here are usually dependencies and not user code. Symbolic links exist because of the pnpm strategy.
if (!existsSync(dest)) {
await fs.symlink(target, dest, isDir ? 'dir' : 'file');
}
} else if (!isDir) {
await fs.copyFile(origin, dest);
}

View file

@ -1,19 +1,28 @@
import { relative as relativePath } from 'node:path';
import { fileURLToPath } from 'node:url';
import { relative } from 'node:path';
import { copyFilesToFunction } from './fs.js';
import type { AstroIntegrationLogger } from 'astro';
export async function copyDependenciesToFunction({
entry,
outDir,
includeFiles,
excludeFiles,
}: {
entry: URL;
outDir: URL;
includeFiles: URL[];
excludeFiles: URL[];
}): Promise<{ handler: string }> {
export async function copyDependenciesToFunction(
{
entry,
outDir,
includeFiles,
excludeFiles,
logger,
}: {
entry: URL;
outDir: URL;
includeFiles: URL[];
excludeFiles: URL[];
logger: AstroIntegrationLogger;
},
// we want to pass the caching by reference, and not by value
cache: object
): Promise<{ handler: string }> {
const entryPath = fileURLToPath(entry);
logger.info(`Bundling function ${relative(fileURLToPath(outDir), entryPath)}`);
// Get root of folder of the system (like C:\ on Windows or / on Linux)
let base = entry;
@ -31,6 +40,7 @@ export async function copyDependenciesToFunction({
// If you have a route of /dev this appears in source and NFT will try to
// scan your local /dev :8
ignore: ['/dev/**'],
cache,
});
for (const error of result.warnings) {

View file

@ -1,4 +1,10 @@
import type { AstroAdapter, AstroConfig, AstroIntegration, RouteData } from 'astro';
import type {
AstroAdapter,
AstroConfig,
AstroIntegration,
RouteData,
AstroIntegrationLogger,
} from 'astro';
import { AstroError } from 'astro/errors';
import glob from 'fast-glob';
import { basename } from 'node:path';
@ -78,16 +84,27 @@ export default function vercelServerless({
// Extra files to be merged with `includeFiles` during build
const extraFilesToInclude: URL[] = [];
async function createFunctionFolder(funcName: string, entry: URL, inc: URL[]) {
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)) || [],
});
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/
@ -167,7 +184,7 @@ You can set functionPerRoute: false to prevent surpassing the limit.`
}
},
'astro:build:done': async ({ routes }) => {
'astro:build:done': async ({ routes, logger }) => {
// Merge any includes from `vite.assetsInclude
if (_config.vite.assetsInclude) {
const mergeGlobbedIncludes = (globPattern: unknown) => {
@ -192,7 +209,7 @@ You can set functionPerRoute: false to prevent surpassing the limit.`
if (_entryPoints.size) {
for (const [route, entryFile] of _entryPoints) {
const func = basename(entryFile.toString()).replace(/\.mjs$/, '');
await createFunctionFolder(func, entryFile, filesToInclude);
await createFunctionFolder(func, entryFile, filesToInclude, logger);
routeDefinitions.push({
src: route.pattern.source,
dest: func,
@ -202,7 +219,8 @@ You can set functionPerRoute: false to prevent surpassing the limit.`
await createFunctionFolder(
'render',
new URL(serverEntry, buildTempFolder),
filesToInclude
filesToInclude,
logger
);
routeDefinitions.push({ src: '/.*', dest: 'render' });
}