typescriptmsbuild

Are there sources for the Microsoft.TypeScript.MSBuild nuget available?


I can't find the source of the nuget package https://www.nuget.org/packages/Microsoft.TypeScript.MSBuild and underlaying TypeScript.Tasks.dll.

Are they open sourced?


Solution

  • Microsoft.TypeScript.MSBuild is a closed source library (at the time of writing this), despite the misleading link to microsoft/TypeScript mentioned in the comments above. However, it is not obfuscated and hence can be inspected with IlSpy or similar tools.

    I've developed an open-source replacement BlazingTs (nothing to do with Blazor, targets netstandard2.0), motivated by getting rid of some extra stuff Microsoft.TypeScript.MSBuild does when scanning the solution for TS files, resulting in tsconfig.json not being used correctly in hot starts sometimes.

    The part with MSBuild step BlazingTs does is very similar to what Microsoft.TypeScript.MSBuild is doing, if you don't feel like opening IlSpy, it's the closest to the source code of it I can link to.

    The gist of it essentially:

    1. In /build define YourProj.targets; This will be picked by a convention.
    2. This file allows us to inject MSBuild steps. We need to run tsc.js early in the process. There are multiple steps we could do that in, we've used BeforeBuild (see the table of predefined steps)
    3. To execute tsc.js we need to invoke node. We could either supply it or expect the host to have one already. Both BlazingTs and Microsoft.TypeScript.MSBuild do the latter.
    4. We also need tsc.js and friends. In YourProj.csproj add a link to a folder we will ship our copy of the compiler in:

    Example:

    <Content Include="tools\typescript\**\*">
      <Pack>true</Pack>
      <PackagePath>tools\typescript</PackagePath>
    </Content>
    
    1. in tools\typescript we copy whatever the result of npm i typescript would add to packages. The folder will include /bin, /lib, and a bunch of loose files. See here.
    2. To invoke tsc we can use a simple shell script (tsc2.sh, tsc2.cmd in BlazingTs), this will be placed in the same folder:

    Example:

    #!/bin/bash
    
    # Get the directory where the script is located
    SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
    LOG_FILE="$SCRIPT_DIR/typescript.log"
    
    # Initialize log file
    echo "[TypeScript] Starting compilation..." > "$LOG_FILE"
    
    # Determine Node executable
    if [ -f "$SCRIPT_DIR/node" ]; then
        NODE_EXEC="$SCRIPT_DIR/node"
    else
        NODE_EXEC="node"
    fi
    
    echo "[TypeScript] Using Node from: $NODE_EXEC" >> "$LOG_FILE"
    
    # Version check
    VERSION_LINE=$("$NODE_EXEC" "$SCRIPT_DIR/bin/tsc" --version)
    echo "[TypeScript] $VERSION_LINE" >> "$LOG_FILE"
    
    # Transpilation
    echo "[TypeScript] Running tsc with arguments: $@" >> "$LOG_FILE"
    "$NODE_EXEC" "$SCRIPT_DIR/bin/tsc" "$@" >> "$LOG_FILE" 2>&1
    
    EXIT_CODE=$?
    if [ $EXIT_CODE -eq 0 ]; then
        echo "[TypeScript] Compilation completed successfully" >> "$LOG_FILE"
    else
        echo "[TypeScript] Compilation failed with error code $EXIT_CODE" >> "$LOG_FILE"
    fi
    
    exit $EXIT_CODE
    

    For cross-platform compatibility, one would add an equivalent script for Windows or whatever other target.

    1. Finally we invoke the script from the added MSBuild step:

    Example:

    <PropertyGroup>
        <TypeScriptToolsPath>$([MSBuild]::NormalizePath('$(MSBuildThisFileDirectory)', '..', 'tools', 'typescript'))</TypeScriptToolsPath>
        <TypeScriptLogFile>$([MSBuild]::NormalizePath('$(TypeScriptToolsPath)', 'typescript.log'))</TypeScriptLogFile>
        <IsWindows Condition="'$(OS)' == 'Windows_NT'">true</IsWindows>
        <IsWindows Condition="'$(OS)' != 'Windows_NT'">false</IsWindows>
        <TypeScriptCompilerScript Condition="'$(IsWindows)' == 'true'">"$(TypeScriptToolsPath)\tsc2.cmd"</TypeScriptCompilerScript>
        <TypeScriptCompilerScript Condition="'$(IsWindows)' != 'true'">$(TypeScriptToolsPath)/tsc2.sh</TypeScriptCompilerScript>
    </PropertyGroup>
    
    <UsingTask TaskName="ReadTypeScriptLog" TaskFactory="RoslynCodeTaskFactory"
               AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
        <ParameterGroup>
            <LogFile ParameterType="System.String" Required="true" />
        </ParameterGroup>
        <Task>
            <Using Namespace="System.IO"/>
            <Code Type="Fragment" Language="cs">
                <![CDATA[
          if (File.Exists(LogFile))
          {
              string content = File.ReadAllText(LogFile);
              Log.LogMessage(MessageImportance.High, "TypeScript Build Log:");
              Log.LogMessage(MessageImportance.High, "------------------------");
              Log.LogMessage(MessageImportance.High, content);
              Log.LogMessage(MessageImportance.High, "------------------------");
          }
        ]]>
            </Code>
        </Task>
    </UsingTask>
    
    <Target Name="MakeTypeScriptShellScriptExecutable" BeforeTargets="CustomCompileTypeScript" Condition="'$(IsWindows)' != 'true'">
        <Exec Command="chmod +x &quot;$(TypeScriptCompilerScript)&quot;" />
    </Target>
    
    <Target Name="CustomCompileTypeScript" BeforeTargets="BeforeBuild">
        <Message Text="----- Starting TypeScript compilation via BlazingTs -----" Importance="high" />
    
        <Delete Files="$(TypeScriptLogFile)" Condition="Exists('$(TypeScriptLogFile)')" />
    
        <Exec
                Command="$(TypeScriptCompilerScript) --project &quot;$(ProjectDir)tsconfig.json&quot;"
                WorkingDirectory="$(TypeScriptToolsPath)"
                IgnoreExitCode="true"
        />
    
        <ReadTypeScriptLog LogFile="$(TypeScriptLogFile)" />
    
        <Message Text="----- Finished TypeScript compilation via BlazingTs -----" Importance="high" />
    </Target>