Skip to content

Conversation

@javiercn
Copy link
Member

@javiercn javiercn commented Jan 2, 2026

Summary

Fixes #51822, #52301

This PR adds support for integrating TypeScript outputs from the Microsoft.TypeScript.MSBuild NuGet package with ASP.NET Core Static Web Assets in Razor Class Libraries.

Problem

When using TypeScript with Static Web Assets in RCLs, rebuild operations fail because:

  1. Timing issue: TypeScript compilation runs during the Compile phase, AFTER Static Web Assets discovery has already occurred
  2. Stale references: During Clean, TypeScript targets delete the .js files, but Content items (added by Razor SDK globbing) persist in memory
  3. Build failure: DefineStaticWebAssets then fails because it finds Content items referencing files that no longer exist

Solution

Added Microsoft.NET.Sdk.StaticWebAssets.TypeScript.targets which:

  • Hooks into ResolveStaticWebAssetsInputsDependsOn to register TypeScript outputs AFTER compilation
  • Removes TypeScript outputs from Content before CoreClean and ResolveProjectStaticWebAssets to prevent stale item references
  • Uses DefineStaticWebAssets/DefineStaticWebAssetEndpoints for proper integration with compression, fingerprinting, and manifest generation
  • Creates a manifest file (staticwebassets.typescript.files.txt) to track registered assets across builds

Changes

File Description
Microsoft.NET.Sdk.StaticWebAssets.TypeScript.targets New targets file with TypeScript integration logic
Microsoft.NET.Sdk.StaticWebAssets.targets Added conditional import when TypeScript package is detected
TypeScriptIntegrationTest.cs 8 E2E integration tests
Microsoft.NET.Sdk.StaticWebAssets.Tests.csproj Added assembly metadata for TypeScript version
eng/Versions.props Added MicrosoftTypeScriptMSBuildPackageVersion
Directory.Packages.props Added Microsoft.TypeScript.MSBuild package reference

Test Coverage

All 8 integration tests pass:

How It Works

┌─────────────────────────────────────────────────────────────────────┐
│                         Build Pipeline                               │
├─────────────────────────────────────────────────────────────────────┤
│  1. ResolveCoreStaticWebAssets     (discovers existing wwwroot)     │
│  2. CompileTypeScriptWithTSConfig  (TypeScript → JS in wwwroot)     │
│  3. RegisterTypeScriptStaticWebAssets ◄── NEW: hooks in here        │
│     └─ DefineStaticWebAssets       (registers JS as SWA)            │
│     └─ DefineStaticWebAssetEndpoints (creates endpoint mappings)    │
│  4. Compression, fingerprinting, manifest generation                │
└─────────────────────────────────────────────────────────────────────┘

During rebuild, the RemoveTypeScriptFromContentBeforeSwaDiscovery target runs before CoreClean to prevent stale Content item references.

Fixes #51822

This commit adds support for integrating TypeScript outputs from the
Microsoft.TypeScript.MSBuild NuGet package with ASP.NET Core Static Web Assets
in Razor Class Libraries.

## Problem
When using TypeScript with Static Web Assets in RCLs, rebuild operations fail
because:
1. TypeScript compilation runs during the Compile phase, AFTER Static Web Assets
   discovery has already occurred
2. During Clean, TypeScript targets delete the .js files, but Content items
   (added by Razor SDK globbing) persist in memory
3. DefineStaticWebAssets then fails because it finds Content items referencing
   files that no longer exist

## Solution
Added Microsoft.NET.Sdk.StaticWebAssets.TypeScript.targets which:
- Hooks into ResolveStaticWebAssetsInputsDependsOn to register TypeScript
  outputs AFTER compilation
- Removes TypeScript outputs from Content before CoreClean and
  ResolveProjectStaticWebAssets to prevent stale item references
- Uses DefineStaticWebAssets/DefineStaticWebAssetEndpoints for proper
  integration with compression, fingerprinting, and manifest generation
- Creates a manifest file (staticwebassets.typescript.files.txt) to track
  registered assets across builds

