Skip to content

Commit 7f0fa76

Browse files
author
Tom Chauveau
committed
feat: typescript SDK resolve value by reference.
Fixes dagger#8676. Refactor typedef resolution to be resolve by reference. The introspection starts from the entrypoint (which is the class named the same as the module) and recursively resolve every references. Refactor the TS compiler API to be abstract with a AST that handle lookups, typedef resolutions and default value resolution. Use `imports` to resolve default value that refers to an exported variable. Add custom introspection error. Improve error handling to point to the line that couldn't be introspected. Add supports for native `enum` keyword and `type` native keyword. Simplify overall code abstractions too minimal. Avoid long abstractions functions. Signed-off-by: Tom Chauveau <[email protected]>
1 parent 9de79cc commit 7f0fa76

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+2419
-1628
lines changed

core/integration/testdata/modules/typescript/minimal/index.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,12 @@ export class Minimal {
4949
}
5050

5151
@func()
52-
echoOptional(msg = "default"): string {
52+
echoOptional(msg: string = "default"): string {
5353
return this.echo(msg);
5454
}
5555

5656
@func()
57-
echoOptionalSlice(msg = ["foobar"]): string {
57+
echoOptionalSlice(msg: string[] = ["foobar"]): string {
5858
return this.echo(msg.join("+"));
5959
}
6060

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { DaggerSDKError, DaggerSDKErrorOptions } from "./DaggerSDKError.js"
2+
import { ERROR_CODES, ERROR_NAMES } from "./errors-codes.js"
3+
4+
export class IntrospectionError extends DaggerSDKError {
5+
name = ERROR_NAMES.IntrospectionError
6+
code = ERROR_CODES.IntrospectionError
7+
8+
constructor(message: string, options?: DaggerSDKErrorOptions) {
9+
super(message, options)
10+
}
11+
}

sdk/typescript/common/errors/errors-codes.ts

+5
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@ export const ERROR_CODES = {
4848
* (@link ExecError}
4949
*/
5050
ExecError: "D109",
51+
52+
/**
53+
* {@link IntrospectionError}
54+
*/
55+
IntrospectionError: "D110",
5156
} as const
5257

5358
type ErrorCodesType = typeof ERROR_CODES

sdk/typescript/entrypoint/entrypoint.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export async function entrypoint() {
2828
const moduleName = await dag.currentModule().name()
2929
const parentName = await fnCall.parentName()
3030

31-
const scanResult = scan(files, moduleName)
31+
const scanResult = await scan(files, moduleName)
3232

3333
// Pre allocate the result, we got one in both case.
3434
// eslint-disable-next-line @typescript-eslint/no-explicit-any

sdk/typescript/entrypoint/invoke.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { FunctionNotFound } from "../common/errors/FunctionNotFound.js"
22
import { Executor } from "../introspector/executor/executor.js"
33
import { registry } from "../introspector/registry/registry.js"
4-
import { Constructor } from "../introspector/scanner/abtractions/constructor.js"
5-
import { DaggerEnum } from "../introspector/scanner/abtractions/enum.js"
6-
import { Method } from "../introspector/scanner/abtractions/method.js"
7-
import { DaggerModule } from "../introspector/scanner/abtractions/module.js"
8-
import { DaggerObject } from "../introspector/scanner/abtractions/object.js"
4+
import { DaggerConstructor as Constructor } from "../introspector/scanner/dagger_module/constructor.js"
5+
import { DaggerEnumBase } from "../introspector/scanner/dagger_module/enumBase.js"
6+
import { DaggerFunction as Method } from "../introspector/scanner/dagger_module/function.js"
7+
import { DaggerModule } from "../introspector/scanner/dagger_module/module.js"
8+
import { DaggerObjectBase } from "../introspector/scanner/dagger_module/objectBase.js"
99
import { InvokeCtx } from "./context.js"
1010
import {
1111
loadResult,
@@ -74,7 +74,7 @@ export async function invoke(
7474
}
7575

7676
if (result) {
77-
let returnType: DaggerObject | DaggerEnum
77+
let returnType: DaggerObjectBase | DaggerEnumBase
7878

7979
// Handle alias serialization by getting the return type to load
8080
// if the function called isn't a constructor.

sdk/typescript/entrypoint/load.ts

+28-11
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@ import Module from "node:module"
44
import { dag, TypeDefKind } from "../api/client.gen.js"
55
import { Executor } from "../introspector/executor/executor.js"
66
import { Args } from "../introspector/executor/executor.js"
7-
import { Constructor } from "../introspector/scanner/abtractions/constructor.js"
8-
import { DaggerEnum } from "../introspector/scanner/abtractions/enum.js"
9-
import { Method } from "../introspector/scanner/abtractions/method.js"
10-
import { DaggerModule } from "../introspector/scanner/abtractions/module.js"
11-
import { DaggerObject } from "../introspector/scanner/abtractions/object.js"
12-
import { TypeDef } from "../introspector/scanner/typeDefs.js"
7+
import { DaggerConstructor as Constructor } from "../introspector/scanner/dagger_module/constructor.js"
8+
import { DaggerEnum } from "../introspector/scanner/dagger_module/enum.js"
9+
import { DaggerEnumBase } from "../introspector/scanner/dagger_module/enumBase.js"
10+
import { DaggerFunction as Method } from "../introspector/scanner/dagger_module/function.js"
11+
import { DaggerModule } from "../introspector/scanner/dagger_module/module.js"
12+
import { DaggerObject } from "../introspector/scanner/dagger_module/object.js"
13+
import { DaggerObjectBase } from "../introspector/scanner/dagger_module/objectBase.js"
14+
import { TypeDef } from "../introspector/scanner/typedef.js"
1315
import { InvokeCtx } from "./context.js"
1416

1517
/**
@@ -32,7 +34,7 @@ export function loadInvokedObject(
3234
module: DaggerModule,
3335
parentName: string,
3436
): DaggerObject {
35-
return module.objects[parentName]
37+
return module.objects[parentName] as DaggerObject
3638
}
3739

3840
export function loadInvokedMethod(
@@ -60,12 +62,16 @@ export async function loadArgs(
6062
const args: Args = {}
6163

6264
// Load arguments
63-
for (const argName of method.getArgOrder()) {
65+
for (const argName of method.getArgsOrder()) {
6466
const argument = method.arguments[argName]
6567
if (!argument) {
6668
throw new Error(`could not find argument ${argName}`)
6769
}
6870

71+
if (!argument.type) {
72+
throw new Error(`could not find type for argument ${argName}`)
73+
}
74+
6975
const loadedArg = await loadValue(
7076
executor,
7177
ctx.fnArgs[argName],
@@ -118,6 +124,10 @@ export async function loadParentState(
118124
throw new Error(`could not find parent property ${key}`)
119125
}
120126

127+
if (!property.type) {
128+
throw new Error(`could not find type for parent property ${key}`)
129+
}
130+
121131
parentState[property.name] = await loadValue(executor, value, property.type)
122132
}
123133

@@ -192,8 +202,11 @@ export function loadObjectReturnType(
192202
module: DaggerModule,
193203
object: DaggerObject,
194204
method: Method,
195-
): DaggerObject | DaggerEnum {
205+
): DaggerObjectBase | DaggerEnumBase {
196206
const retType = method.returnType
207+
if (!retType) {
208+
throw new Error(`could not find return type for ${method.name}`)
209+
}
197210

198211
switch (retType.kind) {
199212
case TypeDefKind.ListKind: {
@@ -218,7 +231,7 @@ export function loadObjectReturnType(
218231
export async function loadResult(
219232
result: any,
220233
module: DaggerModule,
221-
object: DaggerObject | DaggerEnum,
234+
object: DaggerObjectBase | DaggerEnumBase,
222235
): Promise<any> {
223236
// Handle IDable objects
224237
if (result && typeof result?.id === "function") {
@@ -246,7 +259,11 @@ export async function loadResult(
246259
throw new Error(`could not find result property ${key}`)
247260
}
248261

249-
let referencedObject: DaggerObject | undefined = undefined
262+
if (!property.type) {
263+
throw new Error(`could not find type for result property ${key}`)
264+
}
265+
266+
let referencedObject: DaggerObjectBase | undefined = undefined
250267

251268
// Handle nested objects
252269
if (property.type.kind === TypeDefKind.ObjectKind) {

sdk/typescript/entrypoint/register.ts

+9-9
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,17 @@ import {
66
TypeDef,
77
TypeDefKind,
88
} from "../api/client.gen.js"
9-
import { Arguments } from "../introspector/scanner/abtractions/argument.js"
10-
import { Constructor } from "../introspector/scanner/abtractions/constructor.js"
11-
import { Method } from "../introspector/scanner/abtractions/method.js"
12-
import { DaggerModule } from "../introspector/scanner/abtractions/module.js"
9+
import { DaggerArguments as Arguments } from "../introspector/scanner/dagger_module/argument.js"
10+
import { DaggerConstructor as Constructor } from "../introspector/scanner/dagger_module/constructor.js"
11+
import { DaggerFunction as Method } from "../introspector/scanner/dagger_module/function.js"
12+
import { DaggerModule } from "../introspector/scanner/dagger_module/module.js"
1313
import {
1414
EnumTypeDef,
1515
ListTypeDef,
1616
ObjectTypeDef,
1717
ScalarTypeDef,
1818
TypeDef as ScannerTypeDef,
19-
} from "../introspector/scanner/typeDefs.js"
19+
} from "../introspector/scanner/typedef.js"
2020

2121
/**
2222
* Register the module files and returns its ID
@@ -50,7 +50,7 @@ export async function register(
5050
if (field.isExposed) {
5151
typeDef = typeDef.withField(
5252
field.alias ?? field.name,
53-
addTypeDef(field.type),
53+
addTypeDef(field.type!),
5454
{
5555
description: field.description,
5656
},
@@ -99,7 +99,7 @@ function addConstructor(constructor: Constructor, owner: TypeDef): Function_ {
9999
*/
100100
function addFunction(fct: Method): Function_ {
101101
return dag
102-
.function_(fct.alias ?? fct.name, addTypeDef(fct.returnType))
102+
.function_(fct.alias ?? fct.name, addTypeDef(fct.returnType!))
103103
.withDescription(fct.description)
104104
.with(addArg(fct.arguments))
105105
}
@@ -114,7 +114,7 @@ function addArg(args: Arguments): (fct: Function_) => Function_ {
114114
description: arg.description,
115115
}
116116

117-
let typeDef = addTypeDef(arg.type)
117+
let typeDef = addTypeDef(arg.type!)
118118
if (arg.isOptional) {
119119
typeDef = typeDef.withOptional(true)
120120
}
@@ -132,7 +132,7 @@ function addArg(args: Arguments): (fct: Function_) => Function_ {
132132
// to workaround the fact that the API isn't aware of the default value and will
133133
// expect it to be set as required input.
134134
if (arg.defaultValue) {
135-
if (isPrimitiveType(arg.type)) {
135+
if (isPrimitiveType(arg.type!)) {
136136
opts.defaultValue = arg.defaultValue as string & { __JSON: never }
137137
} else {
138138
typeDef = typeDef.withOptional(true)

0 commit comments

Comments
 (0)