Skip to content
Open
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
3 changes: 3 additions & 0 deletions .dagger/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@ source = ".."

# Marker filename that skips generate when found at or above a TypeScript SDK module root.
# settings.skipGenerateFilename = ""

[modules.e2e]
source = "modules/e2e"
Empty file added .dagger/lock
Empty file.
Empty file.
Empty file.
Empty file.
7 changes: 7 additions & 0 deletions .dagger/modules/e2e/fixtures/config/app/dagger.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "config-app",
"engineVersion": "latest",
"sdk": {
"source": "typescript"
}
}
3 changes: 3 additions & 0 deletions .dagger/modules/e2e/fixtures/config/app/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"type": "module"
}
9 changes: 9 additions & 0 deletions .dagger/modules/e2e/fixtures/config/app/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { object, func } from "@dagger.io/dagger"

@object()
export class ConfigApp {
@func()
hello(): string {
return "hello"
}
}
10 changes: 10 additions & 0 deletions .dagger/modules/e2e/fixtures/config/app/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"compilerOptions": {
"target": "ES2020",
"experimentalDecorators": true,
"paths": {
"@dagger.io/dagger": ["./sdk/index.ts"],
"@dagger.io/dagger/telemetry": ["./sdk/telemetry.ts"]
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "config-configured-deno",
"engineVersion": "latest",
"sdk": {
"source": "typescript"
}
}
13 changes: 13 additions & 0 deletions .dagger/modules/e2e/fixtures/config/configured-deno/deno.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"nodeModulesDir": "auto",
"compilerOptions": {
"experimentalDecorators": true
},
"imports": {
"@dagger.io/dagger": "./sdk/index.ts",
"@dagger.io/dagger/telemetry": "./sdk/telemetry.ts"
},
"dagger": {
"baseImage": "denoland/deno:alpine-2.0.0"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { object, func } from "@dagger.io/dagger"

@object()
export class ConfigConfiguredDeno {
@func()
hello(): string {
return "hello"
}
}
7 changes: 7 additions & 0 deletions .dagger/modules/e2e/fixtures/config/configured/dagger.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "config-configured",
"engineVersion": "latest",
"sdk": {
"source": "typescript"
}
}
7 changes: 7 additions & 0 deletions .dagger/modules/e2e/fixtures/config/configured/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "module",
"packageManager": "pnpm@8.15.4",
"dagger": {
"baseImage": "node:23.2.0-alpine"
}
}
9 changes: 9 additions & 0 deletions .dagger/modules/e2e/fixtures/config/configured/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { object, func } from "@dagger.io/dagger"

@object()
export class ConfigConfigured {
@func()
hello(): string {
return "hello"
}
}
10 changes: 10 additions & 0 deletions .dagger/modules/e2e/fixtures/config/configured/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"compilerOptions": {
"target": "ES2020",
"experimentalDecorators": true,
"paths": {
"@dagger.io/dagger": ["./sdk/index.ts"],
"@dagger.io/dagger/telemetry": ["./sdk/telemetry.ts"]
}
}
}
1 change: 1 addition & 0 deletions .dagger/modules/e2e/fixtures/generate/app/.gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/sdk/** linguist-generated
4 changes: 4 additions & 0 deletions .dagger/modules/e2e/fixtures/generate/app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/sdk
/**/node_modules/**
/**/.pnpm-store/**
/.env
3 changes: 3 additions & 0 deletions .dagger/modules/e2e/fixtures/generate/app/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"type": "module"
,"dependencies":{"typescript":"5.9.3"}}
41 changes: 41 additions & 0 deletions .dagger/modules/e2e/fixtures/generate/app/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* A generated module for GenerateApp functions
*
* This module has been generated via dagger module init and serves as a reference to
* basic module structure as you get started with Dagger.
*
* Two functions have been pre-created. You can modify, delete, or add to them,
* as needed. They demonstrate usage of arguments and return types using simple
* echo and grep commands. The functions can be called from the dagger CLI or
* from one of the SDKs.
*
* The first line in this comment block is a short description line and the
* rest is a long description with more detail on the module's purpose or usage,
* if appropriate. All modules should have a short description.
*/
import { dag, Container, Directory, object, func } from "@dagger.io/dagger"

