codegen

package
v2.4.0 Latest Latest
Warning

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

Go to latest
Published: May 1, 2025 License: Apache-2.0 Imports: 41 Imported by: 0

Documentation

Overview

Package codegen implements the core code generation functionality of stencil. This package contains everything from the template functions exposed to stencil templates, to the core stencil renderer that powers everything.

Example (FromJson)
example := `
{
	"a": "b",
	"c": "d"
}
`
fmt.Println(fromJSON(example))
Output:

map[a:b c:d] <nil>
Example (FromYaml)
example := `
a: b
c: d
`
fmt.Println(fromYAML(example))
Output:

map[a:b c:d] <nil>
Example (Quotejoinstrings)
example := []string{"a", "b", "c"}
fmt.Println(quotejoinstrings(example, " "))
Output:

"a" "b" "c"
Example (ToJson)
example := map[string]interface{}{
	"a": "b",
	"c": "d",
}
fmt.Println(toJSON(example))
Output:

{"a":"b","c":"d"} <nil>
Example (ToYaml)
example := map[string]interface{}{
	"a": "b",
	"c": "d",
}
fmt.Println(toYAML(example))
Output:

a: b
c: d <nil>

Index

Examples

Constants

This section is empty.

Variables

View Source
var Default = template.FuncMap{
	"Dereference":      dereference,
	"QuoteJoinStrings": quotejoinstrings,
	"toYaml":           toYAML,
	"fromYaml":         fromYAML,
	"toJson":           toJSON,
	"fromJson":         fromJSON,
	"error":            tplError,
}

Default are stock template functions that don't impact the generation of a file. Anything that does that should be located in the scope of the file renderer function instead

View Source
var ErrStopProcessingTemplate = errors.New("stop processing template")

ErrStopProcessingTemplate is an error that can be returned to immediately stop processing a template.

Currently, only used in the [ModuleCaller].

Functions

func NewFuncMap

func NewFuncMap(st *Stencil, t *Template, log slogext.Logger) template.FuncMap

NewFuncMap returns the standard func map for a template

Types

type BlockVersion added in v2.2.0

type BlockVersion int8

BlockVersion is the version of a block

const (
	BlockVersion1 BlockVersion
	BlockVersion2
)

Contains valid BlockVersion values

type File

type File struct {

	// Deleted denotes this file as being deleted, if this is
	// true then f.contents should not be used.
	Deleted bool

	// Skipped denotes this file as being skipped, if this is
	// true then f.contents should not be used.
	Skipped bool

	// SkippedReason is the reason why this file was skipped
	SkippedReason string

	// Warnings is an array of warnings that were created
	// while rendering this template
	Warnings []string
	// contains filtered or unexported fields
}

File is a file that was created by a rendered template

func NewFile

func NewFile(path string, mode os.FileMode, modTime time.Time, sourceTemplate *Template) (*File, error)

NewFile creates a new file, an existing file at the given path is parsed to read blocks from, if it exists. An error is returned if the file is unable to be read for a reason other than not existing.

func (*File) AddDeprecationNotice

func (f *File) AddDeprecationNotice(msg string)

AddDeprecationNotice adds a deprecation notice to a file

func (*File) Block

func (f *File) Block(name string) string

Block returns the contents of a given block.

func (*File) Bytes

func (f *File) Bytes() []byte

Bytes returns the contents of this file as bytes

func (*File) IsDir

func (f *File) IsDir() bool

IsDir returns if this is file is a directory or not Note: We only support rendering files currently

func (*File) ModTime

func (f *File) ModTime() time.Time

ModTime returns the last modification time for the file

func (*File) Mode

func (f *File) Mode() os.FileMode

Mode returns the file mode

func (*File) Name

func (f *File) Name() string

Name returns the name of the file

func (*File) SetContents

func (f *File) SetContents(contents string)

SetContents updates the contents of the current file

func (*File) SetMode

func (f *File) SetMode(mode os.FileMode)

SetMode updates the mode of the file

func (*File) SetPath

