types_splitter_plugin

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 14, 2023 License: MIT Imports: 13 Imported by: 0

README

TypesSplitterPlugin Coverage Status Go Report Card Go Reference

This plugin splits the generated resolvers from gqlgen into multiple files.

When working with big projects, the generated resolvers file can become quite big and hard to navigate. This plugin allows you to split the generated resolvers into multiple files based on your configuration.

Usage

Configuration

You need a Yaml configuration file in your project, in this example we will call it gqlgen_plugins.yml.

types_splitter:
  types:
    -
      name: Posts
      prefix: posts
    -
      name: Manager
      prefix: managers.users
    -
      name: User
      prefix: users
  queries:
    -
      prefix: posts
      matches:
        - post
    -
      prefix: users
      matches:
        - user|manager
    -
      prefix: editors
      matches:
        - editor
  • types_splitter is the name of the plugin

  • types is the list of types to split

    • name is the name of the type
    • prefix is the prefix we want to apply to the generated file. eg. managers.users will generate managers.users.resolvers.go
  • queries is the list of queries to split (queries being Query, Mutation, Subscription types)

    • prefix is the prefix we want to apply to the generated file. eg. users will generate users.queries.resolvers.go or users.mutations.resolvers.go should their be any matches.
    • matches is the list of queries to match. eg. user|manager will match user and manager queries (ie. getUser).

Note that the order of the types and queries is important as the first match will be used.

Custom plugin

One way to use the plugin is to create a custom plugin that will load the configuration file and pass it to the plugin.

Create a new file called generate.go where your resolvers.go live, and add the following code:

//go:build ignore
package main

import (
    "github.com/99designs/gqlgen/api"
    "github.com/99designs/gqlgen/codegen/config"
    "github.com/99designs/gqlgen/plugin"
    splitter "github.com/nicored/types_splitter_plugin"
)

func main() {
    cfg, err := config.LoadConfigFromDefaultLocations()
    if err != nil {
        panic(err)
    }

    tsPluging, err := splitter.New("gqlgen_plugins.yml")
	if err != nil {
		panic(err)
    }

    err = api.Generate(cfg,
        api.AddPlugin(tsPluging),
    )
    if err != nil {
        panic(err)
    }
}

Then in your resolvers.go file, add the following comment:

//go:generate go run generate.go

And run it with go generate ./...

How it works

The plugin will go through all the types and queries and will generate a map of types and fields to files.

Then based on your configuration, it will determine whether to split the type or query into a new file, or keep it in its original file.

If an original source file is emptied after the split, it will be deleted.

If a query type (Query, Mutation, Subscription) is emptied after the split, it will be deleted and the source of the first definition of the query will become the main source file (eg. type Query instead of an extended type extend type Query)

Limitations

I made this plugin for my own use, so you may experience issues with it depending on your use case:

OS

It doesn't currently work on Windows:

  • Fix for Windows

Types

The following splits aren't supported, but could easily be added if needed:

  • Interfaces are left out
  • Enums are left out
  • Input types are left out
  • Scalars are left out
  • Unions are left out

Parsing:

I used the data from Config.Schema, and parsed the content in a rather naive way, so if you have a complex schema, it may not work as expected.

The found that the parser doesn't output consistent results for Position. Comments on fields are not included in the Position data, but comments on types are. So I had to use a workaround to get the correct position starting from the comment to the end of the field/type definition.

  • Use a lexer/parser to parse source input
  • The approach to move type definitions and fields to their new source is rather naive, and may lead to issues with complex schema.
  • Comments with "#" may not be parsed and moved correctly.
  • Removal of extra lines could lead to issues as the approach is also rather naive.

Contributing

Contributions are welcomed, send an MR or open an issue if you find a bug or want to add a feature.

For instance, the ability to set what the main source file for query types should be would be a nice addition ;)

  • Ability to set the main source file for query types
  • More unit-tests

License

MIT

Documentation

Index

Constants

View Source
const (
	PluginName      = "types_splitter"
	ResolversSuffix = ".resolvers"
)

Variables

This section is empty.

Functions

This section is empty.

Types

type DefObjectType

type DefObjectType uint32
const (
	DefQueryObject DefObjectType = iota
	DefMutationObject
	DefSubscriptionObject
	DefTypeObject
	DefInputObject
	DefScalar
)

