Skip to content

Commit

Permalink
fix: fix tar node module resolution and sort dependency tree (#134)
Browse files Browse the repository at this point in the history
  • Loading branch information
beyondkmp authored Sep 15, 2024
1 parent ec28d83 commit 82d3a96
Show file tree
Hide file tree
Showing 14 changed files with 4,285 additions and 22 deletions.
5 changes: 5 additions & 0 deletions .changeset/gold-hats-kneel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"app-builder-bin": patch
---

fix: fix for handling native dependencies, such as `tar` node module
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ lint:
test:
cd pkg/node-modules/pnpm-demo/ && pnpm install
cd pkg/node-modules/npm-demo/ && npm install
cd pkg/node-modules/tar-demo/ && npm install
cd pkg/node-modules/yarn-demo/ && yarn
go test -v ./pkg/...

assets:
Expand Down
7 changes: 5 additions & 2 deletions pkg/log/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"io"
"os"

"github.com/develar/app-builder/pkg/zap-cli-encoder"
zap_cli_encoder "github.com/develar/app-builder/pkg/zap-cli-encoder"
"github.com/mattn/go-colorable"
"github.com/mattn/go-isatty"
"go.uber.org/zap"
Expand Down Expand Up @@ -74,5 +74,8 @@ func Debug(msg string, fields ...zapcore.Field) {
}

func IsDebugEnabled() bool {
if LOG == nil {
return false
}
return LOG.Core().Enabled(zap.DebugLevel)
}
}
72 changes: 56 additions & 16 deletions pkg/node-modules/nodeModuleCollector.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package node_modules
import (
"os"
"path/filepath"
"sort"
"strings"

"github.com/develar/app-builder/pkg/fs"
Expand All @@ -23,22 +24,32 @@ type Dependency struct {
OptionalDependencies map[string]string `json:"optionalDependencies"`
Binary *DependencyBinary `json:"binary"`

parent *Dependency
conflictDependency map[string]*Dependency
dir string
isOptional int
alias string
}

type Collector struct {
rootDependency *Dependency
unresolvedDependencies map[string]bool

excludedDependencies map[string]bool
allDependencies []*Dependency

NodeModuleDirToDependencyMap map[string]*map[string]*Dependency `json:"nodeModuleDirToDependencyMap"`
DependencyMap map[string]*Dependency `json:"dependencyMap"`

HoiestedDependencyMap map[string]*Dependency `json:"hoiestedDependencyMap"`
}

func (t *Collector) readDependencyTree(dependency *Dependency) error {
if t.rootDependency == nil {
t.rootDependency = dependency
} else {
t.allDependencies = append(t.allDependencies, dependency)
}

maxQueueSize := len(dependency.Dependencies) + len(dependency.OptionalDependencies)

if maxQueueSize == 0 {
Expand Down Expand Up @@ -81,33 +92,62 @@ func (t *Collector) readDependencyTree(dependency *Dependency) error {
if err != nil {
return err
}
t.AddDependencyMap(queue[i], dependency)
queue[i].parent = dependency
}

return nil
}

func (t *Collector) AddDependencyMap(childDependency *Dependency, parentDependency *Dependency) {
if t.DependencyMap == nil {
t.DependencyMap = make(map[string]*Dependency)
func (t *Collector) writeToParentConflicDependency(d *Dependency) {
p := d.parent
last := d
for p != t.rootDependency {
if p.conflictDependency != nil {
if c, ok := p.conflictDependency[d.Name]; ok {
if c.Version == d.Version {
return
}
break
}
}
last = p
p = p.parent
}

name := childDependency.Name
if d, ok := t.DependencyMap[name]; ok {
if d.Version != childDependency.Version {
if parentDependency.conflictDependency == nil {
parentDependency.conflictDependency = make(map[string]*Dependency)
if last.conflictDependency == nil {
last.conflictDependency = make(map[string]*Dependency)
}
last.conflictDependency[d.Name] = d
}

func (t *Collector) processHoistDependencyMap() {
t.HoiestedDependencyMap = make(map[string]*Dependency)
for _, d := range t.allDependencies {
if e, ok := t.HoiestedDependencyMap[d.Name]; ok {
if e.Version != d.Version {
if d.parent == t.rootDependency {
t.HoiestedDependencyMap[d.Name] = d
t.writeToParentConflicDependency(e)
} else {
t.writeToParentConflicDependency(d)
}
}
parentDependency.conflictDependency[name] = childDependency
} else {
t.HoiestedDependencyMap[d.Name] = d
}
} else {
t.DependencyMap[name] = childDependency

}
}

func (t *Collector) processDependencies(list *map[string]string, nodeModuleDir string, isOptional bool, queue *[]*Dependency, queueIndex int) (int, error) {
unresolved := make([]string, 0)
for name := range *list {

names := make([]string, 0, len(*list))
for k := range *list {
names = append(names, k)
}
sort.Strings(names)

for _, name := range names {
if strings.HasPrefix(name, "@types/") {
continue
}
Expand Down Expand Up @@ -206,7 +246,7 @@ func (t *Collector) resolveDependency(parentNodeModuleDir string, name string) (
if dependencyNameToDependency != nil {
dependency := (*dependencyNameToDependency)[name]
if dependency != nil {
return nil, nil
return dependency, nil
}
}

Expand Down
68 changes: 65 additions & 3 deletions pkg/node-modules/nodeModuleCollector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,15 @@ func TestReadDependencyTreeByNpm(t *testing.T) {

err = collector.readDependencyTree(dependency)
g.Expect(err).NotTo(HaveOccurred())
collector.processHoistDependencyMap()

r := lo.FlatMap(lo.Values(collector.NodeModuleDirToDependencyMap), func(it *map[string]*Dependency, i int) []string {
return lo.Keys(*it)
})
g.Expect(r).To(ConsistOf([]string{
"js-tokens", "react", "remote", "loose-envify",
}))
remoteModule := collector.DependencyMap["@electron/remote"]
remoteModule := collector.HoiestedDependencyMap["@electron/remote"]
g.Expect(remoteModule.alias).To(Equal("remote"))
g.Expect(remoteModule.Name).To(Equal("@electron/remote"))
}
Expand All @@ -54,18 +56,78 @@ func TestReadDependencyTreeByPnpm(t *testing.T) {

err = collector.readDependencyTree(dependency)
g.Expect(err).NotTo(HaveOccurred())
collector.processHoistDependencyMap()
r := lo.FlatMap(lo.Values(collector.NodeModuleDirToDependencyMap), func(it *map[string]*Dependency, i int) []string {
return lo.Keys(*it)
})
g.Expect(r).To(ConsistOf([]string{
"js-tokens", "react", "remote", "loose-envify",
}))

remoteModule := collector.DependencyMap["@electron/remote"]
remoteModule := collector.HoiestedDependencyMap["@electron/remote"]
g.Expect(remoteModule.Name).To(Equal("@electron/remote"))
g.Expect(remoteModule.alias).To(Equal("remote"))
g.Expect(remoteModule.dir).To(Equal(filepath.Join(dir, "node_modules/.pnpm/@[email protected][email protected]/node_modules/@electron/remote")))

reactModule := collector.DependencyMap["react"]
reactModule := collector.HoiestedDependencyMap["react"]
g.Expect(reactModule.dir).To(Equal(filepath.Join(dir, "node_modules/.pnpm/[email protected]/node_modules/react")))
}

func TestReadDependencyTreeForTar(t *testing.T) {
g := NewGomegaWithT(t)

collector := &Collector{
unresolvedDependencies: make(map[string]bool),
excludedDependencies: make(map[string]bool),
NodeModuleDirToDependencyMap: make(map[string]*map[string]*Dependency),
}

dir := path.Join(Dirname(), "tar-demo")

dependency, err := readPackageJson(dir)
dependency.dir = dir

g.Expect(err).NotTo(HaveOccurred())

err = collector.readDependencyTree(dependency)
g.Expect(err).NotTo(HaveOccurred())
collector.processHoistDependencyMap()

r := lo.FlatMap(lo.Values(collector.NodeModuleDirToDependencyMap), func(it *map[string]*Dependency, i int) []string {
return lo.Keys(*it)
})
g.Expect(len(r)).To(Equal(46))

g.Expect(collector.HoiestedDependencyMap["tar"].dir).To(Equal(filepath.Join(dir, "node_modules/tar")))
g.Expect(collector.HoiestedDependencyMap["minipass"].Version).To(Equal("7.1.2"))
g.Expect(collector.HoiestedDependencyMap["minizlib"].Version).To(Equal("3.0.1"))
g.Expect(collector.HoiestedDependencyMap["tar"].conflictDependency["ansi-regex"].Version).To(Equal("5.0.1"))
}

func TestReadDependencyTreeForYarn(t *testing.T) {
g := NewGomegaWithT(t)

collector := &Collector{
unresolvedDependencies: make(map[string]bool),
excludedDependencies: make(map[string]bool),
NodeModuleDirToDependencyMap: make(map[string]*map[string]*Dependency),
}

dir := path.Join(Dirname(), "yarn-demo/packages/test-app")

dependency, err := readPackageJson(dir)
dependency.dir = dir

g.Expect(err).NotTo(HaveOccurred())

err = collector.readDependencyTree(dependency)
g.Expect(err).NotTo(HaveOccurred())
collector.processHoistDependencyMap()

g.Expect(collector.HoiestedDependencyMap["foo"].dir).To(Equal(filepath.Join(Dirname(), "yarn-demo/packages/foo")))
g.Expect(collector.HoiestedDependencyMap["foo"].Version).To(Equal("1.0.0"))
g.Expect(collector.HoiestedDependencyMap["foo"].conflictDependency["ms"].dir).To(Equal(filepath.Join(Dirname(), "yarn-demo/node_modules/ms")))
g.Expect(collector.HoiestedDependencyMap["foo"].conflictDependency["ms"].Version).To(Equal("2.0.0"))
g.Expect(collector.HoiestedDependencyMap["ms"].Version).To(Equal("2.1.1"))
g.Expect(collector.HoiestedDependencyMap["ms"].dir).To(Equal(filepath.Join(Dirname(), "yarn-demo/packages/test-app/node_modules/ms")))
}
Loading

0 comments on commit 82d3a96

Please sign in to comment.