Skip to content

Fix port replacers in templates to prevent unintended replacements - #65165

Merged
javiercn merged 3 commits into
mainfrom
copilot/fix-bootstrap-js-port-replacement
Jan 22, 2026
Merged

javiercn merged 3 commits into
mainfrom
copilot/fix-bootstrap-js-port-replacement

Conversation

Copilot AI commented Jan 21, 2026 •

Copy link
Copy Markdown
Contributor
  • You've read the Contributor Guide and Code of Conduct.
  • You've included unit or integration tests for your change, where applicable.
  • You've included inline docs for your change, where applicable.
  • There's an open issue for the PR that you are making. If you'd like to propose a new feature or change, please open an issue to discuss the change or find an existing issue.

Constrain port replacers to URL contexts only

Description

Port replacers in template.json were performing global replacements, corrupting numeric values in generated files. For example, interval: 5000 in bootstrap.bundle.js would become interval: 5126 when a random port was assigned.

Added onlyIf: [{"after": "localhost:"}] constraint to kestrelHttpPortReplacer and kestrelHttpsPortReplacer symbols:

"kestrelHttpPortReplacer": {
  "type": "generated",
  "generator": "coalesce",
  "parameters": {
    "sourceVariableName": "kestrelHttpPort",
    "fallbackVariableName": "kestrelHttpPortGenerated"
  },
  "replaces": "5000",
  "onlyIf": [{
    "after": "localhost:"
  }]
}

Applied to all web project templates:

  • BlazorWeb-CSharp, ComponentsWebAssembly-CSharp
  • StarterWeb-CSharp/FSharp, WebApi-CSharp/FSharp, WebApiAot-CSharp
  • RazorPagesWeb-CSharp, EmptyWeb-CSharp/FSharp, GrpcService-CSharp

Replacement now occurs only in launchSettings.json URLs, preserving all other numeric values.

Original prompt

Problem

When creating a Blazor project using dotnet new blazorwasm -o projectname, the port number replacement is incorrectly modifying the bootstrap.bundle.js file. Specifically, Default$b.interval at line 1131 in projectname/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js is being set to the same value as the port number in launchSettings.json.

This is happening because the kestrelHttpPortReplacer and kestrelHttpsPortReplacer symbols in template.json are configured to replace 5500 and 5501 globally across all files, without restricting the replacement to only occur in the context of localhost: URLs.

See issue: dotnet/sdk#48811

Solution

Add onlyIf constraints to the port replacer symbols to ensure they only perform replacements when the port number is preceded by localhost:. This approach is already used successfully in the dotnet/extensions templates.

In src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/.template.config/template.json, modify the following symbols:

kestrelHttpPortReplacer

Change from:

"kestrelHttpPortReplacer": {
  "type": "generated",
  "generator": "coalesce",
  "parameters": {
    "sourceVariableName": "kestrelHttpPort",
    "fallbackVariableName": "kestrelHttpPortGenerated"
  },
  "replaces": "5500"
},

To:

"kestrelHttpPortReplacer": {
  "type": "generated",
  "generator": "coalesce",
  "parameters": {
    "sourceVariableName": "kestrelHttpPort",
    "fallbackVariableName": "kestrelHttpPortGenerated"
  },
  "replaces": "5500",
  "onlyIf": [{
    "after": "localhost:"
  }]
},

kestrelHttpsPortReplacer

Change from:

"kestrelHttpsPortReplacer": {
  "type": "generated",
  "generator": "coalesce",
  "parameters": {
    "sourceVariableName": "kestrelHttpsPort",
    "fallbackVariableName": "kestrelHttpsPortGenerated"
  },
  "replaces": "5501"
},

To:

"kestrelHttpsPortReplacer": {
  "type": "generated",
  "generator": "coalesce",
  "parameters": {
    "sourceVariableName": "kestrelHttpsPort",
    "fallbackVariableName": "kestrelHttpsPortGenerated"
  },
  "replaces": "5501",
  "onlyIf": [{
    "after": "localhost:"
  }]
},

Notes

This pull request was created from Copilot chat.


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI and others added 2 commits January 21, 2026 19:04
Co-authored-by: MackinnonBuck <10456961+MackinnonBuck@users.noreply.github.com>
…rect replacements

Co-authored-by: MackinnonBuck <10456961+MackinnonBuck@users.noreply.github.com>
@MackinnonBuck
MackinnonBuck marked this pull request as ready for review January 22, 2026 00:04
@MackinnonBuck
MackinnonBuck requested a review from a team as a code owner January 22, 2026 00:04
Copilot AI review requested due to automatic review settings January 22, 2026 00:04

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR constrains Kestrel port replacements in project templates so they only occur in URL contexts, preventing unintended modification of numeric literals (e.g., in bootstrap.bundle.js) when random ports are assigned.