type Definition

type Definition struct {
	*ast.Definition

	Content string
	Fields  FieldDefinitions

	// ActualPosition is the actual position of the definition in the source
	// including documentation and its entire scope
	ActualPosition *ast.Position
	// contains filtered or unexported fields
}

Definition is a wrapper around ast.Definition that implements Positioner which is used for Query, Mutation, Subscription, and Type definitions

func WrapDefinition

func WrapDefinition(def *ast.Definition, typ DefObjectType) *Definition

func (*Definition) ActualPos

func (d *Definition) ActualPos() *ast.Position

ActualPos returns the actual position of the definition in the source

func (*Definition) AddFields

func (d *Definition) AddFields(fields FieldDefinitions)

func (*Definition) Pos

func (d *Definition) Pos() *ast.Position

Pos returns the position of the definition including the source ref

func (*Definition) ShiftOffset

func (d *Definition) ShiftOffset(offset, lines int)

ShiftOffset shifts the position of the definition by the given offset and lines

type Definitions

type Definitions []*Definition

Definitions is a list of Definition

func WrapDefinitions

func WrapDefinitions(defList ast.DefinitionList, typ DefObjectType) Definitions

WrapDefinitions wraps a list of ast.Definition into a list of Definition

func (Definitions) PosAfter

func (ds Definitions) PosAfter(offset int) Positioners

PosAfter returns a list of Positioners that are defined after the given offset

func (Definitions) ShiftOffset

func (ds Definitions) ShiftOffset(offset int, lines int)

ShiftOffset shifts the position of the definitions by the given offset and lines

type FieldDefType

type FieldDefType uint32
const (
	DefQueryField FieldDefType = iota
	DefMutationField
	DefSubscriptionField
	DefObjectField
)

type FieldDefinition

type FieldDefinition struct {
	*ast.FieldDefinition

	Content        string
	ActualPosition *ast.Position
	// contains filtered or unexported fields
}

FieldDefinition is a wrapper around ast.FieldDefinition that implements Positioner which is used for Field definitions in Query, Mutation, Subscription, and Type definitions

func WrapFieldDefinition

func WrapFieldDefinition(field *ast.FieldDefinition, typ FieldDefType) *FieldDefinition

func (*FieldDefinition) ActualPos

func (fd *FieldDefinition) ActualPos() *ast.Position

func (*FieldDefinition) Pos

func (fd *FieldDefinition) Pos() *ast.Position

Pos returns the position of the field definition including the source ref

func (*FieldDefinition) ShiftOffset

func (fd *FieldDefinition) ShiftOffset(offset, lines int)

ShiftOffset shifts the position of the field definition by the given offset and lines

type FieldDefinitions

type FieldDefinitions []*FieldDefinition

FieldDefinitions is a list of FieldDefinition

func (FieldDefinitions) FindAstField

func (ds FieldDefinitions) FindAstField(field *ast.FieldDefinition) *FieldDefinition

FindAstField returns the FieldDefinition that wraps the given ast.FieldDefinition

func (FieldDefinitions) PosAfter

func (ds FieldDefinitions) PosAfter(offset int) Positioners

PosAfter returns a list of Positioners that are defined after the given offset

func (FieldDefinitions) ShiftOffset

func (ds FieldDefinitions) ShiftOffset(offset int, lines int)

ShiftOffset shifts the position of the FieldDefinitions by the given offset and lines

type FileNamer

type FileNamer interface {
	FileName() string
	Prefixed(prefix string) string
}

FileNamer is an interface for types that can return their name

type PluginsCfg

type PluginsCfg struct {
	Splitter *SplitterConfig `yaml:"types_splitter"`
}

PluginsCfg is a configuration for the plugin.

type Positioner

type Positioner interface {
	Pos() *ast.Position
	ActualPos() *ast.Position
}

Positioner is an interface for types that can return their position

type Positioners

type Positioners interface {
	PosAfter(offset int) Positioners
}

Positioners is an interface for types that can return a list of Positioners

type QuerySplitConfig

