summaryrefslogtreecommitdiff
path: root/packages/integrations
diff options
context:
space:
mode:
Diffstat (limited to 'packages/integrations')
-rw-r--r--packages/integrations/vercel/package.json2
-rw-r--r--packages/integrations/vercel/src/lib/fs.ts6
-rw-r--r--packages/integrations/vercel/src/lib/nft.ts84
-rw-r--r--packages/integrations/vercel/src/serverless/adapter.ts16
4 files changed, 86 insertions, 22 deletions
diff --git a/packages/integrations/vercel/package.json b/packages/integrations/vercel/package.json
index 1d57806ba..37b16a388 100644
--- a/packages/integrations/vercel/package.json
+++ b/packages/integrations/vercel/package.json
@@ -45,7 +45,7 @@
},
"dependencies": {
"@astrojs/webapi": "^1.1.0",
- "@vercel/nft": "^0.18.2"
+ "@vercel/nft": "^0.22.1"
},
"devDependencies": {
"astro": "workspace:*",
diff --git a/packages/integrations/vercel/src/lib/fs.ts b/packages/integrations/vercel/src/lib/fs.ts
index d7a833cd0..64c4c69ba 100644
--- a/packages/integrations/vercel/src/lib/fs.ts
+++ b/packages/integrations/vercel/src/lib/fs.ts
@@ -5,8 +5,12 @@ export async function writeJson<T>(path: PathLike, data: T) {
await fs.writeFile(path, JSON.stringify(data), { encoding: 'utf-8' });
}
-export async function emptyDir(dir: PathLike): Promise<void> {
+export async function removeDir(dir: PathLike) {
await fs.rm(dir, { recursive: true, force: true, maxRetries: 3 });
+}
+
+export async function emptyDir(dir: PathLike): Promise<void> {
+ await removeDir(dir);
await fs.mkdir(dir, { recursive: true });
}
diff --git a/packages/integrations/vercel/src/lib/nft.ts b/packages/integrations/vercel/src/lib/nft.ts
index b18cb73ef..ba3677583 100644
--- a/packages/integrations/vercel/src/lib/nft.ts
+++ b/packages/integrations/vercel/src/lib/nft.ts
@@ -1,38 +1,90 @@
import { nodeFileTrace } from '@vercel/nft';
import * as fs from 'node:fs/promises';
+import nodePath from 'node:path';
import { fileURLToPath } from 'node:url';
export async function copyDependenciesToFunction(
- root: URL,
- functionFolder: URL,
- serverEntry: string
-) {
- const entryPath = fileURLToPath(new URL(`./${serverEntry}`, functionFolder));
+ entry: URL,
+ outDir: URL
+): Promise<{ handler: string }> {
+ const entryPath = fileURLToPath(entry);
+
+ // Get root of folder of the system (like C:\ on Windows or / on Linux)
+ let base = entry;
+ while (fileURLToPath(base) !== fileURLToPath(new URL('../', base))) {
+ base = new URL('../', base);
+ }
const result = await nodeFileTrace([entryPath], {
- base: fileURLToPath(root),
+ base: fileURLToPath(base),
});
- for (const file of result.fileList) {
- if (file.startsWith('.vercel/')) continue;
- const origin = new URL(file, root);
- const dest = new URL(file, functionFolder);
+ if (result.fileList.size === 0) throw new Error('[@astrojs/vercel] No files found');
+
+ for (const error of result.warnings) {
+ if (error.message.startsWith('Failed to resolve dependency')) {
+ const [, module, file] = /Cannot find module '(.+?)' loaded from (.+)/.exec(error.message)!;
+
+ // The import(astroRemark) sometimes fails to resolve, but it's not a problem
+ if (module === '@astrojs/') continue;
+
+ if (entryPath === file) {
+ console.warn(
+ `[@astrojs/vercel] The module "${module}" couldn't be resolved. This may not be a problem, but it's worth checking.`
+ );
+ } else {
+ console.warn(
+ `[@astrojs/vercel] The module "${module}" inside the file "${file}" couldn't be resolved. This may not be a problem, but it's worth checking.`
+ );
+ }
+ } else {
+ throw error;
+ }
+ }
- const meta = await fs.stat(origin);
- const isSymlink = (await fs.lstat(origin)).isSymbolicLink();
+ const fileList = [...result.fileList];
+
+ let commonAncestor = nodePath.dirname(fileList[0]);
+ for (const file of fileList.slice(1)) {
+ while (!file.startsWith(commonAncestor)) {
+ commonAncestor = nodePath.dirname(commonAncestor);
+ }
+ }
+
+ for (const file of fileList) {
+ const origin = new URL(file, base);
+ const dest = new URL(nodePath.relative(commonAncestor, file), outDir);
+
+ const realpath = await fs.realpath(origin);
+ const isSymlink = realpath !== fileURLToPath(origin);
+ const isDir = (await fs.stat(origin)).isDirectory();
// Create directories recursively
- if (meta.isDirectory() && !isSymlink) {
+ if (isDir && !isSymlink) {
await fs.mkdir(new URL('..', dest), { recursive: true });
} else {
await fs.mkdir(new URL('.', dest), { recursive: true });
}
if (isSymlink) {
- const link = await fs.readlink(origin);
- await fs.symlink(link, dest, meta.isDirectory() ? 'dir' : 'file');
- } else {
+ const realdest = fileURLToPath(
+ new URL(
+ nodePath.relative(nodePath.join(fileURLToPath(base), commonAncestor), realpath),
+ outDir
+ )
+ );
+ await fs.symlink(
+ nodePath.relative(fileURLToPath(new URL('.', dest)), realdest),
+ dest,
+ isDir ? 'dir' : 'file'
+ );
+ } else if (!isDir) {
await fs.copyFile(origin, dest);
}
}
+
+ return {
+ // serverEntry location inside the outDir
+ handler: nodePath.relative(nodePath.join(fileURLToPath(base), commonAncestor), entryPath),
+ };
}
diff --git a/packages/integrations/vercel/src/serverless/adapter.ts b/packages/integrations/vercel/src/serverless/adapter.ts
index 970141cd0..f5ae4e8cb 100644
--- a/packages/integrations/vercel/src/serverless/adapter.ts
+++ b/packages/integrations/vercel/src/serverless/adapter.ts
@@ -1,6 +1,6 @@
import type { AstroAdapter, AstroConfig, AstroIntegration } from 'astro';
-import { getVercelOutput, writeJson } from '../lib/fs.js';
+import { getVercelOutput, removeDir, writeJson } from '../lib/fs.js';
import { copyDependenciesToFunction } from '../lib/nft.js';
import { getRedirects } from '../lib/redirects.js';
@@ -16,6 +16,7 @@ function getAdapter(): AstroAdapter {
export default function vercelEdge(): AstroIntegration {
let _config: AstroConfig;
+ let buildTempFolder: URL;
let functionFolder: URL;
let serverEntry: string;
@@ -39,11 +40,18 @@ export default function vercelEdge(): AstroIntegration {
'astro:build:start': async ({ buildConfig }) => {
buildConfig.serverEntry = serverEntry = 'entry.js';
buildConfig.client = new URL('./static/', _config.outDir);
- buildConfig.server = functionFolder = new URL('./functions/render.func/', _config.outDir);
+ buildConfig.server = buildTempFolder = new URL('./dist/', _config.root);
+ functionFolder = new URL('./functions/render.func/', _config.outDir);
},
'astro:build:done': async ({ routes }) => {
// Copy necessary files (e.g. node_modules/)
- await copyDependenciesToFunction(_config.root, functionFolder, serverEntry);
+ const { handler } = await copyDependenciesToFunction(
+ new URL(serverEntry, buildTempFolder),
+ functionFolder
+ );
+
+ // Remove temporary folder
+ await removeDir(buildTempFolder);
// Enable ESM
// https://aws.amazon.com/blogs/compute/using-node-js-es-modules-and-top-level-await-in-aws-lambda/
@@ -55,7 +63,7 @@ export default function vercelEdge(): AstroIntegration {
// https://vercel.com/docs/build-output-api/v3#vercel-primitives/serverless-functions/configuration
await writeJson(new URL(`./.vc-config.json`, functionFolder), {
runtime: getRuntime(),
- handler: serverEntry,
+ handler,
launcherType: 'Nodejs',
});