cuetsy

package module
v0.1.11 Latest Latest
Warning

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

Go to latest
Published: Jul 23, 2023 License: Apache-2.0 Imports: 14 Imported by: 4

README

Cuetsy Logo

Installation · Usage

cuetsy

Converting CUE objects to their TypeScript equivalent (highly experimental!)

  • CUE makes defining and validating canonical data specification easy
  • TypeScript is dominant in the frontend, but cannot natively benefit from this
  • CUE types often have direct TypeScript equivalents, so cuetsy can bridge this gap

Example

CUETypeScript
DiceFaces: 1 | 2 | 3 | 4 | 5 | 6 @cuetsy(kind="type")

Animal: {
    Name: string
    Sound: string
} @cuetsy(kind="interface")

LeggedAnimal: Animal & {
    Legs: int
} @cuetsy(kind="interface")

Pets: "Cat" | "Dog" | "Horse" @cuetsy(kind="enum")
export type DiceFaces = 1 | 2 | 3 | 4 | 5 | 6;
export interface Animal {
  Name: string;
  Sound: string;
}
export interface LeggedAnimal extends Animal {
  Legs: number;
}
export enum Pets {
  Cat = "Cat",
  Dog = "Dog",
  Horse = "Horse",
}

Status

Cuetsy is experimental. The following are supported:

Installation

Cuetsy can be installed using Go 1.16+

$ go install github.com/grafana/cuetsy/cmd/cuetsy

Usage

cuetsy must be invoked on files as follows:

$ cuetsy [file.cue]

This will create a logically equivalent [file].ts

Alternatively, cuetsy can be used as a library for more customized code generation.

Union Types

CUE TypeScript @cuetsy(kind)
Disjunction Union Type type

Union types are expressed in CUE and TypeScript nearly the same way, namely a series of disjunctions (a | b | c):

CUETypeScript
MyUnion: 1 | 2 | 3 | 4 | 5 | 6 @cuetsy(kind="type")
export type MyUnion = 1 | 2 | 3 | 4 | 5 | 6;

Interfaces

CUE TypeScript @cuetsy(kind)
Struct Interface interface

TypeScript interfaces are expressed as regular structs in CUE.

Caveats:

CUETypeScript
MyInterface: {
    Num: number
    Text: string
    List: [...number]
    Truth: bool
} @cuetsy(kind="interface")
export interface MyInterface {
  List: number[];
  Num: number;
  Text: string;
  Truth: boolean;
}

Inheritance

Interfaces can optionally extend another interface. If a type marked for export as a kind="interface" is unified (whether by & or embedding) with another type marked for export as an interface, it will produce extend in output:

CUETypeScript
AInterface: {
    AField: string
} @cuetsy(kind="interface")

ByUnifying: AInterface & {
    BField: int
} @cuetsy(kind="interface")

ByEmbedding: {
    AInterface
    CField: bool
} @cuetsy(kind="interface")
export interface AInterface {
  AField: string;
}

export interface ByUnifying extends AInterface {
  BField: number;
}

export interface ByEmbedding extends AInterface {
  CField: boolean;
}

Enums

CUE TypeScript @cuetsy(kind)
Disjunction String enums, Numeric enums enum

TypeScript's enums are union types, and are a mostly-exact mapping of what can be expressed with CUE's disjunctions. Disjunctions may contain only string or numeric values.

For string enums, the member names (keys) of the TypeScript enum are automatically inferred as the titled camel-case variant of their string value, but may be explicitly specified using the memberNames attribute. For a numeric enum, memberNames must be specified.

CUE TypeScript
AutoCamel: "foo" | "bar" @cuetsy(kind="enum")
ManualCamel: "foo" | "bar" @cuetsy(kind="enum",memberNames="Foo|Bar")
Arbitrary: "foo" | "bar" @cuetsy(kind="enum",memberNames="Zip|Zap")
Numeric: 0 | 1 | 2 @cuetsy(kind="enum",memberNames="Zero|One|Two")
export enum AutoCamel {
  Bar = 'bar',
  Foo = 'foo',
}

export enum ManualCamel {
  Bar = 'bar',
  Foo = 'foo',
}

export enum Arbitrary {
  Zap = 'bar',
  Zip = 'foo',
}

export enum Numeric {
  One = 1,
  Two = 2,
  Zero = 0,
}

Defaults

CUE TypeScript
Defaults const

Cuetsy can optionally generate a const for each type that holds default values. For that, mark CUE Default Values to your type definitions:

CUETypeScript
MyUnion: 1 | 2 | *3 @cuetsy(kind="type")

MyEnum: "foo" | *"bar" @cuetsy(kind="enum")

MyInterface: {
    num: int | *6
    txt: string | *"CUE"
    enm: MyDisjEnum
} @cuetsy(kind="interface")
export type MyUnion = 1 | 2 | 3;

