Skip to content

Commit

Permalink
feat: Handle symlinks, support pnpm and monorepo, (#89)
Browse files Browse the repository at this point in the history
* feat: Handle symlinks, support pnpm and monorepo, ref: #84

* chore: suggested changes
  • Loading branch information
rxliuli authored Sep 12, 2023
1 parent c92c3a2 commit 6c7d1e1
Show file tree
Hide file tree
Showing 12 changed files with 272 additions and 6 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ app-builder-bin/readme.md
app-builder-bin/**/app-builder*
app-builder-bin/win/**/app-builder.exe

/.idea/shelf/
/.idea/shelf/
node_modules/
5 changes: 4 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/develar/app-builder

go 1.17
go 1.18

require (
github.com/aclements/go-rabin v0.0.0-20170911142644-d0b643ea1a4c
Expand Down Expand Up @@ -46,11 +46,14 @@ require (
require (
github.com/hpcloud/tail v1.0.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/samber/lo v1.38.1
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect
golang.org/x/text v0.3.7 // indirect
gopkg.in/fsnotify.v1 v1.4.7 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/yaml.v2 v2.2.8 // indirect
)

require golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect

//replace github.com/develar/go-pkcs12 => ../go-pkcs12
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/go-bindata/go-bindata v1.0.0 h1:DZ34txDXWn1DyWa+vQf7V9ANc2ILTtrEjtlsdJRF26M=
github.com/go-bindata/go-bindata v1.0.0/go.mod h1:xK8Dsgwmeed+BBsSy2XTopBn/8uK2HWuGSnA11C3Joo=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
Expand Down Expand Up @@ -82,6 +84,8 @@ github.com/pkg/xattr v0.4.6 h1:0vqthLIMxQKA9VscyMcxjvAUGvyfzlk009vwLE8OZJg=
github.com/pkg/xattr v0.4.6/go.mod h1:sBD3RAqlr8Q+RC3FutZcikpT8nyDrIEEBw2J744gVWs=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM=
github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA=
github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c=
github.com/segmentio/ksuid v1.0.4/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
Expand All @@ -104,6 +108,8 @@ go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 h1:3MTrJm4PyNL9NBqvYDSj3DHl46qQakyfqfWo4jgfaEM=
golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20220302094943-723b81ca9867 h1:TcHcE0vrmgzNH1v3ppjcMGbhG5+9fMuvOmUYwNEF4q4=
golang.org/x/image v0.0.0-20220302094943-723b81ca9867/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
Expand Down
23 changes: 23 additions & 0 deletions pkg/fs/findParent.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package fs

import (
"os"
"path"
"path/filepath"
)

func pathExists(path string) bool {
_, err := os.Stat(path)
return err == nil
}

func FindParentWithFile(cwd string, file string) string {
if pathExists(path.Join(cwd, file)) {
return cwd
}
parent := filepath.Dir(cwd)
if parent == cwd {
return ""
}
return FindParentWithFile(parent, file)
}
49 changes: 49 additions & 0 deletions pkg/node-modules/cli_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package node_modules

import (
"encoding/json"
"fmt"
"os/exec"
"path"
"testing"

"github.com/develar/app-builder/pkg/fs"
. "github.com/onsi/gomega"
"github.com/samber/lo"
)

type NodeTreeDepItem struct {
Name string `json:"name"`
Version string `json:"version"`
}

type NodeTreeItem struct {
Dir string `json:"dir"`
Deps []NodeTreeDepItem `json:"deps"`
}

func nodeDepTree(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", "--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 TestNodeDepTreeCmd(t *testing.T) {
nodeDepTree(t, path.Join(Dirname(), "npm-demo"))
nodeDepTree(t, path.Join(Dirname(), "pnpm-demo"))
}
11 changes: 11 additions & 0 deletions pkg/node-modules/helper_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package node_modules

import (
"path"
"runtime"
)

func Dirname() string {
_, filename, _, _ := runtime.Caller(1)
return path.Dir(filename)
}
19 changes: 15 additions & 4 deletions pkg/node-modules/nodeModuleCollector.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import (
"path/filepath"
"strings"

"github.com/develar/app-builder/pkg/fs"
"github.com/develar/app-builder/pkg/log"
"github.com/develar/errors"
"github.com/json-iterator/go"
jsoniter "github.com/json-iterator/go"
"go.uber.org/zap"
)

Expand All @@ -21,9 +22,9 @@ 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
dir string
isOptional int
}

Expand Down Expand Up @@ -188,7 +189,11 @@ func (t *Collector) resolveDependency(parentNodeModuleDir string, name string) (
}
}

dependencyDir := filepath.Join(parentNodeModuleDir, name)
realParentNodeModuleDir := fs.FindParentWithFile(parentNodeModuleDir, name)
if realParentNodeModuleDir == "" {
return nil, nil
}
dependencyDir := filepath.Join(realParentNodeModuleDir, name)
dependency, err := readPackageJson(dependencyDir)
if err != nil {
if os.IsNotExist(err) {
Expand Down Expand Up @@ -220,6 +225,12 @@ func findNearestNodeModuleDir(dir string) (string, error) {
return "", nil
}

realDir, err := filepath.EvalSymlinks(dir)
if err != nil {
return "", errors.WithStack(err)
}
dir = realDir

guardCount := 0
for {
nodeModuleDir := filepath.Join(dir, "node_modules")
Expand Down
59 changes: 59 additions & 0 deletions pkg/node-modules/nodeModuleCollector_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package node_modules

import (
"path"
"testing"

. "github.com/onsi/gomega"
"github.com/samber/lo"
)

func TestReadDependencyTreeByNpm(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(), "npm-demo")

dependency, err := readPackageJson(dir)
dependency.dir = dir
g.Expect(err).NotTo(HaveOccurred())

err = collector.readDependencyTree(dependency)
g.Expect(err).NotTo(HaveOccurred())
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", "loose-envify",
}))
}

func TestReadDependencyTreeByPnpm(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(), "pnpm-demo")

dependency, err := readPackageJson(dir)
dependency.dir = dir
g.Expect(err).NotTo(HaveOccurred())

err = collector.readDependencyTree(dependency)
g.Expect(err).NotTo(HaveOccurred())
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", "loose-envify",
}))
}
43 changes: 43 additions & 0 deletions pkg/node-modules/npm-demo/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions pkg/node-modules/npm-demo/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "npm-demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"react": "^18.2.0"
}
}
15 changes: 15 additions & 0 deletions pkg/node-modules/pnpm-demo/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "pnpm-demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"react": "^18.2.0"
}
}
30 changes: 30 additions & 0 deletions pkg/node-modules/pnpm-demo/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 6c7d1e1

Please sign in to comment.