From 61f20b36ca5722cdc96dd263a5108d1dbdbda7ec Mon Sep 17 00:00:00 2001 From: Guillaume de Rouville Date: Thu, 13 Aug 2026 21:49:48 -0700 Subject: [PATCH] Remove the polyfill dependency The TypeScript SDK used dagger/polyfill for managed module discovery, generation, client generation, and config edits. Those behaviors now live in Workspace and ModuleSource. Read managed modules from currentModule.asSDK(workspace).modules, thread the Workspace through each generation step, and compare the final workspace with the input. Remove the polyfill and bump the engine requirement together so earlier staged edits are not returned twice. Signed-off-by: Guillaume de Rouville --- .dagger/modules/e2e/discovery.dang | 2 +- .dagger/modules/e2e/generate.dang | 6 +- README.md | 10 ++-- dagger.json | 9 +-- dagger.lock | 1 - mod-config.dang | 8 +-- mod.dang | 9 +-- typescript-sdk.dang | 94 +++++++----------------------- 8 files changed, 38 insertions(+), 101 deletions(-) diff --git a/.dagger/modules/e2e/discovery.dang b/.dagger/modules/e2e/discovery.dang index c1aa098..90d7db3 100644 --- a/.dagger/modules/e2e/discovery.dang +++ b/.dagger/modules/e2e/discovery.dang @@ -1,7 +1,7 @@ """ Checks for `typescriptSdk.modules` — which managed modules are in scope. -Discovery intersects two things: the config directories the polyfill finds from +Discovery intersects two things: the config directories findConfigDirs finds from the client's cwd, and the engine-owned list of modules this SDK manages (`currentModule.asSDK.modules`). So the workspace decides *what* is a module of ours and the cwd decides *how much of it* a command acts on. diff --git a/.dagger/modules/e2e/generate.dang b/.dagger/modules/e2e/generate.dang index ea71c02..735d102 100644 --- a/.dagger/modules/e2e/generate.dang +++ b/.dagger/modules/e2e/generate.dang @@ -25,8 +25,8 @@ type GenerateChecks { emit its typed bindings alongside the module's own — otherwise the module's source cannot import it and the generated tree does not type-check. - Both sides here are dagger-module.toml modules, the CLI 1.0 format: before the - polyfill included dagger-module.toml dir deps in the generated context this + Both sides here are dagger-module.toml modules, the CLI 1.0 format: before + dagger-module.toml dir deps were included in the generated context this failed with "dir module source does not contain a dagger config file". """ generateDependencyCheck(ws: Workspace!): Void @check { @@ -111,7 +111,7 @@ type GenerateChecks { on a re-anchored snapshot), so generating from a subdirectory for real cannot run as a check. Which modules a cwd selects is covered by `discovery:modules-cwd-check`, and re-rooting the result into cwd coordinates - belongs to the polyfill, which checks it in its own generate-from-subdir case. + belongs to the engine, which checks it in its own generate-from-subdir case. """ generateAllScopeCheck(ws: Workspace!): Void @check { let outside = fixtures.discoverySnapshot( diff --git a/README.md b/README.md index 37b40fb..e7bddf2 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ This module implements the Dagger CLI 1.0 SDK contract: the engine calls its generate`. It also exposes `targetRuntime` (`"typescript"`), so modules it creates run on the built-in TypeScript runtime. -Backed by [`github.com/dagger/polyfill`](https://github.com/dagger/polyfill). +It uses the engine's native `Workspace` and `ModuleSource` APIs directly. ## Install @@ -205,11 +205,9 @@ dagger call dagger-typescript-sdk generate-all-module dagger call dagger-typescript-sdk generate-all-client ``` -`modules` returns the modules this SDK manages — the -`[[modules..as-sdk.modules]]` entries the engine owns — intersected with -the ones visible from your current directory. A module is discovered through the -directory holding its config, so a module whose `source` points elsewhere is -found at its config path, not from inside its source tree. +`modules` returns the registered modules this SDK manages that are in scope from +your current directory: every module at or below it, plus the nearest enclosing +module when the current directory itself is not registered. ## Skipping generation diff --git a/dagger.json b/dagger.json index 28c9169..4b5027b 100644 --- a/dagger.json +++ b/dagger.json @@ -1,15 +1,8 @@ { "name": "typescript-sdk", - "engineVersion": "v1.0.0-0", + "engineVersion": "v1.0.0-beta.10", "sdk": { "source": "dang" }, - "dependencies": [ - { - "name": "polyfill", - "source": "github.com/dagger/polyfill@main", - "pin": "16627066d1852106320bdc0cfa0e5f901efe5970" - } - ], "source": "." } diff --git a/dagger.lock b/dagger.lock index 7e5723d..122494c 100644 --- a/dagger.lock +++ b/dagger.lock @@ -1,3 +1,2 @@ [["version","1"]] ["","git.head",["https://github.com/dagger/sdk-sdk"],"e1747f4b6221fa24da080701e027243e0cc5fa33","float"] -["","git.ref",["https://github.com/dagger/polyfill","main"],"ec3ea84a2351b4beb06ecece951f2e5ef66509ff","float"] \ No newline at end of file diff --git a/mod-config.dang b/mod-config.dang index afc2591..415e633 100644 --- a/mod-config.dang +++ b/mod-config.dang @@ -82,7 +82,7 @@ type ModConfig { withPm.withExec(["module-config", "set-base-image", toolPath, baseImage]) } let edited = withImg.file(toolPath).contents - polyfill.workspace(ws).fork.withNewFile(targetFile, edited).changes + ws.withNewFile("/" + targetFile, edited).changes(ws) } } @@ -92,7 +92,7 @@ type ModConfig { unsetPackageManager: Changeset! { if (runtime == Runtime.DENO) { # Deno modules carry no package.json, so there is nothing to unset. - polyfill.workspace(ws).fork.changes + ws.changes(ws) } else { edit(packageJsonPath, ["module-config", "unset-package-manager", toolPath]) } @@ -176,13 +176,13 @@ type ModConfig { let edit(file: String!, args: [String!]!): Changeset! { if (hasFile(file)) { let edited = tool(file).withExec(args).file(toolPath).contents - polyfill.workspace(ws).fork.withNewFile(file, edited).changes + ws.withNewFile("/" + file, edited).changes(ws) } else { # Nothing to unset when the target file is absent. Routing through tool() # would seed the missing file as "{}", making the unset a no-op, and then # write that empty file back as a stray addition (e.g. an empty # package.json in a Deno module). Return an empty Changeset instead. - polyfill.workspace(ws).fork.changes + ws.changes(ws) } } } diff --git a/mod.dang b/mod.dang index c4e5d25..c68e28f 100644 --- a/mod.dang +++ b/mod.dang @@ -51,7 +51,7 @@ type Mod { through the engine rather than assumed equal to rootPath. """ sourcePath: String! { - let subpath = polyfill.workspace(ws).moduleSource("/" + rootPath).core.sourceSubpath + let subpath = ws.moduleSource("/" + rootPath).sourceSubpath if (subpath == "" or subpath == ".") { "." } else { subpath } } @@ -90,12 +90,9 @@ type Mod { """ generate(ws: Workspace!): Changeset! { if (skipGenerate(ws)) { - polyfill.workspace(ws).fork.changes + ws.changes(ws) } else { - # Stage the local dependency closure so this module's codegen sees - # up-to-date dependency bindings before generating it. - let stagedWs = ws.withChanges(polyfill.workspace(ws).moduleSource("/" + rootPath).core.generateLocalDependencies(ws)) - polyfill.workspace(stagedWs).moduleSource("/" + rootPath).generate.changes + ws.moduleSource("/" + rootPath).generate(ws).changes(ws) } } } diff --git a/typescript-sdk.dang b/typescript-sdk.dang index 69eaec9..6134171 100644 --- a/typescript-sdk.dang +++ b/typescript-sdk.dang @@ -41,29 +41,20 @@ type TypescriptSdk { Return every TypeScript SDK module this workspace manages that is visible from the client's current location. - Discovery is anchored at the client's cwd (never the workspace root): the - nearest enclosing module plus every module at or below the cwd, intersected - with the SDK's engine-owned list of managed modules - (currentModule.asSDK.modules). So running from a subdirectory acts on the - project you're in — and the projects beneath it — not the whole workspace. - - Discovery is the polyfill's cwd-aware findConfigDirs (dagger/dagger#13688); - this maps its cwd-relative results to workspace-root-relative paths and keeps - the ones this SDK manages. + The engine selects the modules directly from the workspace's registered SDK + modules: every module at or below the cwd, plus the nearest enclosing module + when the cwd itself is not managed. No filesystem discovery or intersection + is required. A module is found through the directory holding its config, so one whose `source` field points elsewhere is discovered at its config path and not from inside its own source tree. Address those by path (see `mod`). """ modules(ws: Workspace!): [Mod!]! { - let managed = currentModule.asSDK(workspace: ws).modules.{{path}} - let cwd = clientCwd(ws) - polyfill.workspace(ws) - .findConfigDirs(moduleConfigFilenames, exclude: ["**/node_modules/**"]) - .map { dir => moduleRelPath(cwd, dir) } - .uniq - .filter { path => managed.filter { m => m.path == path }.length > 0 } - .map { path => Mod(rootPath: path, ws: ws, skipGenerateFilename: skipGenerateFilename) } + currentModule + .asSDK(workspace: ws) + .modules.{{path}} + .map { module => Mod(rootPath: module.path, ws: ws, skipGenerateFilename: skipGenerateFilename) } } """ @@ -77,32 +68,12 @@ type TypescriptSdk { """ Whether a workspace-root-relative path is in scope from `cwd`: at or below it, - or an ancestor of it — the same cone findConfigDirs walks for modules. + or an ancestor of it. Client entries use the same cwd cone as managed modules. """ let inCwdScope(cwd: String!, path: String!): Boolean! { cwd == "." or path == cwd or path.hasPrefix(cwd + "/") or cwd.hasPrefix(path + "/") } - """ - Resolve a findConfigDirs result — a cwd-relative path, at or below the cwd - ("." , "sub/dir") or a strict ancestor (".." , "../..") — against the cwd into a - workspace-root-relative path, the format both asSDK module paths and - Mod.rootPath use. - """ - let moduleRelPath(cwd: String!, dir: String!): String! { - let base = if (cwd == "" or cwd == ".") { [] } else { cwd.split("/") } - let segs = dir.split("/").reduce(base) { acc, seg => - if (seg == "..") { - acc.dropLast(1) - } else if (seg == "." or seg == "") { - acc - } else { - acc + [seg] - } - } - if (segs.length == 0) { "." } else { segs.join("/") } - } - """ Return the TypeScript SDK module at or above a workspace path. @@ -124,7 +95,7 @@ type TypescriptSdk { let modPath = if (findUp) { # Nearest enclosing module config, regardless of filename order: the deepest # hit wins, so a closer dagger.json is not shadowed by an ancestor - # dagger-module.toml (and vice versa) — matching polyfill findConfigDirs. + # dagger-module.toml (and vice versa) — matching Workspace.findRoots. let foundConfigPath = moduleConfigFilenames.reduce(null) { acc, name => let found = ws.findUp(name, path) if (configHitDepth(found) > configHitDepth(acc)) { found } else { acc } @@ -232,8 +203,6 @@ type TypescriptSdk { rawPath.trimSuffix("/") } - let fork = polyfill.workspace(ws).fork - # An empty name means "the default", not the templates/ directory itself — # which exists, so it would pass the check below and render every starter as # a subdirectory of the new module. @@ -254,7 +223,7 @@ type TypescriptSdk { let renderedSource = renderedTemplate(name, starter, runtime, existing, modPath) let templateSource = configuredTemplate(renderedSource, runtime, packageManager, baseImage) - fork.withDirectory(modPath, templateSource).changes + ws.withNewDirectory("/" + modPath, templateSource).changes(ws) } } @@ -281,7 +250,7 @@ type TypescriptSdk { """ dev: Boolean! = false, ): Changeset! { - polyfill.workspace(ws).fork.changes + ws.changes(ws) } """ @@ -570,9 +539,8 @@ type TypescriptSdk { """ path: String!, ): Changeset! { - let pws = polyfill.workspace(ws) - let modSrc = pws.moduleSource(module).core - pws.fork.withDirectory(path, clientDirectory( + let modSrc = ws.moduleSource(module) + ws.withNewDirectory("/" + path, clientDirectory( modSrc.clientSchemaIntrospectionJSON.contents, modSrc.moduleOriginalName, modSrc.engineVersion, @@ -583,7 +551,7 @@ type TypescriptSdk { modSrc.asString, modSrc.pin, existingClientConfig(ws, path) - )).changes + )).changes(ws) } """ @@ -594,29 +562,12 @@ type TypescriptSdk { Modules with the generate skip marker are skipped. """ generateAllModule(ws: Workspace!): Changeset! @generate { - let pws = polyfill.workspace(ws) - - let changes = modules(ws) + modules(ws) .filter { mod => mod.skipGenerate(ws) == false } - .map { mod => - # Stage this module's local dependency closure first (leaf-first, possibly - # across SDKs) so its codegen sees up-to-date dependency bindings. The dep - # codegen is ephemeral: taking the changeset against the staged workspace - # cancels it out, leaving only each module's own changes. - let stagedWs = ws.withChanges(pws.moduleSource("/" + mod.rootPath).core.generateLocalDependencies(ws)) - polyfill.workspace(stagedWs).moduleSource("/" + mod.rootPath).generate.changes + .reduce(ws) { stagedWs, mod => + stagedWs.moduleSource("/" + mod.rootPath).generate(stagedWs) } - - # Force the per-module codegen to evaluate concurrently: selecting a field on - # the whole list resolves every element in one pass, where folding them into - # one changeset would walk them one at a time. - changes.{{isEmpty}} - - # Fold onto pws.fork.changes (empty), never ws.changes: under the engine's - # nested ModuleSource.generateLocalDependencies the incoming ws already has a - # dependency closure staged, and re-including it would be re-rooted under the - # dependent and octopus-merged against the same files it just generated. - pws.fork.changes.withChangesets(changes) + .changes(ws) } """ @@ -635,15 +586,14 @@ type TypescriptSdk { dependency — skips clients that live elsewhere in the workspace. """ generateAllClient(ws: Workspace!): Changeset! @generate { - let pws = polyfill.workspace(ws) let cwd = clientCwd(ws) currentModule.asSDK(ws).clients .{{path, module, moduleSource.{{ clientSchemaIntrospectionJSON.{{ contents }}, moduleOriginalName, engineVersion, kind, pin, asString }} }} .filter { client => inCwdScope(cwd, client.path) } - .reduce(pws.fork) { fork, client => + .reduce(ws) { stagedWs, client => let m = client.moduleSource - fork.withDirectory(client.path, clientDirectory( + stagedWs.withNewDirectory("/" + client.path, clientDirectory( m.clientSchemaIntrospectionJSON.contents, m.moduleOriginalName, m.engineVersion, @@ -656,6 +606,6 @@ type TypescriptSdk { existingClientConfig(ws, client.path) )) } - .changes + .changes(ws) } }