Skip to content

Commit 7424eb4

Browse files
committed
Add support for non-standard package.json locations in npm tarballs
1 parent 01dcbe0 commit 7424eb4

File tree

2 files changed

+91
-12
lines changed

2 files changed

+91
-12
lines changed

artifactory/commands/npm/publish.go

Lines changed: 86 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ import (
44
"archive/tar"
55
"compress/gzip"
66
"errors"
7+
"io"
8+
"os"
9+
"path/filepath"
10+
"strings"
11+
712
"github.com/jfrog/build-info-go/build"
813
biutils "github.com/jfrog/build-info-go/build/utils"
914
gofrogcmd "github.com/jfrog/gofrog/io"
@@ -20,10 +25,6 @@ import (
2025
"github.com/jfrog/jfrog-client-go/utils/errorutils"
2126
"github.com/jfrog/jfrog-client-go/utils/io/content"
2227
"github.com/jfrog/jfrog-client-go/utils/log"
23-
"io"
24-
"os"
25-
"path/filepath"
26-
"strings"
2728
)
2829

2930
const (
@@ -344,24 +345,97 @@ func (npc *NpmPublishCommand) readPackageInfoFromTarball(packedFilePath string)
344345
return errorutils.CheckError(err)
345346
}
346347

347-
tarReader := tar.NewReader(gZipReader)
348+
// Use our new priority-based approach to find package.json
349+
packageJsonPath, packageJson, err := findBestPackageJson(tar.NewReader(gZipReader))
350+
if err != nil {
351+
return errorutils.CheckErrorf("Could not find 'package.json' in the compressed npm package: " + packedFilePath)
352+
}
353+
354+
// Log which package.json was selected if it's not in the standard location
355+
if packageJsonPath != "package/package.json" {
356+
log.Info("Using non-standard package.json location: " + packageJsonPath)
357+
}
358+
359+
// Parse the package.json content
360+
npc.packageInfo, err = biutils.ReadPackageInfo(packageJson, npc.npmVersion)
361+
return err
362+
}
363+
364+
// findBestPackageJson scans the tarball and returns the best package.json file based on priority
365+
func findBestPackageJson(tarReader *tar.Reader) (string, []byte, error) {
366+
type packageJsonCandidate struct {
367+
path string
368+
priority int
369+
content []byte
370+
}
371+
372+
// Lower number = higher priority
373+
var bestCandidate *packageJsonCandidate
374+
375+
// Scan all entries in the tarball
348376
for {
349377
hdr, err := tarReader.Next()
350378
if err != nil {
351379
if err == io.EOF {
352-
return errorutils.CheckErrorf("Could not find 'package.json' in the compressed npm package: " + packedFilePath)
380+
break
353381
}
354-
return errorutils.CheckError(err)
382+
return "", nil, errorutils.CheckError(err)
355383
}
356-
if hdr.Name == "package/package.json" {
357-
packageJson, err := io.ReadAll(tarReader)
384+
385+
// Check if this is a package.json file
386+
if strings.HasSuffix(hdr.Name, "package.json") {
387+
// Read content
388+
content, err := io.ReadAll(tarReader)
358389
if err != nil {
359-
return errorutils.CheckError(err)
390+
log.Debug("Error reading " + hdr.Name + ": " + err.Error())
391+
continue
392+
}
393+
394+
// Calculate priority based on path
395+
priority := calculatePackageJsonPriority(hdr.Name)
396+
candidate := &packageJsonCandidate{
397+
path: hdr.Name,
398+
priority: priority,
399+
content: content,
400+
}
401+
402+
// Update best candidate if this one has higher priority
403+
if bestCandidate == nil || candidate.priority < bestCandidate.priority {
404+
bestCandidate = candidate
405+
}
406+
407+
// Standard location is highest priority, can stop searching
408+
if hdr.Name == "package/package.json" {
409+
break
360410
}
361-
npc.packageInfo, err = biutils.ReadPackageInfo(packageJson, npc.npmVersion)
362-
return err
363411
}
364412
}
413+
414+
// If no package.json was found, return error
415+
if bestCandidate == nil {
416+
return "", nil, errorutils.CheckErrorf("no package.json found")
417+
}
418+
419+
return bestCandidate.path, bestCandidate.content, nil
420+
}
421+
422+
// calculatePackageJsonPriority determines the priority of a package.json file based on its path
423+
// Lower number = higher priority
424+
func calculatePackageJsonPriority(path string) int {
425+
// Standard location gets highest priority
426+
if path == "package/package.json" {
427+
return 1
428+
}
429+
430+
parts := strings.Split(path, "/")
431+
432+
// Root directory package.json (like node/package.json) gets second priority
433+
if len(parts) == 2 && parts[1] == "package.json" {
434+
return 2
435+
}
436+
437+
// Other locations get lower priority based on nesting depth
438+
return 3 + len(parts)
365439
}
366440

367441
func deleteCreatedTarball(packedFilesPath []string) error {

artifactory/commands/npm/publish_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ func TestReadPackageInfoFromTarball(t *testing.T) {
2323
filePath: filepath.Join("..", "testdata", "npm", "npm-example-0.0.4.tgz"),
2424
packageName: "npm-example",
2525
packageVersion: "0.0.4",
26+
}, {
27+
// Test case for non-standard structure where package.json is in a custom location
28+
filePath: filepath.Join("..", "testdata", "npm", "node-package-1.0.0.tgz"),
29+
packageName: "nonstandard-package",
30+
packageVersion: "1.0.0",
2631
},
2732
}
2833
for _, test := range testCases {

0 commit comments

Comments
 (0)