20

I'm trying to write a function in C# that takes in a string containing typescript code and returns a string containing JavaScript code. Is there a library function for this?

Cœur
  • 37,241
  • 25
  • 195
  • 267
mpen
  • 272,448
  • 266
  • 850
  • 1,236
  • You could automate the command line compiler... – SWeko Dec 26 '12 at 21:02
  • Could have a look at: [this answer](http://stackoverflow.com/a/3182411/1344760) – RichardTowers Dec 26 '12 at 21:08
  • @SWeko: I'm looking at `tsc` but I don't see anything that will take in TS and output JS. It only works with files as far as I can see. – mpen Dec 26 '12 at 21:09
  • @RichardTowers: Huh? What's that got to do with TypeScript? I don't see a single mention of TS. – mpen Dec 26 '12 at 21:10
  • Sorry, that was a bit obtuse. The typescript compiler compiles to JavaScript, if you want to run it from C# then one approach would be to use a JS interpreter that you can run from the CLR/DLR. – RichardTowers Dec 26 '12 at 21:12
  • 3
    Well, save to ts file -> compile to js file -> File.ReadAllText... – SWeko Dec 26 '12 at 21:18
  • @RichardTowers: I don't need to run it in C#, just compile :-) – mpen Dec 26 '12 at 21:23
  • @SWeko: I need to write it to a temporary file every time I want to compile TypeScript? I'm going to be doing this a lot, I don't want to be hitting my HDD every time. – mpen Dec 26 '12 at 21:23
  • It's not a smart solution, but it's a solution. I'm very interested in a possible answer to this myself. – SWeko Dec 26 '12 at 21:28
  • There's also a similar thread [in the TypeScript forums](http://typescript.codeplex.com/discussions/397810). – RichardTowers Dec 26 '12 at 21:43
  • GitHub issues [here](https://github.com/Microsoft/TypeScript/issues/1226) and [here](https://github.com/Microsoft/TypeScript/issues/2118) – mpen Mar 07 '15 at 00:42

3 Answers3

13

You can use Process to invoke the compiler, specify --out file.js to a temporary folder and read the contents of the compiled file.

I made a little app to do that:

Usage

TypeScriptCompiler.Compile(@"C:\tmp\test.ts");

To get the JS string

string javascriptSource = File.ReadAllText(@"C:\tmp\test.js");

Full source with example and comments:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                // compiles a TS file
                TypeScriptCompiler.Compile(@"C:\tmp\test.ts");

                // if no errors were found, read the contents of the compile file
                string javascriptSource = File.ReadAllText(@"C:\tmp\test.js");
            }
            catch (InvalidTypeScriptFileException ex)
            {
                // there was a compiler error, show the compiler output
                Console.WriteLine(ex.Message);
            }

            Console.ReadKey();
        }
    }

    public static class TypeScriptCompiler
    {
        // helper class to add parameters to the compiler
        public class Options
        {
            private static Options @default;
            public static Options Default
            {
                get
                {
                    if (@default == null)
                        @default = new Options();

                    return @default;
                }
            }

            public enum Version
            {
                ES5,
                ES3,
            }

            public bool EmitComments { get; set; }
            public bool GenerateDeclaration { get; set; }
            public bool GenerateSourceMaps { get; set; }
            public string OutPath { get; set; }
            public Version TargetVersion { get; set; }

            public Options() { }

            public Options(bool emitComments = false
                , bool generateDeclaration = false
                , bool generateSourceMaps = false
                , string outPath = null
                , Version targetVersion = Version.ES5)
            {
                EmitComments = emitComments;
                GenerateDeclaration = generateDeclaration;
                GenerateSourceMaps = generateSourceMaps;
                OutPath = outPath;
                TargetVersion = targetVersion;
            }
        }

        public static void Compile(string tsPath, Options options = null)
        {
            if (options == null)
                options = Options.Default;

            var d = new Dictionary<string,string>();

            if (options.EmitComments)
                d.Add("-c", null);

            if (options.GenerateDeclaration)
                d.Add("-d", null);

            if (options.GenerateSourceMaps)
                d.Add("--sourcemap", null);

            if (!String.IsNullOrEmpty(options.OutPath))
                d.Add("--out", options.OutPath);

            d.Add("--target", options.TargetVersion.ToString());

            // this will invoke `tsc` passing the TS path and other
            // parameters defined in Options parameter
            Process p = new Process();

            ProcessStartInfo psi = new ProcessStartInfo("tsc", tsPath + " " + String.Join(" ", d.Select(o => o.Key + " " + o.Value)));

            // run without showing console windows
            psi.CreateNoWindow = true;
            psi.UseShellExecute = false;

            // redirects the compiler error output, so we can read
            // and display errors if any
            psi.RedirectStandardError = true;

            p.StartInfo = psi;

            p.Start();

            // reads the error output
            var msg = p.StandardError.ReadToEnd();

            // make sure it finished executing before proceeding 
            p.WaitForExit();

            // if there were errors, throw an exception
            if (!String.IsNullOrEmpty(msg))
                throw new InvalidTypeScriptFileException(msg);
        }
    }

    public class InvalidTypeScriptFileException : Exception
    {
        public InvalidTypeScriptFileException() : base()
        {

        }
        public InvalidTypeScriptFileException(string message) : base(message)
        {

        }
    }
}
BrunoLM
  • 97,872
  • 84
  • 296
  • 452
  • 1
    This is probably the best solution, although I really wish I could keep it all in memory (input and output). – mpen Jul 08 '13 at 22:32
  • 2
    I face with a 'File not found' with a Win32Exception, but my path for my TS file is correct. I think it's tha path to the TSC compiler which is not correct. Have you faced with this issue one time ? EDIT: I specify directly the path for my TSC compiler, now I have this message : "The specified executable is not a valid application for this operating system platform." – Christophe Gigax Jul 07 '17 at 08:35
5

Perhaps you could use a JavaScript interpreter like JavaScriptDotNet to run the typescript compiler tsc.js from C#.

Something like:

string tscJs = File.ReadAllText("tsc.js");

using (var context = new JavascriptContext())
{
    // Some trivial typescript:
    var typescriptSource = "window.alert('hello world!');";
    context.SetParameter("typescriptSource", typescriptSource);
    context.SetParameter("result", "");

    // Build some js to execute:
    string script = tscJs + @"
result = TypeScript.compile(""typescriptSource"")";

    // Execute the js
    context.Run(script);

    // Retrieve the result (which should be the compiled JS)
    var js = context.GetParameter("result");
    Assert.AreEqual(typescriptSource, js);
}

Obviously that code would need some serious work. If this did turn out to be feasible, I'd certainly be interested in the result.

You'd also probably want to modify tsc so that it could operate on strings in memory rather than requiring file IO.

Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
RichardTowers
  • 4,682
  • 1
  • 26
  • 43
  • Oh..you meant run the `tsc` compiler via JavaScriptDotNet, not the output. Still quite a roundabout way of doing it, but in theory I guess it should work... – mpen Dec 26 '12 at 21:48
  • 2
    Yep, sorry for not being clearer. It's difficult talking about three languages at once! – RichardTowers Dec 26 '12 at 21:50
3

The TypeScript compiler file officially runs on either node.js or Windows Script Host - it is written in TypeScript itself (and transpiled to JavaScript). It requires a script host that can access the file system.

So essentially, you can run TypeScript from any language as long as you can wrap it in a script engine that supports the file system operations required.

If you wanted to compile TypeScript to JavaScript purely in C#, you would end up writing a C# clone of the compiler.

Fenton
  • 241,084
  • 71
  • 387
  • 401