Skip to content

Fix OverrideHtmlAssetPlaceholders to fingerprint assets with ./ relative prefix#53407

Draft
Copilot wants to merge 2 commits intomainfrom
copilot/fix-override-html-asset-placeholders
Draft

Fix OverrideHtmlAssetPlaceholders to fingerprint assets with ./ relative prefix#53407
Copilot wants to merge 2 commits intomainfrom
copilot/fix-override-html-asset-placeholders

Conversation

Copy link
Contributor

Copilot AI commented Mar 12, 2026

GetFingerprintedAssetPath only performed exact dictionary lookups against label-keyed mappings (e.g. main.mjs), so asset placeholders using ./-relative paths (common in generated importmaps) were never fingerprinted — ./main#[.{fingerprint}].mjs would be left as-is instead of being replaced with the fingerprinted endpoint URL.

Changes

  • OverrideHtmlAssetPlaceholders.cs — after an exact lookup miss, if assetPath starts with ./, retry with the prefix stripped via Substring(2). IncludeOnlyHardFingerprintedModules semantics are preserved for both lookup paths. Method visibility changed from private to internal for testability. Substring(2) is used over the [2..] range operator since the project multi-targets net472.

  • OverrideHtmlAssetPlaceholdersTest.cs — added unit tests for the new ./-prefix fallback, covering .js/.mjs extensions, no-match passthrough, IncludeOnlyHardFingerprintedModules with hard- and soft-fingerprinted assets.

Example

<!-- Before: left unchanged because ./main.mjs != main.mjs in the label map -->
<script type="module" src="./main#[.{fingerprint}].mjs"></script>

<!-- After: correctly resolved to the fingerprinted endpoint URL -->
<script type="module" src="./main.abc1234.mjs"></script>
Original prompt

Goal

Fix #53406: OverrideHtmlAssetPlaceholders should fingerprint module asset placeholders even when the module src/import path uses a relative ./ prefix (e.g. ./main#[.{fingerprint}].mjs).

Background

Microsoft.NET.Sdk.StaticWebAssets task OverrideHtmlAssetPlaceholders uses a regex (_assetsRegex) to locate asset placeholder strings and then uses GetFingerprintedAssetPath(urlMappings, assetPath) to map the non-fingerprinted label to the fingerprinted endpoint URL.

Currently, GetFingerprintedAssetPath only attempts an exact lookup in the urlMappings dictionary. This fails when assetPath contains a ./ prefix (common in generated importmaps), because the mapping keys are based on labels like main.js, while the extracted assetPath can be ./main.js.

Required changes

  1. Update task logic

    • File: src/StaticWebAssetsSdk/Tasks/OverrideHtmlAssetPlaceholders.cs
    • Update GetFingerprintedAssetPath(Dictionary<string, ResourceAsset> urlMappings, string assetPath) so that it supports an optional leading ./.
    • Behavior should be minimal and backward compatible:
      • First try exact match of assetPath.
      • If not found and assetPath starts with ./, retry lookup with the prefix stripped (assetPath.Substring(2)), while still returning the original form (with ./) if not fingerprinted.
      • Preserve IncludeOnlyHardFingerprintedModules semantics.
      • Do not introduce broad path normalization (no Path.GetFullPath, no OS-specific separator changes) — only handle the ./ prefix.
  2. Update/add unit tests

    • Update existing tests or add new ones to cover the new behavior.
    • At minimum:
      • Add a unit test that demonstrates ./main#[.{fingerprint}].js (or .mjs) gets replaced with the fingerprinted endpoint URL when a mapping exists for main.js/main.mjs.
    • Preferred places:
      • test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/OverrideHtmlAssetPlaceholdersTest.cs (task-level behavior test).
      • Optionally, also extend integration coverage in test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssetsFingerprintingTest.cs by adding a data row that uses ./main.js and ensures fingerprinting occurs.
  3. Keep changes minimal

    • Follow repo coding style; avoid unrelated refactors.
    • Ensure no unused usings are introduced.

Acceptance criteria

  • OverrideHtmlAssetPlaceholders correctly fingerprints placeholders when the asset path begins with ./.
  • Existing behavior for paths without ./ remains unchanged.
  • Unit tests pass and include coverage for the new ./ scenario.

Notes

  • Code search results may be incomplete; confirm there aren’t additional call sites/tests that should be updated.
  • Ensure the fix applies to both .js and .mjs module routes as applicable.

The following is the prior conversation context from the user's chat exploration (may be truncated):

User: @dotnet/sdk/issues/53406

There is a feature OverrideHtmlAssetPlaceholders in Microsoft.NET.Sdk.StaticWebAssets, it uses regex to find values of attributes and tries to put in fingerprint. It does it by finding corelating endpoint. Currently we support only exact match, but when generating impormap, we generate relative paths, like ./main.js. We should support ./ prefix here as well. Update related unit tests as well

Assistant: I’ve submitted the requested repository changes for your approval, and I’m waiting on your accept/reject response before I can continue with applying them and validating the update (including the unit test changes for the ./ prefix case).

User: @copilot Accepted Confirmation: Are you sure?

Assistant: You’re right about where the bug is: OverrideHtmlAssetPlaceholders only fingerprints when the matched assetPath exactly equals a key in the label→endpoint mapping, and the mapping keys are the endpoint labels (e.g. main.js), not variants like ./main.js. So when HTML contains:

<script type="module" src="./main#[.{fingerprint}].mjs"></script>

the regex captures assetPath as ./main.mjs, and urlMappings.TryGetValue("./main.mjs", ...) fails, so it leaves the path unchanged.

Minimal fix

Update GetFingerprintedAssetPath(...) to also attempt a lookup after stripping the leading ./ (and only that prefix), while still returning the original value if nothing matches. This keeps behavior minimal and avoids broader URL normalization changes.

Pseudo-shape of the change (not applied yet):

  • Try exact match (assetPath)
  • If it starts with "./", try match for assetPath.Substring(2)
  • Return the fingerprinted URL if found (and respecting IncludeOnlyHardFingerprintedModules), otherwise return the original assetPath

Unit tests to update/add

You should add coverage that exercises the lookup behavior (not just regex matching):

  1. Task-level test (best): add a unit test in `OverrideHtmlAssetPlaceholdersT...

This pull request was created from Copilot chat.


🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.

Co-authored-by: maraf <10020471+maraf@users.noreply.github.com>
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.

[browser] fingerprints don't work for modules with ./ prefix

2 participants