Introduction
I write this tutorial primarily to demonstrate how to quickly create a simple application with support for npm, Webpack, and TypeScript based on an initial ASP.NET Core application template (which will run debugging from Visual Studio).
Motivation
I’m pretty new to the web and JavaScript in particular. However, I work with .Net constantly. Also, I believe that knowing JavaScript and all sorts of different JavaScript-based frameworks is essential. To test just another library, usually, you need to install a bunch of stuff so that it could actually work.
Usually, you need a server (via node.js), npm, and something like Webpack (for Less or some template engine/minifier). I dug into ASP.NET a little (not Core, but the traditional one) and roughly imagine how to deal with the server part, but the frontend is a dark forest for me.
The problem is that the technologies on the web usually focus not on ASP.NET, but on node.js, php, and other mainstream stuff. Examples in the documentation are mostly based on the latter ones, and it’s not that easy for an uninitiated person to set up all this from scratch for ASP.NET.
In this tutorial, I provide a step-by-step guide for the creation of a simple working application from a new template project on ASP.NET, that will include:
- npm – required for installation of various libraries in JavaScript, it is required everywhere.
- Webpack – required if JavaScript and other content needs to be packaged, minified, if you need to use Less instead of corny CSS, HTML template engines, JavaScript transpilers and the like.
- TypeScript – it can be used as a transpiler from newer versions of JavaScript to old ones. You can also use the TypeScript language itself. You get two in one, plus it’s a sin not to use it since Visual Studio supports it well.
Being able to quickly create such an application, you can readily learn any web technologies (including npm itself, Webpack, and TypeScript), while the server will be on its own .NET.
At the moment, ASP.NET already has the built-in mechanisms similar to those in Webpack. Also, the out-of-the-box Studio (at least, VS 2017) works with TypeScript and compiles it automatically if you create a config file for the TypeScript- a compiler.
But all these things are supported by ASP.NET developers, not the web community.
The standard TypeScript compiler in this tutorial is turned off, and the one that is the module for npm is used.
Preparation
To get started, you need to download and install the following:
- Visual Studio 2017 Community – When installing components, select everything that is related to ASP.NET and Core
- Node.js and npm – Node.js is required to run npm and the Webpack script when building a project in Visual Studio. Download and install the latest version of node.js, npm will be installed with it.
Creating project
Open Visual Studio, create a new project according to the standard ASP.NET Core Web Application (.NET Core) template. The template is in Templates – Visual C# – Web. If there is no such template, you probably do not have any components installed (you need to run Visual Studio installer again and install them).
Now we have a starting project, that should be compiled and run.
If git is used, we should add the .gitignore file to the folder with the solution (initialize the git repository from there). The .gitignore file for solutions in Visual Studio can be taken from here.
Simplify sample project
Next, we will change the project to make it as simple as possible.
Let’s make Hello World out of it.
Delete the following files:
- json (and its inner .bowerrc), bundleconfig.json
- all contents of the wwwroot You can leave favicon.ico.
- the Views folder – delete all its contents, except Views/Home/Index.cshtml
In the Controllers/HomeController.cs file, replace the contents with the following code in the HomeController class:
public IActionResult Index() => View(); public string Error() => "Error";
In fact, everything is being removed here except the Index() and Error() methods, but Error() now returns the “Error” string instead of the view (since we previously deleted the file with this view).
The final touch is to replace the contents of the Views/Home/Index.cshtml file using the following script:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Index</title> </head> <body> <h1>Hello world</h1> </body> </html>
All right, now we have a basic simplified project. It can be compiled and run, the page will display the text “Hello world”.
Adding npm
Add the package.json file to the project
{ "version": "1.0.0", "name": "yourappname", "private": true }
Enter the project name in lowercase.
npm can write to the npm-debug.log log. We should exclude it from the project, so it won’t disturb us. You can do this either by opening the context menu in the Studio and selecting Exclude From Project. Or, if it is not yet created (most likely, it is not), you can edit the .csproj project file by adding the following statement to the root tag <Project>:
<ItemGroup> <None Remove="npm-debug.log" /> </ItemGroup>
So, we have the npm support, Studio now shows the “npm” node in the project dependencies.
If you build a project (and when you open a project, modify the package.json file, etc.), the process of restoring the npm dependencies will start, given that they are specified (now they are not). The dependencies are downloaded to the node_modules folder in the project folder (the Studio does not take this folder as a part of the project).
Adding Webpack
Webpack is an npm dependency, so add it to package.json as a dependency. Since it will not be used in the client-side JavaScript code, it is declared as dev dependency. For us, this means that it will be an additional compilation tool for Visual Studio, it will be neither on the server nor on the client. The client will have the content (including JavaScript code) that Webpack will generate.
In addition to Webpack itself, we will also need two things that will make our work easier (however, they are not required):
- A plugin to Webpack that will clean up the folder in which Webpack will create files (the folder will be referred to as wwwroot/bundle in this tutorial).
- A Visual Studio extension that allows you to run Webpack every time you build an ASP.NET application from the Studio automatically.
First, download and install the extension for Visual Studio. It can be found by the name “NPM Task Runner” or downloaded here.
Now add the following lines to package.json (add inside the root curly braces):
"devDependencies": { "webpack": "^2.5.1", "clean-webpack-plugin": "^0.1.16" }, "scripts": { "webpack-script": "webpack" }, "-vs-binding": { "BeforeBuild": [ "webpack-script" ] }
In “devDependencies“, we specified the Webpack and the plugin to clean up the content it generates.
In “scripts“, we specified a script named “webpack-script” that will run Webpack (At this point, it will generate content, translate the code, etc.). The installed Visual Studio extension makes this script visible to Studio as a task that can be executed. So, we can schedule this task to run when building the application.
In “-vs-binding“, we specified that Visual Studio should call the “webpack-script” task (which the Studio now sees thanks to the installed extension) each time before building the project.
Now you need to configure the Webpack itself. It is configured using webpack.config.js, which will be executed via node.js at the time of calling the webpack-script task. Add the webpack.config.js file to the project and fill it with the following code:
"use strict" { // Required to form a complete output path let path = require('path'); // Plugin for cleaning up the output folder (bundle) before creating a new one const CleanWebpackPlugin = require('clean-webpack-plugin'); // Path to the output folder const bundleFolder = "wwwroot/bundle/"; module.exports = { // Application entry point entry: "./Scripts/main.js", // Output file output: { filename: 'script.js', path: path.resolve(__dirname, bundleFolder) }, plugins: [ new CleanWebpackPlugin([bundleFolder]) ] }; }
We’ve set up the input and output paths to JavaScript files and also specified the previously added plugin to clean up the output folder.
The output webpack file generates based on the input one. We still do not have the input file, so we need to create it. Create the Scripts/main.js file with the following content:
document.getElementById("helloworld").innerText = "Hello world from script";
The input file will not be available to a user since it is located in the Scripts folder, not wwwroot, the output file generated by the Webpack will go into the wwwroot/bundle/ folder and will be accessible to the client.
On the client side, the output file will be located in ~/bundle/script.js, where ~ is the address of the site on which the web application is running.
Let’s change the Views/Home/Index.cshtml file so that it includes the output file, and the script could change the text on the page (by the id of the “helloworld” element).
Replace the contents of the <body> tag with the following:
<h1 id="helloworld"></h1> <script src="~/bundle/script.js"></script>
If git is used, we need to exclude the files generated by Webpack. To do this, create another .gitignore file, but this time in the project’s folder (do not confuse with the solution’s folder)
# files generated by Webpack wwwroot/bundle/
Studio will show the .gitignore file in the project. To avoid this, in the .csproj file, add the following lines inside the <Project> root tag (or using the file context menu -> Exclude From Project)
<ItemGroup> <None Remove=".gitignore" /> </ItemGroup>
That’s all, now the project is fully configured to use npm and Webpack. If you compile and run the application, the page should display the text “Hello world from script”.
At this point, you can go ahead and install JavaScript libraries by declaring them in the “dependencies” section of package.json and use them in Scripts/main.js by connecting these libraries as modules using the require function (“library name”). For example, if we install the “jquery” library in this way, the Scripts/main.js file can be rewritten as follows (Note: this is just an example, you do not need to install jquery or modify main.js to continue)
var $ = require('jquery'); $("#helloworld").text("Hello world");
The only thing is that the input file will not work in Visual Studio. However, this can be fixed by adding the TypeScript support.
Adding TypeScript
First of all, you need to disable the standard TypeScript transcription. To do this, add the following lines inside the <Project> root tag in the .csproj file:
<PropertyGroup> <TypeScriptCompileBlocked>true</TypeScriptCompileBlocked> </PropertyGroup>
Next, you need to add TypeScript as the dev dependency for npm. Add the following code to the package.json file in the “devDependencies” section:
"typescript": "^2.3.2", "ts-loader": "^2.0.3"
Now you need to create a configuration file for the TypeScript compiler. Create the tsconfig.json file with the following contents:
{ "compilerOptions": { "noImplicitAny": false, "noEmitOnError": true, "removeComments": false, "alwaysStrict": true, "allowSyntheticDefaultImports": true, "lib": [ "dom", "es5", "es2015.promise" ], "allowJs": true, "target": "es5", "module": "es2015", "moduleResolution": "node", "sourceMap": true }, "include": [ "./Scripts/*" ], "compileOnSave": false }
The description of some values specified in this file:
- “allowJs“: true – enables translating from JavaScript to JavaScript (from the new version of the language to the old one. The extension of files .js)
- “target“: “es5” – the output version of JavaScript, into which TypeScript and the input JavaScript will be transcompiled
- “module“: “es2015” – the syntax for working with modules
- “moduleResolution“: “node” – module resolution strategy – same as in node.js
- “sourceMap“: true – enables data generation for debugging of TypeScript
- “include“: [“./Scripts/*“] – specify where to get the .ts and .js sources, which must be transcompiled in the output JavaScript
Next, you need to pair TypeScript with Webpack. To do this, replace the script file for the webpack.config.js Webpack configuration with the following (in fact, we change some portions, but to avoid writing all the changes, I post the full file):
"use strict" { // Required to form a complete output path let path = require('path'); // Plagin for cleaning up the output folder (bundle) before creating a new one const CleanWebpackPlugin = require('clean-webpack-plugin'); // Path to the output folder const bundleFolder = "wwwroot/bundle/"; module.exports = { // Application entry point entry: "./Scripts/main.ts", // Output file output: { filename: 'script.js', path: path.resolve(__dirname, bundleFolder) }, module: { rules: [ { test: /\.tsx?$/, loader: "ts-loader", exclude: /node_modules/, }, ] }, resolve: { extensions: [".tsx", ".ts", ".js"] }, plugins: [ new CleanWebpackPlugin([bundleFolder]) ], // Include the generation of debugging information within the output file // (Required for debugging client scripts) devtool: "inline-source-map" }; }
We changed the extension of the input script from .js to .ts, indicated that the input script should be passed via the TypeScript loader (loader: “ts-loader”) and did some other things.
And the last step is to rename the input script file from Scripts/main.js to Scripts/main.ts, the contents can be left the same.
Conclusion
That’s all! Now we have a working project on ASP.NET Core, which has npm, Webpack, and TypeScript, and it all works in one bundle.
Now the TypeScript debugging is available in Visual Studio, you can put a breakpoint in the input .ts file, start the project in the debug mode (you should use Chrome or Internet Explorer browsers, otherwise debugging will not work. Besides, it works in Chrome only when I explicitly refresh the page after loading it. Apparently, the debugger in Chrome does not connect at once). Note that the breakpoints are set in the input files, but the output code actually works (in the output code, webpack writes the information needed for mapping from the output to the input files as a comment).
You can create other input files in the Scripts folder, in the form of .ts or .js, and both of them will fully support the new EcmaScript standard (the output file will have the es5 standard). To attach additional input files, you need to make them modules and attach to main.ts via the import operator or the require () function.
Another small note is that the output files (those in the wwwroot/bundle/ folder) should not be excluded from the project via the .csproj file because if the Studio does not see them, the debugging of the input files stops working.
Tags: .net, asp.net core, visual studio Last modified: September 23, 2021
Nice article, so easy to get started, with clear instructions and explanation.
I had to change one line, for using jquery in typescript file
Replaced var $ = require(‘jquery’);
with import $ from “jquery”; in my main.ts file
Thank you!
Here you can check one more useful article written by Ilya Mikhalin about Simplifying Converters for WPF – http://codingsight.com/simplifying-converters-for-wpf/