Skip to content

Commit 60370cc

Browse files
authored
Use @vercel/nft to create lambdas (#4969)
* revert to using cjs for lambda * use nodeFileTrace to build lambda * lint * fix unrelated lint error * update changesets * update turbo config * add some logging to try and figure out build failures * Revert "add some logging to try and figure out build failures" This reverts commit 6f699e6. * bump turbo * fix some tangentially-related typescript stuff
1 parent e675f64 commit 60370cc

File tree

9 files changed

+382
-116
lines changed

9 files changed

+382
-116
lines changed

Diff for: .changeset/lovely-seahorses-sell.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@sveltejs/adapter-vercel': patch
3+
---
4+
5+
Use @vercel/nft to include dependencies in lambda without bundling with esbuild, when using v3 build output API

Diff for: .prettierrc

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
{
2121
"files": [
2222
"**/CHANGELOG.md",
23+
"**/.svelte-kit/**",
2324
"packages/kit/src/packaging/test/fixtures/**/expected/**/*",
2425
"packages/kit/src/packaging/test/watch/expected/**/*",
2526
"packages/kit/src/packaging/test/watch/package/**/*",

Diff for: package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
"svelte-preprocess": "^4.9.8",
5151
"svelte2tsx": "~0.5.0",
5252
"tiny-glob": "^0.2.9",
53-
"turbo": "^1.2.6",
53+
"turbo": "^1.2.12",
5454
"typescript": "~4.7.2",
5555
"uvu": "^0.5.2"
5656
},

Diff for: packages/adapter-vercel/index.js

+90-33
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import { mkdirSync, writeFileSync } from 'fs';
2-
import { dirname, posix } from 'path';
1+
import fs from 'fs';
2+
import path from 'path';
33
import { fileURLToPath } from 'url';
4+
import { nodeFileTrace } from '@vercel/nft';
45
import esbuild from 'esbuild';
56

67
// rules for clean URLs and trailing slash handling,
@@ -80,6 +81,8 @@ const redirects = {
8081
]
8182
};
8283

84+
const files = fileURLToPath(new URL('./files', import.meta.url).href);
85+
8386
/** @type {import('.')} **/
8487
export default function ({ external = [], edge, split } = {}) {
8588
return {
@@ -113,16 +116,14 @@ async function v1(builder, external) {
113116
builder.rimraf(dir);
114117
builder.rimraf(tmp);
115118

116-
const files = fileURLToPath(new URL('./files', import.meta.url).href);
117-
118119
const dirs = {
119120
static: `${dir}/static`,
120121
lambda: `${dir}/functions/node/render`
121122
};
122123

123124
builder.log.minor('Generating serverless function...');
124125

125-
const relativePath = posix.relative(tmp, builder.getServerDirectory());
126+
const relativePath = path.posix.relative(tmp, builder.getServerDirectory());
126127

127128
builder.copy(`${files}/serverless.js`, `${tmp}/serverless.js`, {
128129
replace: {
@@ -131,7 +132,7 @@ async function v1(builder, external) {
131132
}
132133
});
133134

134-
writeFileSync(
135+
fs.writeFileSync(
135136
`${tmp}/manifest.js`,
136137
`export const manifest = ${builder.generateManifest({
137138
relativePath
@@ -148,7 +149,7 @@ async function v1(builder, external) {
148149
format: 'cjs'
149150
});
150151

151-
writeFileSync(`${dirs.lambda}/package.json`, JSON.stringify({ type: 'commonjs' }));
152+
fs.writeFileSync(`${dirs.lambda}/package.json`, JSON.stringify({ type: 'commonjs' }));
152153

153154
builder.log.minor('Copying assets...');
154155

@@ -173,7 +174,7 @@ async function v1(builder, external) {
173174
status: redirect.status
174175
}));
175176

176-
writeFileSync(
177+
fs.writeFileSync(
177178
`${dir}/config/routes.json`,
178179
JSON.stringify([
179180
...redirects[builder.config.kit.trailingSlash],
@@ -250,10 +251,9 @@ async function v3(builder, external, edge, split) {
250251
* @param {(options: { relativePath: string }) => string} generate_manifest
251252
*/
252253
async function generate_serverless_function(name, pattern, generate_manifest) {
253-
const tmp = builder.getBuildDirectory(`vercel-tmp/${name}`);
254-
const relativePath = posix.relative(tmp, builder.getServerDirectory());
254+
const relativePath = path.posix.relative(tmp, builder.getServerDirectory());
255255

256-
builder.copy(`${files}/serverless.js`, `${tmp}/serverless.js`, {
256+
builder.copy(`${files}/serverless.js`, `${tmp}/index.js`, {
257257
replace: {
258258
SERVER: `${relativePath}/index.js`,
259259
MANIFEST: './manifest.js'
@@ -265,27 +265,12 @@ async function v3(builder, external, edge, split) {
265265
`export const manifest = ${generate_manifest({ relativePath })};\n`
266266
);
267267

268-
await esbuild.build({
269-
entryPoints: [`${tmp}/serverless.js`],
270-
outfile: `${dirs.functions}/${name}.func/index.js`,
271-
target: `node${node_version.full}`,
272-
bundle: true,
273-
platform: 'node',
274-
format: 'cjs',
275-
external
276-
});
277-
278-
write(
279-
`${dirs.functions}/${name}.func/.vc-config.json`,
280-
JSON.stringify({
281-
runtime: `nodejs${node_version.major}.x`,
282-
handler: 'index.js',
283-
launcherType: 'Nodejs'
284-
})
268+
await create_function_bundle(
269+
`${tmp}/index.js`,
270+
`${dirs.functions}/${name}.func`,
271+
`nodejs${node_version.major}.x`
285272
);
286273

287-
write(`${dirs.functions}/${name}.func/package.json`, JSON.stringify({ type: 'commonjs' }));
288-
289274
routes.push({ src: pattern, dest: `/${name}` });
290275
}
291276

@@ -296,7 +281,7 @@ async function v3(builder, external, edge, split) {
296281
*/
297282
async function generate_edge_function(name, pattern, generate_manifest) {
298283
const tmp = builder.getBuildDirectory(`vercel-tmp/${name}`);
299-
const relativePath = posix.relative(tmp, builder.getServerDirectory());
284+
const relativePath = path.posix.relative(tmp, builder.getServerDirectory());
300285

301286
builder.copy(`${files}/edge.js`, `${tmp}/edge.js`, {
302287
replace: {
@@ -392,12 +377,12 @@ async function v3(builder, external, edge, split) {
392377
*/
393378
function write(file, data) {
394379
try {
395-
mkdirSync(dirname(file), { recursive: true });
380+
fs.mkdirSync(path.dirname(file), { recursive: true });
396381
} catch {
397382
// do nothing
398383
}
399384

400-
writeFileSync(file, data);
385+
fs.writeFileSync(file, data);
401386
}
402387

403388
function get_node_version() {
@@ -412,3 +397,75 @@ function get_node_version() {
412397

413398
return { major, full };
414399
}
400+
401+
/**
402+
* @param {string} entry
403+
* @param {string} dir
404+
* @param {string} runtime
405+
*/
406+
async function create_function_bundle(entry, dir, runtime) {
407+
let base = entry;
408+
while (base !== (base = path.dirname(base)));
409+
410+
const traced = await nodeFileTrace([entry], { base });
411+
412+
traced.warnings.forEach((error) => {
413+
// pending https://github.com/vercel/nft/issues/284
414+
if (error.message.startsWith('Failed to resolve dependency node:')) return;
415+
console.error(error);
416+
});
417+
418+
// find common ancestor directory
419+
let common_parts;
420+
421+
for (const file of traced.fileList) {
422+
if (common_parts) {
423+
const parts = file.split(path.sep);
424+
425+
for (let i = 0; i < common_parts.length; i += 1) {
426+
if (parts[i] !== common_parts[i]) {
427+
common_parts = common_parts.slice(0, i);
428+
break;
429+
}
430+
}
431+
} else {
432+
common_parts = path.dirname(file).split(path.sep);
433+
}
434+
}
435+
436+
const ancestor = base + common_parts.join(path.sep);
437+
438+
for (const file of traced.fileList) {
439+
const source = base + file;
440+
const dest = path.join(dir, path.relative(ancestor, source));
441+
442+
const stats = fs.statSync(source);
443+
const is_dir = stats.isDirectory();
444+
445+
const realpath = fs.realpathSync(source);
446+
447+
try {
448+
fs.mkdirSync(path.dirname(dest), { recursive: true });
449+
} catch {
450+
// do nothing
451+
}
452+
453+
if (source !== realpath) {
454+
const realdest = path.join(dir, path.relative(ancestor, realpath));
455+
fs.symlinkSync(path.relative(path.dirname(dest), realdest), dest, is_dir ? 'dir' : 'file');
456+
} else if (!is_dir) {
457+
fs.copyFileSync(source, dest);
458+
}
459+
}
460+
461+
write(
462+
`${dir}/.vc-config.json`,
463+
JSON.stringify({
464+
runtime,
465+
handler: path.relative(base + ancestor, entry),
466+
launcherType: 'Nodejs'
467+
})
468+
);
469+
470+
write(`${dir}/package.json`, JSON.stringify({ type: 'module' }));
471+
}

Diff for: packages/adapter-vercel/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"check": "tsc"
3030
},
3131
"dependencies": {
32+
"@vercel/nft": "^0.18.2",
3233
"esbuild": "^0.14.29"
3334
},
3435
"devDependencies": {

Diff for: packages/adapter-vercel/tsconfig.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44
"checkJs": true,
55
"noEmit": true,
66
"noImplicitAny": true,
7-
"module": "es2020",
7+
"module": "es2022",
88
"moduleResolution": "node",
9+
"target": "es2022",
910
"allowSyntheticDefaultImports": true,
1011
"baseUrl": ".",
1112
"paths": {

Diff for: packages/kit/test/apps/amp/src/app.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/// <reference types="@sveltejs/kit" />

0 commit comments

Comments
 (0)