func (f *File) SetPath(path string) error

SetPath updates the path of this file. This causes the blocks to be parsed again.

func (*File) Size

func (f *File) Size() int64

Size returns the size of the file

func (*File) String

func (f *File) String() string

String returns the contents of this file as a string

func (*File) Sys

func (f *File) Sys() interface{}

Sys implements the os.FileInfo.Sys method. This does not do anything.

func (*File) Write

func (f *File) Write(log slogext.Logger, dryRun bool) error

Write writes a codegen.File to disk based on its current state, logging appropriately

type NewTemplateOpts

type NewTemplateOpts struct {
	// Enable the adoptMode option for the Template file (see [codegen.Template.adoptMode])
	Adopt bool

	// Enable the binary option for the Template file (see [codegen.Template.Binary])
	Binary bool
}

NewTemplateOpts contains options for creating a new Template.

type ReadDirEntry

type ReadDirEntry interface {
	Name() string
	IsDir() bool
}

ReadDirEntry is a partial of os.DirEntry returned by TplStencil.ReadDir

type Stencil

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

Stencil provides the basic functions for stencil templates

func NewStencil

func NewStencil(m *configuration.Manifest, lock *stencil.Lockfile, mods []*modules.Module, log slogext.Logger, adopt bool) *Stencil

NewStencil creates a new, fully initialized Stencil renderer function

func (*Stencil) Close

func (s *Stencil) Close() error

Close closes all resources that should be closed when done rendering templates.

func (*Stencil) GenerateLockfile

func (s *Stencil) GenerateLockfile(tpls []*Template) *stencil.Lockfile

GenerateLockfile generates a stencil.Lockfile based on a list of templates.

func (*Stencil) PostRun

func (s *Stencil) PostRun(ctx context.Context, log slogext.Logger) error

PostRun runs all post run commands specified in the modules that this project depends on

func (*Stencil) RegisterExtensions

func (s *Stencil) RegisterExtensions(ctx context.Context) error

RegisterExtensions registers all extensions on the currently loaded modules.

func (*Stencil) RegisterInprocExtensions

func (s *Stencil) RegisterInprocExtensions(name string, ext apiv1.Implementation)

RegisterInprocExtensions registers the input ext extension directly. This API is used in unit tests to render modules with templates that invoke native extensions: input 'ext' can be either an actual extension or a mock one (feeding fake data into the template).

func (*Stencil) Render

func (s *Stencil) Render(ctx context.Context, log slogext.Logger) ([]*Template, error)

Render renders all templates using the Manifest that was provided to stencil at creation time, returned is the templates that were produced and their associated files.

type Template

type Template struct {

	// Module is the underlying module that's creating this template
	Module *modules.Module

	// Path is the path of this template relative to the owning module
	Path string

	// Files is a list of files that this template generated
	Files []*File

	// Contents is the content of this template
	Contents []byte

	// Binary denotes if a template is a binary "template" (not actually a
	// template) or not.  Binary "templates" are just copied verbatim to the
	// target path, used for embedding binary files into template repos for
	// uses like gradle-wrapper.jar.
	Binary bool

	// Library denotes if a template is a library template or not. Library
	// templates cannot generate files.
	Library bool
	// contains filtered or unexported fields
}

Template is a file that has been processed by stencil

func NewTemplate

func NewTemplate(m *modules.Module, fpath string, mode os.FileMode,
	modTime time.Time, contents []byte, log slogext.Logger, opts *NewTemplateOpts) (*Template, error)

NewTemplate creates a new Template with the current file being the same name with the extension .tpl being removed. If the provided template has the extension .library.tpl, then the Library field is set to true.

func (*Template) ImportPath

func (t *Template) ImportPath() string

ImportPath returns the path to this template, this is meant to denote which module this template is attached to

func (*Template) Parse

func (t *Template) Parse(_ *Stencil) error

Parse parses the provided template and makes it available to be Rendered in the context of the current module.

func (*Template) Render

func (t *Template) Render(st *Stencil, vals *Values) error

