mirror of
https://gitee.com/johng/gf
synced 2026-07-07 22:27:06 +08:00
Apply gci import order changes
This commit is contained in:
@ -40,8 +40,8 @@ type PackageKind int
|
||||
|
||||
const (
|
||||
KindInternal PackageKind = iota // Internal to main module
|
||||
KindExternal // External dependency
|
||||
KindStdLib // Standard library
|
||||
KindExternal // External dependency
|
||||
KindStdLib // Standard library
|
||||
)
|
||||
|
||||
// PackageInfo represents unified information about a Go package.
|
||||
@ -79,11 +79,11 @@ type FilterOptions struct {
|
||||
// It centralizes visited tracking, depth management, and filtering logic
|
||||
// to ensure consistent behavior across different output formats.
|
||||
type TraversalContext struct {
|
||||
visited map[string]bool // Track visited packages to prevent cycles
|
||||
depth int // Current traversal depth
|
||||
maxDepth int // Maximum traversal depth
|
||||
options *FilterOptions // Filtering criteria
|
||||
store *PackageStore // Reference to package store
|
||||
visited map[string]bool // Track visited packages to prevent cycles
|
||||
depth int // Current traversal depth
|
||||
maxDepth int // Maximum traversal depth
|
||||
options *FilterOptions // Filtering criteria
|
||||
store *PackageStore // Reference to package store
|
||||
}
|
||||
|
||||
// PackageStore manages a collection of packages and provides unified data access.
|
||||
@ -91,10 +91,10 @@ type TraversalContext struct {
|
||||
// It replaces the scattered data access patterns in the original analyzer.
|
||||
type PackageStore struct {
|
||||
packages map[string]*PackageInfo // Package data indexed by import path
|
||||
modulePrefix string // Main module path (from go.mod)
|
||||
sortedPkgs []string // Cached sorted package list
|
||||
internalCount int // Cached count of internal packages
|
||||
externalCount int // Cached count of external packages
|
||||
modulePrefix string // Main module path (from go.mod)
|
||||
sortedPkgs []string // Cached sorted package list
|
||||
internalCount int // Cached count of internal packages
|
||||
externalCount int // Cached count of external packages
|
||||
}
|
||||
|
||||
// analyzer handles dependency analysis.
|
||||
@ -105,9 +105,9 @@ type analyzer struct {
|
||||
edges map[string]bool
|
||||
store *PackageStore // New unified package store
|
||||
// Module-level dependency data (from go mod graph)
|
||||
modules map[string]*ModuleInfo // All modules indexed by path
|
||||
moduleGraph map[string][]string // Module dependency graph: module -> dependencies
|
||||
directModules map[string]bool // Direct dependencies (from go.mod require)
|
||||
modules map[string]*ModuleInfo // All modules indexed by path
|
||||
moduleGraph map[string][]string // Module dependency graph: module -> dependencies
|
||||
directModules map[string]bool // Direct dependencies (from go.mod require)
|
||||
}
|
||||
|
||||
// ModuleInfo represents a Go module dependency.
|
||||
@ -169,12 +169,13 @@ func (a *analyzer) detectModulePrefix() string {
|
||||
// loadPackages loads package information using go list with optimized approach.
|
||||
// OPTIMIZATION: Reduced from 3 separate go list calls to 2 efficient calls:
|
||||
// Previously:
|
||||
// 1. go list -json %s (target packages only)
|
||||
// 2. go list -json -deps %s (with dependencies)
|
||||
// 3. go list -json -m all (all modules)
|
||||
// 1. go list -json %s (target packages only)
|
||||
// 2. go list -json -deps %s (with dependencies)
|
||||
// 3. go list -json -m all (all modules)
|
||||
//
|
||||
// Now (optimized):
|
||||
// 1. go list -json -m all (all modules - fast, definitive)
|
||||
// 2. go list -json -deps ./... (all packages with dependencies)
|
||||
// 1. go list -json -m all (all modules - fast, definitive)
|
||||
// 2. go list -json -deps ./... (all packages with dependencies)
|
||||
func (a *analyzer) loadPackages(ctx context.Context, pkgPath string) error {
|
||||
// First, load module information - this is fast and provides module metadata
|
||||
// Load all module dependencies using go list -m all
|
||||
@ -659,13 +660,13 @@ func (a *analyzer) convertInputToFilterOptions(in Input) *FilterOptions {
|
||||
IncludeStdLib: !in.NoStd,
|
||||
Depth: in.Depth,
|
||||
}
|
||||
|
||||
|
||||
// Apply default: if neither internal nor external, include internal only
|
||||
if !in.Internal && !in.External {
|
||||
opts.IncludeInternal = true
|
||||
opts.IncludeExternal = false
|
||||
}
|
||||
|
||||
|
||||
return opts
|
||||
}
|
||||
|
||||
@ -736,14 +737,14 @@ func (opts *FilterOptions) Normalize(modulePrefix string) error {
|
||||
opts.IncludeInternal = true
|
||||
opts.IncludeExternal = false
|
||||
}
|
||||
|
||||
|
||||
// Always include stdlib by default unless explicitly excluded
|
||||
if opts.IncludeStdLib == false {
|
||||
// This is the default (NoStd=true), stdlib is excluded
|
||||
} else {
|
||||
opts.IncludeStdLib = true
|
||||
}
|
||||
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -764,7 +765,7 @@ func (opts *FilterOptions) ShouldInclude(pkg *PackageInfo) bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@ -783,26 +784,26 @@ func (tc *TraversalContext) GetDependencies(pkg string) []string {
|
||||
if !ok {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
|
||||
result := make([]string, 0)
|
||||
seen := make(map[string]bool)
|
||||
|
||||
|
||||
for _, dep := range pkgInfo.Imports {
|
||||
if seen[dep] {
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
depInfo, ok := tc.store.packages[dep]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
if tc.options.ShouldInclude(depInfo) {
|
||||
seen[dep] = true
|
||||
result = append(result, dep)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@ -819,7 +820,7 @@ func (a *analyzer) shortName(pkg string, group bool) string {
|
||||
}
|
||||
return short
|
||||
}
|
||||
|
||||
|
||||
// For external packages, handle grouping differently
|
||||
if group {
|
||||
return a.getExternalGroup(pkg)
|
||||
@ -833,7 +834,7 @@ func (a *analyzer) getExternalGroup(pkg string) string {
|
||||
if a.isStdLib(pkg) {
|
||||
return "stdlib"
|
||||
}
|
||||
|
||||
|
||||
// For external packages, group by domain/organization
|
||||
parts := gstr.Split(pkg, "/")
|
||||
if len(parts) > 0 {
|
||||
@ -870,7 +871,7 @@ func (a *analyzer) getSortedPackages() []string {
|
||||
func (a *analyzer) collectEdges(in Input) map[string]bool {
|
||||
opts := a.convertInputToFilterOptions(in)
|
||||
opts.Normalize(a.modulePrefix)
|
||||
|
||||
|
||||
store := a.buildPackageStore()
|
||||
edges := make(map[string]bool)
|
||||
visited := make(map[string]bool)
|
||||
@ -898,7 +899,7 @@ func (a *analyzer) collectEdgesRecursiveNew(pkgPath string, opts *FilterOptions,
|
||||
visited[pkgPath] = true
|
||||
|
||||
fromName := a.shortName(pkgPath, in.Group)
|
||||
|
||||
|
||||
for _, dep := range pkgInfo.Imports {
|
||||
depInfo, ok := store.packages[dep]
|
||||
if !ok || !opts.ShouldInclude(depInfo) {
|
||||
@ -918,10 +919,10 @@ func (a *analyzer) collectEdgesRecursiveNew(pkgPath string, opts *FilterOptions,
|
||||
// getDependencyStats returns statistics about dependencies using new system.
|
||||
func (a *analyzer) getDependencyStats(_ Input) map[string]any {
|
||||
stats := make(map[string]any)
|
||||
|
||||
|
||||
var internalCount, externalCount, stdlibCount int
|
||||
externalGroups := make(map[string]int)
|
||||
|
||||
|
||||
store := a.buildPackageStore()
|
||||
opts := &FilterOptions{
|
||||
IncludeInternal: true,
|
||||
@ -929,12 +930,12 @@ func (a *analyzer) getDependencyStats(_ Input) map[string]any {
|
||||
IncludeStdLib: true,
|
||||
Depth: 0,
|
||||
}
|
||||
|
||||
|
||||
for _, pkgInfo := range store.packages {
|
||||
if !opts.ShouldInclude(pkgInfo) {
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
if pkgInfo.IsStdLib {
|
||||
stdlibCount++
|
||||
} else if pkgInfo.Kind == KindInternal {
|
||||
@ -945,13 +946,13 @@ func (a *analyzer) getDependencyStats(_ Input) map[string]any {
|
||||
externalGroups[group]++
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
stats["total"] = len(store.packages)
|
||||
stats["internal"] = internalCount
|
||||
stats["external"] = externalCount
|
||||
stats["stdlib"] = stdlibCount
|
||||
stats["external_groups"] = externalGroups
|
||||
|
||||
|
||||
return stats
|
||||
}
|
||||
|
||||
@ -966,7 +967,7 @@ func (ps *PackageStore) TraverseDependencies(
|
||||
options: options,
|
||||
store: ps,
|
||||
}
|
||||
|
||||
|
||||
result := make([]string, 0)
|
||||
ps.traverseRecursive(root, ctx, &result)
|
||||
return result
|
||||
@ -981,22 +982,22 @@ func (ps *PackageStore) traverseRecursive(
|
||||
if ctx.maxDepth > 0 && ctx.depth >= ctx.maxDepth {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if ctx.Visit(pkg) {
|
||||
return // Already visited
|
||||
}
|
||||
|
||||
|
||||
pkgInfo, ok := ps.packages[pkg]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if !ctx.options.ShouldInclude(pkgInfo) {
|
||||
return // Filtered out
|
||||
}
|
||||
|
||||
|
||||
*result = append(*result, pkg)
|
||||
|
||||
|
||||
ctx.depth++
|
||||
for _, dep := range pkgInfo.Imports {
|
||||
ps.traverseRecursive(dep, ctx, result)
|
||||
@ -1010,7 +1011,7 @@ func (ps *PackageStore) TraverseReverse(
|
||||
options *FilterOptions,
|
||||
) []string {
|
||||
result := make([]string, 0)
|
||||
|
||||
|
||||
// Build reverse dependency map on-the-fly
|
||||
for _, pkg := range ps.packages {
|
||||
for _, dep := range pkg.Imports {
|
||||
@ -1019,7 +1020,7 @@ func (ps *PackageStore) TraverseReverse(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sort.Strings(result)
|
||||
return result
|
||||
}
|
||||
@ -1027,7 +1028,7 @@ func (ps *PackageStore) TraverseReverse(
|
||||
// buildPackageStore converts current analyzer state to PackageStore for new traversal system.
|
||||
func (a *analyzer) buildPackageStore() *PackageStore {
|
||||
store := newPackageStore(a.modulePrefix)
|
||||
|
||||
|
||||
// Convert go packages to PackageInfo
|
||||
for path, goPkg := range a.packages {
|
||||
pkgInfo := &PackageInfo{
|
||||
@ -1039,7 +1040,7 @@ func (a *analyzer) buildPackageStore() *PackageStore {
|
||||
pkgInfo.Kind = store.identifyPackageKind(pkgInfo)
|
||||
store.packages[path] = pkgInfo
|
||||
}
|
||||
|
||||
|
||||
return store
|
||||
}
|
||||
|
||||
@ -1047,16 +1048,16 @@ func (a *analyzer) buildPackageStore() *PackageStore {
|
||||
func (a *analyzer) getFilteredPackages(in Input) []*PackageInfo {
|
||||
opts := a.convertInputToFilterOptions(in)
|
||||
opts.Normalize(a.modulePrefix)
|
||||
|
||||
|
||||
store := a.buildPackageStore()
|
||||
result := make([]*PackageInfo, 0)
|
||||
|
||||
|
||||
for _, pkgInfo := range store.packages {
|
||||
if opts.ShouldInclude(pkgInfo) {
|
||||
result = append(result, pkgInfo)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@ -1066,16 +1067,16 @@ func (a *analyzer) getFilteredDependencies(pkgPath string, in Input) []string {
|
||||
if !ok {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
|
||||
opts := a.convertInputToFilterOptions(in)
|
||||
opts.Normalize(a.modulePrefix)
|
||||
|
||||
|
||||
store := a.buildPackageStore()
|
||||
ctx := &TraversalContext{
|
||||
visited: make(map[string]bool),
|
||||
options: opts,
|
||||
store: store,
|
||||
}
|
||||
|
||||
|
||||
return ctx.GetDependencies(pkgPath)
|
||||
}
|
||||
|
||||
@ -104,4 +104,4 @@ func TestDependencyStats(t *testing.T) {
|
||||
t.Assert(stats["external"], 1)
|
||||
t.Assert(stats["stdlib"], 1)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -48,7 +48,7 @@ func (a *analyzer) generateTree(in Input) string {
|
||||
fmt.Fprintf(&sb, " Internal: %v\n", stats["internal"])
|
||||
fmt.Fprintf(&sb, " External: %v\n", stats["external"])
|
||||
fmt.Fprintf(&sb, " Standard library: %v\n", stats["stdlib"])
|
||||
|
||||
|
||||
if groups, ok := stats["external_groups"].(map[string]int); ok && len(groups) > 0 {
|
||||
sb.WriteString(" External groups:\n")
|
||||
for group, count := range groups {
|
||||
@ -77,10 +77,10 @@ func (a *analyzer) generateTree(in Input) string {
|
||||
if !ok || !opts.ShouldInclude(pkgInfo) {
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
shortName := a.shortName(pkgPath, in.Group)
|
||||
sb.WriteString(shortName + "\n")
|
||||
|
||||
|
||||
// Use new traversal system
|
||||
a.printTreeNodeNew(&sb, pkgPath, "", in, ctx, 0)
|
||||
}
|
||||
@ -114,8 +114,6 @@ func (a *analyzer) findRootPackages() []string {
|
||||
return roots
|
||||
}
|
||||
|
||||
|
||||
|
||||
// printTreeNodeNew prints tree node using new traversal system.
|
||||
func (a *analyzer) printTreeNodeNew(sb *strings.Builder, pkgPath string, prefix string, in Input, ctx *TraversalContext, depth int) {
|
||||
if ctx.maxDepth > 0 && depth >= ctx.maxDepth {
|
||||
@ -163,16 +161,16 @@ func (a *analyzer) printTreeNodeNew(sb *strings.Builder, pkgPath string, prefix
|
||||
// generateList generates simple list output using new traversal system.
|
||||
func (a *analyzer) generateList(in Input) string {
|
||||
var sb strings.Builder
|
||||
|
||||
|
||||
// Prepare options
|
||||
opts := a.convertInputToFilterOptions(in)
|
||||
opts.Normalize(a.modulePrefix)
|
||||
|
||||
|
||||
// Add statistics header if showing external dependencies
|
||||
if in.External {
|
||||
stats := a.getDependencyStats(in)
|
||||
sb.WriteString("# Dependency Statistics\n")
|
||||
fmt.Fprintf(&sb, "# Total: %v, Internal: %v, External: %v, Stdlib: %v\n",
|
||||
fmt.Fprintf(&sb, "# Total: %v, Internal: %v, External: %v, Stdlib: %v\n",
|
||||
stats["total"], stats["internal"], stats["external"], stats["stdlib"])
|
||||
sb.WriteString("\n")
|
||||
}
|
||||
@ -278,7 +276,7 @@ func (a *analyzer) generateJSON(in Input) string {
|
||||
store := a.buildPackageStore()
|
||||
|
||||
result := make(map[string]any)
|
||||
|
||||
|
||||
// Add dependency nodes
|
||||
nodes := make([]*depNode, 0)
|
||||
for _, pkgPath := range a.getSortedPackages() {
|
||||
@ -293,10 +291,10 @@ func (a *analyzer) generateJSON(in Input) string {
|
||||
nodes = append(nodes, node)
|
||||
}
|
||||
result["dependencies"] = nodes
|
||||
|
||||
|
||||
// Add statistics
|
||||
result["statistics"] = a.getDependencyStats(in)
|
||||
|
||||
|
||||
// Add metadata
|
||||
result["metadata"] = map[string]any{
|
||||
"module": a.modulePrefix,
|
||||
@ -307,7 +305,7 @@ func (a *analyzer) generateJSON(in Input) string {
|
||||
"external": in.External,
|
||||
"nostd": in.NoStd,
|
||||
}
|
||||
|
||||
|
||||
data, err := json.MarshalIndent(result, "", " ")
|
||||
if err != nil {
|
||||
return fmt.Sprintf("Error: %v", err)
|
||||
@ -361,7 +359,7 @@ func (a *analyzer) buildDepNode(pkg *goPackage, in Input, depth int) *depNode {
|
||||
func (a *analyzer) generateReverse(in Input) string {
|
||||
opts := a.convertInputToFilterOptions(in)
|
||||
opts.Normalize(a.modulePrefix)
|
||||
|
||||
|
||||
store := a.buildPackageStore()
|
||||
|
||||
// Build reverse dependency map
|
||||
|
||||
@ -195,7 +195,7 @@ func (a *analyzer) handleGraphAPI(w http.ResponseWriter, r *http.Request, in Inp
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
data := a.buildModuleGraphData(in)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(data)
|
||||
@ -288,13 +288,13 @@ func (a *analyzer) handlePackagesAPI(w http.ResponseWriter, r *http.Request, in
|
||||
UsedByCount: usedByMap[shortName],
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// Add statistics to response
|
||||
result := map[string]any{
|
||||
"packages": packages,
|
||||
"statistics": a.getDependencyStats(in),
|
||||
}
|
||||
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(result)
|
||||
}
|
||||
@ -398,7 +398,7 @@ func (a *analyzer) handleTreeAPI(w http.ResponseWriter, r *http.Request, in Inpu
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
output := a.generateModuleTree(in)
|
||||
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
||||
w.Write([]byte(output))
|
||||
@ -442,7 +442,7 @@ func (a *analyzer) handleListAPI(w http.ResponseWriter, r *http.Request, in Inpu
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
output := a.generateModuleList(in)
|
||||
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
||||
w.Write([]byte(output))
|
||||
@ -880,7 +880,7 @@ func (s *serverState) handleAnalyzeAPI(w http.ResponseWriter, r *http.Request) {
|
||||
// IMPORTANT: Load module dependencies from the actual module directory
|
||||
// We need to run commands in moduleDir context to get the correct direct/indirect info
|
||||
// Running in tempDir would show everything as indirect since we're not using them there
|
||||
|
||||
|
||||
// IMPORTANT: Load direct dependencies info FIRST
|
||||
// This must be done before parsing go mod graph so that IsDirect flag is correct
|
||||
listModCmd := exec.Command("go", "list", "-m", "-json", "all")
|
||||
@ -1051,7 +1051,7 @@ func (a *analyzer) handleModulesAPI(w http.ResponseWriter, in Input) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
modules := a.getFilteredModules(in)
|
||||
|
||||
// Build module list
|
||||
|
||||
@ -41,7 +41,7 @@ func Test_FilterOptions_Normalize(t *testing.T) {
|
||||
IncludeStdLib: false,
|
||||
}
|
||||
opts.Normalize("github.com/gogf/gf/v2")
|
||||
|
||||
|
||||
// After normalization, internal should be included by default
|
||||
t.Assert(opts.IncludeInternal, true)
|
||||
t.Assert(opts.IncludeExternal, false)
|
||||
@ -57,7 +57,7 @@ func Test_FilterOptions_ShouldInclude(t *testing.T) {
|
||||
IncludeExternal: false,
|
||||
IncludeStdLib: true,
|
||||
}
|
||||
|
||||
|
||||
internalPkg := &PackageInfo{
|
||||
Kind: KindInternal,
|
||||
IsStdLib: false,
|
||||
@ -70,7 +70,7 @@ func Test_FilterOptions_ShouldInclude(t *testing.T) {
|
||||
Kind: KindStdLib,
|
||||
IsStdLib: true,
|
||||
}
|
||||
|
||||
|
||||
t.Assert(opts.ShouldInclude(internalPkg), true)
|
||||
t.Assert(opts.ShouldInclude(externalPkg), false)
|
||||
t.Assert(opts.ShouldInclude(stdlibPkg), true)
|
||||
@ -83,7 +83,7 @@ func Test_TraversalContext_Visit(t *testing.T) {
|
||||
ctx := &TraversalContext{
|
||||
visited: make(map[string]bool),
|
||||
}
|
||||
|
||||
|
||||
// First visit should return false
|
||||
t.Assert(ctx.Visit("pkg1"), false)
|
||||
// Second visit should return true
|
||||
@ -105,7 +105,7 @@ func Test_GuessModuleRoot(t *testing.T) {
|
||||
{"github.com/gogf/gf/v2/os/gfile", "github.com/gogf/gf/v2"},
|
||||
{"github.com/gogf/gf/v2/os", "github.com/gogf/gf/v2"},
|
||||
}
|
||||
|
||||
|
||||
for _, test := range tests {
|
||||
result := guessModuleRoot(test.input)
|
||||
t.AssertEQ(result, test.expect)
|
||||
@ -117,7 +117,7 @@ func Test_GuessModuleRoot(t *testing.T) {
|
||||
func Test_ConvertInputToFilterOptions(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a := newAnalyzer()
|
||||
|
||||
|
||||
input := Input{
|
||||
Internal: true,
|
||||
External: false,
|
||||
@ -126,9 +126,9 @@ func Test_ConvertInputToFilterOptions(t *testing.T) {
|
||||
Direct: false,
|
||||
Depth: 3,
|
||||
}
|
||||
|
||||
|
||||
opts := a.convertInputToFilterOptions(input)
|
||||
|
||||
|
||||
t.Assert(opts.IncludeInternal, true)
|
||||
t.Assert(opts.IncludeExternal, false)
|
||||
t.Assert(opts.IncludeStdLib, false) // NoStd=true means !IncludeStdLib
|
||||
@ -239,16 +239,16 @@ func Test_Dep_Group(t *testing.T) {
|
||||
func Test_ModuleLevel_Direct(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
ctx := gctx.New()
|
||||
|
||||
|
||||
// Test module level with direct only
|
||||
in := Input{
|
||||
Module: true,
|
||||
Direct: true,
|
||||
Format: "list",
|
||||
}
|
||||
|
||||
|
||||
t.Logf("Input.Module: %v, Input.Direct: %v", in.Module, in.Direct)
|
||||
|
||||
|
||||
_, err := Dep.Index(ctx, in)
|
||||
t.AssertNil(err)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user