Skip to content

Commit 96faaaf

Browse files
authored
fix: ignore type-only errored modules from module graph (#7072)
1 parent 3d2a644 commit 96faaaf

4 files changed

Lines changed: 82 additions & 6 deletions

File tree

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

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1369,6 +1369,49 @@ describe.skipIf(lt(denoVersion, '2.4.2'))(
13691369

13701370
await cleanup()
13711371
})
1372+
1373+
test('With a type-only import from a directory', async () => {
1374+
const systemLogger = vi.fn()
1375+
const { basePath, cleanup, distPath } = await useFixture('import-types-directory', { copyDirectory: true })
1376+
const declarations: Declaration[] = [
1377+
{
1378+
function: 'func1',
1379+
path: '/func1',
1380+
},
1381+
]
1382+
1383+
await bundle([join(basePath, 'netlify/edge-functions')], distPath, declarations, {
1384+
basePath,
1385+
featureFlags: {
1386+
edge_bundler_generate_tarball: true,
1387+
},
1388+
systemLogger,
1389+
})
1390+
1391+
expect(
1392+
systemLogger.mock.calls.find((call) => call[0] === 'Could not track dependencies in edge function:'),
1393+
).toBeUndefined()
1394+
1395+
const expectedOutput = {
1396+
func1: 'ok',
1397+
}
1398+
1399+
const manifestFile = await readFile(resolve(distPath, 'manifest.json'), 'utf8')
1400+
const manifest = JSON.parse(manifestFile)
1401+
1402+
const tarballPath = join(distPath, manifest.bundles[0].asset)
1403+
const tarballResult = await runTarball(tarballPath)
1404+
expect(tarballResult).toStrictEqual(expectedOutput)
1405+
1406+
const eszipPath = join(distPath, manifest.bundles[1].asset)
1407+
const eszipResult = await runESZIP(eszipPath)
1408+
expect(eszipResult).toStrictEqual(expectedOutput)
1409+
1410+
// Both formats should produce identical output.
1411+
expect(tarballResult).toStrictEqual(eszipResult)
1412+
1413+
await cleanup()
1414+
})
13721415
},
13731416
50_000,
13741417
)

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

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -252,17 +252,44 @@ async function getRequiredSourceFiles(
252252

253253
const graph = JSON.parse(stdout) as ModuleGraphJson
254254

255+
// Collect the specifiers of every module reachable through a *code* (runtime)
256+
// import edge. Deno's module graph classifies each dependency as either a `code`
257+
// edge (a real runtime import) or a `type`-only edge (`import type`, `@deno-types`).
258+
// Type-only edges are erased during transpilation and are never loaded at runtime
259+
// (`deno run` doesn't type-check), so the files they point to don't belong in the
260+
// bundle. The entry points themselves are always runtime.
261+
const runtimeSpecifiers = new Set<string>(graph.roots)
262+
for (const module of graph.modules) {
263+
for (const dependency of module.dependencies ?? []) {
264+
if (dependency.code?.specifier) {
265+
runtimeSpecifiers.add(dependency.code.specifier)
266+
}
267+
}
268+
}
269+
255270
// Extract all local files from the module graph
256271
for (const module of graph.modules) {
257-
if (module.specifier.startsWith('file://')) {
258-
if (module.error?.startsWith('Module not found')) {
259-
// Module graph contains all found imported/required modules, even if they don't actually exist
260-
// This can happen for optional dependencies (dynamic import or require in try/catch).
272+
if (!module.specifier.startsWith('file://')) {
273+
continue
274+
}
275+
276+
if (module.error) {
277+
// A module reachable only through type-only edges (e.g. a directory specifier
278+
// behind `import type`) can fail to resolve as an ES module. That's safe to
279+
// ignore: the runtime never loads it.
280+
if (!runtimeSpecifiers.has(module.specifier)) {
281+
continue
282+
}
283+
284+
if (module.error.startsWith('Module not found')) {
285+
// Module graph contains all found imported/required modules, even if they don't
286+
// actually exist. This can happen for optional dependencies (dynamic import or
287+
// require in try/catch).
261288
continue
262289
}
263-
const filePath = fileURLToPath(module.specifier)
264-
localFiles.add(filePath)
265290
}
291+
292+
localFiles.add(fileURLToPath(module.specifier))
266293
}
267294
}
268295

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import type { OK } from "../../types";
2+
3+
export default function handler() {
4+
return new Response("ok" satisfies OK);
5+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export type OK = "ok";

0 commit comments

Comments
 (0)