Skip to content

Commit bdfec9a

Browse files
fix: fix generation of EF tar file (#6616)
1 parent 1e7332a commit bdfec9a

File tree

4 files changed

+52
-65
lines changed

4 files changed

+52
-65
lines changed

packages/edge-bundler/node/bundler.test.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import process from 'process'
55
import { pathToFileURL } from 'url'
66

77
import { gte, lt } from 'semver'
8+
import * as tar from 'tar'
89
import tmp from 'tmp-promise'
910
import { test, expect, vi, describe } from 'vitest'
1011

@@ -770,6 +771,17 @@ describe.skipIf(lt(denoVersion, '2.4.2'))(
770771
const tarballResult = await runTarball(tarballPath)
771772
expect(tarballResult).toStrictEqual(expectedOutput)
772773

774+
const entries: string[] = []
775+
776+
await tar.list({
777+
file: tarballPath,
778+
onReadEntry: (entry) => {
779+
entries.push(entry.path)
780+
},
781+
})
782+
783+
expect(entries).toStrictEqual(['___netlify-edge-functions.json', 'deno.json', 'func1.js'])
784+
773785
const eszipPath = join(distPath, manifest.bundles[1].asset)
774786
const eszipResult = await runESZIP(eszipPath)
775787
expect(eszipResult).toStrictEqual(expectedOutput)

packages/edge-bundler/node/formats/tarball.ts

Lines changed: 16 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ import { DenoBridge } from '../bridge.js'
99
import { Bundle, BundleFormat } from '../bundle.js'
1010
import { EdgeFunction } from '../edge_function.js'
1111
import { FeatureFlags } from '../feature_flags.js'
12+
import { listRecursively } from '../utils/fs.js'
1213
import { ImportMap } from '../import_map.js'
13-
import { getDirectoryHash, getStringHash } from '../utils/sha256.js'
14+
import { getFileHash } from '../utils/sha256.js'
1415

1516
const TARBALL_EXTENSION = '.tar'
1617

@@ -42,8 +43,8 @@ export const bundle = async ({
4243
importMap,
4344
vendorDirectory,
4445
}: BundleTarballOptions): Promise<Bundle> => {
45-
const sideFilesDir = await tmp.dir({ unsafeCleanup: true })
46-
const cleanup = [sideFilesDir.cleanup]
46+
const bundleDir = await tmp.dir({ unsafeCleanup: true })
47+
const cleanup = [bundleDir.cleanup]
4748

4849
let denoDir = vendorDirectory ? path.join(vendorDirectory, 'deno_dir') : undefined
4950

@@ -87,71 +88,47 @@ export const bundle = async ({
8788
'--quiet',
8889
'--code-splitting',
8990
'--outdir',
90-
distDirectory,
91+
bundleDir.path,
9192
...functions.map((func) => func.path),
9293
],
9394
{
9495
cwd: basePath,
9596
},
9697
)
9798

98-
const manifestPath = path.join(sideFilesDir.path, 'manifest.json')
99+
const manifestPath = path.join(bundleDir.path, '___netlify-edge-functions.json')
99100
const manifestContents = JSON.stringify(manifest)
100101
await fs.writeFile(manifestPath, manifestContents)
101102

102-
const denoConfigPath = path.join(sideFilesDir.path, 'deno.json')
103+
const denoConfigPath = path.join(bundleDir.path, 'deno.json')
103104
const denoConfigContents = JSON.stringify(importMap.getContentsWithRelativePaths())
104105
await fs.writeFile(denoConfigPath, denoConfigContents)
105106

106-
const rootLevel = await fs.readdir(distDirectory)
107-
const hash = await getDirectoryHash(distDirectory)
108107
const tarballPath = path.join(distDirectory, buildID + TARBALL_EXTENSION)
109-
110108
await fs.mkdir(path.dirname(tarballPath), { recursive: true })
111109

112-
// Adding all the bundled files.
113110
await tar.create(
114111
{
115-
cwd: distDirectory,
116-
file: tarballPath,
117-
onWriteEntry(entry) {
118-
entry.path = getUnixPath(`./${entry.path}`)
119-
},
120-
},
121-
rootLevel,
122-
)
123-
124-
// Adding `deno.json`.
125-
await tar.update(
126-
{
127-
cwd: distDirectory,
112+
cwd: bundleDir.path,
128113
file: tarballPath,
114+
noDirRecurse: true,
129115
onWriteEntry(entry) {
130-
entry.path = './deno.json'
131-
},
132-
},
133-
[denoConfigPath],
134-
)
116+
const relativePath = path.relative(bundleDir.path, path.join('/', entry.path))
117+
const normalizedPath = getUnixPath(relativePath)
135118

136-
// Adding the manifest file.
137-
await tar.update(
138-
{
139-
cwd: distDirectory,
140-
file: tarballPath,
141-
onWriteEntry(entry) {
142-
entry.path = './___netlify-edge-functions.json'
119+
entry.path = normalizedPath
143120
},
144121
},
145-
[manifestPath],
122+
await listRecursively(bundleDir.path),
146123
)
147124

148-
await Promise.all(cleanup)
125+
const hash = await getFileHash(tarballPath)
149126

150-
const finalHash = [hash, getStringHash(manifestContents), getStringHash(denoConfigContents)].join('')
127+
await Promise.allSettled(cleanup)
151128

152129
return {
153130
extension: TARBALL_EXTENSION,
154131
format: BundleFormat.TARBALL,
155-
hash: finalHash,
132+
hash,
156133
}
157134
}

packages/edge-bundler/node/utils/fs.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,26 @@
1-
import path from 'path'
1+
import { promises as fs } from 'node:fs'
2+
import path from 'node:path'
3+
4+
export const listRecursively = async (dirPath: string): Promise<string[]> => {
5+
const entries: string[] = []
6+
7+
async function walk(currentPath: string): Promise<void> {
8+
const dirents = await fs.readdir(currentPath, { withFileTypes: true })
9+
for (const dirent of dirents) {
10+
const fullPath = path.join(currentPath, dirent.name)
11+
12+
if (dirent.isDirectory()) {
13+
await walk(fullPath)
14+
} else if (dirent.isFile() || dirent.isSymbolicLink()) {
15+
entries.push(fullPath)
16+
}
17+
}
18+
}
19+
20+
await walk(dirPath)
21+
22+
return entries.sort((a, b) => a.localeCompare(b))
23+
}
224

325
/**
426
* Returns all the directories obtained by traversing `inner` and its parents

packages/edge-bundler/node/utils/sha256.ts

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,5 @@
11
import crypto from 'node:crypto'
2-
import { createReadStream, promises as fs } from 'node:fs'
3-
import path from 'node:path'
4-
5-
export const getDirectoryHash = async (dirPath: string): Promise<string> => {
6-
const entries: string[] = []
7-
8-
async function walk(currentPath: string): Promise<void> {
9-
const dirents = await fs.readdir(currentPath, { withFileTypes: true })
10-
for (const dirent of dirents) {
11-
const fullPath = path.join(currentPath, dirent.name)
12-
const relativePath = path.relative(dirPath, fullPath)
13-
14-
if (dirent.isDirectory()) {
15-
await walk(fullPath)
16-
} else if (dirent.isFile() || dirent.isSymbolicLink()) {
17-
const fileHash = await getFileHash(fullPath)
18-
entries.push(`${relativePath}:${fileHash}`)
19-
}
20-
}
21-
}
22-
23-
await walk(dirPath)
24-
25-
return getStringHash(entries.sort((a, b) => a.localeCompare(b)).join('\n'))
26-
}
2+
import { createReadStream } from 'node:fs'
273

284
export const getFileHash = (path: string): Promise<string> => {
295
const hash = crypto.createHash('sha256')

0 commit comments

Comments
 (0)