From 07aa83bb14177f211f3fcdcf565ce823d27c5913 Mon Sep 17 00:00:00 2001 From: Long Ho Date: Thu, 23 Jul 2026 00:18:28 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20Fix=20package=20tree=20resolutio?= =?UTF-8?q?n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/entries/vite-plugin.spec.ts | 51 +++++++++++++++++++++++++++++++++ src/entries/vite-plugin.ts | 7 +++-- 2 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 src/entries/vite-plugin.spec.ts diff --git a/src/entries/vite-plugin.spec.ts b/src/entries/vite-plugin.spec.ts new file mode 100644 index 00000000..d24409a5 --- /dev/null +++ b/src/entries/vite-plugin.spec.ts @@ -0,0 +1,51 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +const mocks = vi.hoisted(() => ({ + cpSync: vi.fn(), + createRequire: vi.fn(), + existsSync: vi.fn(), + mkdirSync: vi.fn(), + readFileSync: vi.fn(), +})); + +vi.mock('node:fs', () => ({ + cpSync: mocks.cpSync, + existsSync: mocks.existsSync, + mkdirSync: mocks.mkdirSync, + readFileSync: mocks.readFileSync, +})); +vi.mock('node:module', () => ({ createRequire: mocks.createRequire })); + +import { datadogVitePlugin } from './vite-plugin'; + +describe('datadogVitePlugin', () => { + beforeEach(() => { + vi.resetAllMocks(); + }); + + it('resolves transitive dependencies from their parent package', () => { + const rootResolve = vi.fn((pkg: string) => { + if (pkg === 'dd-trace') return '/packages/dd-trace/index.js'; + if (pkg === '@datadog/electron-sdk') return '/packages/electron-sdk/index.js'; + throw new Error(`Cannot resolve ${pkg}`); + }); + const ddTraceResolve = vi.fn(() => '/packages/nested-package/index.js'); + + mocks.createRequire.mockImplementation((path: string) => ({ + resolve: path === '/packages/dd-trace/package.json' ? ddTraceResolve : rootResolve, + })); + mocks.existsSync.mockImplementation((path: string) => + path.startsWith('/packages/') ? path.endsWith('/package.json') : false + ); + mocks.readFileSync.mockImplementation((path: string) => + JSON.stringify({ dependencies: path === '/packages/dd-trace/package.json' ? { 'nested-package': '1.0.0' } : {} }) + ); + + datadogVitePlugin().writeBundle?.({ dir: '/output' }); + + expect(ddTraceResolve).toHaveBeenCalledWith('nested-package'); + expect(mocks.cpSync).toHaveBeenCalledWith('/packages/nested-package', '/output/node_modules/nested-package', { + recursive: true, + }); + }); +}); diff --git a/src/entries/vite-plugin.ts b/src/entries/vite-plugin.ts index 89d9a68b..8f4c5318 100644 --- a/src/entries/vite-plugin.ts +++ b/src/entries/vite-plugin.ts @@ -77,13 +77,13 @@ export function datadogVitePlugin(): VitePlugin { const destModules = join(outDir, 'node_modules'); const visited = new Set(); - function copyPackageTree(pkg: string): void { + function copyPackageTree(pkg: string, resolveFrom = _require): void { if (visited.has(pkg)) return; visited.add(pkg); try { // Resolve the package's main entry, then walk up to find the root - const entryPath = _require.resolve(pkg); + const entryPath = resolveFrom.resolve(pkg); let pkgDir = dirname(entryPath); while (pkgDir !== dirname(pkgDir) && !existsSync(join(pkgDir, 'package.json'))) { pkgDir = dirname(pkgDir); @@ -99,8 +99,9 @@ export function datadogVitePlugin(): VitePlugin { const pkgJson = JSON.parse(readFileSync(join(pkgDir, 'package.json'), 'utf8')) as { dependencies?: Record; }; + const packageRequire = createRequire(join(pkgDir, 'package.json')); for (const dep of Object.keys(pkgJson.dependencies ?? {})) { - copyPackageTree(dep); + copyPackageTree(dep, packageRequire); } } catch { console.warn(`[datadog] Failed to copy package '${pkg}' to build output`);