Skip to content

Add Deno support to the TypeScript AppHost toolchain resolver#18627

Open
rickylabs wants to merge 2 commits into
microsoft:mainfrom
rickylabs:feat/deno-typescript-apphost-toolchain
Open

Add Deno support to the TypeScript AppHost toolchain resolver#18627
rickylabs wants to merge 2 commits into
microsoft:mainfrom
rickylabs:feat/deno-typescript-apphost-toolchain

Conversation

@rickylabs

Copy link
Copy Markdown

Add Deno support to the TypeScript AppHost toolchain resolver

Closes #16218.

What

Adds a Deno branch to the CLI's TypeScript-AppHost toolchain resolver
(src/Aspire.Cli/Projects/TypeScriptAppHostToolchainResolver.cs), so an apphost.mts can run under
the Deno runtime exactly the way Bun already does today. This is the CLI/toolchain half of Deno
support — the counterpart to #16218's tracked scope (toolchain resolution, CLI dependency handling,
scaffolding, debugging).

Detection keys on deno.lock / deno.json(c) (not the packageManager field). Command vectors:

  • install: deno install
  • type-check: deno check {file}
  • run: deno run -A {file}
  • watch: deno run -A --check --watch {file}
  • task: deno task --cwd <dir> <script>

Diff is small and additive: +47 / -1 lines vs main — it slots a Deno case alongside the
existing Bun/Node cases in the same resolver. No new architectural surface.

