From 2355f3226716b5056b9f702099d2ccdbb9f57004 Mon Sep 17 00:00:00 2001 From: Amilliox Date: Sun, 28 Jun 2026 14:44:37 +0000 Subject: [PATCH] fix #3466: include bundledDependencies in workspace npm pack --- workspaces/libnpmpack/lib/index.js | 38 +++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/workspaces/libnpmpack/lib/index.js b/workspaces/libnpmpack/lib/index.js index 2ef6391e439d7..edb2333d42201 100644 --- a/workspaces/libnpmpack/lib/index.js +++ b/workspaces/libnpmpack/lib/index.js @@ -5,7 +5,7 @@ const npa = require('npm-package-arg') const runScript = require('@npmcli/run-script') const path = require('node:path') const Arborist = require('@npmcli/arborist') -const { writeFile } = require('node:fs/promises') +const { writeFile, mkdir, symlink, unlink } = require('node:fs/promises') module.exports = pack async function pack (spec = 'file:.', opts = {}) { @@ -63,6 +63,37 @@ async function pack (spec = 'file:.', opts = {}) { }) } + // When packing a workspace directory, hoisted bundled dependencies need to + // appear inside the workspace's node_modules so that packlist includes them + // with correct paths (node_modules//file, not ../../node_modules/...). + // Create temporary symlinks before tar, clean up after. + const cleanupSymlinks = [] + const wsNmDir = spec.type === 'directory' ? path.join(spec.fetchSpec, 'node_modules') : null + if (wsNmDir && opts.prefix && spec.fetchSpec !== opts.prefix) { + const rootArb = new Arborist({ path: opts.prefix }) + const fullTree = await rootArb.loadActual() + for (const node of fullTree.inventory.values()) { + if (node.path === spec.fetchSpec) { + node.root = node + const toBundle = node.package.bundleDependencies || [] + for (const dep of toBundle) { + const edge = node.edgesOut.get(dep) + if (!edge || edge.peer || edge.dev) continue + const depNode = node.edgesOut.get(dep).to + if (!depNode) continue + const depRealPath = depNode.realpath + const linkPath = path.join(wsNmDir, dep) + try { + await mkdir(wsNmDir, { recursive: true }) + await symlink(path.relative(path.dirname(linkPath), depRealPath), linkPath) + cleanupSymlinks.push(linkPath) + } catch {} + } + break + } + } + } + // packs tarball const tarballOpts = { ...opts, @@ -85,6 +116,11 @@ async function pack (spec = 'file:.', opts = {}) { await writeFile(destination, tarball) } + // Clean up temporary symlinks + for (const link of cleanupSymlinks) { + try { await unlink(link) } catch {} + } + if (spec.type === 'directory' && !opts.ignoreScripts) { // postpack await runScript({