## Changes
- Added Microsoft.NET.Sdk.StaticWebAssets.TypeScript.targets
- Added conditional import in main targets when TypeScript package is detected
- Added TypeScriptIntegrationTest.cs with 8 E2E tests
- Added Microsoft.TypeScript.MSBuild to central package management
Comment on lines 38 to 44
<!--
Target: ResolveTypeScriptStaticWebAssetsConfiguration

Sets up the path for the TypeScript manifest file.
DependsOnTargets ResolveStaticWebAssetsConfiguration to ensure
_StaticWebAssetsManifestBase is defined.
-->
Copy link
Member Author

Choose a reason for hiding this comment

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

Suggested change
<!--
Target: ResolveTypeScriptStaticWebAssetsConfiguration
Sets up the path for the TypeScript manifest file.
DependsOnTargets ResolveStaticWebAssetsConfiguration to ensure
_StaticWebAssetsManifestBase is defined.
-->

Comment on lines +59 to +64
<PropertyGroup>
<ResolveStaticWebAssetsInputsDependsOn>
RegisterTypeScriptStaticWebAssets;
$(ResolveStaticWebAssetsInputsDependsOn)
</ResolveStaticWebAssetsInputsDependsOn>
</PropertyGroup>
Copy link
Member Author

Choose a reason for hiding this comment

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

Move this before the target definitions

Comment on lines 91 to 95
<!--
Write manifest file to track registered assets across builds.
WriteOnlyWhenDifferent prevents unnecessary timestamp changes for incremental builds.
FileWrites ensures the manifest is cleaned up during Clean target.
-->
Copy link
Member Author

Choose a reason for hiding this comment

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

Suggested change
<!--
Write manifest file to track registered assets across builds.
WriteOnlyWhenDifferent prevents unnecessary timestamp changes for incremental builds.
FileWrites ensures the manifest is cleaned up during Clean target.
-->

Comment on lines 127 to 131
<!--
Define endpoints for TypeScript assets.
This creates the endpoint entries that map URL paths to assets,
which are needed for the runtime to serve the files correctly.
-->
Copy link
Member Author

Choose a reason for hiding this comment

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

Suggested change
<!--
Define endpoints for TypeScript assets.
This creates the endpoint entries that map URL paths to assets,
which are needed for the runtime to serve the files correctly.
-->

Copy link
Contributor

Copilot AI left a comment

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 adds TypeScript Static Web Assets integration for Razor Class Libraries to fix rebuild failures when using the Microsoft.TypeScript.MSBuild NuGet package. The solution introduces a new targets file that properly registers TypeScript-compiled JavaScript outputs as static web assets after compilation completes.

Key Changes:

  • Introduces RegisterTypeScriptStaticWebAssets target that hooks into the Static Web Assets pipeline to register TypeScript outputs after compilation
  • Implements RemoveTypeScriptFromContentBeforeSwaDiscovery to prevent stale Content item references during rebuild operations
  • Adds comprehensive E2E test coverage with 8 integration tests covering build, rebuild, clean, publish, and incremental build scenarios

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
Microsoft.NET.Sdk.StaticWebAssets.TypeScript.targets New targets file implementing TypeScript/SWA integration with manifest tracking and proper cleanup
Microsoft.NET.Sdk.StaticWebAssets.targets Conditional import of TypeScript targets when TypeScript package is detected
TypeScriptIntegrationTest.cs Comprehensive test suite with 8 tests covering all key scenarios
Microsoft.NET.Sdk.StaticWebAssets.Tests.csproj Assembly metadata for TypeScript package version configuration
Versions.props TypeScript MSBuild package version definition (5.9.3)
Directory.Packages.props Package version reference for central package management

Copy link
Member

@MackinnonBuck MackinnonBuck left a comment

Choose a reason for hiding this comment

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

Looks great, just a small question about a test.

Comment on lines +118 to +120
// Wait a bit and do incremental build
await Task.Delay(100);

Copy link
Member

Choose a reason for hiding this comment

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

Why is this needed? Does the test become flaky without it? Is there another way we can detect when the right conditions are met for the test to continue?

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.

InvalidOperationException REBUILD fails due to "no file exists for the asset at either location" BUILD works

3 participants