Changes:

  • Added onlyIf conditions to kestrelHttpPortReplacer and kestrelHttpsPortReplacer across all relevant web project templates so replacements only occur when the port is preceded by localhost:.
  • Left IIS port replacers unchanged (still global), after confirming their literal values (e.g., 8080, 44300) are not used elsewhere in template content.
  • Normalized a minor JSON formatting issue in GrpcService-CSharp ("NativeAot": { spacing).

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/ProjectTemplates/Web.ProjectTemplates/content/WebApiAot-CSharp/.template.config/template.json Adds onlyIf on kestrelHttpPortReplacer so HTTP port substitution only happens in localhost: URL contexts for the native AOT Web API template.
src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-FSharp/.template.config/template.json Constrains both HTTP and HTTPS Kestrel port replacers with onlyIf conditions to avoid global numeric replacements.
src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/.template.config/template.json Mirrors the F# Web API changes for C#, adding onlyIf constraints to Kestrel HTTP/HTTPS port replacers.
src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-FSharp/.template.config/template.json Adds onlyIf to Kestrel HTTP/HTTPS port replacers so only localhost: URLs are affected in the starter F# web template.
src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/.template.config/template.json Same onlyIf constraints added for the C# starter web template’s Kestrel HTTP/HTTPS port replacers.
src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/.template.config/template.json Restricts Kestrel HTTP/HTTPS port replacements to URL contexts via onlyIf to avoid corrupting other numeric values.
src/ProjectTemplates/Web.ProjectTemplates/content/GrpcService-CSharp/.template.config/template.json Adds onlyIf to Kestrel HTTP/HTTPS port replacers and tidies JSON spacing for the NativeAot symbol.
src/ProjectTemplates/Web.ProjectTemplates/content/EmptyWeb-FSharp/.template.config/template.json Constrains Kestrel HTTP/HTTPS port replacers with onlyIf so only localhost: URLs are rewritten in the empty F# template.
src/ProjectTemplates/Web.ProjectTemplates/content/EmptyWeb-CSharp/.template.config/template.json Same Kestrel HTTP/HTTPS onlyIf constraints applied for the empty C# template.
src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/.template.config/template.json Adds onlyIf to Kestrel HTTP/HTTPS replacers for the Blazor WebAssembly components template to avoid global numeric replacements.
src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/.template.config/template.json Adds onlyIf to Kestrel HTTP/HTTPS port replacers (the source of the reported issue), constraining replacements to localhost: URLs and preventing corruption of bundled JS numeric literals.

Comment on lines +299 to 303
"replaces": "5500",
"onlyIf": [{
"after": "localhost:"
}]
},

Copilot AI Jan 22, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These new onlyIf constraints change how the template engine performs port substitutions, but there doesn't appear to be a regression test asserting that ports are only replaced in URL contexts (for example, ensuring numeric constants in bootstrap.bundle.js are not modified). Consider adding a template test that generates a Blazor WebAssembly project, reads wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js, and verifies that known numeric literals (such as the carousel interval value) remain unchanged and do not get replaced with the assigned Kestrel HTTP/HTTPS ports.

Copilot uses AI. Check for mistakes.
@javiercn
javiercn merged commit f9ba7d6 into main Jan 22, 2026
31 checks passed
@javiercn
javiercn deleted the copilot/fix-bootstrap-js-port-replacement branch January 22, 2026 14:39
@dotnet-policy-service dotnet-policy-service Bot added this to the 11.0-preview1 milestone Jan 22, 2026
JamesNK pushed a commit to microsoft/aspire that referenced this pull request Sep 13, 2026
The dynamic port symbols (webHttpPortReplacer, apiServiceHttpPortReplacer,
appHostHttpPortReplacer, etc.) used a bare numeric "replaces" value, so the
template engine substituted that number everywhere in generated content, not
just in launchSettings.json. Since the default ports (5000, 7000, 5301, 7301,
15000, 17000, 19000, 20000, 21000, 22000) also occur as plain numeric literals
in vendored files such as wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js
(e.g. carousel interval: 5000), those files ended up with churned values that
differ between generated projects, making diffs across template runs noisy.

Add "onlyIf": [{"after": "localhost:"}] to each port replacer so substitution
only fires in "localhost:<port>" contexts, matching the fix already applied
upstream for the same class of bug (dotnet/aspnetcore#65165, dotnet/sdk#48811).

Fixes #20030
JamesNK pushed a commit to microsoft/aspire that referenced this pull request Sep 14, 2026
The dynamic port symbols (webHttpPortReplacer, apiServiceHttpPortReplacer,
appHostHttpPortReplacer, etc.) used a bare numeric "replaces" value, so the
template engine substituted that number everywhere in generated content, not
just in launchSettings.json. Since the default ports (5000, 7000, 5301, 7301,
15000, 17000, 19000, 20000, 21000, 22000) also occur as plain numeric literals
in vendored files such as wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js
(e.g. carousel interval: 5000), those files ended up with churned values that
differ between generated projects, making diffs across template runs noisy.

Add "onlyIf": [{"after": "localhost:"}] to each port replacer so substitution
only fires in "localhost:<port>" contexts, matching the fix already applied
upstream for the same class of bug (dotnet/aspnetcore#65165, dotnet/sdk#48811).

Fixes #20030
joperezr pushed a commit to microsoft/aspire that referenced this pull request Sep 14, 2026
The dynamic port symbols (webHttpPortReplacer, apiServiceHttpPortReplacer,
appHostHttpPortReplacer, etc.) used a bare numeric "replaces" value, so the
template engine substituted that number everywhere in generated content, not
just in launchSettings.json. Since the default ports (5000, 7000, 5301, 7301,
15000, 17000, 19000, 20000, 21000, 22000) also occur as plain numeric literals
in vendored files such as wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js
(e.g. carousel interval: 5000), those files ended up with churned values that
differ between generated projects, making diffs across template runs noisy.

Add "onlyIf": [{"after": "localhost:"}] to each port replacer so substitution
only fires in "localhost:<port>" contexts, matching the fix already applied
upstream for the same class of bug (dotnet/aspnetcore#65165, dotnet/sdk#48811).

Fixes #20030

Co-authored-by: Bart Koelman <104792814+bart-vmware@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.

4 participants