Render renders the provided template, the produced files are rendered onto the Files field of the template struct.

type TplError

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

TplError is a wrapper for an [error] that can be returned by function templates. This is due to template turning them into runtime panics by default.

func (TplError) Error

func (e TplError) Error() string

Error returns the error message.

func (TplError) Unwrap

func (e TplError) Unwrap() error

Unwrap returns the underlying error.

type TplFile

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

TplFile is the current file we're writing output to in a template. This can be changed via file.SetPath and written to by file.Install. When a template does not call file.SetPath a default file is created that matches the current template path with the extension '.tpl' removed from the path and operated on.

func (*TplFile) Block

func (f *TplFile) Block(name string) string

Block returns the contents of a given block

## <<Stencil::Block(name)>>
Hello, world!
## <</Stencil::Block>>

## <<Stencil::Block(name)>>
{{- /* Only output if the block is set */}}
{{- if not (empty (file.Block "name")) }}
{{ file.Block "name" }}
{{- end }}
## <</Stencil::Block>>

## <<Stencil::Block(name)>>
{{- /* Short hand syntax. Adds newline if no contents */}}
{{ file.Block "name" }}
## <</Stencil::Block>>

func (*TplFile) Create

func (f *TplFile) Create(path string, mode os.FileMode, modTime time.Time) (out string, err error)

Create creates a new file that is rendered by the current template

If the template has a single file with no contents this file replaces it.

