mirror of
https://github.com/withastro/astro.git
synced 2025-03-10 23:01:26 -05:00
fix(vercel): now works with monorepos (#5033)
* Upgraded nft * Handle monorepo better * Changeset * Fixed common ancestor * Fixed outdir
This commit is contained in:
parent
a7d58b4663
commit
bf4ba4aee1
4 changed files with 86 additions and 22 deletions
|
@ -45,7 +45,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@astrojs/webapi": "^1.1.0",
|
||||
"@vercel/nft": "^0.18.2"
|
||||
"@vercel/nft": "^0.22.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"astro": "workspace:*",
|
||||
|
|
|
@ -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 });
|
||||
}
|
||||
|
||||
|
|
|
@ -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');
|
||||
|
||||
const meta = await fs.stat(origin);
|
||||
const isSymlink = (await fs.lstat(origin)).isSymbolicLink();
|
||||
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 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),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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',
|
||||
});
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue