github.com/niko0xdev/gqlgen@v0.17.55-0.20240120102243-2ecff98c3e37/codegen/config/config.go (about)

     1  package config
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"go/types"
     7  	"io"
     8  	"os"
     9  	"path/filepath"
    10  	"regexp"
    11  	"sort"
    12  	"strings"
    13  
    14  	"github.com/vektah/gqlparser/v2"
    15  	"github.com/vektah/gqlparser/v2/ast"
    16  	"golang.org/x/tools/go/packages"
    17  	"gopkg.in/yaml.v3"
    18  
    19  	"github.com/niko0xdev/gqlgen/codegen/templates"
    20  	"github.com/niko0xdev/gqlgen/internal/code"
    21  )
    22  
    23  type Config struct {
    24  	SchemaFilename                StringList                 `yaml:"schema,omitempty"`
    25  	Exec                          ExecConfig                 `yaml:"exec"`
    26  	Model                         PackageConfig              `yaml:"model,omitempty"`
    27  	Federation                    PackageConfig              `yaml:"federation,omitempty"`
    28  	Resolver                      ResolverConfig             `yaml:"resolver,omitempty"`
    29  	AutoBind                      []string                   `yaml:"autobind"`
    30  	Models                        TypeMap                    `yaml:"models,omitempty"`
    31  	StructTag                     string                     `yaml:"struct_tag,omitempty"`
    32  	Directives                    map[string]DirectiveConfig `yaml:"directives,omitempty"`
    33  	GoBuildTags                   StringList                 `yaml:"go_build_tags,omitempty"`
    34  	GoInitialisms                 GoInitialismsConfig        `yaml:"go_initialisms,omitempty"`
    35  	OmitSliceElementPointers      bool                       `yaml:"omit_slice_element_pointers,omitempty"`
    36  	OmitGetters                   bool                       `yaml:"omit_getters,omitempty"`
    37  	OmitInterfaceChecks           bool                       `yaml:"omit_interface_checks,omitempty"`
    38  	OmitComplexity                bool                       `yaml:"omit_complexity,omitempty"`
    39  	OmitGQLGenFileNotice          bool                       `yaml:"omit_gqlgen_file_notice,omitempty"`
    40  	OmitGQLGenVersionInFileNotice bool                       `yaml:"omit_gqlgen_version_in_file_notice,omitempty"`
    41  	OmitRootModels                bool                       `yaml:"omit_root_models,omitempty"`
    42  	StructFieldsAlwaysPointers    bool                       `yaml:"struct_fields_always_pointers,omitempty"`
    43  	ReturnPointersInUmarshalInput bool                       `yaml:"return_pointers_in_unmarshalinput,omitempty"`
    44  	ResolversAlwaysReturnPointers bool                       `yaml:"resolvers_always_return_pointers,omitempty"`
    45  	NullableInputOmittable        bool                       `yaml:"nullable_input_omittable,omitempty"`
    46  	EnableModelJsonOmitemptyTag   *bool                      `yaml:"enable_model_json_omitempty_tag,omitempty"`
    47  	SkipValidation                bool                       `yaml:"skip_validation,omitempty"`
    48  	SkipModTidy                   bool                       `yaml:"skip_mod_tidy,omitempty"`
    49  	Sources                       []*ast.Source              `yaml:"-"`
    50  	Packages                      *code.Packages             `yaml:"-"`
    51  	Schema                        *ast.Schema                `yaml:"-"`
    52  
    53  	// Deprecated: use Federation instead. Will be removed next release
    54  	Federated bool `yaml:"federated,omitempty"`
    55  }
    56  
    57  var cfgFilenames = []string{".gqlgen.yml", "gqlgen.yml", "gqlgen.yaml"}
    58  
    59  // DefaultConfig creates a copy of the default config
    60  func DefaultConfig() *Config {
    61  	return &Config{
    62  		SchemaFilename:                StringList{"schema.graphql"},
    63  		Model:                         PackageConfig{Filename: "models_gen.go"},
    64  		Exec:                          ExecConfig{Filename: "generated.go"},
    65  		Directives:                    map[string]DirectiveConfig{},
    66  		Models:                        TypeMap{},
    67  		StructFieldsAlwaysPointers:    true,
    68  		ReturnPointersInUmarshalInput: false,
    69  		ResolversAlwaysReturnPointers: true,
    70  		NullableInputOmittable:        false,
    71  	}
    72  }
    73  
    74  // LoadDefaultConfig loads the default config so that it is ready to be used
    75  func LoadDefaultConfig() (*Config, error) {
    76  	config := DefaultConfig()
    77  
    78  	for _, filename := range config.SchemaFilename {
    79  		filename = filepath.ToSlash(filename)
    80  		var err error
    81  		var schemaRaw []byte
    82  		schemaRaw, err = os.ReadFile(filename)
    83  		if err != nil {
    84  			return nil, fmt.Errorf("unable to open schema: %w", err)
    85  		}
    86  
    87  		config.Sources = append(config.Sources, &ast.Source{Name: filename, Input: string(schemaRaw)})
    88  	}
    89  
    90  	return config, nil
    91  }
    92  
    93  // LoadConfigFromDefaultLocations looks for a config file in the current directory, and all parent directories
    94  // walking up the tree. The closest config file will be returned.
    95  func LoadConfigFromDefaultLocations() (*Config, error) {
    96  	cfgFile, err := findCfg()
    97  	if err != nil {
    98  		return nil, err
    99  	}
   100  
   101  	err = os.Chdir(filepath.Dir(cfgFile))
   102  	if err != nil {
   103  		return nil, fmt.Errorf("unable to enter config dir: %w", err)
   104  	}
   105  	return LoadConfig(cfgFile)
   106  }
   107  
   108  var path2regex = strings.NewReplacer(
   109  	`.`, `\.`,
   110  	`*`, `.+`,
   111  	`\`, `[\\/]`,
   112  	`/`, `[\\/]`,
   113  )
   114  
   115  // LoadConfig reads the gqlgen.yml config file
   116  func LoadConfig(filename string) (*Config, error) {
   117  	b, err := os.ReadFile(filename)
   118  	if err != nil {
   119  		return nil, fmt.Errorf("unable to read config: %w", err)
   120  	}
   121  
   122  	return ReadConfig(bytes.NewReader(b))
   123  }
   124  
   125  func ReadConfig(cfgFile io.Reader) (*Config, error) {
   126  	config := DefaultConfig()
   127  
   128  	dec := yaml.NewDecoder(cfgFile)
   129  	dec.KnownFields(true)
   130  
   131  	if err := dec.Decode(config); err != nil {
   132  		return nil, fmt.Errorf("unable to parse config: %w", err)
   133  	}
   134  
   135  	if err := CompleteConfig(config); err != nil {
   136  		return nil, err
   137  	}
   138  
   139  	return config, nil
   140  }
   141  
   142  // CompleteConfig fills in the schema and other values to a config loaded from
   143  // YAML.
   144  func CompleteConfig(config *Config) error {
   145  	defaultDirectives := map[string]DirectiveConfig{
   146  		"skip":        {SkipRuntime: true},
   147  		"include":     {SkipRuntime: true},
   148  		"deprecated":  {SkipRuntime: true},
   149  		"specifiedBy": {SkipRuntime: true},
   150  	}
   151  
   152  	for key, value := range defaultDirectives {
   153  		if _, defined := config.Directives[key]; !defined {
   154  			config.Directives[key] = value
   155  		}
   156  	}
   157  
   158  	preGlobbing := config.SchemaFilename
   159  	config.SchemaFilename = StringList{}
   160  	for _, f := range preGlobbing {
   161  		var matches []string
   162  
   163  		// for ** we want to override default globbing patterns and walk all
   164  		// subdirectories to match schema files.
   165  		if strings.Contains(f, "**") {
   166  			pathParts := strings.SplitN(f, "**", 2)
   167  			rest := strings.TrimPrefix(strings.TrimPrefix(pathParts[1], `\`), `/`)
   168  			// turn the rest of the glob into a regex, anchored only at the end because ** allows
   169  			// for any number of dirs in between and walk will let us match against the full path name
   170  			globRe := regexp.MustCompile(path2regex.Replace(rest) + `$`)
   171  
   172  			if err := filepath.Walk(pathParts[0], func(path string, info os.FileInfo, err error) error {
   173  				if err != nil {
   174  					return err
   175  				}
   176  
   177  				if globRe.MatchString(strings.TrimPrefix(path, pathParts[0])) {
   178  					matches = append(matches, path)
   179  				}
   180  
   181  				return nil
   182  			}); err != nil {
   183  				return fmt.Errorf("failed to walk schema at root %s: %w", pathParts[0], err)
   184  			}
   185  		} else {
   186  			var err error
   187  			matches, err = filepath.Glob(f)
   188  			if err != nil {
   189  				return fmt.Errorf("failed to glob schema filename %s: %w", f, err)
   190  			}
   191  		}
   192  
   193  		for _, m := range matches {
   194  			if config.SchemaFilename.Has(m) {
   195  				continue
   196  			}
   197  			config.SchemaFilename = append(config.SchemaFilename, m)
   198  		}
   199  	}
   200  
   201  	for _, filename := range config.SchemaFilename {
   202  		filename = filepath.ToSlash(filename)
   203  		var err error
   204  		var schemaRaw []byte
   205  		schemaRaw, err = os.ReadFile(filename)
   206  		if err != nil {
   207  			return fmt.Errorf("unable to open schema: %w", err)
   208  		}
   209  
   210  		config.Sources = append(config.Sources, &ast.Source{Name: filename, Input: string(schemaRaw)})
   211  	}
   212  
   213  	config.GoInitialisms.setInitialisms()
   214  
   215  	return nil
   216  }
   217  
   218  func (c *Config) Init() error {
   219  	if c.Packages == nil {
   220  		c.Packages = code.NewPackages(
   221  			code.WithBuildTags(c.GoBuildTags...),
   222  		)
   223  	}
   224  
   225  	if c.Schema == nil {
   226  		if err := c.LoadSchema(); err != nil {
   227  			return err
   228  		}
   229  	}
   230  
   231  	err := c.injectTypesFromSchema()
   232  	if err != nil {
   233  		return err
   234  	}
   235  
   236  	err = c.autobind()
   237  	if err != nil {
   238  		return err
   239  	}
   240  
   241  	c.injectBuiltins()
   242  	// prefetch all packages in one big packages.Load call
   243  	c.Packages.LoadAll(c.packageList()...)
   244  
   245  	//  check everything is valid on the way out
   246  	err = c.check()
   247  	if err != nil {
   248  		return err
   249  	}
   250  
   251  	return nil
   252  }
   253  
   254  func (c *Config) packageList() []string {
   255  	pkgs := []string{
   256  		"github.com/niko0xdev/gqlgen/graphql",
   257  		"github.com/niko0xdev/gqlgen/graphql/introspection",
   258  	}
   259  	pkgs = append(pkgs, c.Models.ReferencedPackages()...)
   260  	pkgs = append(pkgs, c.AutoBind...)
   261  	return pkgs
   262  }
   263  
   264  func (c *Config) ReloadAllPackages() {
   265  	c.Packages.ReloadAll(c.packageList()...)
   266  }
   267  
   268  func (c *Config) IsRoot(def *ast.Definition) bool {
   269  	return def == c.Schema.Query || def == c.Schema.Mutation || def == c.Schema.Subscription
   270  }
   271  
   272  func (c *Config) injectTypesFromSchema() error {
   273  	c.Directives["goModel"] = DirectiveConfig{
   274  		SkipRuntime: true,
   275  	}
   276  
   277  	c.Directives["goField"] = DirectiveConfig{
   278  		SkipRuntime: true,
   279  	}
   280  
   281  	c.Directives["goTag"] = DirectiveConfig{
   282  		SkipRuntime: true,
   283  	}
   284  
   285  	for _, schemaType := range c.Schema.Types {
   286  		if c.IsRoot(schemaType) {
   287  			continue
   288  		}
   289  
   290  		if bd := schemaType.Directives.ForName("goModel"); bd != nil {
   291  			if ma := bd.Arguments.ForName("model"); ma != nil {
   292  				if mv, err := ma.Value.Value(nil); err == nil {
   293  					c.Models.Add(schemaType.Name, mv.(string))
   294  				}
   295  			}
   296  
   297  			if ma := bd.Arguments.ForName("models"); ma != nil {
   298  				if mvs, err := ma.Value.Value(nil); err == nil {
   299  					for _, mv := range mvs.([]interface{}) {
   300  						c.Models.Add(schemaType.Name, mv.(string))
   301  					}
   302  				}
   303  			}
   304  
   305  			if fg := bd.Arguments.ForName("forceGenerate"); fg != nil {
   306  				if mv, err := fg.Value.Value(nil); err == nil {
   307  					c.Models.ForceGenerate(schemaType.Name, mv.(bool))
   308  				}
   309  			}
   310  		}
   311  
   312  		if schemaType.Kind == ast.Object || schemaType.Kind == ast.InputObject {
   313  			for _, field := range schemaType.Fields {
   314  				if fd := field.Directives.ForName("goField"); fd != nil {
   315  					forceResolver := c.Models[schemaType.Name].Fields[field.Name].Resolver
   316  					fieldName := c.Models[schemaType.Name].Fields[field.Name].FieldName
   317  
   318  					if ra := fd.Arguments.ForName("forceResolver"); ra != nil {
   319  						if fr, err := ra.Value.Value(nil); err == nil {
   320  							forceResolver = fr.(bool)
   321  						}
   322  					}
   323  
   324  					if na := fd.Arguments.ForName("name"); na != nil {
   325  						if fr, err := na.Value.Value(nil); err == nil {
   326  							fieldName = fr.(string)
   327  						}
   328  					}
   329  
   330  					if c.Models[schemaType.Name].Fields == nil {
   331  						c.Models[schemaType.Name] = TypeMapEntry{
   332  							Model:       c.Models[schemaType.Name].Model,
   333  							ExtraFields: c.Models[schemaType.Name].ExtraFields,
   334  							Fields:      map[string]TypeMapField{},
   335  						}
   336  					}
   337  
   338  					c.Models[schemaType.Name].Fields[field.Name] = TypeMapField{
   339  						FieldName: fieldName,
   340  						Resolver:  forceResolver,
   341  					}
   342  				}
   343  			}
   344  		}
   345  	}
   346  
   347  	return nil
   348  }
   349  
   350  type TypeMapEntry struct {
   351  	Model         StringList              `yaml:"model,omitempty"`
   352  	ForceGenerate bool                    `yaml:"forceGenerate,omitempty"`
   353  	Fields        map[string]TypeMapField `yaml:"fields,omitempty"`
   354  
   355  	// Key is the Go name of the field.
   356  	ExtraFields map[string]ModelExtraField `yaml:"extraFields,omitempty"`
   357  }
   358  
   359  type TypeMapField struct {
   360  	Resolver        bool   `yaml:"resolver"`
   361  	FieldName       string `yaml:"fieldName"`
   362  	GeneratedMethod string `yaml:"-"`
   363  }
   364  
   365  type ModelExtraField struct {
   366  	// Type is the Go type of the field.
   367  	//
   368  	// It supports the builtin basic types (like string or int64), named types
   369  	// (qualified by the full package path), pointers to those types (prefixed
   370  	// with `*`), and slices of those types (prefixed with `[]`).
   371  	//
   372  	// For example, the following are valid types:
   373  	//  string
   374  	//  *github.com/author/package.Type
   375  	//  []string
   376  	//  []*github.com/author/package.Type
   377  	//
   378  	// Note that the type will be referenced from the generated/graphql, which
   379  	// means the package it lives in must not reference the generated/graphql
   380  	// package to avoid circular imports.
   381  	// restrictions.
   382  	Type string `yaml:"type"`
   383  
   384  	// OverrideTags is an optional override of the Go field tag.
   385  	OverrideTags string `yaml:"overrideTags"`
   386  
   387  	// Description is an optional the Go field doc-comment.
   388  	Description string `yaml:"description"`
   389  }
   390  
   391  type StringList []string
   392  
   393  func (a *StringList) UnmarshalYAML(unmarshal func(interface{}) error) error {
   394  	var single string
   395  	err := unmarshal(&single)
   396  	if err == nil {
   397  		*a = []string{single}
   398  		return nil
   399  	}
   400  
   401  	var multi []string
   402  	err = unmarshal(&multi)
   403  	if err != nil {
   404  		return err
   405  	}
   406  
   407  	*a = multi
   408  	return nil
   409  }
   410  
   411  func (a StringList) Has(file string) bool {
   412  	for _, existing := range a {
   413  		if existing == file {
   414  			return true
   415  		}
   416  	}
   417  	return false
   418  }
   419  
   420  func (c *Config) check() error {
   421  	if c.Models == nil {
   422  		c.Models = TypeMap{}
   423  	}
   424  
   425  	type FilenamePackage struct {
   426  		Filename string
   427  		Package  string
   428  		Declaree string
   429  	}
   430  
   431  	fileList := map[string][]FilenamePackage{}
   432  
   433  	if err := c.Models.Check(); err != nil {
   434  		return fmt.Errorf("config.models: %w", err)
   435  	}
   436  	if err := c.Exec.Check(); err != nil {
   437  		return fmt.Errorf("config.exec: %w", err)
   438  	}
   439  	fileList[c.Exec.ImportPath()] = append(fileList[c.Exec.ImportPath()], FilenamePackage{
   440  		Filename: c.Exec.Filename,
   441  		Package:  c.Exec.Package,
   442  		Declaree: "exec",
   443  	})
   444  
   445  	if c.Model.IsDefined() {
   446  		if err := c.Model.Check(); err != nil {
   447  			return fmt.Errorf("config.model: %w", err)
   448  		}
   449  		fileList[c.Model.ImportPath()] = append(fileList[c.Model.ImportPath()], FilenamePackage{
   450  			Filename: c.Model.Filename,
   451  			Package:  c.Model.Package,
   452  			Declaree: "model",
   453  		})
   454  	}
   455  	if c.Resolver.IsDefined() {
   456  		if err := c.Resolver.Check(); err != nil {
   457  			return fmt.Errorf("config.resolver: %w", err)
   458  		}
   459  		fileList[c.Resolver.ImportPath()] = append(fileList[c.Resolver.ImportPath()], FilenamePackage{
   460  			Filename: c.Resolver.Filename,
   461  			Package:  c.Resolver.Package,
   462  			Declaree: "resolver",
   463  		})
   464  	}
   465  	if c.Federation.IsDefined() {
   466  		if err := c.Federation.Check(); err != nil {
   467  			return fmt.Errorf("config.federation: %w", err)
   468  		}
   469  		fileList[c.Federation.ImportPath()] = append(fileList[c.Federation.ImportPath()], FilenamePackage{
   470  			Filename: c.Federation.Filename,
   471  			Package:  c.Federation.Package,
   472  			Declaree: "federation",
   473  		})
   474  		if c.Federation.ImportPath() != c.Exec.ImportPath() {
   475  			return fmt.Errorf("federation and exec must be in the same package")
   476  		}
   477  	}
   478  	if c.Federated {
   479  		return fmt.Errorf("federated has been removed, instead use\nfederation:\n    filename: path/to/federated.go")
   480  	}
   481  
   482  	for importPath, pkg := range fileList {
   483  		for _, file1 := range pkg {
   484  			for _, file2 := range pkg {
   485  				if file1.Package != file2.Package {
   486  					return fmt.Errorf("%s and %s define the same import path (%s) with different package names (%s vs %s)",
   487  						file1.Declaree,
   488  						file2.Declaree,
   489  						importPath,
   490  						file1.Package,
   491  						file2.Package,
   492  					)
   493  				}
   494  			}
   495  		}
   496  	}
   497  
   498  	return nil
   499  }
   500  
   501  type TypeMap map[string]TypeMapEntry
   502  
   503  func (tm TypeMap) Exists(typeName string) bool {
   504  	_, ok := tm[typeName]
   505  	return ok
   506  }
   507  
   508  func (tm TypeMap) UserDefined(typeName string) bool {
   509  	m, ok := tm[typeName]
   510  	return ok && len(m.Model) > 0
   511  }
   512  
   513  func (tm TypeMap) Check() error {
   514  	for typeName, entry := range tm {
   515  		for _, model := range entry.Model {
   516  			if strings.LastIndex(model, ".") < strings.LastIndex(model, "/") {
   517  				return fmt.Errorf("model %s: invalid type specifier \"%s\" - you need to specify a struct to map to", typeName, entry.Model)
   518  			}
   519  		}
   520  	}
   521  	return nil
   522  }
   523  
   524  func (tm TypeMap) ReferencedPackages() []string {
   525  	var pkgs []string
   526  
   527  	for _, typ := range tm {
   528  		for _, model := range typ.Model {
   529  			if model == "map[string]interface{}" || model == "interface{}" {
   530  				continue
   531  			}
   532  			pkg, _ := code.PkgAndType(model)
   533  			if pkg == "" || inStrSlice(pkgs, pkg) {
   534  				continue
   535  			}
   536  			pkgs = append(pkgs, code.QualifyPackagePath(pkg))
   537  		}
   538  	}
   539  
   540  	sort.Slice(pkgs, func(i, j int) bool {
   541  		return pkgs[i] > pkgs[j]
   542  	})
   543  	return pkgs
   544  }
   545  
   546  func (tm TypeMap) Add(name string, goType string) {
   547  	modelCfg := tm[name]
   548  	modelCfg.Model = append(modelCfg.Model, goType)
   549  	tm[name] = modelCfg
   550  }
   551  
   552  func (tm TypeMap) ForceGenerate(name string, forceGenerate bool) {
   553  	modelCfg := tm[name]
   554  	modelCfg.ForceGenerate = forceGenerate
   555  	tm[name] = modelCfg
   556  }
   557  
   558  type DirectiveConfig struct {
   559  	SkipRuntime bool `yaml:"skip_runtime"`
   560  }
   561  
   562  func inStrSlice(haystack []string, needle string) bool {
   563  	for _, v := range haystack {
   564  		if needle == v {
   565  			return true
   566  		}
   567  	}
   568  
   569  	return false
   570  }
   571  
   572  // findCfg searches for the config file in this directory and all parents up the tree
   573  // looking for the closest match
   574  func findCfg() (string, error) {
   575  	dir, err := os.Getwd()
   576  	if err != nil {
   577  		return "", fmt.Errorf("unable to get working dir to findCfg: %w", err)
   578  	}
   579  
   580  	cfg := findCfgInDir(dir)
   581  
   582  	for cfg == "" && dir != filepath.Dir(dir) {
   583  		dir = filepath.Dir(dir)
   584  		cfg = findCfgInDir(dir)
   585  	}
   586  
   587  	if cfg == "" {
   588  		return "", os.ErrNotExist
   589  	}
   590  
   591  	return cfg, nil
   592  }
   593  
   594  func findCfgInDir(dir string) string {
   595  	for _, cfgName := range cfgFilenames {
   596  		path := filepath.Join(dir, cfgName)
   597  		if _, err := os.Stat(path); err == nil {
   598  			return path
   599  		}
   600  	}
   601  	return ""
   602  }
   603  
   604  func (c *Config) autobind() error {
   605  	if len(c.AutoBind) == 0 {
   606  		return nil
   607  	}
   608  
   609  	ps := c.Packages.LoadAll(c.AutoBind...)
   610  
   611  	for _, t := range c.Schema.Types {
   612  		if c.Models.UserDefined(t.Name) || c.Models[t.Name].ForceGenerate {
   613  			continue
   614  		}
   615  
   616  		for i, p := range ps {
   617  			if p == nil || p.Module == nil {
   618  				return fmt.Errorf("unable to load %s - make sure you're using an import path to a package that exists", c.AutoBind[i])
   619  			}
   620  
   621  			autobindType := c.lookupAutobindType(p, t)
   622  			if autobindType != nil {
   623  				c.Models.Add(t.Name, autobindType.Pkg().Path()+"."+autobindType.Name())
   624  				break
   625  			}
   626  		}
   627  	}
   628  
   629  	for i, t := range c.Models {
   630  		if t.ForceGenerate {
   631  			continue
   632  		}
   633  
   634  		for j, m := range t.Model {
   635  			pkg, typename := code.PkgAndType(m)
   636  
   637  			// skip anything that looks like an import path
   638  			if strings.Contains(pkg, "/") {
   639  				continue
   640  			}
   641  
   642  			for _, p := range ps {
   643  				if p.Name != pkg {
   644  					continue
   645  				}
   646  				if t := p.Types.Scope().Lookup(typename); t != nil {
   647  					c.Models[i].Model[j] = t.Pkg().Path() + "." + t.Name()
   648  					break
   649  				}
   650  			}
   651  		}
   652  	}
   653  
   654  	return nil
   655  }
   656  
   657  func (c *Config) lookupAutobindType(p *packages.Package, schemaType *ast.Definition) types.Object {
   658  	// Try binding to either the original schema type name, or the normalized go type name
   659  	for _, lookupName := range []string{schemaType.Name, templates.ToGo(schemaType.Name)} {
   660  		if t := p.Types.Scope().Lookup(lookupName); t != nil {
   661  			return t
   662  		}
   663  	}
   664  
   665  	return nil
   666  }
   667  
   668  func (c *Config) injectBuiltins() {
   669  	builtins := TypeMap{
   670  		"__Directive":         {Model: StringList{"github.com/niko0xdev/gqlgen/graphql/introspection.Directive"}},
   671  		"__DirectiveLocation": {Model: StringList{"github.com/niko0xdev/gqlgen/graphql.String"}},
   672  		"__Type":              {Model: StringList{"github.com/niko0xdev/gqlgen/graphql/introspection.Type"}},
   673  		"__TypeKind":          {Model: StringList{"github.com/niko0xdev/gqlgen/graphql.String"}},
   674  		"__Field":             {Model: StringList{"github.com/niko0xdev/gqlgen/graphql/introspection.Field"}},
   675  		"__EnumValue":         {Model: StringList{"github.com/niko0xdev/gqlgen/graphql/introspection.EnumValue"}},
   676  		"__InputValue":        {Model: StringList{"github.com/niko0xdev/gqlgen/graphql/introspection.InputValue"}},
   677  		"__Schema":            {Model: StringList{"github.com/niko0xdev/gqlgen/graphql/introspection.Schema"}},
   678  		"Float":               {Model: StringList{"github.com/niko0xdev/gqlgen/graphql.FloatContext"}},
   679  		"String":              {Model: StringList{"github.com/niko0xdev/gqlgen/graphql.String"}},
   680  		"Boolean":             {Model: StringList{"github.com/niko0xdev/gqlgen/graphql.Boolean"}},
   681  		"Int": {Model: StringList{
   682  			"github.com/niko0xdev/gqlgen/graphql.Int",
   683  			"github.com/niko0xdev/gqlgen/graphql.Int32",
   684  			"github.com/niko0xdev/gqlgen/graphql.Int64",
   685  		}},
   686  		"ID": {
   687  			Model: StringList{
   688  				"github.com/niko0xdev/gqlgen/graphql.ID",
   689  				"github.com/niko0xdev/gqlgen/graphql.IntID",
   690  			},
   691  		},
   692  	}
   693  
   694  	for typeName, entry := range builtins {
   695  		if !c.Models.Exists(typeName) {
   696  			c.Models[typeName] = entry
   697  		}
   698  	}
   699  
   700  	// These are additional types that are injected if defined in the schema as scalars.
   701  	extraBuiltins := TypeMap{
   702  		"Time":   {Model: StringList{"github.com/niko0xdev/gqlgen/graphql.Time"}},
   703  		"Map":    {Model: StringList{"github.com/niko0xdev/gqlgen/graphql.Map"}},
   704  		"Upload": {Model: StringList{"github.com/niko0xdev/gqlgen/graphql.Upload"}},
   705  		"Any":    {Model: StringList{"github.com/niko0xdev/gqlgen/graphql.Any"}},
   706  	}
   707  
   708  	for typeName, entry := range extraBuiltins {
   709  		if t, ok := c.Schema.Types[typeName]; !c.Models.Exists(typeName) && ok && t.Kind == ast.Scalar {
   710  			c.Models[typeName] = entry
   711  		}
   712  	}
   713  }
   714  
   715  func (c *Config) LoadSchema() error {
   716  	if c.Packages != nil {
   717  		c.Packages = code.NewPackages(
   718  			code.WithBuildTags(c.GoBuildTags...),
   719  		)
   720  	}
   721  
   722  	if err := c.check(); err != nil {
   723  		return err
   724  	}
   725  
   726  	schema, err := gqlparser.LoadSchema(c.Sources...)
   727  	if err != nil {
   728  		return err
   729  	}
   730  
   731  	if schema.Query == nil {
   732  		schema.Query = &ast.Definition{
   733  			Kind: ast.Object,
   734  			Name: "Query",
   735  		}
   736  		schema.Types["Query"] = schema.Query
   737  	}
   738  
   739  	c.Schema = schema
   740  	return nil
   741  }
   742  
   743  func abs(path string) string {
   744  	absPath, err := filepath.Abs(path)
   745  	if err != nil {
   746  		panic(err)
   747  	}
   748  	return filepath.ToSlash(absPath)
   749  }