diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 0000000..bb8b6f0 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,4 @@ +# Formatting-only commits, ignored by GitHub blame and `git blame` (git config blame.ignoreRevsFile .git-blame-ignore-revs) + +# Format MyApp.Client with Oxfmt +3961aa9878be57a2108dba2d5f48e31a055ae7b9 diff --git a/.github/workflows/build-container.yml b/.github/workflows/build-container.yml index 79ec6f4..6b5ebcf 100644 --- a/.github/workflows/build-container.yml +++ b/.github/workflows/build-container.yml @@ -65,16 +65,13 @@ jobs: echo "client_exists=false" >> $GITHUB_OUTPUT fi - - name: Setup Node.js + - name: Setup Vite+ and install npm dependencies if: steps.check_client.outputs.client_exists == 'true' - uses: actions/setup-node@v7 + uses: voidzero-dev/setup-vp@v1.22.0 with: - node-version: 24 - - - name: Install npm dependencies - if: steps.check_client.outputs.client_exists == 'true' - working-directory: ./MyApp.Client - run: npm install + node-version: '24' + working-directory: ./MyApp.Client + cache: true - name: Build client if: steps.check_client.outputs.client_exists == 'true' diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6873c14..231fd42 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -49,3 +49,27 @@ jobs: fi working-directory: ./MyApp.Tests + client: + runs-on: ubuntu-latest + steps: + - name: checkout + uses: actions/checkout@v7 + + - name: Setup Vite+ and install npm dependencies + uses: voidzero-dev/setup-vp@v1.22.0 + with: + node-version: '24' + working-directory: ./MyApp.Client + cache: true + + - name: check (format, lint, type check) + run: vp check + working-directory: ./MyApp.Client + + - name: test + run: vp test run + working-directory: ./MyApp.Client + + - name: build + run: vp build + working-directory: ./MyApp.Client diff --git a/AGENTS.md b/AGENTS.md index d6c3590..3518f78 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -14,14 +14,15 @@ A full-stack .NET 10 + React 19 + Vite template combining ServiceStack backend w # Start both .NET and Vite dev servers (from project root) dotnet watch -# After making changes to C# DTOs restart .NET before regenerating TypeScript DTOs by running: +# After changing C# DTOs, restart .NET: TypeScript DTOs (dtos.ts) are regenerated on startup. +# Manual regeneration is rarely needed (requires the .NET app to be running): cd MyApp.Client && npm run dtos ``` ### Building ```bash -# Build frontend (TypeScript + Vite) +# Build frontend (Vite+) cd MyApp.Client && npm run build # Build backend (.NET) @@ -31,10 +32,23 @@ dotnet build cd MyApp && npm run ui:build ``` +### Checking (format, lint, type check) + +The frontend uses the [Vite+](https://viteplus.dev) toolchain (`vp` CLI): Vite 8 (Rolldown), Vitest, Oxlint and Oxfmt. +All tool config (`lint`, `fmt`, `test`) lives in [vite.config.ts](MyApp.Client/vite.config.ts). There is no ESLint, Prettier or `tsc` step. + +```bash +cd MyApp.Client && npm run check # Format + lint + type check (run before committing) +cd MyApp.Client && npm run lint # Lint + type check only (Oxlint) +cd MyApp.Client && npm run fmt # Format all files (Oxfmt) +``` + +`src/lib/dtos.ts` is generated, so it is excluded from lint and format. + ### Testing ```bash -# Frontend tests (Vitest) +# Frontend tests (Vitest via Vite+), import from 'vite-plus/test' cd MyApp.Client && npm run test # Watch mode cd MyApp.Client && npm run test:ui # UI mode cd MyApp.Client && npm run test:run # Single run @@ -111,12 +125,12 @@ MyApp/ # .NET Backend (hosts both .NET and Vite React) MyApp.Client/ # React Frontend ├── src/ │ ├── lib/ -│ │ ├── dtos.ts # Auto-generated from C# (via `npm run dtos`) +│ │ ├── dtos.ts # Auto-generated from C# on .NET App startup │ │ ├── gateway.ts # ServiceStack JsonServiceClient │ │ └── utils.ts # Utility functions │ ├── components/ # React components │ └── styles/ # Tailwind CSS -└── vite.config.ts # Vite config for dev mode +└── vite.config.ts # Vite+ config: dev/build, lint, fmt, test MyApp.ServiceModel/ # DTOs & API contracts ├── *.cs # C# Request/Response DTOs @@ -260,13 +274,15 @@ ServiceStack's AutoQuery generates full CRUD APIs from declarative request DTOs. ### TypeScript DTO Generation -After changing C# DTOs in `MyApp.ServiceModel/`, restart the .NET Server then run: +`MyApp.Client/src/lib/dtos.ts` is regenerated automatically every time the .NET App starts (e.g. `dotnet run` or a `dotnet watch` restart). +This is done by the `dtos` StartupTask registered in [Configure.StartupTasks.GenerateDtos.cs](MyApp/Configure.StartupTasks.GenerateDtos.cs). +So after changing C# DTOs in `MyApp.ServiceModel/`, restarting the .NET Server is all that's needed, and the Vite dev server auto-reloads. + +Running `npm run dtos` manually is unnecessary, but it's available if needed. It calls the running .NET App's `/types/typescript` endpoint to update `dtos.ts`: ```bash cd MyApp.Client && npm run dtos ``` -This calls ServiceStack's `/types/typescript` endpoint and updates `dtos.ts` with type-safe client DTOs. The Vite dev server auto-reloads. - ### okai AutoQuery Code Generation The `npx okai` tool generates C# AutoQuery APIs and migrations from TypeScript data models (`.d.ts` files): @@ -577,8 +593,8 @@ Configured in [Configure.BackgroundJobs.cs](MyApp/Configure.BackgroundJobs.cs) u 1. **Start dev servers:** `dotnet watch` (starts both .NET and Vite) 2. **Make backend changes:** Edit C# files in `MyApp.ServiceModel` or `MyApp.ServiceInterface` -3. **Restart .NET Server** -4. **Regenerate DTOs:** `cd MyApp.Client && npm run dtos` +3. **Restart .NET Server:** this also regenerates `dtos.ts` (`npm run dtos` isn't needed) +4. **Use updated DTOs** from `@/lib/dtos` in the frontend 5. **Make frontend changes:** Edit React files in `MyApp.Client/src` 6. **Add new CRUD feature:** - `npx okai init Feature` diff --git a/MyApp.Client/eslint.config.js b/MyApp.Client/eslint.config.js deleted file mode 100644 index b19330b..0000000 --- a/MyApp.Client/eslint.config.js +++ /dev/null @@ -1,23 +0,0 @@ -import js from '@eslint/js' -import globals from 'globals' -import reactHooks from 'eslint-plugin-react-hooks' -import reactRefresh from 'eslint-plugin-react-refresh' -import tseslint from 'typescript-eslint' -import { defineConfig, globalIgnores } from 'eslint/config' - -export default defineConfig([ - globalIgnores(['dist']), - { - files: ['**/*.{ts,tsx}'], - extends: [ - js.configs.recommended, - tseslint.configs.recommended, - reactHooks.configs['recommended-latest'], - reactRefresh.configs.vite, - ], - languageOptions: { - ecmaVersion: 2020, - globals: globals.browser, - }, - }, -]) diff --git a/MyApp.Client/index.html b/MyApp.Client/index.html index 6e2a085..74126ec 100644 --- a/MyApp.Client/index.html +++ b/MyApp.Client/index.html @@ -1,13 +1,13 @@ -
- - - -© {new Date().getFullYear()} My App
+ + Privacy Policy + +