@object()
export class GenerateApp {
/**
* Returns a container that echoes whatever string argument is provided
*/
@func()
containerEcho(stringArg: string): Container {
return dag.container().from("alpine:latest").withExec(["echo", stringArg])
}

/**
* Returns lines that match a pattern in the files of the provided Directory
*/
@func()
async grepDir(directoryArg: Directory, pattern: string): Promise<string> {
return dag
.container()
.from("alpine:latest")
.withMountedDirectory("/mnt", directoryArg)
.withWorkdir("/mnt")
.withExec(["grep", "-R", pattern, "."])
.stdout()
}
}
13 changes: 13 additions & 0 deletions .dagger/modules/e2e/fixtures/generate/app/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"compilerOptions": {
"target": "ES2022",
"moduleResolution": "Node",
"experimentalDecorators": true,
"strict": true,
"skipLibCheck": true,
"paths": {
"@dagger.io/dagger": ["./sdk/index.ts"],
"@dagger.io/dagger/telemetry": ["./sdk/telemetry.ts"]
}
}
}
8 changes: 8 additions & 0 deletions .dagger/modules/e2e/fixtures/generate/app/yarn.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1


typescript@5.9.3:
version "5.9.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.3.tgz#5b4f59e15310ab17a216f5d6cf53ee476ede670f"
integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==
111 changes: 109 additions & 2 deletions .dagger/modules/e2e/main.dang
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ type E2e {
let existingNodePath: String! = fixtureRoot + "/init-existing/node-app"
let existingDenoPath: String! = fixtureRoot + "/init-existing/deno-app"
let existingBunPath: String! = fixtureRoot + "/init-existing/bun-app"
let configModulePath: String! = fixtureRoot + "/config/app"
let configuredModulePath: String! = fixtureRoot + "/config/configured"
let configuredDenoModulePath: String! = fixtureRoot + "/config/configured-deno"

"""
Fail the current check when a condition is false.
Expand Down Expand Up @@ -65,6 +68,16 @@ type E2e {
assert(changes.removedPaths.length == 0, "dependency edit should not remove generated files")
}

"""
Assert that a config edit modified only the named file (no adds, no removes).
"""
let assertOnlyFileChanged(changes: Changeset!, path: String!, message: String!): Void {
assert(contains(changes.modifiedPaths, path), message + ": did not modify " + path)
assert(changes.modifiedPaths.length == 1, message + ": modified more than one file")
assert(changes.addedPaths.length == 0, message + ": should not add files")
assert(changes.removedPaths.length == 0, message + ": should not remove files")
}

"""
The helper should expose the generate skip marker used by callers.
"""
Expand Down Expand Up @@ -185,7 +198,7 @@ type E2e {
pub generateCheck(ws: Workspace!): Void @check {
let changes = typescriptSdk.mod(ws, path: generateModulePath).generate(ws)

assertAdded(changes, generateModulePath + "/src/index.ts")
assertAdded(changes, generateModulePath + "/sdk/index.ts")

null
}
Expand All @@ -196,7 +209,7 @@ type E2e {
pub generateAllCheck(ws: Workspace!): Void @check {
let changes = typescriptSdk.generateAll(ws)

assertAdded(changes, generateModulePath + "/src/index.ts")
assertAdded(changes, generateModulePath + "/sdk/index.ts")
assert(contains(changes.addedPaths, lookupModulePath + "/src/index.ts") == false, "generateAll generated a lookup fixture that should be skipped")
assert(contains(changes.addedPaths, depsModulePath + "/src/index.ts") == false, "generateAll generated dependency fixtures that should be skipped")
assert(contains(changes.addedPaths, skipModulePath + "/src/index.ts") == false, "generateAll generated a skipped module")
Expand Down Expand Up @@ -288,6 +301,100 @@ type E2e {
null
}

"""
Config readers should reflect package.json (or deno.json), and mutators
should edit only the targeted config file while preserving unrelated keys.
"""
pub configCheck(ws: Workspace!): Void @check {
let app = typescriptSdk.mod(ws, path: configModulePath).config
let configured = typescriptSdk.mod(ws, path: configuredModulePath).config
let configuredDeno = typescriptSdk.mod(ws, path: configuredDenoModulePath).config

# Defaults: unconfigured node module reads empty.
assert(app.packageManager == "", "default packageManager should be empty")
assert(app.baseImage == "", "default baseImage should be empty")

# Configured node module reads the override values.
assert(configured.packageManager == "pnpm@8.15.4", "configured packageManager should read pnpm@8.15.4")
assert(configured.baseImage == "node:23.2.0-alpine", "configured baseImage should read the override")

# Deno module: no package.json, baseImage comes from deno.json.
assert(configuredDeno.packageManager == "", "deno-only module should expose no packageManager")
assert(configuredDeno.baseImage == "denoland/deno:alpine-2.0.0", "deno baseImage should read from deno.json")

let appPkg = configModulePath + "/package.json"

let pm = app.setPackageManager("yarn@1.22.22")
assertOnlyFileChanged(pm, appPkg, "setPackageManager")
assertContains(pm.after.file(appPkg).contents, "yarn@1.22.22", "setPackageManager did not write the new value")
assertContains(pm.after.file(appPkg).contents, "\"type\": \"module\"", "setPackageManager dropped unrelated keys")

let img = app.setBaseImage("node:23.2.0-alpine")
assertOnlyFileChanged(img, appPkg, "setBaseImage")
assertContains(img.after.file(appPkg).contents, "node:23.2.0-alpine", "setBaseImage did not write the image")

let configuredPkg = configuredModulePath + "/package.json"

let unsetPm = configured.unsetPackageManager
assertOnlyFileChanged(unsetPm, configuredPkg, "unsetPackageManager")
assertNotContains(unsetPm.after.file(configuredPkg).contents, "packageManager", "unsetPackageManager left the field")
assertContains(unsetPm.after.file(configuredPkg).contents, "node:23.2.0-alpine", "unsetPackageManager clobbered dagger.baseImage")

let unsetImg = configured.unsetBaseImage
assertOnlyFileChanged(unsetImg, configuredPkg, "unsetBaseImage")
assertNotContains(unsetImg.after.file(configuredPkg).contents, "baseImage", "unsetBaseImage left the override")
assertContains(unsetImg.after.file(configuredPkg).contents, "pnpm@8.15.4", "unsetBaseImage clobbered packageManager")

# Deno modules: baseImage edits route to deno.json, not package.json.
let configuredDenoFile = configuredDenoModulePath + "/deno.json"

let denoImg = configuredDeno.setBaseImage("denoland/deno:latest")
assertOnlyFileChanged(denoImg, configuredDenoFile, "setBaseImage on deno module")
assertContains(denoImg.after.file(configuredDenoFile).contents, "denoland/deno:latest", "setBaseImage did not write to deno.json")

let unsetDenoImg = configuredDeno.unsetBaseImage
assertOnlyFileChanged(unsetDenoImg, configuredDenoFile, "unsetBaseImage on deno module")
assertNotContains(unsetDenoImg.after.file(configuredDenoFile).contents, "baseImage", "unsetBaseImage left the override in deno.json")
assertContains(unsetDenoImg.after.file(configuredDenoFile).contents, "nodeModulesDir", "unsetBaseImage clobbered unrelated deno keys")

null
}

"""
Init flags should write configuration into the generated config files. By
default, no config keys are written; the runtime decides which file the
base image override lands in.
"""
pub initConfigCheck(ws: Workspace!): Void @check {
let configuredPath = outputRoot + "/init-configured"
let configuredDenoPath = outputRoot + "/init-configured-deno"
let defaultPath = outputRoot + "/init-config-default"

let configured = typescriptSdk.init(ws, name: "init-configured", path: configuredPath, packageManager: "pnpm@8.15.4", baseImage: "node:23.2.0-alpine")
let pkg = configuredPath + "/package.json"
assertAdded(configured, pkg)
assert(configured.modifiedPaths.length == 0, "configured init should not modify existing files")
assert(configured.removedPaths.length == 0, "configured init should not remove files")
assertContains(configured.layer.file(pkg).contents, "pnpm@8.15.4", "init --package-manager not written")
assertContains(configured.layer.file(pkg).contents, "node:23.2.0-alpine", "init --base-image not written")

# Deno init with baseImage routes to deno.json; no package.json is created.
let configuredDeno = typescriptSdk.init(ws, name: "init-configured-deno", path: configuredDenoPath, runtime: TypescriptSdkRuntime.DENO, baseImage: "denoland/deno:alpine-2.0.0")
let denoFile = configuredDenoPath + "/deno.json"
assertAdded(configuredDeno, denoFile)
assertContains(configuredDeno.layer.file(denoFile).contents, "denoland/deno:alpine-2.0.0", "init deno --base-image not written")
assert(contains(configuredDeno.addedPaths, configuredDenoPath + "/package.json") == false, "deno init should not generate package.json even with baseImage")

# Default init has no config keys.
let default = typescriptSdk.init(ws, name: "init-config-default", path: defaultPath)
let defaultPkg = defaultPath + "/package.json"
assertAdded(default, defaultPkg)
assertNotContains(default.layer.file(defaultPkg).contents, "packageManager", "default init should not write packageManager")
assertNotContains(default.layer.file(defaultPkg).contents, "baseImage", "default init should not write baseImage")

null
}

"""
Listing modules in a workspace should return every module whose
dagger.json declares the TypeScript SDK, and nothing else.
Expand Down
38 changes: 38 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,44 @@ settings are preserved.
`init` only seeds template and config files. Run `mod ... generate` to
produce the generated SDK.

### Configure a module at creation

`init` accepts configuration flags written into the module's `package.json`
(or `deno.json` for Deno modules):

```sh
dagger call typescript-sdk init --name my-module \
--package-manager pnpm@8.15.4 \
--base-image node:23.2.0-alpine
```

Both flags are optional. By default no `packageManager` field is written and
no base image override is set. `--package-manager` is not valid with
`--runtime DENO`.

## Configure an existing module

Read current configuration:

```sh
dagger call typescript-sdk mod --path my-module config package-manager
dagger call typescript-sdk mod --path my-module config base-image
```

Change configuration (each prints a diff to confirm before writing):

```sh
dagger call typescript-sdk mod --path my-module \
config set-package-manager --value pnpm@8.15.4
dagger call typescript-sdk mod --path my-module \
config set-base-image --image node:23.2.0-alpine
dagger call typescript-sdk mod --path my-module config unset-package-manager
dagger call typescript-sdk mod --path my-module config unset-base-image
```

`base-image` writes to `deno.json` for Deno modules and to `package.json`
otherwise — matching where the engine reads it from.

## Generate SDK files

For a single module:
Expand Down
2 changes: 1 addition & 1 deletion dagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
{
"name": "polyfill",
"source": "github.com/dagger/sdk-sdk/polyfill@main",
"pin": "d6ab6406586e6b3853b8936b2b3a96bba2554071"
"pin": "09da9576688ec4b495ae4a9443a4719da86ed25e"
}
],
"source": "."
Expand Down
Loading