Use Web API as a dynamic runtime Typescript compiler

Strath

October 8th, 2012

Use Web API as a dynamic runtime Typescript compiler

Because you don't need to precompile Typescript

There’s been a lot of community buzz about Typescript recently – and rightfully so – as it solves a lot of Javascript’s issues, and despite being in its infancy, shows a tremendous amount of potential already.

One of the features (or side effects, depending on how you look at it) of Typescript, is that your are required to compile your code, to produce JS output. Sure, it is possible to dynamically compile Typescript in the browser (using its JS compiler), but that requires you to reference Typescript.js which is roughly 250 kB, and might be a tough pill to swallow.

However, using the ASP.NET Swiss army knife called Web API, and an approach we already disuussed on this blog before, let me show you how you can quite smoothly leverage on Web API pipeline to dynamically compile your Typescript code into Javascript at runtime.

More after the jump.

The idea

So, as mentioned, instead of compiling Typescript manually each time you change anything, we will let Web API handle this for us – using a custom MediaTypeFormatter.

All you need to do is reference the JS script through a specifically pre-configured Web API route/controller, and let the Web API pipeline do the heavy lifting via a MediaTypeFormatter.

The route and a the controller

We would like to be able to reference our dynamically compiled Typescript files from the HTML like this:

To do that, let’s start with a typical MVC4, Web API project.

We need a custom route, and a simple controller:

The route allows us to pass a name parameter and an extension (to match the filename.js path we required), and the controller simply forwards the name of the file to the formatter which will do all the work.

The plugin

In order to make this work, we need the command-line Typescript compiler. Grab it from the official website. It’s the middle one (plugin) – it says Visual Studio 2012, but that doesn’t matter if you have 2010 – we are really interested in the command line tool anyway.

Once installed, you can find the TCS.exe, the command line Typescript compiler under C:\Program Files (x86)\Microsoft SDKs\TypeScript\0.8.0.0\. Let’s copy all the compiler files over to our solution, to the TS folder under the root of our project (or anywhere else, but then you need to update the folder references I used in my code).

The formatter/compiler

Note that the code shown here should be adapted to fit your specific needs (such as persistence mechanisms, error handling and what not) – I doodled this while watching Sunday Night Football so it is by no means perfect – but hopefully points you in a right direction.

Before we move on to writing to the stream, which is the heart of everything, notice that we set a few defaults here – we add UriPathExtensionMapping so that the formatter kicks in for all .js requests. All the output produced will be returned as application/javascript as that’s how browsers expect to get their JS files. We support requests for text/html, as that’s how some of the browsers would issue them.

We only support serializing (one way formatter only, no deserializing) and only objects of type string (since really all we are interested in, are the paths to the files).

WriteToStreamAsync

The MediaTypeFormatter method that writes to the stream will behave accordingly:
1. Take the name of the file (Typescript file)
2. Check if the TS file exists
3. Look into the cache – if the reference to the TS file exists there, use the MD5 checksum to make sure the file TS hasn’t changed since last read
4a. If it hasn’t, return the contents from the cache
4b. If it has or if it didn’t exist in the cache in the first place, use tcs.exe (which we copied to our website root earlier) to compile the Typescript file and return JS
5. Cache the latest JS output and the MD5 checksum of the TS for later

This is a very similar technique we used in the post a couple of weeks ago – invoking a command line application from C#. The good thing is that we only do it once – I chose to cache the result in memory, but there are several approaches you could take, i.e return a path to the JS file that has been produced by the TCS compiler. In principle, the main thing we want to do is to make sure that by using MD5 we do not unnecessarily invoke the compiler – instead we only do that when the source Typescript file changes.

Notice that we use the StandardError property of the TCS Process to determine whether the compiler ran successfully. If there are any compilation errors, this property will contain the details; otherwise it will stay empty.

Trying it

Let’s imagine I have a demo TS file, called demo.ts:

I can now reference it in my HTML like this (recall our routes from earlier) – using same name, only with .js extension (even though such Javascript does not physically exist – all we are interested in is hitting the controller and getting into the Web API pipeline):

Now the Web API will compile this on the fly and produce the expected JS output in the browser – and remember, we didn’t compile the Typescript at all:

The JS is now cached, and all subsequent requests do not result in recompilation as well.
I can now use the JS any way I would like to:

If I change the Typescript code to something else – i.e. let’s modify the sing method:

I don’t need to recompile anything, simply refresh the page – now the MD5 checksums don’t match anymore so it causes TS to recompile and the new version of JS is returned (and cached for future use):

Summary

As mentioned, Typescript can be compiled using purely raw Javascript – and that would be a nice option if you are ready to swallow the additional 250 kB of JS to be included in your page/app (by the way, if someone is interested in how to do that, let me know and I can blog about that).

However, with some Web API hackiness (is that even a word?), using some of the techniques we discussed on this blog before, it is really easy to provide a dynamic runtime compilation of your Typescript code!

Be Sociable, Share!

  • Pingback: The Morning Brew - Chris Alcock » The Morning Brew #1205

  • Chouteau

    Juste énorme ! I’m fan !

  • Pingback: Dew Drop – October 8, 2012 (#1,417) | Alvin Ashcraft's Morning Dew

  • niu tech

    Have you tried my TypeScript Compile project from https://github.com/niutech/typescript-compile ? It does the same but much simplier, just by adding two JS files. Take a look at the demo.

    • Filip W

      Yeah, like I mentioned, the only problem with that is you need to reference typescript source (~250kb) and have the re-compile process run upon every request.

      • niu tech

        Yes, but you download the js files only once and you can gzip them, subsequent requests will use a browser cache. And compilation in TypeScript Compile is one time as well, the compiled js code is saved in a sessionStorage cache.

  • http://greatrexpectations.com Steve Greatrex

    Would it be possible to integrate this with the bundling & minification features?

  • http://twitter.com/joeriks Jonas Eriksson

    Works good, thanks for sharing! Some questions: 1. the mediaformatter needs to be instantiated on appstart, right? i.e. GlobalConfiguration.Configuration.Formatters.Add(new TypeScriptMediaTypeFormatter()); 2. In what namespace&dll is ObjectCache? I could not find it. 3. Tiny typo: tsc.exe to “TS folder” should be “TSC folder”

  • http://twitter.com/s0nica Valerio

    Bookmarked, as usually :)