| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171 | 
							- package main
 
- import (
 
- 	_ "embed"
 
- 	"encoding/json"
 
- 	"io/fs"
 
- 	"log"
 
- 	"os"
 
- 	"path/filepath"
 
- 	"regexp"
 
- 	"strings"
 
- 	"text/template"
 
- )
 
- //go:embed "templates/deps.dot.tmpl"
 
- var tpl string
 
- // ComposerName is the reserved name of the Composer specifications file.
 
- const ComposerName = "composer.json"
 
- /*
 
- In most cases: <owner>/<project>, but some special cases are valid too, like
 
- "php", "ext-<extension>", "lib-<library>"
 
- */
 
- type projectID string
 
- type semverClause string
 
- type node struct {
 
- 	ID    projectID
 
- 	Path  string
 
- 	Links map[projectID]semverClause
 
- }
 
- type composer struct {
 
- 	ID         projectID `json:"name"`
 
- 	Require    map[projectID]semverClause
 
- 	RequireDev map[projectID]semverClause
 
- }
 
- func (n *node) SetRequire(reqs map[projectID]semverClause) {
 
- 	// todo Probably use a positive list instead of a negative list
 
- 	parts := []string{
 
- 		`api-platform/`,
 
- 		`aporat/`,
 
- 		`babdev/`,
 
- 		`cocur/`,
 
- 		`composer/`,
 
- 		`doctrine/`,
 
- 		`eo/`,
 
- 		`ergebnis/`,
 
- 		`evence/`,
 
- 		`ext-`,
 
- 		`firebase/`,
 
- 		`flow/`,
 
- 		`friendsofphp/`,
 
- 		`friendsofsymfony/`,
 
- 		`google/`,
 
- 		`guzzlehttp/`,
 
- 		`http-interop/`,
 
- 		`hwi/`,
 
- 		`incenteev/`,
 
- 		`kelvinmo/`,
 
- 		`knpuniversity/`,
 
- 		`league/`,
 
- 		`liip/`,
 
- 		`myclabs/`,
 
- 		`nelmio/`,
 
- 		`nesbot/`,
 
- 		`oneup/`,
 
- 		`php$`,
 
- 		`php-amqplib/`,
 
- 		`php-http/`,
 
- 		`php-parallel-lint/`,
 
- 		`phpdocumentor/`,
 
- 		`phpro/`,
 
- 		`phpseclib/`,
 
- 		`phpstan/`,
 
- 		`psr/`,
 
- 		`pyrech/`,
 
- 		`ramsey/`,
 
- 		`rector/`,
 
- 		`sensio/`,
 
- 		`sensiolabs/`,
 
- 		`snc/`,
 
- 		`spatie/`,
 
- 		`stof/`,
 
- 		`symfony/`,
 
- 		`tgalopin/`,
 
- 		`thecodingmachine/`,
 
- 		`twig/`,
 
- 		`vich/`,
 
- 		`wearejust/`,
 
- 		`white-october/`,
 
- 		`wikimedia/`,
 
- 		`yokai/`,
 
- 	}
 
- 	excluded, err := regexp.Compile(`^(` + strings.Join(parts, `|`) + `)`)
 
- 	// Regex is a literal, so it should never happen.
 
- 	if err != nil {
 
- 		panic(err)
 
- 	}
 
- 	for id, ver := range reqs {
 
- 		if excluded.MatchString(string(id)) {
 
- 			continue
 
- 		}
 
- 		n.Links[id] = ver
 
- 	}
 
- }
 
- func scanComposer(dir string, nodes map[string]node, path string) error {
 
- 	f, err := os.Open(path)
 
- 	if err != nil {
 
- 		return err
 
- 	}
 
- 	dec := json.NewDecoder(f)
 
- 	c := composer{}
 
- 	if err := dec.Decode(&c); err != nil {
 
- 		return err
 
- 	}
 
- 	shortPath := path[len(dir)+1 : len(path)-len(ComposerName)-1]
 
- 	if c.ID == "" {
 
- 		c.ID = projectID(strings.Join([]string{"__magic", shortPath}, "/"))
 
- 	}
 
- 	n := node{
 
- 		ID:    c.ID,
 
- 		Path:  shortPath,
 
- 		Links: map[projectID]semverClause{},
 
- 	}
 
- 	n.SetRequire(c.Require)
 
- 	nodes[shortPath] = n
 
- 	return nil
 
- }
 
- func walk(nodes map[string]node, dir string) error {
 
- 	return filepath.WalkDir(dir, func() fs.WalkDirFunc {
 
- 		return func(path string, d fs.DirEntry, err error) error {
 
- 			if err != nil {
 
- 				return err
 
- 			}
 
- 			name := d.Name()
 
- 			if name != ComposerName {
 
- 				return nil
 
- 			}
 
- 			return scanComposer(dir, nodes, path)
 
- 		}
 
- 	}())
 
- }
 
- func main() {
 
- 	nodes := make(map[string]node)
 
- 	if len(os.Args) != 2 {
 
- 		log.Fatalf("Usage: %s <root dir>", os.Args[0])
 
- 	}
 
- 	dirName := os.Args[1]
 
- 	stat, err := os.Stat(dirName)
 
- 	if err != nil {
 
- 		log.Fatalf("Cannot read %s: %v", dirName, err)
 
- 	}
 
- 	if !stat.IsDir() {
 
- 		log.Fatalf("%s is not a directory", dirName)
 
- 	}
 
- 	err = walk(nodes, dirName)
 
- 	if err != nil {
 
- 		log.Fatalf("Walking %s: %v", dirName, err)
 
- 	}
 
- 	tpl, err := template.New("dot").Parse(tpl)
 
- 	if err != nil {
 
- 		log.Fatalf("Parsing template: %v", err)
 
- 	}
 
- 	_ = tpl.Execute(os.Stdout, nodes)
 
- }
 
 
  |