Support package-names with dashes in them (#232)
Support package-names with dashes in them We were smart about aliasing if you have name-collisions, but not if your package name is something that's not a valid identifier, like `"path/to/my-package"`, which Go for better or worse allows. Now we remove all the invalid characters (in practice mainly dashes, dots, and leading digits). Fixes #231. Test plan: make check
This commit is contained in:
@@ -171,6 +171,10 @@ func TestGenerateWithConfig(t *testing.T) {
|
||||
Generated: "generated.go",
|
||||
ContextType: "github.com/Khan/genqlient/internal/testutil.MyContext",
|
||||
}},
|
||||
{"CustomContextWithAlias", "", nil, &Config{
|
||||
Generated: "generated.go",
|
||||
ContextType: "github.com/Khan/genqlient/internal/testutil/junk---fun.name.MyContext",
|
||||
}},
|
||||
{"StructReferences", "", nil, &Config{
|
||||
StructReferences: true,
|
||||
Generated: "generated-structrefs.go",
|
||||
|
||||
+30
-1
@@ -2,14 +2,43 @@ package generate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// makeIdentifier takes a string and returns a valid go identifier like it.
|
||||
//
|
||||
// If the string is an identifier, return the input. Otherwise, munge it to
|
||||
// make a valid identifier, which at worst (if the input is entirely emoji,
|
||||
// say) means coming up with one out of whole cloth. This identifier need not
|
||||
// be particularly unique; the caller may add a suffix.
|
||||
func makeIdentifier(candidateIdentifier string) string {
|
||||
if token.IsIdentifier(candidateIdentifier) {
|
||||
return candidateIdentifier
|
||||
}
|
||||
|
||||
var goodChars strings.Builder
|
||||
for _, c := range candidateIdentifier {
|
||||
// modified from token.IsIdentifier
|
||||
if unicode.IsLetter(c) || c == '_' ||
|
||||
// digits only valid after first char
|
||||
goodChars.Len() > 0 && unicode.IsDigit(c) {
|
||||
goodChars.WriteRune(c)
|
||||
}
|
||||
}
|
||||
if goodChars.Len() > 0 {
|
||||
return goodChars.String()
|
||||
}
|
||||
|
||||
return "alias"
|
||||
}
|
||||
|
||||
func (g *generator) addImportFor(pkgPath string) (alias string) {
|
||||
pkgName := pkgPath[strings.LastIndex(pkgPath, "/")+1:]
|
||||
pkgName := makeIdentifier(pkgPath[strings.LastIndex(pkgPath, "/")+1:])
|
||||
alias = pkgName
|
||||
suffix := 2
|
||||
for g.usedAliases[alias] {
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package generate
|
||||
|
||||
import (
|
||||
"go/token"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMakeIdentifier(t *testing.T) {
|
||||
tests := []struct {
|
||||
testName string
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{"GoodIdentifier", "myIdent", "myIdent"},
|
||||
{"GoodIdentifierNumbers", "myIdent1234", "myIdent1234"},
|
||||
{"NumberPrefix", "1234myIdent", "myIdent"},
|
||||
{"OnlyNumbers", "1234", "alias"},
|
||||
{"Dashes", "my-ident", "myident"},
|
||||
// Note: most Go implementations won't actually allow
|
||||
// this package-path, but the spec is pretty vague
|
||||
// so make sure to handle it.
|
||||
{"JunkAnd", "my!!\\\\\nident", "myident"},
|
||||
{"JunkOnly", "!!\\\\\n", "alias"},
|
||||
{"Accents", "née", "née"},
|
||||
{"Kanji", "日本", "日本"},
|
||||
{"EmojiAnd", "ident👍", "ident"},
|
||||
{"EmojiOnly", "👍", "alias"},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
t.Run(test.testName, func(t *testing.T) {
|
||||
actual := makeIdentifier(test.input)
|
||||
if actual != test.expected {
|
||||
t.Errorf("mismatch:\ngot: %s\nwant: %s", actual, test.expected)
|
||||
}
|
||||
if !token.IsIdentifier(actual) {
|
||||
t.Errorf("not a valid identifier: %s", actual)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
// Code generated by github.com/Khan/genqlient, DO NOT EDIT.
|
||||
|
||||
package queries
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/Khan/genqlient/graphql"
|
||||
junkfunname "github.com/Khan/genqlient/internal/testutil/junk---fun.name"
|
||||
)
|
||||
|
||||
// Check that context_type from genqlient.yaml implements context.Context.
|
||||
var _ context.Context = (junkfunname.MyContext)(nil)
|
||||
|
||||
// SimpleQueryResponse is returned by SimpleQuery on success.
|
||||
type SimpleQueryResponse struct {
|
||||
// user looks up a user by some stuff.
|
||||
//
|
||||
// See UserQueryInput for what stuff is supported.
|
||||
// If query is null, returns the current user.
|
||||
User SimpleQueryUser `json:"user"`
|
||||
}
|
||||
|
||||
// GetUser returns SimpleQueryResponse.User, and is useful for accessing the field via an interface.
|
||||
func (v *SimpleQueryResponse) GetUser() SimpleQueryUser { return v.User }
|
||||
|
||||
// SimpleQueryUser includes the requested fields of the GraphQL type User.
|
||||
// The GraphQL type's documentation follows.
|
||||
//
|
||||
// A User is a user!
|
||||
type SimpleQueryUser struct {
|
||||
// id is the user's ID.
|
||||
//
|
||||
// It is stable, unique, and opaque, like all good IDs.
|
||||
Id string `json:"id"`
|
||||
}
|
||||
|
||||
// GetId returns SimpleQueryUser.Id, and is useful for accessing the field via an interface.
|
||||
func (v *SimpleQueryUser) GetId() string { return v.Id }
|
||||
|
||||
func SimpleQuery(
|
||||
ctx junkfunname.MyContext,
|
||||
client graphql.Client,
|
||||
) (*SimpleQueryResponse, error) {
|
||||
req := &graphql.Request{
|
||||
OpName: "SimpleQuery",
|
||||
Query: `
|
||||
query SimpleQuery {
|
||||
user {
|
||||
id
|
||||
}
|
||||
}
|
||||
`,
|
||||
}
|
||||
var err error
|
||||
|
||||
var data SimpleQueryResponse
|
||||
resp := &graphql.Response{Data: &data}
|
||||
|
||||
err = client.MakeRequest(
|
||||
ctx,
|
||||
req,
|
||||
resp,
|
||||
)
|
||||
|
||||
return &data, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user