Skip to content

Commit

Permalink
feat: add flatten option to node-dep-tree for rendering dependency …
Browse files Browse the repository at this point in the history
…conflicts in a different manner (#116)
  • Loading branch information
beyondkmp authored Apr 29, 2024
1 parent ccb1896 commit be4e7ec
Show file tree
Hide file tree
Showing 4 changed files with 164 additions and 7 deletions.
5 changes: 5 additions & 0 deletions .changeset/twelve-islands-carry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"app-builder-bin": minor
---

feat: add flatten option to `node-dep-tree` for rendering dependency conflicts in a different manner
26 changes: 26 additions & 0 deletions pkg/node-modules/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,27 @@ type NodeTreeItem struct {
Deps []NodeTreeDepItem `json:"deps"`
}

func nodeDepPath(t *testing.T, dir string) {
g := NewGomegaWithT(t)
rootPath := fs.FindParentWithFile(Dirname(), "go.mod")
cmd := exec.Command("go", "run", path.Join(rootPath, "main.go"), "node-dep-tree", "--flatten", "--dir", dir)
output, err := cmd.Output()
if err != nil {
fmt.Println("err", err)
}
g.Expect(err).NotTo(HaveOccurred())
var j []NodeTreeItem
json.Unmarshal(output, &j)
r := lo.FlatMap(j, func(it NodeTreeItem, i int) []string {
return lo.Map(it.Deps, func(it NodeTreeDepItem, i int) string {
return it.Name
})
})
g.Expect(r).To(ConsistOf([]string{
"react", "js-tokens", "loose-envify",
}))
}

func nodeDepTree(t *testing.T, dir string) {
g := NewGomegaWithT(t)
rootPath := fs.FindParentWithFile(Dirname(), "go.mod")
Expand All @@ -47,3 +68,8 @@ func TestNodeDepTreeCmd(t *testing.T) {
nodeDepTree(t, path.Join(Dirname(), "npm-demo"))
nodeDepTree(t, path.Join(Dirname(), "pnpm-demo"))
}

func TestNodeDepPathCmd(t *testing.T) {
nodeDepTree(t, path.Join(Dirname(), "npm-demo"))
nodeDepTree(t, path.Join(Dirname(), "pnpm-demo"))
}
27 changes: 24 additions & 3 deletions pkg/node-modules/nodeModuleCollector.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@ type Dependency struct {
Version string `json:"version"`
Dependencies map[string]string `json:"dependencies"`
OptionalDependencies map[string]string `json:"optionalDependencies"`
Binary *DependencyBinary `json:"binary`
Binary *DependencyBinary `json:"binary"`

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

type Collector struct {
Expand All @@ -34,6 +35,7 @@ type Collector struct {
excludedDependencies map[string]bool

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

func (t *Collector) readDependencyTree(dependency *Dependency) error {
Expand Down Expand Up @@ -79,11 +81,30 @@ func (t *Collector) readDependencyTree(dependency *Dependency) error {
if err != nil {
return err
}
t.AddDependencyMap(queue[i], dependency)
}

return nil
}

func (t *Collector) AddDependencyMap(childDependency *Dependency, parentDependency *Dependency) {
if t.DependencyMap == nil {
t.DependencyMap = make(map[string]*Dependency)
}

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)
}
parentDependency.conflictDependency[name] = childDependency
}
} 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 {
Expand Down
113 changes: 109 additions & 4 deletions pkg/node-modules/tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@ import (
"strings"

"github.com/alecthomas/kingpin"
"github.com/json-iterator/go"
jsoniter "github.com/json-iterator/go"
)

func ConfigureCommand(app *kingpin.Application) {
command := app.Command("node-dep-tree", "")

dir := command.Flag("dir", "").Required().String()
flatten := command.Flag("flatten", "").Bool()
excludedDependencies := command.Flag("exclude-dep", "").Strings()

command.Action(func(context *kingpin.ParseContext) error {
Expand All @@ -36,15 +37,19 @@ func ConfigureCommand(app *kingpin.Application) {
if err != nil {
return err
}

dependency.dir = *dir
err = collector.readDependencyTree(dependency)
if err != nil {
return err
}

jsonWriter := jsoniter.NewStream(jsoniter.ConfigFastest, os.Stdout, 32*1024)
writeResult(jsonWriter, collector)
if *flatten {
writeFlattenResult(jsonWriter, collector)
} else {
writeResult(jsonWriter, collector)

}
err = jsonWriter.Flush()
if err != nil {
return err
Expand All @@ -54,6 +59,106 @@ func ConfigureCommand(app *kingpin.Application) {
})
}

func writeConflictDependencyList(jsonWriter *jsoniter.Stream, dependencyMap map[string]*Dependency) {
dependencies := make([]*Dependency, len(dependencyMap))
index := 0
for _, v := range dependencyMap {
dependencies[index] = v
index++
}

if len(dependencies) > 1 {
sort.Slice(dependencies, func(i, j int) bool {
return dependencies[i].Name < dependencies[j].Name
})
}

jsonWriter.WriteArrayStart()
isFirst := true
for _, dependency := range dependencies {
if isFirst {
isFirst = false
} else {
jsonWriter.WriteMore()
}

jsonWriter.WriteObjectStart()

jsonWriter.WriteObjectField("name")
jsonWriter.WriteString(dependency.Name)

jsonWriter.WriteMore()
jsonWriter.WriteObjectField("version")
jsonWriter.WriteString(dependency.Version)

jsonWriter.WriteMore()
jsonWriter.WriteObjectField("dir")
jsonWriter.WriteString(dependency.dir)

if dependency.isOptional == 1 {
jsonWriter.WriteMore()
jsonWriter.WriteObjectField("optional")
jsonWriter.WriteBool(true)
}
jsonWriter.WriteObjectEnd()
}
jsonWriter.WriteArrayEnd()

}

func writeFlattenResult(jsonWriter *jsoniter.Stream, collector *Collector) {
dependencies := make([]*Dependency, len(collector.DependencyMap))
index := 0
for _, v := range collector.DependencyMap {
dependencies[index] = v
index++
}

if len(dependencies) > 1 {
sort.Slice(dependencies, func(i, j int) bool {
return dependencies[i].Name < dependencies[j].Name
})
}

jsonWriter.WriteArrayStart()
isFirst := true
for _, dependency := range dependencies {
if isFirst {
isFirst = false
} else {
jsonWriter.WriteMore()
}

jsonWriter.WriteObjectStart()

jsonWriter.WriteObjectField("name")
jsonWriter.WriteString(dependency.Name)

jsonWriter.WriteMore()
jsonWriter.WriteObjectField("version")
jsonWriter.WriteString(dependency.Version)

jsonWriter.WriteMore()
jsonWriter.WriteObjectField("dir")
jsonWriter.WriteString(dependency.dir)

if dependency.isOptional == 1 {
jsonWriter.WriteMore()
jsonWriter.WriteObjectField("optional")
jsonWriter.WriteBool(true)
}

if dependency.conflictDependency != nil {
jsonWriter.WriteMore()
jsonWriter.WriteObjectField("conflictDependency")
writeConflictDependencyList(jsonWriter, dependency.conflictDependency)
}
jsonWriter.WriteObjectEnd()
}
jsonWriter.WriteArrayEnd()

}

func writeResult(jsonWriter *jsoniter.Stream, collector *Collector) {
moduleDirs := make([]string, len(collector.NodeModuleDirToDependencyMap))
index := 0
Expand Down Expand Up @@ -191,4 +296,4 @@ func pathSorter(a []string, b []string) bool {
}

return false
}
}

0 comments on commit be4e7ec

Please sign in to comment.