Skip to content

Conversation

@andrei-micuda
Copy link

Summary

This PR adds an optional vite-prefix attribute to the existing ViteTagHelper. When present, the helper will prepend the provided prefix to the manifest-resolved asset path before emitting the href or src attribute. If the combined prefix+path begins with ~, it is expanded using UrlHelper.Content (so Request.PathBase still applies). Dev server behavior is unchanged, and manifest resolution is preserved.

Motivation

When an ASP.NET Core app is hosted behind proxies that add dynamic external prefixes (for example /sso/<guid>/...) or when you need assets referenced relative to the current document, emitting app-root absolute paths isn't flexible enough.

This change preserves all the benefits of Vite's manifest (hashed filenames and CSS chunk discovery) while giving developers control over final URL generation through:

  • Relative prefixes (e.g., ../ for document-relative paths)
  • Static prefixes (e.g., /static/ for CDN or specific mount points)
  • Tilde expansion (e.g., ~/prefix/ for PathBase-aware URLs)

Changes Made

ViteTagHelper.cs

  • Added new property: [HtmlAttributeName("vite-prefix")] public string? VitePrefix { get; set; }
  • Implemented ResolveCssFilePath() and ResolveJsFilePath() helper methods to apply prefix logic
  • When VitePrefix is provided and non-empty, the tag helper prepends the normalized prefix to the resolved manifest path
  • If the combined string starts with ~, UrlHelper.Content is used for expansion
  • CSS chunk generation applies the same prefix logic to each <link> tag generated
  • Fixed code quality issues:
    • Added output.Attributes.RemoveAll(VITE_PREFIX_ATTRIBUTE) to ensure the custom attribute doesn't leak into HTML output
    • Changed string comparisons to use char literals ('/', '~') for better performance
    • Added missing blank line for consistency with repository style

Documentation

  • Updated README.md (root) with vite-prefix section and examples
  • Updated src/library/Vite.AspNetCore/README.md with detailed usage examples

Tests

  • Added comprehensive test suite in src/tests/ViteNetProject.Tests/VitePrefixTests.cs
  • 6 test cases covering all scenarios:
    • No prefix (existing behavior preserved)
    • Relative prefix (../)
    • Absolute prefix (/static/)
    • Tilde prefix (~/prefix/)
    • CSS chunk generation with prefix applied
    • Automatic slash normalization
  • Added [Collection("Sequential")] attribute to prevent test isolation issues
  • All tests passing ✅

Dependencies

  • Added Moq package (v4.20.72) for unit testing

Backward Compatibility

No breaking changes - this is a fully backward-compatible addition:

  • Default behavior is unchanged when vite-prefix is not set
  • All existing functionality continues to work exactly as before
  • The vite-prefix attribute is completely optional

Usage Examples

Relative Prefix (Document-Relative Paths)

<script type="module" vite-src="~/main.ts" vite-prefix="../"></script>
<!-- Renders: <script type="module" src="../js/main.abc123.js"></script> -->

Static Prefix (CDN or Mount Point)

<link rel="stylesheet" vite-href="~/styles/app.css" vite-prefix="/static/" />
<!-- Renders: <link rel="stylesheet" href="/static/css/app.def456.css" /> -->

Tilde Prefix (PathBase-Aware)

<script type="module" vite-src="~/main.ts" vite-prefix="~/assets/"></script>
<!-- Renders: <script type="module" src="/myapp/assets/js/main.abc123.js"></script> -->
<!-- (when Request.PathBase = "/myapp") -->

- Add optional vite-prefix attribute for custom asset path prefixes
- Support relative, absolute, and tilde-prefixed paths
- Add comprehensive unit tests for all scenarios
- Update documentation in README files
@Eptagone
Copy link
Owner

Eptagone commented Nov 5, 2025

Hi, what's the difference of this vs setting a base path?

@andrei-micuda
Copy link
Author

Hi — thanks for reviewing this. For my use case I needed my Vite assets to be referenced with a document-relative path so they live under a subfolder inside wwwroot used for the React app (e.g. ../react/main.ts). I tried setting Vite's base in vite.config.ts, but that option requires an absolute or URL-like path and doesn't accept relative values like "../", so it couldn't produce document-relative URLs from the manifest.

Because of that limitation I needed the ability to prepend a custom prefix (including relative prefixes such as ../) after manifest resolution — which is exactly what vite-prefix allows. Example:

vite-href/vite-src in markup: ~/main.ts with vite-prefix="../react/"
rendered output: ../react/js/main.abc123.js

This keeps hashed filenames and CSS chunk discovery provided by the manifest, while allowing document-relative or proxy/CDN prefixes that Vite's base can't express. Happy to add any extra examples or a short integration note to the README if you want.

@Eptagone
Copy link
Owner

This keeps hashed filenames and CSS chunk discovery provided by the manifest, while allowing document-relative or proxy/CDN prefixes that Vite's base can't express. Happy to add any extra examples or a short integration note to the README if you want.

Yes, please add an example project in examples for this use-case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants