Skip to content

Commit ab85a30

Browse files
committed
Use tar npm package instead of tar command for cross-platform support
- Add tar@7.5.1 as devDependency (same version used by pacote) - Replace tar command with tar npm package API for extraction and creation - Add comprehensive documentation explaining why tarball repacking is necessary - Works on all platforms without requiring external tar command
1 parent 3ea742e commit ab85a30

3 files changed

Lines changed: 33 additions & 10 deletions

File tree

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@
131131
"read-yaml-file": "2.1.0",
132132
"semver": "7.7.2",
133133
"source-map-support": "0.5.21",
134+
"tar": "7.5.1",
134135
"taze": "19.6.0",
135136
"trash": "10.0.0",
136137
"tsx": "4.20.5",

pnpm-lock.yaml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

scripts/install-npm-packages.mjs

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ import os from 'node:os'
6767
import path from 'node:path'
6868
import { pathToFileURL } from 'node:url'
6969

70+
import tar from 'tar'
71+
7072
import { parseArgs } from '../registry/dist/lib/parse-args.js'
7173

7274
import { cleanTestScript } from '../test/utils/script-cleaning.mjs'
@@ -610,7 +612,9 @@ async function installPackage(packageInfo) {
610612
throw new Error('Downloaded tarball is empty')
611613
}
612614

613-
await runCommand('tar', ['-xzf', 'archive.tar.gz'], {
615+
// Extract using tar package for cross-platform compatibility.
616+
await tar.x({
617+
file: archivePath,
614618
cwd: tempExtractDir,
615619
})
616620

@@ -695,17 +699,32 @@ async function installPackage(packageInfo) {
695699
// File doesn't exist, ignore.
696700
})
697701

698-
// pnpm respects the files field even when installing from file:// directories.
699-
// Create a new tarball that includes all files (cross-platform compatible).
702+
// CRITICAL: pnpm respects the "files" field even when installing from file:// directories.
703+
// Even though we removed the "files" field from package.json, pnpm still filters files
704+
// during installation if we point it to a directory. To work around this pnpm limitation,
705+
// we create a NEW tarball that contains ALL files (including test files that were
706+
// originally excluded by the "files" field).
707+
//
708+
// WHY THIS IS NECESSARY:
709+
// 1. GitHub tarballs often have "files": ["index.js"] which excludes test files
710+
// 2. We remove this field from package.json to preserve test files
711+
// 3. But pnpm STILL filters files when installing from a file:// directory URL
712+
// 4. Creating a new tarball bypasses pnpm's filtering since tarballs don't support
713+
// selective file inclusion - they contain everything that's in them
714+
//
715+
// CROSS-PLATFORM: Uses the 'tar' npm package (same as pacote) which works on all platforms
716+
// including Windows 10+ without requiring external tar command.
700717
const repackedTarball = path.join(tempExtractDir, 'repacked.tgz')
701-
const tarArgs = WIN32
702-
? ['-czf', 'repacked.tgz', extractedDir.name]
703-
: ['-czf', 'repacked.tgz', extractedDir.name]
704-
await runCommand('tar', tarArgs, {
705-
cwd: tempExtractDir,
706-
})
718+
await tar.c(
719+
{
720+
gzip: true,
721+
file: repackedTarball,
722+
cwd: tempExtractDir,
723+
},
724+
[extractedDir.name],
725+
)
707726

708-
// Use file:// URL to point pnpm to our repacked tarball.
727+
// Use file:// URL to point pnpm to our repacked tarball that contains all files.
709728
packageSpec = pathToFileURL(repackedTarball).href
710729
modifiedPackagePath = tempExtractDir
711730
}

0 commit comments

Comments
 (0)