@@ -28,15 +28,36 @@ export interface Changes {
2828
2929export function parseLockfile (
3030 path : string ,
31- contentStr : string | null = null
31+ contentStr : string | null = null ,
32+ includeTransitive : boolean = false
3233) : Record < string , PackageInfo > {
3334 try {
3435 const content = contentStr || fs . readFileSync ( path , 'utf8' ) ;
3536 const data : LockfileData = JSON . parse ( content ) ;
3637 let packages : Record < string , PackageInfo | string > = { } ;
3738
3839 if ( data . packages ) {
39- packages = data . packages ;
40+ if ( includeTransitive ) {
41+ packages = { ...data . packages } ;
42+ } else {
43+ // For lockfile v3, we only want to compare the direct dependencies
44+ // to keep the diff clean and avoid GitHub PR comment limits.
45+ // Direct dependencies are listed in the root package ('')
46+ const rootPackage = data . packages [ '' ] as LockfileData | undefined ;
47+ const directDeps = new Set ( [
48+ ...Object . keys ( rootPackage ?. dependencies || { } ) ,
49+ ...Object . keys ( rootPackage ?. devDependencies || { } ) ,
50+ ...Object . keys ( rootPackage ?. optionalDependencies || { } ) ,
51+ ] ) ;
52+
53+ for ( const [ key , info ] of Object . entries ( data . packages ) ) {
54+ if ( key === '' ) continue ;
55+ const name = key . replace ( / ^ n o d e _ m o d u l e s \/ / , '' ) ;
56+ if ( directDeps . has ( name ) && ! key . includes ( '/node_modules/' ) ) {
57+ packages [ key ] = info ;
58+ }
59+ }
60+ }
4061 } else {
4162 packages = {
4263 ...( data . dependencies || { } ) ,
@@ -77,7 +98,7 @@ export function comparePackages(
7798 for ( const pkg of allPackages ) {
7899 const inBase = basePackages . has ( pkg ) ;
79100 const inHead = headPackages . has ( pkg ) ;
80- const name = pkg . replace ( ' node_modules/' , '' ) ;
101+ const name = pkg . replace ( / ^ n o d e _ m o d u l e s \/ / , '' ) ;
81102
82103 // Skip packages that have no version (e.g., aliases or metadata without version)
83104 if ( inHead && ! head [ pkg ] . version ) continue ;
@@ -100,7 +121,9 @@ export function comparePackages(
100121}
101122
102123export function getCompareLink ( packageName : string ) : string {
103- return `[Compare](https://www.npmjs.com/package/${ packageName } ?activeTab=versions)` ;
124+ // Use only the package name without node_modules path
125+ const name = packageName . split ( 'node_modules/' ) . pop ( ) || packageName ;
126+ return `[Compare](https://www.npmjs.com/package/${ name } ?activeTab=versions)` ;
104127}
105128
106129export function formatMarkdown ( changes : Changes ) : string {
@@ -145,8 +168,15 @@ export function formatMarkdown(changes: Changes): string {
145168
146169// Main execution
147170export function run ( ) : void {
148- const baseRef = core . getInput ( 'base-ref' ) || 'main' ;
149- const lockfilePath = core . getInput ( 'path' ) || 'package-lock.json' ;
171+ const getCustomInput = ( name : string ) => {
172+ return (
173+ core . getInput ( name ) || process . env [ `INPUT_${ name . toUpperCase ( ) . replace ( / - / g, '_' ) } ` ] || ''
174+ ) ;
175+ } ;
176+
177+ const baseRef = getCustomInput ( 'base-ref' ) || 'main' ;
178+ const lockfilePath = getCustomInput ( 'path' ) || 'package-lock.json' ;
179+ const includeTransitive = getCustomInput ( 'include-transitive' ) === 'true' ;
150180
151181 // Check if lockfile exists
152182 if ( ! fs . existsSync ( lockfilePath ) ) {
@@ -161,7 +191,11 @@ export function run(): void {
161191 // Get the merge base to ensure we only see changes from the current branch
162192 let baseRevision = `origin/${ baseRef } ` ;
163193 try {
164- const mergeBase = execSync ( `git merge-base "origin/${ baseRef } " HEAD` ) . toString ( ) . trim ( ) ;
194+ const mergeBase = execSync ( `git merge-base "origin/${ baseRef } " HEAD` , {
195+ maxBuffer : 50 * 1024 * 1024 ,
196+ } )
197+ . toString ( )
198+ . trim ( ) ;
165199 if ( mergeBase ) {
166200 baseRevision = mergeBase ;
167201 }
@@ -173,7 +207,9 @@ export function run(): void {
173207
174208 // Check if lockfile changed
175209 try {
176- const diff = execSync ( `git diff --name-only "${ baseRevision } " HEAD` ) . toString ( ) ;
210+ const diff = execSync ( `git diff --name-only "${ baseRevision } " HEAD` , {
211+ maxBuffer : 50 * 1024 * 1024 ,
212+ } ) . toString ( ) ;
177213 const changedFiles = diff
178214 . split ( '\n' )
179215 . map ( ( line ) => line . trim ( ) )
@@ -196,6 +232,17 @@ export function run(): void {
196232 if ( err instanceof Error ) {
197233 console . error ( 'Error running git diff:' , err . message ) ;
198234 }
235+ // If git diff fails (e.g. shallow clone), we don't want to proceed with an empty base
236+ // as it would report all dependencies as added.
237+ core . setOutput ( 'has_changes' , 'false' ) ;
238+ core . setOutput (
239+ 'diff' ,
240+ `_Error comparing branches. This may be due to a shallow clone. Try setting fetch-depth: 0 in actions/checkout._`
241+ ) ;
242+ core . setOutput ( 'added_count' , '0' ) ;
243+ core . setOutput ( 'removed_count' , '0' ) ;
244+ core . setOutput ( 'updated_count' , '0' ) ;
245+ return ;
199246 }
200247
201248 core . setOutput ( 'has_changes' , 'true' ) ;
@@ -205,13 +252,15 @@ export function run(): void {
205252 try {
206253 baseContent = execSync ( `git show "${ baseRevision } :${ lockfilePath } "` , {
207254 stdio : [ 'pipe' , 'pipe' , 'ignore' ] ,
255+ maxBuffer : 50 * 1024 * 1024 ,
208256 } ) . toString ( ) ;
209257 } catch {
210258 // If it fails, baseContent remains '{}'
259+ // This could happen if the file didn't exist in the base branch
211260 }
212261
213- const base = parseLockfile ( lockfilePath , baseContent ) ;
214- const head = parseLockfile ( lockfilePath ) ;
262+ const base = parseLockfile ( lockfilePath , baseContent , includeTransitive ) ;
263+ const head = parseLockfile ( lockfilePath , null , includeTransitive ) ;
215264 const changes = comparePackages ( base , head ) ;
216265 const markdown = formatMarkdown ( changes ) ;
217266
0 commit comments