From af1c49f29617c9a79739d5c7d017414e25570764 Mon Sep 17 00:00:00 2001 From: Sebastien Ros Date: Thu, 30 Apr 2026 15:30:37 -0700 Subject: [PATCH 1/6] Fix Python starter health check template Update the Python starter TypeScript AppHost to use the supported withHttpHealthCheck options object and align its root build script with AppHost-only type checking. Add E2E coverage that verifies the generated starter builds successfully. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Templating/Templates/py-starter/apphost.ts | 2 +- .../Templating/Templates/py-starter/package.json | 16 ++++++++++------ .../Templates/py-starter/tsconfig.json | 15 +++++++++++---- .../PythonReactTemplateTests.cs | 7 ++++++- 4 files changed, 28 insertions(+), 12 deletions(-) diff --git a/src/Aspire.Cli/Templating/Templates/py-starter/apphost.ts b/src/Aspire.Cli/Templating/Templates/py-starter/apphost.ts index e51954bafab..d3c55007e81 100644 --- a/src/Aspire.Cli/Templating/Templates/py-starter/apphost.ts +++ b/src/Aspire.Cli/Templating/Templates/py-starter/apphost.ts @@ -17,7 +17,7 @@ const app = await builder .withReference(cache) .waitFor(cache) // {{/redis}} - .withHttpHealthCheck("/health"); + .withHttpHealthCheck({ path: "/health" }); // Run the Vite frontend after the API and inject the API URL for local proxying. const frontend = await builder diff --git a/src/Aspire.Cli/Templating/Templates/py-starter/package.json b/src/Aspire.Cli/Templating/Templates/py-starter/package.json index 7878cd35dbe..a60da992c9a 100644 --- a/src/Aspire.Cli/Templating/Templates/py-starter/package.json +++ b/src/Aspire.Cli/Templating/Templates/py-starter/package.json @@ -3,12 +3,16 @@ "private": true, "type": "module", "scripts": { - "lint": "eslint apphost.ts", - "predev": "npm run lint", - "dev": "aspire run", - "prebuild": "npm run lint", - "build": "tsc", - "watch": "tsc --watch" + "aspire:lint": "eslint apphost.ts", + "aspire:start": "aspire run", + "aspire:build": "tsc -p tsconfig.json", + "aspire:dev": "tsc --watch -p tsconfig.json", + "lint": "npm run aspire:lint", + "predev": "npm run aspire:lint", + "dev": "npm run aspire:start", + "prebuild": "npm run aspire:lint", + "build": "npm run aspire:build", + "watch": "npm run aspire:dev" }, "dependencies": { "vscode-jsonrpc": "^8.2.0" diff --git a/src/Aspire.Cli/Templating/Templates/py-starter/tsconfig.json b/src/Aspire.Cli/Templating/Templates/py-starter/tsconfig.json index 1e44664428b..939a631a123 100644 --- a/src/Aspire.Cli/Templating/Templates/py-starter/tsconfig.json +++ b/src/Aspire.Cli/Templating/Templates/py-starter/tsconfig.json @@ -1,8 +1,15 @@ { "compilerOptions": { - "target": "esnext", - "module": "esnext", - "moduleResolution": "bundler", + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "skipLibCheck": true, + "outDir": "./dist/apphost", + "rootDir": ".", "strict": true - } + }, + "include": ["apphost.ts", ".modules/**/*.ts"], + "exclude": ["node_modules"] } diff --git a/tests/Aspire.Cli.EndToEnd.Tests/PythonReactTemplateTests.cs b/tests/Aspire.Cli.EndToEnd.Tests/PythonReactTemplateTests.cs index 38f133b34dc..b7adcac53a2 100644 --- a/tests/Aspire.Cli.EndToEnd.Tests/PythonReactTemplateTests.cs +++ b/tests/Aspire.Cli.EndToEnd.Tests/PythonReactTemplateTests.cs @@ -42,7 +42,12 @@ public async Task CreateAndRunPythonReactProject() await auto.EnterAsync(); await auto.WaitForSuccessPromptAsync(counter); - // Step 3: Start and stop the project + // Step 3: Verify the generated TypeScript AppHost builds successfully. + await auto.TypeAsync("npm run build"); + await auto.EnterAsync(); + await auto.WaitForSuccessPromptAsync(counter, TimeSpan.FromMinutes(2)); + + // Step 4: Start and stop the project await auto.AspireStartAsync(counter); await auto.AspireStopAsync(counter); From 493664ec75cf3afa4bb3c04cd5f6b4639224a29e Mon Sep 17 00:00:00 2001 From: Sebastien Ros Date: Thu, 30 Apr 2026 15:39:51 -0700 Subject: [PATCH 2/6] Use fail-fast helper for Python starter build test Update the Python React template E2E build verification to use the existing fail-fast command helper so npm build failures are surfaced immediately. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- tests/Aspire.Cli.EndToEnd.Tests/PythonReactTemplateTests.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/Aspire.Cli.EndToEnd.Tests/PythonReactTemplateTests.cs b/tests/Aspire.Cli.EndToEnd.Tests/PythonReactTemplateTests.cs index b7adcac53a2..c5f15576660 100644 --- a/tests/Aspire.Cli.EndToEnd.Tests/PythonReactTemplateTests.cs +++ b/tests/Aspire.Cli.EndToEnd.Tests/PythonReactTemplateTests.cs @@ -43,9 +43,7 @@ public async Task CreateAndRunPythonReactProject() await auto.WaitForSuccessPromptAsync(counter); // Step 3: Verify the generated TypeScript AppHost builds successfully. - await auto.TypeAsync("npm run build"); - await auto.EnterAsync(); - await auto.WaitForSuccessPromptAsync(counter, TimeSpan.FromMinutes(2)); + await auto.RunCommandFailFastAsync("npm run build", counter, TimeSpan.FromMinutes(2)); // Step 4: Start and stop the project await auto.AspireStartAsync(counter); From c3e95ad923e0a4cc3a89f2c3ec9146307589a1fc Mon Sep 17 00:00:00 2001 From: Sebastien Ros Date: Thu, 30 Apr 2026 19:13:06 -0700 Subject: [PATCH 3/6] Verify TypeScript templates build Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../TypeScriptEmptyAppHostTemplateTests.cs | 2 ++ .../Aspire.Cli.EndToEnd.Tests/TypeScriptStarterTemplateTests.cs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/tests/Aspire.Cli.EndToEnd.Tests/TypeScriptEmptyAppHostTemplateTests.cs b/tests/Aspire.Cli.EndToEnd.Tests/TypeScriptEmptyAppHostTemplateTests.cs index 19cc6aa538a..847cd9126c0 100644 --- a/tests/Aspire.Cli.EndToEnd.Tests/TypeScriptEmptyAppHostTemplateTests.cs +++ b/tests/Aspire.Cli.EndToEnd.Tests/TypeScriptEmptyAppHostTemplateTests.cs @@ -40,6 +40,8 @@ public async Task CreateAndRunTypeScriptEmptyAppHostProject() await auto.EnterAsync(); await auto.WaitForSuccessPromptAsync(counter); + await auto.RunCommandFailFastAsync("npm run build", counter, TimeSpan.FromMinutes(2)); + await auto.AspireStartAsync(counter); await auto.AspireStopAsync(counter); diff --git a/tests/Aspire.Cli.EndToEnd.Tests/TypeScriptStarterTemplateTests.cs b/tests/Aspire.Cli.EndToEnd.Tests/TypeScriptStarterTemplateTests.cs index 34cab5277e3..864c1ed1db3 100644 --- a/tests/Aspire.Cli.EndToEnd.Tests/TypeScriptStarterTemplateTests.cs +++ b/tests/Aspire.Cli.EndToEnd.Tests/TypeScriptStarterTemplateTests.cs @@ -56,6 +56,8 @@ public async Task CreateAndRunTypeScriptStarterProject() await auto.EnterAsync(); await auto.WaitForSuccessPromptAsync(counter); + await auto.RunCommandFailFastAsync("npm run build", counter, TimeSpan.FromMinutes(2)); + await auto.AspireStartAsync(counter); await auto.AspireStopAsync(counter); From ed2cf5d8a8b0a5e571ce2ed705f5eac982a7a362 Mon Sep 17 00:00:00 2001 From: Sebastien Ros Date: Thu, 30 Apr 2026 19:26:29 -0700 Subject: [PATCH 4/6] Add build script aliases to TypeScript AppHost scaffold Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../TypeScriptLanguageSupport.cs | 13 ++++++++++++- .../TypeScriptLanguageSupportTests.cs | 9 ++++++--- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/Aspire.Hosting.CodeGeneration.TypeScript/TypeScriptLanguageSupport.cs b/src/Aspire.Hosting.CodeGeneration.TypeScript/TypeScriptLanguageSupport.cs index 06f5df0cd12..425f802e40b 100644 --- a/src/Aspire.Hosting.CodeGeneration.TypeScript/TypeScriptLanguageSupport.cs +++ b/src/Aspire.Hosting.CodeGeneration.TypeScript/TypeScriptLanguageSupport.cs @@ -145,7 +145,8 @@ private static string CreatePackageJson(ScaffoldRequest request) var packageJson = new JsonObject(); var packageJsonPath = Path.Combine(request.TargetPath, PackageJsonFileName); - if (!File.Exists(packageJsonPath)) + var isGreenfield = !File.Exists(packageJsonPath); + if (isGreenfield) { // Greenfield: include root metadata so the scaffold output is a complete package.json. var packageName = request.ProjectName?.ToLowerInvariant() ?? "aspire-apphost"; @@ -168,6 +169,16 @@ private static string CreatePackageJson(ScaffoldRequest request) scripts["aspire:build"] = $"tsc -p {AppHostTsConfigFileName}"; scripts["aspire:dev"] = $"tsc --watch -p {AppHostTsConfigFileName}"; + if (isGreenfield) + { + scripts["lint"] = "npm run aspire:lint"; + scripts["predev"] = "npm run aspire:lint"; + scripts["dev"] = "npm run aspire:start"; + scripts["prebuild"] = "npm run aspire:lint"; + scripts["build"] = "npm run aspire:build"; + scripts["watch"] = "npm run aspire:dev"; + } + EnsureDependency(packageJson, "dependencies", "vscode-jsonrpc", "^8.2.0"); EnsureDependency(packageJson, "devDependencies", "@types/node", "^22.0.0"); EnsureDependency(packageJson, "devDependencies", "eslint", "^10.0.3"); diff --git a/tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/TypeScriptLanguageSupportTests.cs b/tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/TypeScriptLanguageSupportTests.cs index adaab97bdf7..ba5c179b014 100644 --- a/tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/TypeScriptLanguageSupportTests.cs +++ b/tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/TypeScriptLanguageSupportTests.cs @@ -38,9 +38,12 @@ public void Scaffold_CreatesAppHostSpecificScriptsAndTsConfig_ForNewProject() Assert.Equal("tsc -p tsconfig.apphost.json", scripts["aspire:build"]?.GetValue()); Assert.Equal("tsc --watch -p tsconfig.apphost.json", scripts["aspire:dev"]?.GetValue()); Assert.Equal("eslint apphost.ts", scripts["aspire:lint"]?.GetValue()); - Assert.False(scripts.ContainsKey("start")); - Assert.False(scripts.ContainsKey("build")); - Assert.False(scripts.ContainsKey("dev")); + Assert.Equal("npm run aspire:lint", scripts["lint"]?.GetValue()); + Assert.Equal("npm run aspire:lint", scripts["predev"]?.GetValue()); + Assert.Equal("npm run aspire:start", scripts["dev"]?.GetValue()); + Assert.Equal("npm run aspire:lint", scripts["prebuild"]?.GetValue()); + Assert.Equal("npm run aspire:build", scripts["build"]?.GetValue()); + Assert.Equal("npm run aspire:dev", scripts["watch"]?.GetValue()); Assert.Equal("^4.21.0", devDependencies["tsx"]?.GetValue()); Assert.Equal("^5.9.3", devDependencies["typescript"]?.GetValue()); Assert.Equal("^10.0.3", devDependencies["eslint"]?.GetValue()); From 9d9ab879202c8764e176e91576c411b5d0976535 Mon Sep 17 00:00:00 2001 From: Sebastien Ros Date: Thu, 30 Apr 2026 19:44:58 -0700 Subject: [PATCH 5/6] Use AppHost tsconfig for TypeScript AppHost lint Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Templating/Templates/ts-starter/eslint.config.mjs | 3 ++- .../TypeScriptLanguageSupport.cs | 3 ++- .../TypeScriptLanguageSupportTests.cs | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Aspire.Cli/Templating/Templates/ts-starter/eslint.config.mjs b/src/Aspire.Cli/Templating/Templates/ts-starter/eslint.config.mjs index e7e33edb3fd..2c6536d4bd5 100644 --- a/src/Aspire.Cli/Templating/Templates/ts-starter/eslint.config.mjs +++ b/src/Aspire.Cli/Templating/Templates/ts-starter/eslint.config.mjs @@ -8,7 +8,8 @@ export default defineConfig({ extends: [tseslint.configs.base], languageOptions: { parserOptions: { - projectService: true, + project: './tsconfig.apphost.json', + tsconfigRootDir: import.meta.dirname, }, }, rules: { diff --git a/src/Aspire.Hosting.CodeGeneration.TypeScript/TypeScriptLanguageSupport.cs b/src/Aspire.Hosting.CodeGeneration.TypeScript/TypeScriptLanguageSupport.cs index 425f802e40b..94558f85412 100644 --- a/src/Aspire.Hosting.CodeGeneration.TypeScript/TypeScriptLanguageSupport.cs +++ b/src/Aspire.Hosting.CodeGeneration.TypeScript/TypeScriptLanguageSupport.cs @@ -96,7 +96,8 @@ public Dictionary Scaffold(ScaffoldRequest request) extends: [tseslint.configs.base], languageOptions: { parserOptions: { - projectService: true, + project: './tsconfig.apphost.json', + tsconfigRootDir: import.meta.dirname, }, }, rules: { diff --git a/tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/TypeScriptLanguageSupportTests.cs b/tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/TypeScriptLanguageSupportTests.cs index ba5c179b014..5c04cbc6c9b 100644 --- a/tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/TypeScriptLanguageSupportTests.cs +++ b/tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/TypeScriptLanguageSupportTests.cs @@ -56,6 +56,7 @@ public void Scaffold_CreatesAppHostSpecificScriptsAndTsConfig_ForNewProject() Assert.DoesNotContain("\\u003E", files["package.json"]); Assert.Contains("eslint.config.mjs", files.Keys); + Assert.Contains("project: './tsconfig.apphost.json'", files["eslint.config.mjs"]); var tsConfig = ParseJson(files["tsconfig.apphost.json"]); Assert.Equal("./dist/apphost", tsConfig["compilerOptions"]?["outDir"]?.GetValue()); From 94b97928ff4ca53b651b2cd434ce77902fb249cf Mon Sep 17 00:00:00 2001 From: Sebastien Ros Date: Fri, 1 May 2026 10:40:15 -0700 Subject: [PATCH 6/6] Use AppHost tsconfig name for Python starter Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Templating/Templates/py-starter/eslint.config.mjs | 3 ++- src/Aspire.Cli/Templating/Templates/py-starter/package.json | 4 ++-- .../py-starter/{tsconfig.json => tsconfig.apphost.json} | 0 3 files changed, 4 insertions(+), 3 deletions(-) rename src/Aspire.Cli/Templating/Templates/py-starter/{tsconfig.json => tsconfig.apphost.json} (100%) diff --git a/src/Aspire.Cli/Templating/Templates/py-starter/eslint.config.mjs b/src/Aspire.Cli/Templating/Templates/py-starter/eslint.config.mjs index e7e33edb3fd..2c6536d4bd5 100644 --- a/src/Aspire.Cli/Templating/Templates/py-starter/eslint.config.mjs +++ b/src/Aspire.Cli/Templating/Templates/py-starter/eslint.config.mjs @@ -8,7 +8,8 @@ export default defineConfig({ extends: [tseslint.configs.base], languageOptions: { parserOptions: { - projectService: true, + project: './tsconfig.apphost.json', + tsconfigRootDir: import.meta.dirname, }, }, rules: { diff --git a/src/Aspire.Cli/Templating/Templates/py-starter/package.json b/src/Aspire.Cli/Templating/Templates/py-starter/package.json index a60da992c9a..c64a43c21c9 100644 --- a/src/Aspire.Cli/Templating/Templates/py-starter/package.json +++ b/src/Aspire.Cli/Templating/Templates/py-starter/package.json @@ -5,8 +5,8 @@ "scripts": { "aspire:lint": "eslint apphost.ts", "aspire:start": "aspire run", - "aspire:build": "tsc -p tsconfig.json", - "aspire:dev": "tsc --watch -p tsconfig.json", + "aspire:build": "tsc -p tsconfig.apphost.json", + "aspire:dev": "tsc --watch -p tsconfig.apphost.json", "lint": "npm run aspire:lint", "predev": "npm run aspire:lint", "dev": "npm run aspire:start", diff --git a/src/Aspire.Cli/Templating/Templates/py-starter/tsconfig.json b/src/Aspire.Cli/Templating/Templates/py-starter/tsconfig.apphost.json similarity index 100% rename from src/Aspire.Cli/Templating/Templates/py-starter/tsconfig.json rename to src/Aspire.Cli/Templating/Templates/py-starter/tsconfig.apphost.json