This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
MvcFrontendKit is a Node-free frontend bundling toolkit for ASP.NET Core MVC / Razor applications. It wraps esbuild behind a simple .NET + YAML workflow without requiring Node.js or npm.
Status: Design/spec complete, implementation starting. Expect breaking changes until v1.0.
-
Library package (
MvcFrontendKit) - Main class library containing:- Configuration and manifest loaders
- Razor HTML helpers / tag helpers for emitting script/link tags
- Runtime services (manifest cache, per-request component deduplication)
- MSBuild targets that invoke esbuild during Release/Publish builds
- Auto-generates default
frontend.config.yamlon package installation if missing
-
CLI package (
MvcFrontendKit.Cli) - Optional dotnet tool package:dotnet frontend init- scaffoldsfrontend.config.yamldotnet frontend check [--verbose]- validates config and discovers assets
-
Configuration (
frontend.config.yaml) - Single YAML file controlling:- Bundling mode:
single,areas, orviews(default) - Global JS/CSS assets
- Per-view conventions and overrides
- Named components (reusable JS/CSS chunks)
- Import maps for bare module specifiers
- CSS URL policy (root-relative vs relative)
- Esbuild options
- Bundling mode:
-
Manifest (
frontend.manifest.json) - Generated during Prod builds:- Maps logical keys to fingerprinted bundle URLs
- Key format:
global:js,global:css,view:Views/Home/Index,component:datepicker:js, etc. - Keys are case-sensitive matching physical view paths
Development:
- Raw JS/CSS served from
wwwroot/jsandwwwroot/css - Cache-busting via
?v={File.GetLastWriteTimeUtc(path).Ticks} type="module"for JS- Optional import map for bare imports
- No bundling, no minification
Production:
- Bundled + minified JS/CSS in
/dist/jsand/dist/css - Fingerprinted filenames
- Helpers read manifest to emit bundle URLs
- Build fails if manifest is missing or invalid (no silent fallback)
- single - One global JS bundle for entire app
- areas - Global bundle + one per Area (minimal in v1)
- views (recommended) - Global bundle + per-view bundles driven by conventions/overrides
- On NuGet package install: If
frontend.config.yamldoesn't exist, MSBuild targets automatically generate it with sensible defaults - CLI
initcommand: Optionally, devs can rundotnet frontend initto regenerate or use--forceto overwrite - Default template: Matches the full example in SPEC.md Section 3.2 with comprehensive comments
- Developers should NOT need to manually create this file - it appears automatically
- Invalid YAML → build fails with line/column info
- Missing JS/CSS declared in config → build fails
- Invalid/missing manifest in Prod → app startup fails (no silent fallback)
- Default (recommended): Root-relative URLs only (
url("/img/foo.png")) - Build fails if relative URLs (
../) detected in bundled CSS (unlesscssUrlPolicy.allowRelative: true) appBasePathrewrites root-relative URLs for sub-path deployments (e.g.,/hr-app/img/foo.png)
- Logical keys are fully explicit with Action:
Views/Home/Index,Areas/Admin/Settings/Index - Keys are case-sensitive using physical view path casing (typically PascalCase)
/Admin/Settingsand/Admin/Settings/Indexboth resolve to same view key
For a base pattern like wwwroot/js/{Controller}/{Action}, attempts in order:
- camelCase:
mapEditor.js - lowercase:
mapeditor.js - PascalCase:
MapEditor.js - camelCase+Page:
mapEditorPage.js - lowercase+Page:
mapeditorPage.js
- Named reusable JS/CSS units (e.g.,
datepicker,calendar) - Support dependency graph with cycle detection
- Per-request deduplication via
HttpContext.Items - Both JS and CSS are optional per component
- SPEC.md - Complete formal specification (read before making core behavior changes)
- README.md - User-facing documentation
- frontend.config.yaml - Single source of truth for bundling behavior (auto-generated on install)
- frontend.manifest.json - Generated in Prod builds, consumed by helpers at runtime
All implementation must strictly follow SPEC.md. The spec defines:
- Config schema and validation rules
- Dev vs Prod behavior for each mode
- Manifest key namespace and structure
- Helper API contracts
- CSS URL policy enforcement
- Error handling requirements
When implementing features:
- Read the relevant section of SPEC.md first
- Implement exactly as specified
- Do not add undocumented features or change core semantics
- For new features, propose spec changes via issue first
MvcFrontendKit supports automatic compilation of TypeScript and SCSS files:
- TypeScript (
.ts,.tsx): Auto-detected and compiled via esbuild's native TypeScript loader - SCSS/Sass (
.scss,.sass): Auto-detected and compiled via bundled Dart Sass compiler
This is zero-config - just use the file extensions and the tool handles compilation automatically.
Do not use these config keys (reserved for future versions):
cdn- for CDN hosting, SRI, etc.
Targets modern browsers with ES modules and import map support:
- Chrome 89+, Firefox 108+, Safari 16.4+, Edge 89+
- IE11 and older browsers require separate transpilation pipeline (out of scope)
src/MvcFrontendKit/- Core library (config, manifest, helpers)src/MvcFrontendKit.Cli/- CLI tooltests/MvcFrontendKit.Tests/- Test suite
- First install: MSBuild targets check for
frontend.config.yamlat project root - If missing: Generate from embedded template resource with all options commented
- If exists: Leave untouched (user may have customized)
- If deleted: Next build regenerates with warning logged
The auto-generated config must match SPEC.md Section 3.2 exactly:
mode: views(recommended default)webRoot: wwwroot,jsRoot: wwwroot/js,cssRoot: wwwroot/css- Sensible convention patterns for Views and Areas
- Example components (commented out)
- All options with inline comments explaining choices
- Targets only run for Release/Publish builds (not Debug)
- Config generation happens in early target (before build)
- esbuild invocation happens during publish
- Esbuild binaries are RID-specific, embedded in NuGet package
configVersionfield enables schema evolution and migration detection- Esbuild is shipped as RID-specific native binaries via NuGet (no Node/npm required)
- Root-relative URLs in CSS (
/img/*,/icons/*) are treated as external by esbuild - FileSystemWatcher only monitors
frontend.config.yamlfor changes (not individual JS/CSS files) - Per-request state tracked via
HttpContext.Itemsor scoped DI service