Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove dep tree folder #81

Merged
merged 9 commits into from
Feb 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ linters-settings:
min-len: 2
min-occurrences: 2
gocyclo:
min-complexity: 15
min-complexity: 16
godot:
check-all: true
goimports:
Expand Down
15 changes: 12 additions & 3 deletions cmd/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package cmd
import (
"fmt"

"github.com/gabotechs/dep-tree/internal/graph"
"github.com/gabotechs/dep-tree/internal/language"
"github.com/spf13/cobra"

"github.com/gabotechs/dep-tree/internal/check"
Expand All @@ -26,15 +28,22 @@ func CheckCmd() *cobra.Command {
if len(cfg.Check.Entrypoints) == 0 {
return fmt.Errorf(`config file "%s" has no entrypoints`, configPath)
}
parserBuilder, err := makeParserBuilder(cfg.Check.Entrypoints, cfg)
lang, err := inferLang(cfg.Check.Entrypoints, cfg)
if err != nil {
return err
}
parser, err := parserBuilder(args)
parser := language.NewParser(lang)
parser.UnwrapProxyExports = cfg.UnwrapExports
parser.Exclude = cfg.Exclude
if err != nil {
return err
}
return check.Check(parser, &cfg.Check)
return check.Check[*language.FileInfo](
parser,
func(node *graph.Node[*language.FileInfo]) string { return node.Data.RelPath },
&cfg.Check,
graph.NewStdErrCallbacks[*language.FileInfo](),
)
},
}
}
18 changes: 10 additions & 8 deletions cmd/entropy.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package cmd

