eQuantic.UIeQuantic.UI
Docs
Playground
GitHub
DocsServer
BunPackage: npm Packages via .csproj
Edit this page
3 min read
eQuantic.UI allows declaring npm package dependencies directly in .csproj files, keeping the developer experience 100% .NET with zero package.json, zero npm, and zero Node.js.
Quick Start
1
2
3
4
<!-- MyApp.csproj -->
<ItemGroup>
<BunPackage Include="dayjs" Version="1.11.13" />
</ItemGroup>
That's it. On dotnet build, the SDK automatically:
1.
Generates a temporary package.json in obj/eQuantic/npm/
2.
Installs packages using the embedded Bun runtime
3.
Creates a node_modules symlink for tool resolution
4.
Skips installation on incremental builds
How It Works
Build Pipeline
1
2
3
4
5
6
7
8
9
10
11
12
13
dotnet build
│
├── 1. ResolveBunPath ← Extracts embedded Bun from NuGet package
│
├── 2. InstallBunPackages ← NEW: Installs <BunPackage> items
│ ├── Creates obj/eQuantic/npm/package.json
│ ├── Runs: bun add <package>@<version>
│ └── Symlinks: node_modules → obj/eQuantic/npm/node_modules/
│
├── 3. CompileEQuanticUI ← C# → TypeScript → JavaScript
│
├── 4. CopyEQuanticRuntime ← Copies runtime.js
│
File Layout
1
2
3
4
5
6
7
8
9
10
11
MyApp/
├── MyApp.csproj ← BunPackage declared here
├── node_modules/ ← Symlink (gitignored)
│ └── → obj/eQuantic/npm/node_modules/
├── obj/
│ └── eQuantic/
│ └── npm/ ← Hidden from developer
│ ├── package.json ← Auto-generated
│ ├── bun.lock ← Auto-generated
│ └── node_modules/ ← Real packages
└── src/
The developer never sees or touches package.json. The node_modules symlink is gitignored and auto-created.
Usage Examples
Multiple Packages
Multiple BunPackage items are supported. Each runs as a separate bun add:
1
2
3
4
<ItemGroup>
<BunPackage Include="dayjs" Version="1.11.13" />
<BunPackage Include="marked" Version="15.0.6" />
</ItemGroup>
Every one of them is installed (Since 0.2.0-preview.62). Before that only the first was: the install asked "is there a node_modules yet?" again before each package, and the first bun add had made it.
CSS-Only Packages
Works equally well with CSS-only packages imported via @import:
1
2
3
<ItemGroup>
<BunPackage Include="tw-animate-css" Version="1.2.5" />
</ItemGroup>
1
2
/* src/styles.css */
@import "tw-animate-css";
MSBuild Target Details
The InstallBunPackages target lives in Sdk.targets:
1
2
3
4
<Target Name="InstallBunPackages"
AfterTargets="ResolveBunPath"
BeforeTargets="CompileEQuanticUI"
Condition="'@(BunPackage)' != '' And '$(BunPath)' != ''">
Key Behaviors
Behavior
Details
Incremental
Skips if obj/eQuantic/npm/node_modules/ already exists
Clean build
Runs bun add for each package on first build or after dotnet clean
Cross-platform
A symbolic link on every system, made by the SDK through the system's own call and no shell (Since 0.2.0-preview.62). Windows asks for Developer Mode or elevation to make one, as mklink /D did. What is already at node_modules stays: a link of an earlier build, or a folder of your own
Isolation
Packages live in obj/, not project root
No pollution
No package.json, bun.lockb, or node_modules committed to git
Forcing Reinstall
To force a fresh install (e.g., after changing versions):
1
2
3
4
5
6
# Option 1: Delete the npm cache
rm -rf obj/eQuantic/npm
# Option 2: Full clean
dotnet clean
dotnet build
Design Decisions
Why not generate package.json at project root?
•
Pollutes the .NET project with npm artifacts
•
Developers might accidentally commit it
•
Confuses IDEs into treating it as a Node.js project
•
Violates the "100% .NET" principle
Why symlink instead of NODE_PATH?
•
Symlinks are universally supported by the tools that resolve node_modules
•
NODE_PATH has inconsistent behavior across tools
•
The symlink is transparent: if a tool looks for node_modules/, it finds them
Why bun add instead of bun install?
•
bun add works with a minimal {"private": true} package.json
•
MSBuild item batching (%(BunPackage.Identity)) naturally maps to individual bun add calls
•
Each package is installed with its exact version
Why not use bun x --install?
•
bun x is for running CLI tools, not for installing libraries
•
Packages referenced in CSS (@plugin, @import) need to exist in node_modules/
•
bun add is the correct semantic for "install this library"
Comparison
Approach
package.json needed
npm/Node.js needed
Developer experience
Traditional npm
Yes
Yes
Must manage two ecosystems
BunPackage
No
No
Pure .NET, zero friction
Troubleshooting
Packages not updating after version change
Cause: node_modules already exists, so InstallBunPackages skips
Fix:
1
2
rm -rf obj/eQuantic/npm
dotnet build
Symlink permission error on Windows
Cause: Creating symlinks on Windows requires elevated permissions or Developer Mode
Fix: Enable Developer Mode in Windows Settings, or run terminal as Administrator
Related Documentation
•
Build Flow: Complete MSBuild target execution order
•
Package Architecture: Self-contained package design
•
Asset Management: Component asset dependencies