dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.26k stars 9.96k forks source link

Please bring back the typescript enabled CRA template #4375

Closed mlorbetske closed 4 years ago

mlorbetske commented 5 years ago

From @theconnectionist on December 1, 2018 15:17

I love using Visual Studio and the dotnet core platform. Played with the CRA and typescript template last year and came back to it to start creating typescript enabled react apps. I love the way the dotnet core platform combines client and server in one package, but now I have to choose between that great convenience or having typescript built in.

I know an expert can start with the typescript less template and add typescript to it, but this is not something easy or advisable for a novice.

Typescript is touted as a good practice for creating Javascript apps, yet the creator and owner of Typescript drops support for it in their flagship development platform. Please reconsider this decision. Shouldn't be a big deal to create a CRAT template so people looking for Typescript have that choice.

Dropping this support makes me wonder if Typescript will go the way of the dinosaur. Maybe we shouldn't bother coming up to speed on it; perhaps look for alternatives such as Flow?

Copied from original issue: dotnet/templating#1712

poke commented 5 years ago

I’m not entirely sure what you mean with “CRA”. Are you referring to create-react-app?

Anyway, with the SPA templates (both React and Angular), there isn’t actually that much magic going on. The client application templates are bootstrapped using the respective command line tool (create-react-app or angular-cli) and then just fleshed out to show off a few more features.

There is no ASP.NET Core specific stuff within the ClientApp folder though. That means that you can easily remove the files there and recreate the app using the CLI tool. So if you want to use a TypeScript based React template, you could just delete the ClientApp folder and recreate it using the create-react-app --typecript option. You are completely free to do whatever you want within the ClientApp folder, so you can just follow your own client-side development workflow there.

That of course also means that you can just add TypeScript using the official manual to an existing project.

As for providing this through .NET templates directly, you should know that maintaining templates is pretty time-consuming. So the team favors maintaining a few templates while leaving the system flexible enough that you can just scaffold your own projects using other tools.

Know that this decision has no impact on TypeScript on its own. While ASP.NET Core is a large project, it is separate from the modern client-side development, which currently does embrace TypeScript a lot.

ghost commented 5 years ago

There is one big gotcha with using a TS CRA in the ClientApp folder, which is that VS2017 has issues with tsconfig.json files that live in a sub directory rather than the project root.

A big part of that is it has trouble resolving that the tsconfig.json and often thinks there is no tsconfig.json file. So it tries to use a default configuration, which includes compiling TS files and producing JS files. This gets messy and is a headache to navigate/manage.

The way we worked around this is by opening ClientApp in VSCode and only editing the ClientApp in VSCode. That way the tsconfig.json file is in the root of the project, and VSCode picks it up fine. I suspect you could do something similar with VS2017+VS2017, rather than VS2017+VSCode, but I haven't tried that myself.

@poke I think this way of working is along the lines of what you were indicating with:

You are completely free to do whatever you want within the ClientApp folder, so you can just follow your own client-side development workflow there.

But it seems important to point out that while you can "follow your own client-side development workflow there", using a single instance of VS2017 is not something that will work. I suspect using a single IDE instance will be the first thing that devs will try, and that leads to quite a bit of pain while figuring all this out for themselves.


As a side note, we actually took this "separation" a step further, and we have ASP.Net Core acting as a proxy to the CRA DevServer, rather than hosting and running the CRA DevServer itself.

     app.UseSpa(spa =>
     {
         spa.Options.SourcePath = "ClientApp";

         if (env.IsDevelopment())
         {
             // Use this line if you want ASP.Net Core to run the Create-React-App development server.
             //spa.UseReactDevelopmentServer(npmScript: "start");

             // Use this line if you want to manually run the Create-React-App development server.
             spa.UseProxyToSpaDevelopmentServer("http://localhost:3000");
         }
     });

Initial builds with Webpack can take "some time" (we were seeing ~20s for smallish projects) and waiting that long every time a change was made (requiring restarting the ASP.Net Core Server) was painful. By having ASP.Net Core acting as a proxy we can restart the ASP.Net Core server without having to restart the CRA DevServer, and the wait time returns to "as long as it takes to compile the ASP.Net Core Project itself".

Our final setup is along the lines of:

It works well, but unfortunately it's a bit too complex to neatly fit in to a simple template. Maybe the next version of VS will have better support for this nested project way of working.

Finally, I'm not an expert on this stuff, I have no affiliation with anyone/any organisation that creates the tech in this stack, I'm just an "end-user" dev who's been working through these issues to get a working setup. I love VS and ASP.Net Core, and I want to use them as much as possible. I love React and CRA, and I want an easy way to use them. I could be totally wrong, and a single VS instance could work, but I couldn't get it to work, and this is what I ended up with.

poke commented 5 years ago

For what it’s worth, the ASP.NET Core Angular template uses the default Angular CLI setup and has a tsconfig.json located at /ClientApp/tsconfig.json and I am able to edit the Angular application just fine from within Visual Studio (I just tested that).

I don’t know what you are doing with your React application that this does not work but it should work fine. That being said, I do consider VS Code more powerful for editing client side applications, so using that for your development might be a “better” way anyway.

I’d suggest you to make sure that you have not checked the “Compile on Save” option in Visual Studio though (Text Editor → JavaScript/Typescript → Project). Compilation is done by the npm-invoked tooling, so VS shouldn’t be involved in compiling the TypeScript anyway.

ghost commented 5 years ago

Thank you for the reply. It's taken me a little while to get back to you, but I wanted to do some investigation to make sure I could offer more than "I remember it doesn't work, but I don't have specifics".

I do like VSCode a lot, and the Chrome debugging seems more reliable so I'm pretty happy with what we have, and I'll likely stick with our setup. But it's good to know that it should work, and it would be interesting to know what I've done wrong!

I checked "Compile on Save" and none of the options there are selected. Good thought, but no dice.

I went back and looked at some previous projects I've worked with while trying to find a nice workflow for all this. I came across a smaller project ("Small Project") which didn't have much in it, but it did indeed work as you said it should. That perplexed me a bit, so I went and dug out the larger project ("Problematic Project") I'd worked with that I knew was having this issue.

I went to Project Properties >> TypeScript Build tab in the problematic project and all the controls were enabled. i.e. it hasn't "found" the tsconfig.json. In the small project this tab was mostly disabled with a message of "One or more tsconfig.json files detected. Project properties are disabled.".

Having a working project and a not working one, I did a quick "mental diff" of the csproj files and noticed that the small project had <ItemGroup> <Content Include="ClientApp\tsconfig.json" /> </ItemGroup>, where are the problematic project does not. That's a useful lead!

I added <ItemGroup> <Content Include="ClientApp\tsconfig.json" /> </ItemGroup> to the problematic project and ... no difference. Which is a shame, because this "felt" like it was the problem, and would solve it.

A few moments more of "mental diffing" and quiet contemplation, and I realized that I'd put <ItemGroup> <Content Include="ClientApp\tsconfig.json" /> </ItemGroup> before <ItemGroup> <Content Remove="$(SpaRoot)**" /> [...] </ItemGroup>. $(SpaRoot) resolves to ClientApp so the second line was removing what the first line was doing.

I moved <ItemGroup> <Content Include="ClientApp\tsconfig.json" /> </ItemGroup> to be after the second line, and ... it works!

The TypeScript Build tab now correctly shows "One or more tsconfig.json files detected. Project properties are disabled.".

I went back and looked at the code repository. And the commit that added the problematic project didn't have the <ItemGroup> <Content Include="ClientApp\tsconfig.json" /> </ItemGroup> line in it. At a guess CRA added the tsconfig.json file, and VS didn't know that, so it didn't add the required line. But I don't really know. There's no smoking gun.

As an experiment I just made a new project using .Net Core 2.2 and the React template. I updated CRA to 2.1.1, added TS, it generated the tsconfig.json, and the line wasn't added to the csproj.

Again, guessing, but I think the Angular template might already have that line by default because it's so TS-centric. The React one doesn't, and doesn't know it needs to be added.

Looking at the TypeScript Build tab in the "fresh" project showed it couldn't find the tsconfig.json, so I'm pretty sure that's the root cause.

poke commented 5 years ago

So, I’ve just created a new ASP.NET Core React application using dotnet new react and then I manually migrated to TypeScript by updating the dependencies, particularly react-scripts, and then renamed all .js files to .tsx. Upon npm start, it then automatically detected that I have migrated to TypeScript and created a tsconfig.json for me.

Opening the project in Visual Studio then, I was able to successfully edit TypeScript files with full IntelliSense support and with full error reporting in the error list. There was no compilation to .js files done by Visual Studio.