Why (precedent + demand)

  • Bun already proved this two-layer shape. main's resolver handles "Bun"/"bun" but has no
    Deno branch; this PR adds it. Deno mirrors Bun one-for-one and exceeds it on two vectors:
    type-check is native deno check {file} (vs Bun's bun run tsc --noEmit) and watch is native
    deno run -A --check --watch (vs Bun's nodemon wrapper — no nodemon dependency). Resolver test
    coverage parallels the Bun cases (18 Deno-specific tests).
  • Native TypeScript is the Deno win. Deno runs .ts/.mts directly, so the AppHost is
    deno checked and deno rund with no transpile step — something a Node AppHost cannot match
    without a bundler.
  • The demand is logged and scheduled. #16218 ("Track Deno support for TypeScript AppHosts") is
    open on milestone 13.5, split from #15812 ("Support alternative JavaScript runtimes (Bun, Deno)").
    This PR is precisely that tracked work.

Independence

This PR is independently shippable and does not depend on Deno hosting (AddDenoApp, tracked
separately). Ship this first to close #16218; the hosting resource can follow.

Proof (dogfooded against a real framework)

Verified end-to-end against the full NetScript framework (a heavy polyglot Aspire app): its
apphost.mts runs under this resolver (deno check exit 0; the CLI reports the running AppHost under
the fork build). Dogfood-proof record and re-runnable harness:
rickylabs/netscript#295 (umbrella) and the Layer-A tracking issue.

Test plan

  • Resolver unit coverage for the Deno detection + command vectors (parallel to the Bun cases; 18
    Deno-specific tests).
  • Manual: scaffold a deno.json-based AppHost, confirm aspire run selects the Deno toolchain and
    apphost.mts boots.

Checklist

  • Is this feature complete? Yescloses Track Deno support for TypeScript AppHosts #16218; Deno branch parallels Bun/Node one-for-one.
  • Unit + scenario tests? Yes — resolver tests for Deno detection + all command vectors.
  • Public API added? No — internal toolchain-resolver branch only (+47/-1), no new public surface.
  • Security assumptions? Yes (disclosed) — run/watch use deno run -A because Deno is
    deny-by-default and the AppHost needs host access; this mirrors how the Bun/Node cases invoke their
    runtimes. No threat model beyond that runtime-permission note.

🤖 Generated with Claude Code

Extends the CLI TypeScript AppHost toolchain resolver to detect and drive
a Deno toolchain, mirroring the existing npm/bun/yarn/pnpm support.

- Detect Deno via a `packageManager: "deno@x"` hint or deno.lock/deno.json/
  deno.jsonc markers (before the npm package-lock fallback).
- Generate `deno install`, `deno check {appHostFile}` (native no-emit
  type-check), `deno run -A {appHostFile}`, and `deno run -A --check --watch
  {appHostFile}`. Deno is not permissive for package.json projects, so `-A`
  grants the host access the AppHost needs; `--check` makes each watch restart
  type-check to match the nodemon "typecheck && run" behavior. The native
  watcher replaces nodemon.
- Root delegate scripts use `deno task --cwd <dir> <script>` (Deno has no
  `run <script>` for package.json scripts).
- Register deno in CommandPathResolver, the missing-tool warning, and the E2E
  toolchain helpers; make the watch-mode banner assertion toolchain-aware
  (Deno prints "Watcher Process started." instead of nodemon's banner).
- Install a pinned Deno 2.9.0 in the e2e and polyglot-base Docker images.

The doctor tooling check needs no change as it drives off the resolver and
CommandPathResolver.
@rickylabs
rickylabs requested a review from radical as a code owner July 3, 2026 04:05
Copilot AI review requested due to automatic review settings July 3, 2026 04:05
@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 18627

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 18627"

@rickylabs

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds Deno as a supported runtime/package-manager to the CLI's TypeScript AppHost toolchain resolver (TypeScriptAppHostToolchainResolver), slotting a Deno branch alongside the existing npm/Bun/Yarn/pnpm cases. It is the CLI/toolchain half of Deno support tracked by issue #16218, and is designed to be additive and independently shippable (no AddDenoApp hosting resource). Detection keys on packageManager: "deno@…", deno.lock, deno.json, and deno.jsonc; command vectors are deno install, deno check {file}, deno run -A {file}, deno run -A --check --watch {file}, and deno task --cwd <dir> <script> for root delegate scripts.

I verified against the official Deno release notes that the command vectors are correct: deno install (no args) installs dependencies from package.json/deno.json (Deno 2.0+), deno task runs package.json scripts and supports --cwd (Deno 2.1+), and the runtime preserves the existing node extension launch capability (matching the issue's "reuse V8 inspector for a first pass" guidance).

Changes:

  • Add Deno toolchain (enum, packageManager parsing, deno.lock/deno.json/deno.jsonc detection, install/type-check/execute/watch/delegate command vectors).
  • Wire Deno into doctor/missing-tool handling (CommandPathResolver, MissingJavaScriptToolWarning) and add unit/E2E test coverage.
  • Install Deno v2.9.0 in the two E2E Docker images.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/Aspire.Cli/Projects/TypeScriptAppHostToolchainResolver.cs Core change: Deno enum value, detection markers, and install/check/run/watch command vectors.
src/Aspire.Cli/Scaffolding/ScaffoldingService.cs Adds deno task --cwd root delegate script for nested brownfield AppHosts.
src/Aspire.Cli/Utils/CommandPathResolver.cs Adds Deno install display name + link metadata.
src/Aspire.Cli/Utils/MissingJavaScriptToolWarning.cs Adds the Deno missing-tool warning prefix.
tests/Aspire.Cli.Tests/Projects/TypeScriptAppHostToolchainResolverTests.cs Resolver detection + runtime-spec tests for Deno (missing a deno.jsonc case).
tests/Aspire.Cli.Tests/Commands/TypeScriptAppHostToolingCheckTests.cs Doctor availability/missing-link Deno cases.
tests/Aspire.Cli.Tests/Utils/CommandPathResolverTests.cs Deno install message/link assertions.
tests/Aspire.Cli.Tests/Utils/MissingJavaScriptToolWarningTests.cs Deno missing-tool match assertion.
tests/Aspire.Cli.EndToEnd.Tests/TypeScriptPolyglotTests.cs Adds Deno to the toolchain matrix; uses helper for watch-ready text.
tests/Aspire.Cli.EndToEnd.Tests/Helpers/TypeScriptAppHostToolchainTestHelpers.cs Deno command/metadata helpers; introduces GetWatchModeReadyText (Deno value appears mismatched).
tests/Shared/Docker/Dockerfile.e2e / Dockerfile.e2e-polyglot-base Install Deno v2.9.0 for E2E.

Key findings:

  • Moderate: In the E2E helper, GetWatchModeReadyText("deno") returns "Watcher Process started.", but the watch test runs deno task aspire:dev, and aspire:dev is tsc --watch for all toolchains — which emits "Watching for file changes.". The Deno-native banner is never produced by that script, so the watch assertion will time out.
  • Nit: deno.jsonc detection has no resolution test, unlike deno.lock and deno.json.

Comment on lines +99 to +109
/// <summary>
/// Gets the console text that indicates watch mode is active for a toolchain.
/// Node-based toolchains use nodemon; Deno uses its native file watcher, which prints
/// a different banner.
/// </summary>
internal static string GetWatchModeReadyText(string toolchain) =>
NormalizeToolchain(toolchain) switch
{
"deno" => "Watcher Process started.",
_ => "Watching for file changes."
};

Assert.Equal(TypeScriptAppHostToolchain.Deno, resolution.Toolchain);
Assert.Equal($"deno.json found in {workspace.WorkspaceRoot.FullName}", resolution.Reason);
}
internal static string GetWatchModeReadyText(string toolchain) =>
NormalizeToolchain(toolchain) switch
{
"deno" => "Watcher Process started.",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m going to fix this before approval: the test calls deno task aspire:dev, which runs the scaffolded tsc --watch script, so it should wait for TypeScript’s Watching for file changes. banner instead of Deno’s native watcher text. I’ll update the helper/coverage and run the focused Deno validation.

The E2E watch path runs deno task aspire:dev, which executes the scaffolded tsc --watch script. Wait for the TypeScript compiler watch banner instead of Deno's native watcher banner.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Track Deno support for TypeScript AppHosts

3 participants