Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@
<PackageVersion Include="Microsoft.WixToolset.Util.wixext" Version="$(MicrosoftWixToolsetSdkVersion)" />
<PackageVersion Include="Microsoft.WixToolset.UI.wixext" Version="$(MicrosoftWixToolsetSdkVersion)" />
<PackageVersion Include="MSBuild.StructuredLogger" Version="2.3.45" />
<PackageVersion Include="Microsoft.TypeScript.MSBuild" Version="$(MicrosoftTypeScriptMSBuildPackageVersion)" />
<PackageVersion Include="Moq" Version="$(MoqPackageVersion)" />
<PackageVersion Include="NETStandard.Library.NETFramework" Version="$(NETStandardLibraryNETFrameworkVersion)" />
<PackageVersion Include="Newtonsoft.Json" Version="$(NewtonsoftJsonPackageVersion)" />
Expand Down
1 change: 1 addition & 0 deletions eng/Versions.props
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@
<PropertyGroup>
<AwesomeAssertionsVersion>8.0.2</AwesomeAssertionsVersion>
<AwesomeAssertionsJsonVersion>8.0.0</AwesomeAssertionsJsonVersion>
<MicrosoftTypeScriptMSBuildPackageVersion>5.9.3</MicrosoftTypeScriptMSBuildPackageVersion>
<MoqPackageVersion>4.18.4</MoqPackageVersion>
<XunitCombinatorialVersion>1.3.2</XunitCombinatorialVersion>
<MicrosoftDotNetInstallerWindowsSecurityTestDataPackageVersion>8.0.0-beta.23607.1</MicrosoftDotNetInstallerWindowsSecurityTestDataPackageVersion>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
<!--
***********************************************************************************************
Microsoft.NET.Sdk.StaticWebAssets.TypeScript.targets

WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and have
created a backup copy. Incorrect changes to this file will make it
impossible to load or build your projects from the command-line or the IDE.

Copyright (c) .NET Foundation. All rights reserved.
***********************************************************************************************
-->

<!--
Provides integration between the Microsoft.TypeScript.MSBuild NuGet package and
ASP.NET Core Static Web Assets for Razor Class Libraries.

Problem:
The TypeScript MSBuild package outputs compiled .js files to wwwroot, which is treated
as an input folder by the Static Web Assets SDK. However, TypeScript compilation runs
during the Compile phase, AFTER Static Web Assets discovery has already occurred.

This creates two problems:
1. Clean build: TypeScript outputs are not discovered as static web assets because
they don't exist yet when ResolveProjectStaticWebAssets runs.
2. Rebuild: The Razor SDK's default globbing adds wwwroot files to Content. During Clean,
TypeScript targets delete the .js files, but Content items persist in memory.
When Build runs, DefineStaticWebAssets fails because it finds Content items
referencing files that no longer exist.

Solution:
1. Hook into ResolveStaticWebAssetsInputsDependsOn to register TypeScript outputs
as static web assets AFTER compilation.
2. Remove TypeScript outputs from Content before CoreClean and ResolveProjectStaticWebAssets
to prevent stale item references.
-->
<Project ToolsVersion="14.0">

<!--
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.
-->

<Target Name="ResolveTypeScriptStaticWebAssetsConfiguration"
DependsOnTargets="ResolveStaticWebAssetsConfiguration">
<PropertyGroup>
<_TypeScriptSwaManifestPath>$(_StaticWebAssetsManifestBase)staticwebassets.typescript.files.txt</_TypeScriptSwaManifestPath>
</PropertyGroup>
</Target>

<!--
Hook into ResolveStaticWebAssetsInputs - this is the correct extension point
for adding computed/generated assets. It runs:
1. AFTER Compile phase (where TypeScript runs)
2. AFTER ResolveCoreStaticWebAssets (core SWA discovery)
3. BEFORE compression and endpoint generation
-->
<PropertyGroup>
<ResolveStaticWebAssetsInputsDependsOn>
RegisterTypeScriptStaticWebAssets;
$(ResolveStaticWebAssetsInputsDependsOn)
</ResolveStaticWebAssetsInputsDependsOn>
</PropertyGroup>
Comment on lines +14 to +19
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


<!--
Target: RegisterTypeScriptStaticWebAssets

Registers TypeScript outputs as Static Web Assets using the SDK's
DefineStaticWebAssets and DefineStaticWebAssetEndpoints tasks.

This ensures TypeScript outputs get:
- Proper asset identity and metadata
- Compression (.gz, .br files)
- Endpoint definitions in the manifest
- Fingerprinting support (if enabled)

DependsOnTargets:
- CompileTypeScriptWithTSConfig: Ensures TypeScript is compiled first
- GetTypeScriptOutputForPublishing: Populates @(GeneratedJavascript) item group
-->
<Target Name="RegisterTypeScriptStaticWebAssets"
DependsOnTargets="ResolveTypeScriptStaticWebAssetsConfiguration;CompileTypeScriptWithTSConfig;GetTypeScriptOutputForPublishing">