import (
"github.com/gabotechs/dep-tree/internal/graph"
"github.com/gabotechs/dep-tree/internal/language"
"github.com/spf13/cobra"

"github.com/gabotechs/dep-tree/internal/entropy"
Expand All @@ -24,17 +26,17 @@ func EntropyCmd() *cobra.Command {
if err != nil {
return err
}
parserBuilder, err := makeParserBuilder(files, cfg)
lang, err := inferLang(files, cfg)
if err != nil {
return err
}
parser, err := parserBuilder(files)
if err != nil {
return err
}
err = entropy.Render(parser, files, entropy.RenderConfig{
NoOpen: noBrowserOpen,
EnableGui: enableGui,
parser := language.NewParser(lang)
parser.UnwrapProxyExports = cfg.UnwrapExports
parser.Exclude = cfg.Exclude
err = entropy.Render(files, parser, entropy.RenderConfig{
NoOpen: noBrowserOpen,
EnableGui: enableGui,
LoadCallbacks: graph.NewStdErrCallbacks[*language.FileInfo](),
})
return err
},
Expand Down
30 changes: 17 additions & 13 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,14 @@ import (
"os"
"path/filepath"

"github.com/spf13/cobra"
"github.com/spf13/pflag"

"github.com/gabotechs/dep-tree/internal/config"
"github.com/gabotechs/dep-tree/internal/js"
"github.com/gabotechs/dep-tree/internal/language"
"github.com/gabotechs/dep-tree/internal/python"
"github.com/gabotechs/dep-tree/internal/rust"
"github.com/gabotechs/dep-tree/internal/utils"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)

const renderGroupId = "render"
Expand Down Expand Up @@ -93,7 +92,7 @@ $ dep-tree check`,
return root
}

func inferLang(files []string) string {
func inferLang(files []string, cfg *config.Config) (language.Language, error) {
score := struct {
js int
python int
Expand Down Expand Up @@ -125,20 +124,16 @@ func inferLang(files []string) string {
}
}
}
return top.lang
}

func makeParserBuilder(files []string, cfg *config.Config) (language.NodeParserBuilder, error) {
if len(files) == 0 {
if top.lang == "" {
return nil, errors.New("at least one file must be provided")
}
switch inferLang(files) {
switch top.lang {
case "js":
return language.ParserBuilder(js.MakeJsLanguage, &cfg.Js, cfg), nil
return js.MakeJsLanguage(&cfg.Js)
case "rust":
return language.ParserBuilder(rust.MakeRustLanguage, &cfg.Rust, cfg), nil
return rust.MakeRustLanguage(&cfg.Rust)
case "python":
return language.ParserBuilder(python.MakePythonLanguage, &cfg.Python, cfg), nil
return python.MakePythonLanguage(&cfg.Python)
default:
return nil, fmt.Errorf("file \"%s\" not supported", files[0])
}
Expand Down Expand Up @@ -188,6 +183,15 @@ func loadConfig() (*config.Config, error) {
cfg.Python.IgnoreFromImportsAsExports = true
cfg.Python.IgnoreDirectoryImports = true

absExclude := make([]string, len(exclude))
for i, file := range exclude {
if !filepath.IsAbs(file) {
cwd, _ := os.Getwd()
absExclude[i] = filepath.Join(cwd, file)
} else {
absExclude[i] = file
}
}
cfg.Exclude = append(cfg.Exclude, exclude...)
// validate exclusion patterns.
for _, exclusion := range cfg.Exclude {
Expand Down
28 changes: 20 additions & 8 deletions cmd/root_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ import (
"strings"
"testing"

"github.com/gabotechs/dep-tree/internal/config"
"github.com/gabotechs/dep-tree/internal/js"
"github.com/gabotechs/dep-tree/internal/language"
"github.com/gabotechs/dep-tree/internal/python"
"github.com/gabotechs/dep-tree/internal/rust"
"github.com/stretchr/testify/require"

"github.com/gabotechs/dep-tree/internal/utils"
Expand Down Expand Up @@ -107,34 +112,41 @@ func TestInferLang(t *testing.T) {
tests := []struct {
Name string
Files []string
Expected string
Expected language.Language
Error string
}{
{
Name: "only 1 file",
Files: []string{"foo.js"},
Expected: "js",
Expected: &js.Language{},
},
{
Name: "majority of files",
Files: []string{"foo.js", "bar.rs", "foo.rs", "foo.py"},
Expected: "rust",
Expected: &rust.Language{},
},
{
Name: "unrelated files",
Files: []string{"foo.py", "foo.pdf"},
Expected: "python",
Expected: &python.Language{},
},
{
Name: "no match",
Files: []string{"foo.pdf", "bar.docx"},
Expected: "",
Name: "no match",
Files: []string{"foo.pdf", "bar.docx"},
Error: "at least one file must be provided",
},
}

for _, tt := range tests {
t.Run(tt.Name, func(t *testing.T) {
a := require.New(t)
a.Equal(tt.Expected, inferLang(tt.Files))
lang, err := inferLang(tt.Files, &config.Config{})
if tt.Error != "" {
a.ErrorContains(err, tt.Error)
} else {
a.NoError(err)
a.IsType(tt.Expected, lang)
}
})
}
}
26 changes: 22 additions & 4 deletions cmd/tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package cmd
import (
"fmt"

"github.com/gabotechs/dep-tree/internal/graph"
"github.com/gabotechs/dep-tree/internal/language"
"github.com/gabotechs/dep-tree/internal/tree"
"github.com/spf13/cobra"

Expand All @@ -27,22 +29,38 @@ func TreeCmd() *cobra.Command {
return err
}

parserBuilder, err := makeParserBuilder(files, cfg)
lang, err := inferLang(files, cfg)
if err != nil {
return err
}

parser := language.NewParser(lang)
parser.UnwrapProxyExports = cfg.UnwrapExports
parser.Exclude = cfg.Exclude

if jsonFormat {
parser, err := parserBuilder(files)
t, err := tree.NewTree[*language.FileInfo](
files,
parser,
func(node *graph.Node[*language.FileInfo]) string { return node.Data.RelPath },
graph.NewStdErrCallbacks[*language.FileInfo](),
)
if err != nil {
return err
}

rendered, err := tree.PrintStructured(files, parser)
rendered, err := t.RenderStructured()
fmt.Println(rendered)
return err
} else {
return tui.Loop(files, parserBuilder, nil, true, nil)
return tui.Loop[*language.FileInfo](
files,
parser,
func(node *graph.Node[*language.FileInfo]) string { return node.Data.RelPath },
nil,
true,
nil,
graph.NewStdErrCallbacks[*language.FileInfo]())
}
},
}
Expand Down
39 changes: 25 additions & 14 deletions internal/check/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,31 @@ import (
"path/filepath"
"strings"

"github.com/gabotechs/dep-tree/internal/dep_tree"
"github.com/gabotechs/dep-tree/internal/graph"
"github.com/gabotechs/dep-tree/internal/utils"
)

func Check[T any](parser dep_tree.NodeParser[T], cfg *Config) error {
dt := dep_tree.NewDepTree(parser, cfg.Entrypoints).WithStdErrLoader()
err := dt.LoadGraph()
func Check[T any](
parser graph.NodeParser[T],
display func(node *graph.Node[T]) string,
cfg *Config,
callbacks graph.LoadCallbacks[T],
) error {
// 1. build the graph.
files := make([]string, len(cfg.Entrypoints))
for i, file := range cfg.Entrypoints {
files[i] = filepath.Join(cfg.Path, file)
}

g := graph.NewGraph[T]()
err := g.Load(files, parser, callbacks)
if err != nil {
return err
}
// 1. Check for rule violations in the graph.
// 2. Check for rule violations in the graph.
failures := make([]string, 0)
for _, node := range dt.Graph.AllNodes() {
for _, dep := range dt.Graph.FromId(node.Id) {
for _, node := range g.AllNodes() {
for _, dep := range g.FromId(node.Id) {
from, to := cfg.rel(node.Id), cfg.rel(dep.Id)
pass, err := cfg.Check(from, to)
if err != nil {
Expand All @@ -28,14 +39,14 @@ func Check[T any](parser dep_tree.NodeParser[T], cfg *Config) error {
}
}
}
// 2. Check for cycles.
dt.LoadCycles()
// 3. Check for cycles.
cycles := g.RemoveElementaryCycles()
if !cfg.AllowCircularDependencies {
for el := dt.Cycles.Front(); el != nil; el = el.Next() {
formattedCycleStack := make([]string, len(el.Value.Stack))
for i, el := range el.Value.Stack {
if node := dt.Graph.Get(el); node != nil {
formattedCycleStack[i] = parser.Display(node).Name
for _, cycle := range cycles {
formattedCycleStack := make([]string, len(cycle.Stack))
for i, el := range cycle.Stack {
if node := g.Get(el); node != nil {
formattedCycleStack[i] = display(node)
} else {
formattedCycleStack[i] = el
}
Expand Down
12 changes: 8 additions & 4 deletions internal/check/check_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@ import (
"strings"
"testing"

"github.com/gabotechs/dep-tree/internal/graph"
"github.com/stretchr/testify/require"

"github.com/gabotechs/dep-tree/internal/dep_tree"
)

func TestCheck(t *testing.T) {
Expand Down Expand Up @@ -37,7 +36,7 @@ func TestCheck(t *testing.T) {
Failures: []string{
"0 -> 3",
"4 -> 3",
"detected circular dependency: 3 -> 4 -> 3",
"detected circular dependency: 4 -> 3 -> 4",
},
},
}
Expand All @@ -46,7 +45,12 @@ func TestCheck(t *testing.T) {
t.Run(tt.Name, func(t *testing.T) {
a := require.New(t)

err := Check[[]int](&dep_tree.TestParser{Spec: tt.Spec}, tt.Config)
err := Check[[]int](
&graph.TestParser{Spec: tt.Spec},
func(node *graph.Node[[]int]) string { return node.Id },
tt.Config,
nil,
)
if tt.Failures != nil {
msg := err.Error()
failures := strings.Split(msg, "\n")
Expand Down
5 changes: 5 additions & 0 deletions internal/config/.config_test/.excludes.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
exclude:
- '**/foo.js'
- '*/foo.js'
- 'foo/**/foo.js'
- '/**/*.js'
15 changes: 15 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ func ParseConfig(cfgPath string) (*Config, error) {
cfgPath = DefaultConfigPath
}
content, err := os.ReadFile(cfgPath)
// If a specific path was requested, and it does not exist, fail
// If no specific path was requested, and the default config path does not exist, succeed
if os.IsNotExist(err) {
if !isDefault {
return &cfg, err
Expand All @@ -78,6 +80,19 @@ func ParseConfig(cfgPath string) (*Config, error) {
if err != nil {
return &cfg, fmt.Errorf(`config file "%s" is not a valid yml file: %w`, cfgPath, err)
}

exclude := make([]string, len(cfg.Exclude))
for i, pattern := range cfg.Exclude {
if !filepath.IsAbs(pattern) {
exclude[i] = filepath.Join(cfg.Path, pattern)
} else {
exclude[i] = pattern
}
}
if len(exclude) > 0 {
cfg.Exclude = exclude
}

cfg.Check.Init(filepath.Dir(absCfgPath))
return &cfg, nil
}
Loading
Loading