github.com/willdhorn/gqlgen@v0.0.0-20230430193005-695855bca25a/api/generate.go (about)

     1  package api
     2  
     3  import (
     4  	"fmt"
     5  	"regexp"
     6  	"syscall"
     7  
     8  	"github.com/willdhorn/gqlgen/codegen"
     9  	"github.com/willdhorn/gqlgen/codegen/config"
    10  	"github.com/willdhorn/gqlgen/plugin"
    11  	"github.com/willdhorn/gqlgen/plugin/federation"
    12  	"github.com/willdhorn/gqlgen/plugin/modelgen"
    13  	"github.com/willdhorn/gqlgen/plugin/resolvergen"
    14  )
    15  
    16  func Generate(cfg *config.Config, option ...Option) error {
    17  	_ = syscall.Unlink(cfg.Exec.Filename)
    18  	if cfg.Model.IsDefined() {
    19  		_ = syscall.Unlink(cfg.Model.Filename)
    20  	}
    21  
    22  	plugins := []plugin.Plugin{}
    23  	if cfg.Model.IsDefined() {
    24  		plugins = append(plugins, modelgen.New())
    25  	}
    26  	plugins = append(plugins, resolvergen.New())
    27  	if cfg.Federation.IsDefined() {
    28  		if cfg.Federation.Version == 0 { // default to using the user's choice of version, but if unset, try to sort out which federation version to use
    29  			urlRegex := regexp.MustCompile(`(?s)@link.*\(.*url:.*?"(.*?)"[^)]+\)`) // regex to grab the url of a link directive, should it exist
    30  
    31  			// check the sources, and if one is marked as federation v2, we mark the entirety to be generated using that format
    32  			for _, v := range cfg.Sources {
    33  				cfg.Federation.Version = 1
    34  				urlString := urlRegex.FindStringSubmatch(v.Input)
    35  				if urlString != nil && urlString[1] == "https://specs.apollo.dev/federation/v2.0" {
    36  					cfg.Federation.Version = 2
    37  					break
    38  				}
    39  			}
    40  		}
    41  		plugins = append([]plugin.Plugin{federation.New(cfg.Federation.Version)}, plugins...)
    42  	}
    43  
    44  	for _, o := range option {
    45  		o(cfg, &plugins)
    46  	}
    47  
    48  	for _, p := range plugins {
    49  		if inj, ok := p.(plugin.EarlySourceInjector); ok {
    50  			if s := inj.InjectSourceEarly(); s != nil {
    51  				cfg.Sources = append(cfg.Sources, s)
    52  			}
    53  		}
    54  	}
    55  
    56  	if err := cfg.LoadSchema(); err != nil {
    57  		return fmt.Errorf("failed to load schema: %w", err)
    58  	}
    59  
    60  	for _, p := range plugins {
    61  		if inj, ok := p.(plugin.LateSourceInjector); ok {
    62  			if s := inj.InjectSourceLate(cfg.Schema); s != nil {
    63  				cfg.Sources = append(cfg.Sources, s)
    64  			}
    65  		}
    66  	}
    67  
    68  	// LoadSchema again now we have everything
    69  	if err := cfg.LoadSchema(); err != nil {
    70  		return fmt.Errorf("failed to load schema: %w", err)
    71  	}
    72  
    73  	if err := cfg.Init(); err != nil {
    74  		return fmt.Errorf("generating core failed: %w", err)
    75  	}
    76  
    77  	for _, p := range plugins {
    78  		if mut, ok := p.(plugin.ConfigMutator); ok {
    79  			err := mut.MutateConfig(cfg)
    80  			if err != nil {
    81  				return fmt.Errorf("%s: %w", p.Name(), err)
    82  			}
    83  		}
    84  	}
    85  	// Merge again now that the generated models have been injected into the typemap
    86  	data_plugins := make([]interface{}, len(plugins))
    87  	for index := range plugins {
    88  		data_plugins[index] = plugins[index]
    89  	}
    90  	data, err := codegen.BuildData(cfg, data_plugins...)
    91  	if err != nil {
    92  		return fmt.Errorf("merging type systems failed: %w", err)
    93  	}
    94  
    95  	if err = codegen.GenerateCode(data); err != nil {
    96  		return fmt.Errorf("generating core failed: %w", err)
    97  	}
    98  
    99  	if !cfg.SkipModTidy {
   100  		if err = cfg.Packages.ModTidy(); err != nil {
   101  			return fmt.Errorf("tidy failed: %w", err)
   102  		}
   103  	}
   104  
   105  	for _, p := range plugins {
   106  		if mut, ok := p.(plugin.CodeGenerator); ok {
   107  			err := mut.GenerateCode(data)
   108  			if err != nil {
   109  				return fmt.Errorf("%s: %w", p.Name(), err)
   110  			}
   111  		}
   112  	}
   113  
   114  	if err = codegen.GenerateCode(data); err != nil {
   115  		return fmt.Errorf("generating core failed: %w", err)
   116  	}
   117  
   118  	if !cfg.SkipValidation {
   119  		if err := validate(cfg); err != nil {
   120  			return fmt.Errorf("validation failed: %w", err)
   121  		}
   122  	}
   123  
   124  	return nil
   125  }
   126  
   127  func validate(cfg *config.Config) error {
   128  	roots := []string{cfg.Exec.ImportPath()}
   129  	if cfg.Model.IsDefined() {
   130  		roots = append(roots, cfg.Model.ImportPath())
   131  	}
   132  
   133  	if cfg.Resolver.IsDefined() {
   134  		roots = append(roots, cfg.Resolver.ImportPath())
   135  	}
   136  
   137  	cfg.Packages.LoadAll(roots...)
   138  	errs := cfg.Packages.Errors()
   139  	if len(errs) > 0 {
   140  		return errs
   141  	}
   142  	return nil
   143  }