{{- /* Skip the file that generates other files */}
{{- file.Skip }}
{{- define "command" }}
package main

import "fmt"

func main() {
  fmt.Println("hello, world!")
}

{{- end }}

# Generate a "<commandName>.go" file for each command in .arguments.commands
{{- range $_, $commandName := (stencil.Arg "commands") }}
{{- file.Create (printf "cmd/%s.go" $commandName) 0600 now }}
{{- stencil.Include "command" | file.SetContents }}
{{- end }}

func (*TplFile) Delete

func (f *TplFile) Delete() (out string, err error)

Delete deletes the current file being rendered

{{- file.Delete }}

func (*TplFile) MigrateTo added in v2.1.0

func (f *TplFile) MigrateTo(path string) (out string, err error)

MigrateTo migrates the current file to a new path. If the old file doesn't exist, it is treated as a `file.Skip`. If the old file still exists, then it is moved to the new path (via a create and write, then delete of the old path, not a filesystem move). If the MigrateTo target file already exists, it is overwritten.

{{- file.MigrateTo "new/path/to/file.txt" }}

func (*TplFile) Once

func (f *TplFile) Once() (out string, err error)

Once will only generate this file a single time, if it doesn't already exist, and store that fact in the stencil.lock file.

The first time a Once file is generated, it has its provenance stored in the stencil.lock file. Going forward, Once checks the lock file for history of the file, and if it finds it, it performs the same action as file.Skip.

{{- file.Once }}

func (*TplFile) Path

func (f *TplFile) Path() string

Path returns the current path of the file we're writing to

{{ file.Path }}

func (*TplFile) RemoveAll

func (f *TplFile) RemoveAll(path string) (out string, err error)

RemoveAll deletes all the contents in the provided path

{{- file.RemoveAll "path" }}

func (*TplFile) SetContents

func (f *TplFile) SetContents(contents string) error

SetContents sets the contents of file being rendered to the value

This is useful for programmatic file generation within a template.

{{- file.SetContents "Hello, world!" }}

func (*TplFile) SetPath

func (f *TplFile) SetPath(path string) (out string, err error)

SetPath changes the path of the current file being rendered

{{- file.SetPath "new/path/to/file.txt" }}

func (*TplFile) Skip

func (f *TplFile) Skip(reason string) (output string, err error)

Skip skips the current file being rendered

{{- file.Skip "A reason to skip this file" }}

func (*TplFile) Static

func (f *TplFile) Static() (out string, err error)

Static marks the current file as static

Marking a file is equivalent to calling file.Skip, but instead file.Skip is only called if the file already exists. This is useful for files you want to generate but only once. It's generally recommended that you do not do this as it limits your ability to change the file in the future.

{{- file.Static }}

type TplModule

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

TplModule is a per-template struct for executing functions registered onto [ModuleCaller].

func (*TplModule) Call

func (tm *TplModule) Call(name string, args ...any) (any, error)

Call executes a template function by name with the provided arguments. The function must have been exported by the module that provides it through the [Export] function.

Attempting to call a function that does not exist will return an error outside of the first pass where it will return `nil` instead.

The template that is called must return a value using the `return` template function, which is only available in this context.

In addition, all of the file, stencil and other functions are in the context of the owning template, not the template calling the function.

`.` in a template function acts the same way as it does for TplStencil.Include (`stencil.Include`). Meaning, it points to Values. The caller passed data is accessible on `.Data`.

Example:

// module-a
{{- define "HelloWorld" }}
{{- return (printf "Hello, %s!" .Data) }}
{{- end }}
{{ module.Export "HelloWorld" }}

// module-b
{{ module.Call "github.com/rgst-io/module-a.HelloWorld" "Jared" }}
// Output: Hello, Jared

func (*TplModule) Export

func (tm *TplModule) Export(name string) (string, error)

Export registers a function to allow it to be called by other templates.

This is only able to be called in library templates and the function's name must start with a capital letter. Function names are also only eligible to be exported once, if a function is exported twice the second call will be a runtime error.

Example:

{{- define "HelloWorld" }}
{{- return (printf "Hello, %s!" .Data) }}
{{- end }}

{{ module.Export "HelloWorld" }}

type TplStencil

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

TplStencil contains the global functions available to a template for interacting with stencil.

func (*TplStencil) AddToModuleHook

func (s *TplStencil) AddToModuleHook(module, name string, data ...any) (out string, err error)

AddToModuleHook adds to a hook in another module

This functions write to module hook owned by another module for it to operate on. These are not strongly typed so it's best practice to look at how the owning module uses it for now.

{{- /* This writes to a module hook */}}
{{- stencil.AddToModuleHook "github.com/myorg/repo" "myModuleHook" "myData" }}

func (*TplStencil) ApplyTemplate deprecated

func (s *TplStencil) ApplyTemplate(name string, dataSli ...any) (string, error)

ApplyTemplate is an alias to stencil.Include.

Deprecated: Use stencil.Include instead.

func (*TplStencil) Arg

func (s *TplStencil) Arg(pth string) (interface{}, error)

Arg returns the value of an argument in the project's manifest

{{- stencil.Arg "name" }}

func (*TplStencil) Debug

func (s *TplStencil) Debug(args ...interface{}) string

Debug logs the provided arguments under the DEBUG log level (must run stencil with --debug).

{{- stencil.Debug "I'm a log!" }}

func (*TplStencil) Exists

func (s *TplStencil) Exists(name string) bool

Exists returns true if the file exists in the current directory

{{- if stencil.Exists "myfile.txt" }}
{{ stencil.ReadFile "myfile.txt" }}
{{- end }}

func (*TplStencil) GetGlobal

func (s *TplStencil) GetGlobal(name string) any

GetGlobal retrieves a global variable set by SetGlobal. The data returned from this function is unstructured so by averse to panics - look at where it was set to ensure you're dealing with the proper type of data that you think it is.

{{- /* This retrieves a global from the current context of the template module repository */}}
{{ $isGeorgeCool := stencil.GetGlobal "IsGeorgeCool" }}

func (*TplStencil) GetModuleHook

func (s *TplStencil) GetModuleHook(name string) []any

GetModuleHook returns a module block in the scope of this module

This is incredibly useful for allowing other modules to write to files that your module owns. Think of them as extension points for your module. The value returned by this function is always a []any, aka a list.

{{- /* This returns a []any */}}
{{ $hook := stencil.GetModuleHook "myModuleHook" }}
{{- range $hook }}
  {{ . }}
{{- end }}

func (*TplStencil) Include

func (s *TplStencil) Include(name string, dataSli ...any) (string, error)

Include executes a named template (defined through the `define` function) with the provided optional data.

The provided data can be accessed within the defined template under the `.Data` key.

`.` is a copy of Values for the calling template, meaning it is not mutated to reflect that of the template being rendered.

## Examples

### Without Data

{{- define "command"}}
package main

import "fmt"

func main() {
  fmt.Println("hello, world!")
}

{{- end }}

{{- stencil.Include "command" | file.SetContents }}

### With Data

{{- define "command"}}
{{- $cliName := .Data }}
package main

import "fmt"

func main() {
		fmt.Println("hello from {{ $cliName }}!")
}

{{- end }}

{{- range $cliName := stencil.Arg "clis" }}
{{- stencil.Include "command" $cliName | file.SetContents }}
{{- end }}

func (*TplStencil) ReadBlocks

func (s *TplStencil) ReadBlocks(fpath string) (map[string]string, error)

ReadBlocks parses a file and attempts to read the blocks from it, and their data.

As a special case, if the file does not exist, an empty map is returned instead of an error.

**NOTE**: This function does not guarantee that blocks are able to be read during runtime. For example, if you try to read the blocks of a file from another module there is no guarantee that that file will exist before you run this function. Nor is there the ability to tell stencil to do that (stencil does not have any order guarantees). Keep that in mind when using this function.

{{- $blocks := stencil.ReadBlocks "myfile.txt" }}
{{- range $name, $data := $blocks }}
  {{- $name }}
  {{- $data }}
{{- end }}

func (*TplStencil) ReadDir

func (s *TplStencil) ReadDir(name string) ([]ReadDirEntry, error)

ReadDir reads the contents of a directory and returns a list of files/directories

{{ range $entry := stencil.ReadDir "/tests" }}
  {{ if $entry.IsDir }}
    {{ $entry.Name }}
  {{ end }}
{{ end }}

func (*TplStencil) ReadFile

func (s *TplStencil) ReadFile(name string) (string, error)

ReadFile reads a file from the current directory and returns it's contents

{{ stencil.ReadFile "myfile.txt" }}

func (*TplStencil) SetGlobal

func (s *TplStencil) SetGlobal(name string, data any) string

SetGlobal sets a global to be used in the context of the current template module repository. This is useful because sometimes you want to define variables inside of a helpers template file after doing manifest argument processing and then use them within one or more template files to be rendered; however, go templates limit the scope of symbols to the current template they are defined in, so this is not possible without external tooling like this function.

This template function stores (and its inverse, GetGlobal, retrieves) data that is not strongly typed, so use this at your own risk and be averse to panics that could occur if you're using the data it returns in the wrong way.

{{- /* This writes a global into the current context of the template module repository */}}
{{- stencil.SetGlobal "IsGeorgeCool" true -}}

type Values

type Values struct {
	// Git is information about the current git repository, if there is one
	Git git

	// Runtime is information about the current runtime environment
	Runtime runtimeVals

	// Config is strongly typed values from the project manifest
	Config config

	// Module is information about the current module being rendered
	Module module

	// Template is the name of the template being rendered
	Template stencilTemplate

	// Data is only available when a template is being rendered through
	// stencil.Include. It contains the data passed through said
	// call.
	Data any
}

Values is the top level container for variables being passed to a stencil template. When updating this struct, ensure that the receiver functions are updated to reflect the new fields.

func NewValues

func NewValues(ctx context.Context, sm *configuration.Manifest, mods []*modules.Module) *Values

NewValues returns a fully initialized Values based on the current runtime environment.

func (*Values) Copy

func (v *Values) Copy() *Values

Copy returns a copy of the current values

func (*Values) WithModule

func (v *Values) WithModule(name string, ver *resolver.Version) *Values

WithModule returns a copy of the current values with the provided module information being set.

func (*Values) WithTemplate

func (v *Values) WithTemplate(name string) *Values

WithTemplate returns a copy of the current values with the provided template information being set.

Jump to

Keyboard shortcuts

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