Skip to content
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
97 changes: 52 additions & 45 deletions analyzer.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package eapi

import (
"errors"
"fmt"
"go/ast"
"go/build"
"go/token"
"go/types"
"io/fs"
"os"
"path/filepath"
"strings"
Expand Down Expand Up @@ -68,20 +70,6 @@ func (a *Analyzer) Depends(pkgNames ...string) *Analyzer {
return a
}

func (a *Analyzer) Load(packagePath string) {
packagePath, err := filepath.Abs(packagePath)
if err != nil {
panic("invalid package path: " + err.Error())
}

pkgList := a.load(packagePath)
for _, pkg := range pkgList {
a.loadDefinitionsFromPkg(pkg, pkg.Module.Dir)
}

a.packages = append(a.packages, pkgList...)
}

func (a *Analyzer) Process(packagePath string) *Analyzer {
if len(a.plugins) <= 0 {
panic("must register plugin before processing")
Expand All @@ -94,11 +82,12 @@ func (a *Analyzer) Process(packagePath string) *Analyzer {

pkgList := a.load(packagePath)
for _, pkg := range pkgList {
a.loadDefinitionsFromPkg(pkg, pkg.Module.Dir)
a.definitions = make(Definitions)
for _, p := range pkg {
a.loadDefinitionsFromPkg(p, p.Module.Dir)
}
a.processPkg(pkg)
}
a.packages = append(a.packages, pkgList...)

a.processPkg(packagePath)

return a
}
Expand All @@ -117,16 +106,30 @@ func (a *Analyzer) analyze(ctx *Context, node ast.Node) {
}
}

func (a *Analyzer) load(pkgPath string) []*packages.Package {
const entryPackageName = "command-line-arguments"

func (a *Analyzer) load(pkgPath string) [][]*packages.Package {
absPath, err := filepath.Abs(pkgPath)
if err != nil {
panic("invalid package path: " + pkgPath)
}

pkg, err := build.Default.ImportDir(absPath, build.ImportComment)
if err != nil {
panic("import directory failed: " + err.Error())
}
var pkgList []*build.Package
filepath.Walk(absPath, func(path string, info fs.FileInfo, err error) error {
if !info.IsDir() {
return nil
}
pkg, err := build.Default.ImportDir(path, build.ImportComment)
if err != nil {
var noGoErr = &build.NoGoError{}
if errors.As(err, &noGoErr) {
return nil
}
panic("import directory failed: " + err.Error())
}
pkgList = append(pkgList, pkg)
return filepath.SkipDir
})

config := &packages.Config{
Mode: packages.NeedName |
Expand All @@ -141,36 +144,40 @@ func (a *Analyzer) load(pkgPath string) []*packages.Package {
Tests: false,
Dir: absPath,
}
var files []string
for _, filename := range append(pkg.GoFiles, pkg.CgoFiles...) {
files = append(files, filepath.Join(pkgPath, filename))
}
res, err := packages.Load(config, files...)
if err != nil {
panic("load packages failed: " + err.Error())
}

// 前面的 packages.Load() 方法不能解析出以第一层的 Module
// 所以这里手动解析 go.mod
for _, p := range res {
if p.Module != nil {
continue
var res [][]*packages.Package
for _, pkg := range pkgList {
var files []string
for _, filename := range append(pkg.GoFiles, pkg.CgoFiles...) {
files = append(files, filepath.Join(pkg.Dir, filename))
}
packs, err := packages.Load(config, files...)
if err != nil {
panic("load packages failed: " + err.Error())
}

module := a.parseGoModule(pkgPath)
if module == nil {
panic("failed to parse go.mod file in " + pkgPath)
// 前面的 packages.Load() 方法不能解析出以第一层的 Module
// 所以这里手动解析 go.mod
for _, p := range packs {
if p.Module != nil {
continue
}

module := a.parseGoModule(pkgPath)
if module == nil {
panic("failed to parse go.mod file in " + pkgPath)
}
p.Module = module
p.PkgPath = entryPackageName
p.ID = module.Path
}
p.Module = module
p.PkgPath = module.Path
p.ID = module.Path
res = append(res, packs)
}

return res
}

func (a *Analyzer) processPkg(packagePath string) {
for _, pkg := range a.packages {
func (a *Analyzer) processPkg(pkgList []*packages.Package) {
for _, pkg := range pkgList {
moduleDir := pkg.Module.Dir
InspectPackage(pkg, func(pkg *packages.Package) bool {
if pkg.Module == nil || pkg.Module.Dir != moduleDir {
Expand Down
28 changes: 28 additions & 0 deletions analyzer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package eapi

import (
"testing"
)

func TestAnalyzer_load(t *testing.T) {
type args struct {
pkgPath string
}
tests := []struct {
name string
args args
}{
{
name: "multi entrypoint",
args: args{
pkgPath: "./testdata/multi_entry",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
a := &Analyzer{}
a.load(tt.args.pkgPath)
})
}
}
18 changes: 3 additions & 15 deletions plugins/echo/testdata/sample/docs/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -102,21 +102,9 @@
"sample_model.GoodsStatus": {
"description": "\u003ctable\u003e\u003ctr\u003e\u003cth\u003eValue\u003c/th\u003e\u003cth\u003eKey\u003c/th\u003e\u003cth\u003eDescription\u003c/th\u003e\u003c/tr\u003e\u003ctr\u003e\u003ctd\u003e\u003c/td\u003e\u003ctd\u003eGoodsOnSale\u003c/td\u003e\u003ctd\u003e\u003c/td\u003e\u003c/tr\u003e\u003ctr\u003e\u003ctd\u003e\u003c/td\u003e\u003ctd\u003eGoodsOffSale\u003c/td\u003e\u003ctd\u003e\u003c/td\u003e\u003c/tr\u003e\u003ctr\u003e\u003ctd\u003e\u003c/td\u003e\u003ctd\u003eGoodsOutOfStock\u003c/td\u003e\u003ctd\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e",
"enum": [
{
"key": "GoodsOnSale",
"value": 1,
"description": ""
},
{
"key": "GoodsOffSale",
"value": 2,
"description": ""
},
{
"key": "GoodsOutOfStock",
"value": 3,
"description": ""
}
1,
2,
3
],
"title": "ModelGoodsStatus",
"type": "integer"
Expand Down
1 change: 0 additions & 1 deletion plugins/echo/testdata/sample/frontend/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,3 @@ export type ModelUploadFileRes = {
id?: number;
url?: string;
}

6 changes: 6 additions & 0 deletions plugins/gin/gin.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package gin

import (
"fmt"
"go/ast"
"go/token"
"go/types"
"os"
"path"
"regexp"
"strings"
Expand Down Expand Up @@ -167,6 +169,10 @@ func (e *Plugin) parseAPI(ctx *analyzer.Context, callExpr *ast.CallExpr, comment
}

handlerDef := ctx.GetDefinition(handlerFn.Pkg().Path(), handlerFn.Name())
if handlerDef == nil {
fmt.Fprintf(os.Stderr, "handler function %s.%s not found\n", handlerFn.Pkg().Path(), handlerFn.Name())
return
}
handlerFnDef, ok := handlerDef.(*analyzer.FuncDefinition)
if !ok {
return
Expand Down
24 changes: 4 additions & 20 deletions plugins/gin/testdata/server/docs/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -216,26 +216,10 @@
"server_pkg_view.ErrCode": {
"description": "\u003ctable\u003e\u003ctr\u003e\u003cth\u003eValue\u003c/th\u003e\u003cth\u003eKey\u003c/th\u003e\u003cth\u003eDescription\u003c/th\u003e\u003c/tr\u003e\u003ctr\u003e\u003ctd\u003e\u003c/td\u003e\u003ctd\u003eCodeNotFound\u003c/td\u003e\u003ctd\u003eResource not found\u003c/td\u003e\u003c/tr\u003e\u003ctr\u003e\u003ctd\u003e\u003c/td\u003e\u003ctd\u003eCodeCancled\u003c/td\u003e\u003ctd\u003eRequest canceld\u003c/td\u003e\u003c/tr\u003e\u003ctr\u003e\u003ctd\u003e\u003c/td\u003e\u003ctd\u003eCodeUnknown\u003c/td\u003e\u003ctd\u003e\u003c/td\u003e\u003c/tr\u003e\u003ctr\u003e\u003ctd\u003e\u003c/td\u003e\u003ctd\u003eCodeInvalidArgument\u003c/td\u003e\u003ctd\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e",
"enum": [
{
"key": "CodeNotFound",
"value": 10000,
"description": "Resource not found"
},
{
"key": "CodeCancled",
"value": 10001,
"description": "Request canceld"
},
{
"key": "CodeUnknown",
"value": 10002,
"description": ""
},
{
"key": "CodeInvalidArgument",
"value": 10003,
"description": ""
}
10000,
10001,
10002,
10003
],
"ext": {
"type": "enum",
Expand Down
16 changes: 16 additions & 0 deletions testdata/multi_entry/app_a/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package main

import (
"net/http"

"github.com/gin-gonic/gin"
)

func main() {
r := gin.New()
r.GET("/app-a/hello", handleHello)
}

func handleHello(c *gin.Context) {
c.JSON(http.StatusOK, "world")
}
16 changes: 16 additions & 0 deletions testdata/multi_entry/app_b/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package main

import (
"net/http"

"github.com/gin-gonic/gin"
)

func main() {
r := gin.New()
r.GET("/app-b/hello", handleHello)
}

func handleHello(c *gin.Context) {
c.JSON(http.StatusOK, "world")
}
42 changes: 42 additions & 0 deletions testdata/multi_entry/docs/openapi.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"components": {},
"info": {
"title": "",
"version": ""
},
"openapi": "3.0.3",
"paths": {
"/app-a/hello": {
"get": {
"operationId": "main.handleHello",
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"type": "string"
}
}
}
}
}
}
},
"/app-b/hello": {
"get": {
"operationId": "main.handleHello",
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"type": "string"
}
}
}
}
}
}
}
}
}
2 changes: 2 additions & 0 deletions testdata/multi_entry/eapi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
dir: .
plugin: gin
31 changes: 31 additions & 0 deletions testdata/multi_entry/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
module multi_entry

go 1.20

require github.com/gin-gonic/gin v1.9.0

require (
github.com/bytedance/sonic v1.8.0 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.11.2 // indirect
github.com/goccy/go-json v0.10.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.9 // indirect
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
golang.org/x/crypto v0.5.0 // indirect
golang.org/x/net v0.7.0 // indirect
golang.org/x/sys v0.5.0 // indirect
golang.org/x/text v0.7.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
Loading