theconnectionist commented 5 years ago

Thank you folks. Really helpful. I'll try this. As mentioned in my opening post, I'm new to React and Typescript and asp.net, so the best place to start was a reliable boilerplate to get started. Adding one or two simple changes as poke suggested works too.

Thank you all for your experience and insights.

ghost commented 5 years ago

@poke Thanks for trying it out. I can confirm that renaming one of the existing files to TSX does not generate a JS file.

However, if I right click the project and add a new TS file, and put export class Foo { } in it, then a JS file is generated by VS.

Looking at the project properties it's clear that VS is not picking up the tsconfig.json.

image

image

  <PropertyGroup>
    <TargetFramework>netcoreapp2.2</TargetFramework>
    <TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
    <TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
    <IsPackable>false</IsPackable>
    <SpaRoot>ClientApp\</SpaRoot>
    <DefaultItemExcludes>$(DefaultItemExcludes);$(SpaRoot)node_modules\**</DefaultItemExcludes>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.App" />
    <PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" />
  </ItemGroup>

  <ItemGroup>
    <!-- Don't publish the SPA source files, but do show them in the project files list -->
    <Content Remove="$(SpaRoot)**" />
    <None Remove="$(SpaRoot)**" />
    <None Include="$(SpaRoot)**" Exclude="$(SpaRoot)node_modules\**" />
  </ItemGroup>

  <ItemGroup>
    <None Remove="ClientApp\src\file.ts" />
    <None Remove="ClientApp\src\index.tsx" />
  </ItemGroup>

  <ItemGroup>
    <TypeScriptCompile Include="ClientApp\src\file.ts" />
    <TypeScriptCompile Include="ClientApp\src\index.tsx" />
  </ItemGroup>

  <Target Name="DebugEnsureNodeEnv" BeforeTargets="Build" Condition=" '$(Configuration)' == 'Debug' And !Exists('$(SpaRoot)node_modules') ">

If I add that extra line to the csproj (<ItemGroup> <Content Include="ClientApp\tsconfig.json" /> </ItemGroup>) then VS does pick up the tsconfig.json, and adding a ts file doesn't result in a JS file.

image

image

  <PropertyGroup>
    <TargetFramework>netcoreapp2.2</TargetFramework>
    <TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
    <TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
    <IsPackable>false</IsPackable>
    <SpaRoot>ClientApp\</SpaRoot>
    <DefaultItemExcludes>$(DefaultItemExcludes);$(SpaRoot)node_modules\**</DefaultItemExcludes>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.App" />
    <PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" />
  </ItemGroup>

  <ItemGroup>
    <!-- Don't publish the SPA source files, but do show them in the project files list -->
    <Content Remove="$(SpaRoot)**" />
    <None Remove="$(SpaRoot)**" />
    <None Include="$(SpaRoot)**" Exclude="$(SpaRoot)node_modules\**" />
  </ItemGroup>

  <ItemGroup>
    <None Remove="ClientApp\src\file.ts" />
    <None Remove="ClientApp\src\index.tsx" />
  </ItemGroup>

  <ItemGroup>
    <TypeScriptCompile Include="ClientApp\src\file.ts" />
    <TypeScriptCompile Include="ClientApp\src\index.tsx" />
  </ItemGroup>

  <ItemGroup>
    <Content Include="ClientApp\tsconfig.json" />
  </ItemGroup>

  <Target Name="DebugEnsureNodeEnv" BeforeTargets="Build" Condition=" '$(Configuration)' == 'Debug' And !Exists('$(SpaRoot)node_modules') ">

The tsconfig.json has "noEmit": true, in it, which blocks the compile when VS does pick up the tsconfig.json.

ghost commented 5 years ago

Here's the repro steps that more similarly follow your previously stated steps:

And the fix steps:

EDIT:

Out of curiosity I tried it with a dotnet new angular project too.

I expected <ItemGroup> <Content Include="ClientApp\tsconfig.json" /> </ItemGroup> to be in the csproj, but it wasn't.

I repeated the steps adding file.ts with an exported class and I saw the same issue.

image

The angular tsconfig.json has "compileOnSave": false, rather than "noEmit": true,, but the end result should be the same ... no .js and .js.map files.

mkArtakMSFT commented 4 years ago

We're closing this as we have no plans for doing this.