<!-- Filter to only wwwroot outputs that exist on disk -->
<ItemGroup>
<_TypeScriptWwwrootAssets Include="@(GeneratedJavascript)"
Condition="$([System.String]::new('%(Identity)').Contains('wwwroot')) And Exists('%(Identity)')" />
</ItemGroup>

<!--
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.
-->

<WriteLinesToFile
File="$(_TypeScriptSwaManifestPath)"
Lines="@(_TypeScriptWwwrootAssets)"
Overwrite="true"
WriteOnlyWhenDifferent="true"
Condition="'@(_TypeScriptWwwrootAssets)' != ''" />

<ItemGroup>
<FileWrites Include="$(_TypeScriptSwaManifestPath)" />
</ItemGroup>

<!--
Register TypeScript outputs as static web assets.
DefineStaticWebAssets is the SDK task that properly creates StaticWebAsset items
with all required metadata (Identity, RelativePath, ContentRoot, etc.)
-->
<DefineStaticWebAssets
Condition="'@(_TypeScriptWwwrootAssets)' != ''"
CandidateAssets="@(_TypeScriptWwwrootAssets)"
FingerprintCandidates="$(StaticWebAssetsFingerprintContent)"
FingerprintPatterns="@(StaticWebAssetFingerprintPattern)"
RelativePathPattern="wwwroot/**"
SourceType="Discovered"
SourceId="$(PackageId)"
ContentRoot="$(MSBuildProjectDirectory)\wwwroot\"
BasePath="$(StaticWebAssetBasePath)"
AssetMergeSource="$(StaticWebAssetMergeTarget)">
<Output TaskParameter="Assets" ItemName="StaticWebAsset" />
<Output TaskParameter="Assets" ItemName="_TypeScriptStaticWebAssets" />
</DefineStaticWebAssets>

<!--
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.
-->

<DefineStaticWebAssetEndpoints
Condition="'@(_TypeScriptStaticWebAssets)' != ''"
CandidateAssets="@(_TypeScriptStaticWebAssets)"
ContentTypeMappings="@(StaticWebAssetContentTypeMapping)">
<Output TaskParameter="Endpoints" ItemName="StaticWebAssetEndpoint" />
</DefineStaticWebAssetEndpoints>

</Target>

<!--
Target: RemoveTypeScriptFromContentBeforeSwaDiscovery

Removes TypeScript outputs from Content items BEFORE two critical phases:

1. BEFORE CoreClean (during Rebuild):
The Razor SDK's globbing automatically adds *.js files in wwwroot to Content.
During Rebuild, Clean deletes these files but Content items still exist
(they come from SDK glob patterns, not the files themselves).
If we don't remove them before Clean, ResolveProjectStaticWebAssets will
try to process Content items pointing to deleted files and fail.

2. BEFORE ResolveProjectStaticWebAssets (during normal Build):
Prevents duplicate discovery - we register TypeScript outputs manually
via DefineStaticWebAssets, so we don't want the SDK to also discover
them via Content items.

The manifest file is read to know which files were previously registered.
The manifest itself is cleaned up by CoreClean via the FileWrites item group.
-->
<Target Name="RemoveTypeScriptFromContentBeforeSwaDiscovery"
DependsOnTargets="ResolveTypeScriptStaticWebAssetsConfiguration"
BeforeTargets="CoreClean;ResolveProjectStaticWebAssets">

<!-- Read the manifest to know what TypeScript files were registered -->
<ReadLinesFromFile File="$(_TypeScriptSwaManifestPath)"
Condition="Exists('$(_TypeScriptSwaManifestPath)')">
<Output TaskParameter="Lines" ItemName="_RegisteredTypeScriptAssets" />
</ReadLinesFromFile>

<!-- Remove those from Content to prevent duplicate discovery and stale references -->
<ItemGroup>
<Content Remove="@(_RegisteredTypeScriptAssets)" />
</ItemGroup>

</Target>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -538,7 +538,7 @@ Copyright (c) .NET Foundation. All rights reserved.
</Copy>

</Target>

<Target Name="_BuildCopyStaticWebAssetsIfDifferent"
AfterTargets="_SplitStaticWebAssetsByCopyOptions">

Expand Down Expand Up @@ -786,4 +786,6 @@ Copyright (c) .NET Foundation. All rights reserved.

<Import Project="Microsoft.NET.Sdk.StaticWebAssets.HtmlAssetPlaceholders.targets" Condition="'$(OverrideHtmlAssetPlaceholders)' == 'true'" />

<Import Project="Microsoft.NET.Sdk.StaticWebAssets.TypeScript.targets" Condition="'$(EnableTypeScriptNuGetTarget)' == 'true'" />

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@
<_Parameter1>DefaultTestBaselinePackageVersion</_Parameter1>
<_Parameter2>5.0</_Parameter2>
</AssemblyAttribute>
<AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute">
<_Parameter1>MicrosoftTypeScriptMSBuildPackageVersion</_Parameter1>
<_Parameter2>$(MicrosoftTypeScriptMSBuildPackageVersion)</_Parameter2>
</AssemblyAttribute>
</ItemGroup>

<ItemGroup>
Expand Down
Loading
Loading