export const defaultMyUnion: MyUnion = 3;

export enum MyEnum {
  Bar = 'bar',
  Foo = 'foo',
}

export const defaultMyEnum: MyEnum = MyEnum.Bar;

export interface MyInterface {
  enm: MyDisjEnum;
  num: number;
  txt: string;
}

export const defaultMyInterface: Partial<MyInterface> = {
  enm: MyDisjEnum.Bar,
  num: 6,
  txt: 'CUE',
};

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Generate

func Generate(val cue.Value, c Config) (b []byte, err error)

Generate takes a cue.Value and generates the corresponding TypeScript for all top-level members of that value that have appropriate @cuetsy attributes.

Hidden fields are ignored.

func GenerateAST

func GenerateAST(val cue.Value, c Config) (*ts.File, error)

func IgnoreImportMapper added in v0.1.9

func IgnoreImportMapper(path string) (string, error)

IgnoreImportMapper ignores all import paths cuetsy encounters, resulting in no import statements in generated TS output.

Types

type Concreteness

type Concreteness int

Concreteness is a measure of the level of concreteness of a value, where lower values mean more concrete.

Replicated from cuelang.org/internal/core/adt

const (
	BottomLevel Concreteness = iota

	// Concrete indicates a concrete scalar value, list or struct.
	Concrete

	// Constraint indicates a non-concrete scalar value that is more specific,
	// than a top-level type.
	Constraint

	// Type indicates a top-level specific type, for instance, string,
	// bytes, number, or bool.
	Type

	// Any indicates any value, or top.
	Any
)

type Config

type Config struct {
	// ImportMapper determines how CUE imports are mapped to Typescript imports. If
	// nil, any non-stdlib import in the input CUE source will result in a fatal
	// error.
	//
	// Import conversions are only run if the input [cue.Value] or its top-level
	// conjuncts if [cue.Value.Source] returns an [*ast.File]. This eliminates
	// computed values, and values representing nodes other than a root file
	// node.
	ImportMapper

	// Export determines whether generated TypeScript symbols are exported.
	Export bool
}

Config governs certain variable behaviors when converting CUE to Typescript.

type DeclPair added in v0.0.2

type DeclPair struct {
	// The generated type declaration.
	T ts.Decl
	// The default declaration corresponding to T.
	D ts.Decl
}

DeclPair represents a generated type declaration, with its corresponding default declaration.

func GenerateSingleAST added in v0.0.2

func GenerateSingleAST(name string, v cue.Value, t TSType) (*DeclPair, error)

type ImportMapper

type ImportMapper func(string) (string, error)

An ImportMapper takes a string containing a CUE import path (e.g. "github.com/grafana/cuetsy") and returns a string indicating the import path that should be used in the corresponding generated typescript, or an error if no mapping can be made.

An empty string return indicates no TS import statements should be generated for that CUE import path.

type KV

type KV struct {
	K, V string
}

type NewConfig

type NewConfig struct {
	// Subpath is the path within the cue.Instance that cuetsy should treat as the
	// root object to be rendered. Optional.
	Subpath cue.Path

	// Self indicates that, rather than rendering all the attribute-annotated
	// children of provided instance, the instance itself should be rendered.
	Self bool

	// InjectCuetsyAttrs is an optional set of attribute bodies to be dynamically
	// injected onto values as they're being rendered.
	//
	// The keys will be passed to cue.ParsePath(). The value will be treated as
	// the body of a @cuetsy attribute (@cuetsy(<value>)) for values encountered at
	// that path, relative to the root.
	InjectCuetsyAttrs map[string]string
}

type TSType added in v0.0.2

type TSType string

TSType strings indicate the kind of TypeScript declaration to which a CUE value should be translated. They are used in both @cuetsy attributes, and in calls to certain methods.

const (
	// TypeAlias targets conversion of a CUE value to a TypeScript `type`
	// declaration, which are called type aliases:
	// https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#type-aliases
	TypeAlias TSType = "type"

	// TypeInterface targets conversion of a CUE value to a TypeScript `interface`
	// declaration:
	// https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#interfaces
	TypeInterface TSType = "interface"

	// TypeEnum targets conversion of a CUE value to a TypeScript `enum`
	// declaration:
	// https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#enums
	TypeEnum TSType = "enum"
)

type TypeMapper

type TypeMapper interface {
	Value() cue.Value
	Kind() cue.Kind
	Concreteness() Concreteness
	String() string
}

TypeMapper is a general interface to describe a mapping between cue.Value and typescript kinds and types. Will necessarily need to omit expressions.

Directories

Path Synopsis
cmd
internal
ts
ast

Jump to

Keyboard shortcuts

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