Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ func EntrypointTemplateFuncs(module *TypedefModule, opts EntrypointOptions) temp
"renderTypeDef": c.renderTypeDef,
"renderArgCall": c.renderArgCall,
"renderFunctionExpr": c.renderFunctionExpr,
"renderObjectDef": c.renderObjectDef,
"renderFieldCall": c.renderFieldCall,
"renderEnumDef": c.renderEnumDef,
"renderEnumMemberCall": c.renderEnumMemberCall,
"renderInterfaceDef": c.renderInterfaceDef,
"classRuntimeRef": c.classRuntimeRef,
"classTypeRef": c.classTypeRef,
"isExportedClass": c.isExportedClass,
Expand Down Expand Up @@ -241,7 +246,9 @@ func (c *entrypointFuncCtx) renderArgCall(arg *TypedefArgument) string {
if arg.IsOptional {
td += ".withOptional(true)"
}
if hasDefault(arg) {
// An explicit `null` default must still be registered as the arg's default
// (unlike the coercion path's hasDefault, which excludes null on purpose).
if len(arg.DefaultValue) > 0 {
dv, ok := c.resolveDefaultValue(arg)
if !ok {
if !arg.IsOptional {
Expand All @@ -252,9 +259,87 @@ func (c *entrypointFuncCtx) renderArgCall(arg *TypedefArgument) string {
opts["defaultValue"] = fmt.Sprintf("JSON.stringify(%s) as string & { __JSON: never }", string(b))
}
}
if sm := sourceMapExpr(arg.Location); sm != "" {
opts["sourceMap"] = sm
}
return fmt.Sprintf(".withArg(%s, %s%s)", jsString(arg.Name), td, optsLit(opts))
}

// sourceMapExpr renders a `dag.sourceMap(path, line, col)` expression for a
// location captured at codegen time, or "" when absent. The static entrypoint
// replays these baked values at runtime so no-codegen modules keep the same
// source-map comments in dependents' bindings as codegen-at-runtime modules.
func sourceMapExpr(loc *TypedefLocation) string {
if loc == nil {
return ""
}
return fmt.Sprintf("dag.sourceMap(%s, %d, %d)", jsString(loc.Filepath), loc.Line, loc.Column)
}

func (c *entrypointFuncCtx) renderObjectDef(obj *TypedefObject) string {
opts := map[string]string{}
if obj.Description != "" {
opts["description"] = jsString(obj.Description)
}
if obj.Deprecated != "" {
opts["deprecated"] = jsString(obj.Deprecated)
}
if sm := sourceMapExpr(obj.Location); sm != "" {
opts["sourceMap"] = sm
}
return fmt.Sprintf("dag.typeDef().withObject(%s%s)", jsString(obj.Name), optsLit(opts))
}

func (c *entrypointFuncCtx) renderFieldCall(prop *TypedefProperty) string {
opts := map[string]string{}
if prop.Description != "" {
opts["description"] = jsString(prop.Description)
}
if prop.Deprecated != "" {
opts["deprecated"] = jsString(prop.Deprecated)
}
if sm := sourceMapExpr(prop.Location); sm != "" {
opts["sourceMap"] = sm
}
return fmt.Sprintf(".withField(%s, %s%s)", jsString(propFieldName(prop)), c.renderTypeDef(prop.Type), optsLit(opts))
}

func (c *entrypointFuncCtx) renderEnumDef(e *TypedefEnum) string {
opts := map[string]string{}
if e.Description != "" {
opts["description"] = jsString(e.Description)
}
if sm := sourceMapExpr(e.Location); sm != "" {
opts["sourceMap"] = sm
}
return fmt.Sprintf("dag.typeDef().withEnum(%s%s)", jsString(e.Name), optsLit(opts))
}

func (c *entrypointFuncCtx) renderEnumMemberCall(v *TypedefEnumValue) string {
opts := map[string]string{"value": jsString(v.Value)}
if v.Description != "" {
opts["description"] = jsString(v.Description)
}
if v.Deprecated != "" {
opts["deprecated"] = jsString(v.Deprecated)
}
if sm := sourceMapExpr(v.Location); sm != "" {
opts["sourceMap"] = sm
}
return fmt.Sprintf(".withEnumMember(%s%s)", jsString(v.Name), optsLit(opts))
}

func (c *entrypointFuncCtx) renderInterfaceDef(iface *TypedefInterface) string {
opts := map[string]string{}
if iface.Description != "" {
opts["description"] = jsString(iface.Description)
}
if sm := sourceMapExpr(iface.Location); sm != "" {
opts["sourceMap"] = sm
}
return fmt.Sprintf("dag.typeDef().withInterface(%s%s)", jsString(iface.Name), optsLit(opts))
}

func (c *entrypointFuncCtx) resolveDefaultValue(arg *TypedefArgument) (any, bool) {
if !isPrimitive(arg.Type) {
return nil, false
Expand Down Expand Up @@ -288,6 +373,9 @@ func (c *entrypointFuncCtx) renderFunctionExpr(fn *TypedefFunction) string {
if fn.Description != "" {
parts = append(parts, fmt.Sprintf(".withDescription(%s)", jsString(fn.Description)))
}
if sm := sourceMapExpr(fn.Location); sm != "" {
parts = append(parts, fmt.Sprintf(".withSourceMap(%s)", sm))
}
for _, arg := range fn.Arguments {
parts = append(parts, c.renderArgCall(arg))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,49 +43,55 @@ type TypedefFunction struct {
IsCheck bool `json:"isCheck"`
IsGenerator bool `json:"isGenerator"`
IsUp bool `json:"isUp"`
Location *TypedefLocation `json:"location,omitempty"`
ReturnType *TypedefType `json:"returnType,omitempty"`
Arguments []*TypedefArgument `json:"arguments"`
}

type TypedefArgument struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
Deprecated string `json:"deprecated,omitempty"`
Type *TypedefType `json:"type,omitempty"`
IsVariadic bool `json:"isVariadic"`
IsNullable bool `json:"isNullable"`
IsOptional bool `json:"isOptional"`
DefaultValue json.RawMessage `json:"defaultValue,omitempty"`
DefaultPath string `json:"defaultPath,omitempty"`
DefaultAddress string `json:"defaultAddress,omitempty"`
Ignore []string `json:"ignore,omitempty"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
Deprecated string `json:"deprecated,omitempty"`
Type *TypedefType `json:"type,omitempty"`
IsVariadic bool `json:"isVariadic"`
IsNullable bool `json:"isNullable"`
IsOptional bool `json:"isOptional"`
DefaultValue json.RawMessage `json:"defaultValue,omitempty"`
DefaultPath string `json:"defaultPath,omitempty"`
DefaultAddress string `json:"defaultAddress,omitempty"`
Ignore []string `json:"ignore,omitempty"`
Location *TypedefLocation `json:"location,omitempty"`
}

type TypedefProperty struct {
Name string `json:"name"`
Alias string `json:"alias,omitempty"`
Description string `json:"description,omitempty"`
Deprecated string `json:"deprecated,omitempty"`
IsExposed bool `json:"isExposed"`
Type *TypedefType `json:"type,omitempty"`
Name string `json:"name"`
Alias string `json:"alias,omitempty"`
Description string `json:"description,omitempty"`
Deprecated string `json:"deprecated,omitempty"`
IsExposed bool `json:"isExposed"`
Type *TypedefType `json:"type,omitempty"`
Location *TypedefLocation `json:"location,omitempty"`
}

type TypedefEnum struct {
Name string `json:"name"`
Description string `json:"description"`
Location *TypedefLocation `json:"location,omitempty"`
Values map[string]*TypedefEnumValue `json:"values"`
}

type TypedefEnumValue struct {
Name string `json:"name"`
Value string `json:"value"`
Description string `json:"description"`
Deprecated string `json:"deprecated,omitempty"`
Name string `json:"name"`
Value string `json:"value"`
Description string `json:"description"`
Deprecated string `json:"deprecated,omitempty"`
Location *TypedefLocation `json:"location,omitempty"`
}

type TypedefInterface struct {
Name string `json:"name"`
Description string `json:"description"`
Location *TypedefLocation `json:"location,omitempty"`
Functions map[string]*TypedefFunction `json:"functions"`
}

Expand Down
33 changes: 19 additions & 14 deletions helpers/codegen/generator/typescript/templates/functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -426,19 +426,20 @@ func (funcs typescriptTemplateFuncs) queryToClient(s string) string {
// in practice, many of these work just fine as e.g. method
// names, like 'export' and 'from'.
var jsKeywords = map[string]struct{}{
"await": {},
"break": {},
"case": {},
"catch": {},
"class": {},
"const": {},
"continue": {},
"debugger": {},
"default": {},
"delete": {},
"do": {},
"else": {},
"enum": {},
"arguments": {},
"await": {},
"break": {},
"case": {},
"catch": {},
"class": {},
"const": {},
"continue": {},
"debugger": {},
"default": {},
"delete": {},
"do": {},
"else": {},
"enum": {},
// "export": {}, // containr.export
"extends": {},
"false": {},
Expand Down Expand Up @@ -492,8 +493,12 @@ var jsKeywords = map[string]struct{}{
}

// formatEnum formats a GraphQL enum into a TS equivalent
// formatEnum names an enum member. It uses the same PascalCase rule as the rest
// of the generator rather than strcase.ToCamel, which mangles consecutive
// capitals ("EStarGZ" -> "EstarGz" instead of "EStarGz") and would leave a
// module's bindings incompatible with code written against the engine's.
func (funcs typescriptTemplateFuncs) formatEnum(s string) string {
return strcase.ToCamel(s)
return toPascalCase(s)
}

// isArgOptional checks if some arg are optional.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,15 @@ async function register(): Promise<string> {
{{- end }}
{{- range $name := sortedKeysObjects $module.Objects }}
{{- $obj := index $module.Objects $name }}
let obj_{{ $obj.Name }} = dag.typeDef().withObject({{ jsString $obj.Name }}
{{- if $obj.Description }}, { description: {{ jsString $obj.Description }}{{ if $obj.Deprecated }}, deprecated: {{ jsString $obj.Deprecated }}{{ end }} }
{{- else if $obj.Deprecated }}, { deprecated: {{ jsString $obj.Deprecated }} }
{{- end }})
let obj_{{ $obj.Name }} = {{ renderObjectDef $obj }}
{{- range $mName := sortedKeysMethods $obj.Methods }}
{{- $fn := index $obj.Methods $mName }}
obj_{{ $obj.Name }} = obj_{{ $obj.Name }}.withFunction({{ renderFunctionExpr $fn }})
{{- end }}
{{- range $pName := sortedKeysProps $obj.Properties }}
{{- $prop := index $obj.Properties $pName }}
{{- if $prop.IsExposed }}
obj_{{ $obj.Name }} = obj_{{ $obj.Name }}.withField({{ jsString (propFieldName $prop) }}, {{ renderTypeDef $prop.Type }}
{{- if $prop.Description }}, { description: {{ jsString $prop.Description }}{{ if $prop.Deprecated }}, deprecated: {{ jsString $prop.Deprecated }}{{ end }} }
{{- else if $prop.Deprecated }}, { deprecated: {{ jsString $prop.Deprecated }} }
{{- end }})
obj_{{ $obj.Name }} = obj_{{ $obj.Name }}{{ renderFieldCall $prop }}
{{- end }}
{{- end }}
{{- if $obj.Constructor }}
Expand All @@ -32,20 +26,16 @@ async function register(): Promise<string> {
{{- end }}
{{- range $name := sortedKeysEnums $module.Enums }}
{{- $e := index $module.Enums $name }}
let enum_{{ $e.Name }} = dag.typeDef().withEnum({{ jsString $e.Name }}
{{- if $e.Description }}, { description: {{ jsString $e.Description }} }{{ end }})
let enum_{{ $e.Name }} = {{ renderEnumDef $e }}
{{- range $vName := sortedKeysEnumValues $e.Values }}
{{- $v := index $e.Values $vName }}
enum_{{ $e.Name }} = enum_{{ $e.Name }}.withEnumMember({{ jsString $v.Name }}, { value: {{ jsString $v.Value }}
{{- if $v.Description }}, description: {{ jsString $v.Description }}{{ end }}
{{- if $v.Deprecated }}, deprecated: {{ jsString $v.Deprecated }}{{ end }} })
enum_{{ $e.Name }} = enum_{{ $e.Name }}{{ renderEnumMemberCall $v }}
{{- end }}
mod = mod.withEnum(enum_{{ $e.Name }})
{{- end }}
{{- range $name := sortedKeysIfaces $module.Interfaces }}
{{- $iface := index $module.Interfaces $name }}
let iface_{{ $iface.Name }} = dag.typeDef().withInterface({{ jsString $iface.Name }}
{{- if $iface.Description }}, { description: {{ jsString $iface.Description }} }{{ end }})
let iface_{{ $iface.Name }} = {{ renderInterfaceDef $iface }}
{{- range $fnName := sortedKeysMethods $iface.Functions }}
{{- $fn := index $iface.Functions $fnName }}
iface_{{ $iface.Name }} = iface_{{ $iface.Name }}.withFunction({{ renderFunctionExpr $fn }})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
* Compression algorithm to use for image layers.
*/
export enum ImageLayerCompression {
EstarGz = "EStarGZ",
Estargz = ImageLayerCompression.EstarGz,
EStarGz = "EStarGZ",
Estargz = ImageLayerCompression.EStarGz,
Gzip = "Gzip",
Uncompressed = "Uncompressed",
Zstd = "Zstd",
Expand All @@ -16,7 +16,7 @@ export enum ImageLayerCompression {
*/
export function ImageLayerCompressionValueToName(value: ImageLayerCompression): string {
switch (value) {
case ImageLayerCompression.EstarGz:
case ImageLayerCompression.EStarGz:
return "EStarGZ"
case ImageLayerCompression.Gzip:
return "Gzip"
Expand All @@ -36,7 +36,7 @@ export function ImageLayerCompressionValueToName(value: ImageLayerCompression):
export function ImageLayerCompressionNameToValue(name: string): ImageLayerCompression {
switch (name) {
case "EStarGZ":
return ImageLayerCompression.EstarGz
return ImageLayerCompression.EStarGz
case "Gzip":
return ImageLayerCompression.Gzip
case "Uncompressed":
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,14 @@ async function serializeSmoke(__obj: Smoke): Promise<any> {

async function register(): Promise<string> {
let mod = dag.module_()
let obj_Smoke = dag.typeDef().withObject("Smoke", { description: "A module covering the shapes the entrypoint has to dispatch." })
obj_Smoke = obj_Smoke.withFunction(dag.function_("ctr", dag.typeDef().withObject("Container")))
obj_Smoke = obj_Smoke.withFunction(dag.function_("greet", dag.typeDef().withKind(TypeDefKind.StringKind)).withDescription("Greet someone.").withArg("name", dag.typeDef().withKind(TypeDefKind.StringKind)).withArg("loud", dag.typeDef().withKind(TypeDefKind.BooleanKind).withOptional(true)))
obj_Smoke = obj_Smoke.withFunction(dag.function_("nothing", dag.typeDef().withKind(TypeDefKind.VoidKind).withOptional(true)))
obj_Smoke = obj_Smoke.withFunction(dag.function_("read", dag.typeDef().withKind(TypeDefKind.StringKind)).withArg("path", dag.typeDef().withKind(TypeDefKind.StringKind)))
obj_Smoke = obj_Smoke.withField("greeting", dag.typeDef().withKind(TypeDefKind.StringKind))
obj_Smoke = obj_Smoke.withField("source", dag.typeDef().withObject("Directory"), { description: "Where the source lives." })
obj_Smoke = obj_Smoke.withConstructor(dag.function_("", obj_Smoke).withArg("source", dag.typeDef().withObject("Directory")).withArg("greeting", dag.typeDef().withKind(TypeDefKind.StringKind), { defaultValue: JSON.stringify("hello") as string & { __JSON: never } }))
let obj_Smoke = dag.typeDef().withObject("Smoke", { description: "A module covering the shapes the entrypoint has to dispatch.", sourceMap: dag.sourceMap("src/index.ts", 7, 14) })
obj_Smoke = obj_Smoke.withFunction(dag.function_("ctr", dag.typeDef().withObject("Container")).withSourceMap(dag.sourceMap("src/index.ts", 31, 3)))
obj_Smoke = obj_Smoke.withFunction(dag.function_("greet", dag.typeDef().withKind(TypeDefKind.StringKind)).withDescription("Greet someone.").withSourceMap(dag.sourceMap("src/index.ts", 26, 3)).withArg("name", dag.typeDef().withKind(TypeDefKind.StringKind), { sourceMap: dag.sourceMap("src/index.ts", 26, 9) }).withArg("loud", dag.typeDef().withKind(TypeDefKind.BooleanKind).withOptional(true), { sourceMap: dag.sourceMap("src/index.ts", 26, 23) }))
obj_Smoke = obj_Smoke.withFunction(dag.function_("nothing", dag.typeDef().withKind(TypeDefKind.VoidKind).withOptional(true)).withSourceMap(dag.sourceMap("src/index.ts", 41, 3)))
obj_Smoke = obj_Smoke.withFunction(dag.function_("read", dag.typeDef().withKind(TypeDefKind.StringKind)).withSourceMap(dag.sourceMap("src/index.ts", 36, 9)).withArg("path", dag.typeDef().withKind(TypeDefKind.StringKind), { sourceMap: dag.sourceMap("src/index.ts", 36, 14) }))
obj_Smoke = obj_Smoke.withField("greeting", dag.typeDef().withKind(TypeDefKind.StringKind), { sourceMap: dag.sourceMap("src/index.ts", 15, 3) })
obj_Smoke = obj_Smoke.withField("source", dag.typeDef().withObject("Directory"), { description: "Where the source lives.", sourceMap: dag.sourceMap("src/index.ts", 12, 3) })
obj_Smoke = obj_Smoke.withConstructor(dag.function_("", obj_Smoke).withArg("source", dag.typeDef().withObject("Directory"), { sourceMap: dag.sourceMap("src/index.ts", 17, 15) }).withArg("greeting", dag.typeDef().withKind(TypeDefKind.StringKind), { defaultValue: JSON.stringify("hello") as string & { __JSON: never }, sourceMap: dag.sourceMap("src/index.ts", 17, 34) }))
mod = mod.withObject(obj_Smoke)
return await mod.id()
}
Expand Down
6 changes: 3 additions & 3 deletions library/bundle/core.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading