Skip to content

Commit d041ed8

Browse files
committed
Updates AGENTS.md to support vibe coding
1 parent 9f5f6bf commit d041ed8

File tree

3 files changed

+245
-3
lines changed

3 files changed

+245
-3
lines changed

content/ts-react-fba/AGENTS.md

Lines changed: 243 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# GitHub Copilot Instructions — Minimal API File-based App (FBA) Template
1+
# GitHub Copilot Instructions — TypeScript/React Add-On for Minimal API FBA (MSBuild-Driven)
22

33
> Opinionated, execution-ready guidance for building **.NET 10** Minimal APIs as a **single C# file**.
44
@@ -341,6 +341,238 @@ public sealed record User(Guid? Id, string Name);
341341

342342
---
343343

344+
## 24) TypeScript + React (MSBuild-integrated, FBA-friendly)
345+
346+
This add-on shows how to compile a small **React (TypeScript)** front-end during `dotnet build/publish` using **Microsoft.TypeScript.MSBuild** — without converting the FBA into a full project. We keep the app single-file (`api.cs`) and place MSBuild config in **Directory.Build.props/targets** next to it.
347+
348+
> Why this approach
349+
>
350+
> * **Deterministic**: CI/CD calls `dotnet build/publish` and gets front-end assets.
351+
> * **Agent-friendly**: No ad-hoc shell steps; one entry point.
352+
> * **Isolated**: You can later swap to Vite/Webpack with minimal MSBuild changes.
353+
354+
### 24.1 Prerequisites
355+
356+
* **Node.js** (runtime) is required; the NuGet package wires MSBuild targets but does **not** replace Node.
357+
* Pin exact package versions via the **`nuget` MCP server** (see §18). Example directive (place at top of `api.cs`):
358+
359+
```csharp
360+
#:package Microsoft.TypeScript.MSBuild@<ExactVersion> // pin via MCP nuget
361+
```
362+
363+
### 24.2 Minimal layout (co-located with FBA)
364+
365+
```
366+
/
367+
├── api.cs # your Minimal API (FBA)
368+
├── Directory.Build.props # TypeScript/MSBuild settings
369+
├── Directory.Build.targets # build/publish hooks
370+
├── tsconfig.json # TS compiler options (React)
371+
├── package.json # NPM deps for React/TS type packages if desired
372+
├── src/ # TypeScript/React sources
373+
│ ├── main.tsx
374+
│ └── app.tsx
375+
└── wwwroot/ # Build output target for static files
376+
└── index.html
377+
```
378+
379+
> You can keep `package.json` very small (types only) because the **compiler is driven by MSBuild**. If you prefer a toolchain (Vite/Webpack), swap §24.5 “B” later.
380+
381+
### 24.3 ASP.NET static file plumbing (in `api.cs`)
382+
383+
Add static files so the built front-end is served from `/`:
384+
385+
```csharp
386+
// after var app = builder.Build();
387+
app.UseDefaultFiles(); // serves wwwroot/index.html by default
388+
app.UseStaticFiles(); // serves files under wwwroot/*
389+
```
390+
391+
### 24.4 MSBuild settings (no .csproj needed)
392+
393+
**Directory.Build.props**
394+
395+
```xml
396+
<Project>
397+
<!-- Let TypeScript targets light up -->
398+
<PropertyGroup>
399+
<TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
400+
<TypeScriptCompileOnBuild>true</TypeScriptCompileOnBuild>
401+
<TypeScriptNoEmitOnError>true</TypeScriptNoEmitOnError>
402+
403+
<!-- Emit settings (React + modern ESM) -->
404+
<TypeScriptTarget>ES2022</TypeScriptTarget>
405+
<TypeScriptModule>ESNext</TypeScriptModule>
406+
<TypeScriptJSXEmit>ReactJSX</TypeScriptJSXEmit>
407+
408+
<!-- Where compiled JS goes -->
409+
<TypeScriptOutDir>wwwroot/assets</TypeScriptOutDir>
410+
411+
<!-- Optional strictness -->
412+
<TypeScriptStrict>true</TypeScriptStrict>
413+
<TypeScriptSourceMap>true</TypeScriptSourceMap>
414+
</PropertyGroup>
415+
416+
<!-- Tell MSBuild what to compile -->
417+
<ItemGroup>
418+
<TypeScriptCompile Include="src\**\*.ts" />
419+
<TypeScriptCompile Include="src\**\*.tsx" />
420+
</ItemGroup>
421+
422+
<!-- Static site bits included in publish -->
423+
<ItemGroup>
424+
<Content Include="wwwroot\**\*.*" CopyToOutputDirectory="PreserveNewest" />
425+
</ItemGroup>
426+
</Project>
427+
```
428+
429+
**Directory.Build.targets**
430+
431+
```xml
432+
<Project>
433+
<!-- Ensure Node is present before TS compile -->
434+
<Target Name="VerifyNode" BeforeTargets="TypeScriptCompile">
435+
<Exec Command="node --version" IgnoreExitCode="false" />
436+
</Target>
437+
438+
<!-- Hook TS compile into dotnet build/publish -->
439+
<Target Name="ClientBuild" DependsOnTargets="TypeScriptCompile"
440+
BeforeTargets="ResolveReferences" />
441+
442+
<Target Name="ClientPublish" DependsOnTargets="TypeScriptCompile"
443+
BeforeTargets="ComputeFilesToPublish" />
444+
</Project>
445+
```
446+
447+
### 24.5 tsconfig + two build modes
448+
449+
**tsconfig.json (React, ESM, strict)**
450+
451+
```json
452+
{
453+
"compilerOptions": {
454+
"target": "ES2022",
455+
"module": "ESNext",
456+
"jsx": "react-jsx",
457+
"strict": true,
458+
"sourceMap": true,
459+
"outDir": "wwwroot/assets",
460+
"moduleResolution": "bundler",
461+
"skipLibCheck": true,
462+
"noEmitOnError": true
463+
},
464+
"include": ["src/**/*"]
465+
}
466+
```
467+
468+
**A) Pure MSBuild/tsc mode (no bundler)**
469+
470+
* Pros: zero extra tools; simplest to wire.
471+
* Cons: no tree-shaking/bundling; browser must support modern ESM and import maps (or keep it small).
472+
473+
**B) Bundler mode (optional)**
474+
If you need Vite/Webpack/Rspack, replace `ClientBuild/ClientPublish` with:
475+
476+
```xml
477+
<Target Name="ClientBuildBundler" BeforeTargets="ResolveReferences">
478+
<Exec Command="npm ci" />
479+
<Exec Command="npm run build" />
480+
</Target>
481+
482+
<Target Name="ClientPublishBundler" BeforeTargets="ComputeFilesToPublish"
483+
DependsOnTargets="ClientBuildBundler" />
484+
```
485+
486+
…and set `package.json` scripts. Output still goes to `wwwroot`.
487+
488+
### 24.6 Sample front-end
489+
490+
**wwwroot/index.html** (loads ESM entry)
491+
492+
```html
493+
<!doctype html>
494+
<html lang="en">
495+
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
496+
<title>FBA + React</title>
497+
</head>
498+
<body>
499+
<div id="root"></div>
500+
<script type="module" src="/assets/main.js"></script>
501+
</body>
502+
</html>
503+
```
504+
505+
**src/main.tsx**
506+
507+
```ts
508+
import { createRoot } from "react-dom/client";
509+
import { App } from "./app";
510+
511+
createRoot(document.getElementById("root")!).render(<App />);
512+
```
513+
514+
**src/app.tsx**
515+
516+
```ts
517+
import { useEffect, useState } from "react";
518+
519+
export function App() {
520+
const [now, setNow] = useState<string>("loading...");
521+
useEffect(() => {
522+
fetch("/api/v1/time").then(r => r.json()).then(x => setNow(x.now));
523+
}, []);
524+
return (
525+
<main style={{ fontFamily: "system-ui", padding: 24 }}>
526+
<h1>Minimal API + React (TS)</h1>
527+
<p>Server time: {now}</p>
528+
</main>
529+
);
530+
}
531+
```
532+
533+
**package.json** (types only; optional)
534+
535+
```json
536+
{
537+
"private": true,
538+
"devDependencies": {
539+
"@types/react": "^18.3.5",
540+
"@types/react-dom": "^18.3.0",
541+
"typescript": "^5.6.3",
542+
"react": "^18.3.1",
543+
"react-dom": "^18.3.1"
544+
}
545+
}
546+
```
547+
548+
> If you stay in **pure MSBuild** mode, `typescript` here only controls editor typings; **MSBuild invokes its own compiler** from `Microsoft.TypeScript.MSBuild`. Node is still required for the compiler runtime.
549+
550+
### 24.7 Running
551+
552+
```bash
553+
# One command compiles TS and runs the API
554+
dotnet run api.cs
555+
# or build only
556+
dotnet build
557+
# or publish (assets go under publish/wwwroot)
558+
dotnet publish -c Release
559+
```
560+
561+
### 24.8 Testing & hardening
562+
563+
* Add an integration test that hits `/` (serves `index.html`) and `/assets/main.js` after `dotnet build`.
564+
* For production, prefer **bundler mode** (§24.5-B) for code-split, minification, and cache busting.
565+
* Use a **content hash** in asset filenames (via bundler) or append a version query (`/assets/main.js?v=…`) and set long-cache headers.
566+
567+
### 24.9 Troubleshooting
568+
569+
* **`TypeScriptCompile` target not running** → Ensure `Directory.Build.props/targets` are in the **same directory** (or ancestor) as `api.cs`.
570+
* **Node not found** → Install Node, or set PATH in CI.
571+
* **Files not served** → Verify `app.UseDefaultFiles(); app.UseStaticFiles();` and that outputs land in `wwwroot/*`.
572+
* **Interop issues with older browsers** → Use bundler + transpilation to ES2017/ES2018 and polyfills as needed.
573+
574+
---
575+
344576
### Review Checklist
345577

346578
* [ ] `TargetFramework=net10.0`, `Nullable=enable`; AOT choice is deliberate
@@ -349,3 +581,13 @@ public sealed record User(Guid? Id, string Name);
349581
* [ ] ProblemDetails + exception handler; HTTPS, rate limiting as needed
350582
* [ ] Packages (if any) are **pinned via MCP `nuget`**; no TFM downgrades
351583
* [ ] Deterministic outputs; graceful shutdown; health endpoints
584+
585+
---
586+
587+
### Review Checklist (TS/React)
588+
589+
* [ ] `Microsoft.TypeScript.MSBuild` pinned via MCP `nuget` (no floating versions)
590+
* [ ] `Directory.Build.props/targets` present; `TypeScriptCompile` runs on build/publish
591+
* [ ] Static files enabled; `wwwroot/index.html` loads ESM entry
592+
* [ ] CI uses only `dotnet build/publish` (optionally `npm ci` in bundler mode)
593+
* [ ] Production uses bundler (hashing, minify, split) and correct cache headers

content/ts-react-fba/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "ts-react-fba",
33
"version": "1.0.0",
44
"description": "TypeScript React FBA Test Project",
5-
"main": "wwwroot/js/app.js",
5+
"main": "wwwroot/assets/app.js",
66
"scripts": {
77
"build": "tsc",
88
"watch": "tsc --watch",

content/ts-react-fba/tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"removeComments": false,
77
"sourceMap": true,
88
"target": "ES5",
9-
"outDir": "wwwroot/js",
9+
"outDir": "wwwroot/assets",
1010
"jsx": "react",
1111
"module": "umd",
1212
"lib": ["DOM", "ES6"],

0 commit comments

Comments
 (0)