1
- import { mkdirSync , writeFileSync } from 'fs' ;
2
- import { dirname , posix } from 'path' ;
1
+ import fs from 'fs' ;
2
+ import path from 'path' ;
3
3
import { fileURLToPath } from 'url' ;
4
+ import { nodeFileTrace } from '@vercel/nft' ;
4
5
import esbuild from 'esbuild' ;
5
6
6
7
// rules for clean URLs and trailing slash handling,
@@ -80,6 +81,8 @@ const redirects = {
80
81
]
81
82
} ;
82
83
84
+ const files = fileURLToPath ( new URL ( './files' , import . meta. url ) . href ) ;
85
+
83
86
/** @type {import('.') } **/
84
87
export default function ( { external = [ ] , edge, split } = { } ) {
85
88
return {
@@ -113,16 +116,14 @@ async function v1(builder, external) {
113
116
builder . rimraf ( dir ) ;
114
117
builder . rimraf ( tmp ) ;
115
118
116
- const files = fileURLToPath ( new URL ( './files' , import . meta. url ) . href ) ;
117
-
118
119
const dirs = {
119
120
static : `${ dir } /static` ,
120
121
lambda : `${ dir } /functions/node/render`
121
122
} ;
122
123
123
124
builder . log . minor ( 'Generating serverless function...' ) ;
124
125
125
- const relativePath = posix . relative ( tmp , builder . getServerDirectory ( ) ) ;
126
+ const relativePath = path . posix . relative ( tmp , builder . getServerDirectory ( ) ) ;
126
127
127
128
builder . copy ( `${ files } /serverless.js` , `${ tmp } /serverless.js` , {
128
129
replace : {
@@ -131,7 +132,7 @@ async function v1(builder, external) {
131
132
}
132
133
} ) ;
133
134
134
- writeFileSync (
135
+ fs . writeFileSync (
135
136
`${ tmp } /manifest.js` ,
136
137
`export const manifest = ${ builder . generateManifest ( {
137
138
relativePath
@@ -148,7 +149,7 @@ async function v1(builder, external) {
148
149
format : 'cjs'
149
150
} ) ;
150
151
151
- writeFileSync ( `${ dirs . lambda } /package.json` , JSON . stringify ( { type : 'commonjs' } ) ) ;
152
+ fs . writeFileSync ( `${ dirs . lambda } /package.json` , JSON . stringify ( { type : 'commonjs' } ) ) ;
152
153
153
154
builder . log . minor ( 'Copying assets...' ) ;
154
155
@@ -173,7 +174,7 @@ async function v1(builder, external) {
173
174
status : redirect . status
174
175
} ) ) ;
175
176
176
- writeFileSync (
177
+ fs . writeFileSync (
177
178
`${ dir } /config/routes.json` ,
178
179
JSON . stringify ( [
179
180
...redirects [ builder . config . kit . trailingSlash ] ,
@@ -250,10 +251,9 @@ async function v3(builder, external, edge, split) {
250
251
* @param {(options: { relativePath: string }) => string } generate_manifest
251
252
*/
252
253
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 ( ) ) ;
255
255
256
- builder . copy ( `${ files } /serverless.js` , `${ tmp } /serverless .js` , {
256
+ builder . copy ( `${ files } /serverless.js` , `${ tmp } /index .js` , {
257
257
replace : {
258
258
SERVER : `${ relativePath } /index.js` ,
259
259
MANIFEST : './manifest.js'
@@ -265,27 +265,12 @@ async function v3(builder, external, edge, split) {
265
265
`export const manifest = ${ generate_manifest ( { relativePath } ) } ;\n`
266
266
) ;
267
267
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`
285
272
) ;
286
273
287
- write ( `${ dirs . functions } /${ name } .func/package.json` , JSON . stringify ( { type : 'commonjs' } ) ) ;
288
-
289
274
routes . push ( { src : pattern , dest : `/${ name } ` } ) ;
290
275
}
291
276
@@ -296,7 +281,7 @@ async function v3(builder, external, edge, split) {
296
281
*/
297
282
async function generate_edge_function ( name , pattern , generate_manifest ) {
298
283
const tmp = builder . getBuildDirectory ( `vercel-tmp/${ name } ` ) ;
299
- const relativePath = posix . relative ( tmp , builder . getServerDirectory ( ) ) ;
284
+ const relativePath = path . posix . relative ( tmp , builder . getServerDirectory ( ) ) ;
300
285
301
286
builder . copy ( `${ files } /edge.js` , `${ tmp } /edge.js` , {
302
287
replace : {
@@ -392,12 +377,12 @@ async function v3(builder, external, edge, split) {
392
377
*/
393
378
function write ( file , data ) {
394
379
try {
395
- mkdirSync ( dirname ( file ) , { recursive : true } ) ;
380
+ fs . mkdirSync ( path . dirname ( file ) , { recursive : true } ) ;
396
381
} catch {
397
382
// do nothing
398
383
}
399
384
400
- writeFileSync ( file , data ) ;
385
+ fs . writeFileSync ( file , data ) ;
401
386
}
402
387
403
388
function get_node_version ( ) {
@@ -412,3 +397,75 @@ function get_node_version() {
412
397
413
398
return { major, full } ;
414
399
}
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
+ }
0 commit comments