@@ -67,6 +67,8 @@ import os from 'node:os'
6767import path from 'node:path'
6868import { pathToFileURL } from 'node:url'
6969
70+ import tar from 'tar'
71+
7072import { parseArgs } from '../registry/dist/lib/parse-args.js'
7173
7274import { 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