From b75af582e36cc033e6b6fef512b125177f611a2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Ch=C3=A1vez?= Date: Thu, 15 Sep 2022 23:20:35 +0200 Subject: [PATCH] feat: adds alias for the kebab cases of the original targets. Most of the experience in make is around chaining targets using kebab case e.g. make build test e2e-test which isn't supported by mage. This change automatically adds alias for the kebabcase version of the targets if they don't exist. --- mage/main.go | 34 ++++++++++++++++++++- mage/main_test.go | 46 +++++++++++++++++++++++++++++ mage/testdata/kebabcase/magefile.go | 15 ++++++++++ 3 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 mage/testdata/kebabcase/magefile.go diff --git a/mage/main.go b/mage/main.go index a41e7435..4bcc707e 100644 --- a/mage/main.go +++ b/mage/main.go @@ -589,6 +589,38 @@ func Compile(goos, goarch, ldflags, magePath, goCmd, compileTo string, gofiles [ return nil } +var ( + matchFirstCap = regexp.MustCompile("(.)([A-Z][a-z]+)") + matchAllCap = regexp.MustCompile("([a-z0-9])([A-Z])") +) + +// camelToKebab turns a camelcase string into a kebab case one. +func camelToKebab(s string) string { + ks := matchFirstCap.ReplaceAllString(s, "${1}-${2}") + ks = matchAllCap.ReplaceAllString(ks, "${1}-${2}") + return strings.ToLower(ks) +} + +// backfillKebabAliases backfill the existing alias with those infered from camelcase targets. +func backfillKebabAliases(aliases map[string]*parse.Function, funcs []*parse.Function) map[string]*parse.Function { + newAliases := aliases + if newAliases == nil { + newAliases = make(map[string]*parse.Function) + } + for _, f := range funcs { + kebabAlias := camelToKebab(f.Name) + if kebabAlias == f.Name { + continue + } + + if _, ok := newAliases[f.Name]; !ok { + newAliases[kebabAlias] = f + } + } + + return newAliases +} + // GenerateMainfile generates the mage mainfile at path. func GenerateMainfile(binaryName, path string, info *parse.PkgInfo) error { debug.Println("Creating mainfile at", path) @@ -601,7 +633,7 @@ func GenerateMainfile(binaryName, path string, info *parse.PkgInfo) error { data := mainfileTemplateData{ Description: info.Description, Funcs: info.Funcs, - Aliases: info.Aliases, + Aliases: backfillKebabAliases(info.Aliases, info.Funcs), Imports: info.Imports, BinaryName: binaryName, } diff --git a/mage/main_test.go b/mage/main_test.go index 2074c64a..06b04c19 100644 --- a/mage/main_test.go +++ b/mage/main_test.go @@ -1731,6 +1731,24 @@ func TestNamespaceDefault(t *testing.T) { func TestAliasToImport(t *testing.T) { } +func TestAliasToKebabCase(t *testing.T) { + stdout := &bytes.Buffer{} + inv := Invocation{ + Dir: "./testdata/kebabcase", + Stderr: io.Discard, + Stdout: stdout, + Args: []string{"fooBar", "foo-bar"}, + } + code := Invoke(inv) + if code != 0 { + t.Fatalf("expected 0, but got %v", code) + } + expected := "FooBar\nFooBar\n" + if stdout.String() != expected { + t.Fatalf("expected %q, but got %q", expected, stdout.String()) + } +} + func TestWrongDependency(t *testing.T) { stderr := &bytes.Buffer{} inv := Invocation{ @@ -1801,3 +1819,31 @@ func fileData(file string) (exeType, archSize, error) { } return -1, -1, fmt.Errorf("unrecognized executable format") } + +func TestCamelToKebab(t *testing.T) { + testCases := []struct { + camelCaseWord string + kebabWord string + }{ + { + camelCaseWord: "FizzBazz", + kebabWord: "fizz-bazz", + }, + { + camelCaseWord: "C", + kebabWord: "c", + }, + { + camelCaseWord: "DoSomeHTTPStuff", + kebabWord: "do-some-http-stuff", + }, + } + + for _, tCase := range testCases { + t.Run(tCase.camelCaseWord, func(t *testing.T) { + if want, have := tCase.kebabWord, camelToKebab(tCase.camelCaseWord); want != have { + t.Errorf("unexpected transformation, want %q, have %q", want, have) + } + }) + } +} diff --git a/mage/testdata/kebabcase/magefile.go b/mage/testdata/kebabcase/magefile.go new file mode 100644 index 00000000..615ee944 --- /dev/null +++ b/mage/testdata/kebabcase/magefile.go @@ -0,0 +1,15 @@ +//go:build mage +// +build mage + +package main + +import ( + "fmt" +) + +// No default so we can check the list(). + +// Prints out 'FooBar'. +func FooBar() { + fmt.Println("FooBar") +}