Skip to content

Commit 66cba91

Browse files
authored
fix: rename root vendor directory when creating tarball bundle (#6985)
1 parent d215694 commit 66cba91

6 files changed

Lines changed: 88 additions & 4 deletions

File tree

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

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1193,6 +1193,65 @@ describe.skipIf(lt(denoVersion, '2.4.2'))(
11931193

11941194
await cleanup()
11951195
})
1196+
1197+
test('Importing from root vendor directory is handled', async () => {
1198+
const systemLogger = vi.fn()
1199+
const { basePath, cleanup, distPath } = await useFixture('imports_vendor', { copyDirectory: true })
1200+
const declarations: Declaration[] = [
1201+
{
1202+
function: 'func1',
1203+
path: '/func1',
1204+
},
1205+
]
1206+
1207+
await bundle([join(basePath, 'netlify/edge-functions')], distPath, declarations, {
1208+
basePath,
1209+
configPath: join(basePath, '.netlify/edge-functions/config.json'),
1210+
importMapPaths: [resolve(basePath, 'import_map.json')],
1211+
featureFlags: {
1212+
edge_bundler_generate_tarball: true,
1213+
},
1214+
systemLogger,
1215+
})
1216+
1217+
expect(
1218+
systemLogger.mock.calls.find((call) => call[0] === 'Could not track dependencies in edge function:'),
1219+
).toBeUndefined()
1220+
1221+
const expectedOutput = {
1222+
func1: 'hello hello',
1223+
}
1224+
1225+
const manifestFile = await readFile(resolve(distPath, 'manifest.json'), 'utf8')
1226+
const manifest = JSON.parse(manifestFile)
1227+
1228+
expect(manifest.bundling_timing).toEqual({ tarball_ms: expect.any(Number) })
1229+
1230+
const tarballPath = join(distPath, manifest.bundles[0].asset)
1231+
const tarballResult = await runTarball(tarballPath)
1232+
expect(tarballResult).toStrictEqual(expectedOutput)
1233+
1234+
const entries: string[] = []
1235+
1236+
await tar.list({
1237+
file: tarballPath,
1238+
onReadEntry: (entry) => {
1239+
entries.push(entry.path)
1240+
},
1241+
})
1242+
1243+
expect(entries).toContain('./___netlify-edge-functions.json')
1244+
expect(entries).toContain('./deno.json')
1245+
expect(entries).toContain('./netlify/edge-functions/func1.ts')
1246+
// vendor directory content was moved
1247+
expect(entries).toContain('./.root-vendor/hello.ts')
1248+
1249+
const eszipPath = join(distPath, manifest.bundles[1].asset)
1250+
const eszipResult = await runESZIP(eszipPath)
1251+
expect(eszipResult).toStrictEqual(expectedOutput)
1252+
1253+
await cleanup()
1254+
})
11961255
},
11971256
10_000,
11981257
)

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

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ export const bundle = async ({
6161
// Build prefix mappings to transform file:// URLs to relative paths
6262
const npmVendorDir = '.netlify-npm-vendor'
6363
const prefixes: Record<string, string> = {}
64+
const additionalImportMapEntries: Record<string, string> = {}
6465

6566
// Copy pre-bundled npm modules from vendorDirectory if present.
6667
// This supports the legacy approach where npm packages are pre-bundled and mapped
@@ -99,7 +100,17 @@ export const bundle = async ({
99100
}
100101

101102
for (const sourceFile of sourceFilesSet) {
102-
const relativePath = path.relative(commonPath, sourceFile)
103+
let relativePath = path.relative(commonPath, sourceFile)
104+
105+
if (relativePath.startsWith('vendor' + path.sep)) {
106+
// root vendor directory is reserved directory and can't be imported directly from with `vendor: true` or `--vendor` flag
107+
// move from vendor/ to .root-vendor/
108+
relativePath = relativePath.replace(/vendor[\\/]/, `.root-vendor/`)
109+
// and import map rewrite so imports remain resolvable
110+
additionalImportMapEntries['./vendor/'] = `./.root-vendor/`
111+
prefixes[pathToFileURL(path.join(commonPath, 'vendor') + path.sep).href] = './.root-vendor/'
112+
}
113+
103114
const destPath = path.join(bundleDir.path, relativePath)
104115

105116
await fs.mkdir(path.dirname(destPath), { recursive: true })
@@ -112,7 +123,7 @@ export const bundle = async ({
112123
prefixes[pathToFileURL(commonPath + path.sep).href] = './'
113124

114125
// Get import map contents with file:// URLs transformed to relative paths
115-
const importMapContents = importMap.getContents(prefixes)
126+
const importMapContents = importMap.getContents(prefixes, additionalImportMapEntries)
116127

117128
// Create deno.json with import map contents for runtime resolution
118129
const denoConfigPath = path.join(bundleDir.path, 'deno.json')

packages/edge-bundler/node/import_map.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,7 @@ export class ImportMap {
167167
// to full URLs. It takes an optional `prefixes` object that specifies a list
168168
// of prefixes to replace path prefixes (see `applyPrefixesToPath`). Prefixes
169169
// will be applied on both `imports` and `scopes`.
170-
getContents(prefixes: Record<string, string> = {}) {
171-
let imports: Imports = {}
170+
getContents(prefixes: Record<string, string> = {}, imports: Imports = {}) {
172171
let scopes: Record<string, Imports> = {}
173172

174173
this.sources.forEach((file) => {
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"imports": {
3+
"aliased_vendor": "./vendor/hello.ts"
4+
}
5+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import hello from '../../vendor/hello.ts'
2+
import hello2 from 'aliased_vendor'
3+
4+
5+
export default async () => {
6+
return new Response(`${hello()} ${hello2()}`)
7+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function hello() {
2+
return 'hello'
3+
}

0 commit comments

Comments
 (0)