type QuerySplitConfig struct {
	// ResolverPrefix is the prefix that will be added to the resolver file name eg. racing => racing.queries.go.
	ResolverPrefix string `yaml:"prefix"`
	// Matches is a list of string regexes that will be used to match against the query name. They must be ordered by priority.
	Matches []string `yaml:"matches"`
	// contains filtered or unexported fields
}

QuerySplitConfig is a configuration for splitting queries and mutations into multiple files.

type QuerySplitConfigs

type QuerySplitConfigs []QuerySplitConfig

func (QuerySplitConfigs) FindResolverPrefix

func (qs QuerySplitConfigs) FindResolverPrefix(queryName string) (string, bool)

FindResolverPrefix returns the resolver prefix for the given query name.

type QueryViewData

type QueryViewData struct {
	Type   string
	Fields FieldDefinitions
}

type Shifter

type Shifter interface {
	Positioner
	ShiftOffset(offset, lines int)
}

Shifter is an interface for types that can shift their position

type Shifters

type Shifters interface {
	Positioners
	ShiftOffset(offset, lines int)
}

Shifters is an interface for types that can shift a list of Positioners

type Source

type Source struct {
	*ast.Source

	Fields FieldDefinitions
	Types  Definitions
	// contains filtered or unexported fields
}

Source is a wrapper around ast.Source that implements Positioner

func NewSource

func NewSource(name string, sourceType SourceType) (*Source, error)

NewSource creates a new Source

func WrapSource

func WrapSource(s *ast.Source) *Source

WrapSource wraps an ast.Source in a Source

func (*Source) FileName

func (s *Source) FileName() string

FileName returns the name of the source

func (*Source) GenerateInput

func (s *Source) GenerateInput() (string, error)

func (*Source) Prefixed

func (s *Source) Prefixed(prefix string) string

Prefixed adds a prefix to the name of the source and returns it

type SourceType

type SourceType uint32
const (
	OriginalSource SourceType = iota
	SourceQueryExtended
	SourceMutationExtended
	SourceSubscriptionExtended
	SourceObject
	SourceInput
)

type Sources

type Sources []*Source

Sources is a list of Source

func WrapSources

func WrapSources(sources []*ast.Source) Sources

WrapSources wraps a list of ast.Source into a list of Source

type SourcesDefs

type SourcesDefs map[string]Definitions

SourcesDefs is a map of source names to Definitions

type SourcesFields

type SourcesFields map[string]FieldDefinitions

SourcesFields is a map of source names to FieldDefinitions

type SourcesMap

type SourcesMap map[string]*Source

SourcesMap is a map of source names to Sources

type SplitterConfig

type SplitterConfig struct {
	QueryConfig QuerySplitConfigs `yaml:"queries"`
	TypeConfig  TypeSplitConfigs  `yaml:"types"`
}

SplitterConfig is a configuration for splitting queries, mutations, subscriptions and types into multiple files.

type TypeSplitConfig

type TypeSplitConfig struct {
	// Name is the name of the type that will be used to match against the type name. eg. RacingRace
	Name string `yaml:"name"`
	// ResolverPrefix is the prefix that will be added to the resolver file name eg. racing_race => racing_race.resolvers.go.
	ResolverPrefix string `yaml:"prefix"`
}

TypeSplitConfig is a configuration for splitting types into multiple files.

type TypeSplitConfigs

type TypeSplitConfigs []TypeSplitConfig

func (TypeSplitConfigs) FindResolverPrefix

func (ts TypeSplitConfigs) FindResolverPrefix(typeName string) (string, bool)

FindResolverPrefix returns the resolver prefix for the given type name.

type TypeViewData

type TypeViewData struct {
	Type  string
	Types Definitions
}

type TypesSplitterPlugin

type TypesSplitterPlugin struct {
	// contains filtered or unexported fields
}

TypesSplitterPlugin is a plugin that splits the schema into multiple files.

func New

func New(cfgFilePath string) (*TypesSplitterPlugin, error)

New creates a new TypesSplitterPlugin.

func (*TypesSplitterPlugin) MutateConfig

func (s *TypesSplitterPlugin) MutateConfig(genCfg *config.Config) error

MutateConfig implements plugin.ConfigMutator

func (*TypesSplitterPlugin) Name

func (s *TypesSplitterPlugin) Name() string

Name implements plugin.Plugin

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL