Start a new topic

using TypeScript definition files to generate C# bindings/wrappers for javascript libraries

It's great that now we can use our C# classes in typescript, as version 1.0 made it availablefor us.

I'm wondering that could we use the typescript definition files to generate C# bindings/wrappers?  It would made many javascript libraries and frameworks (aurelia, angular, ...) available/accessible in our C# code. 


What dou you think about this? 


That is what I made it for. P.S. I have a new version 2.0.https://github.com/michaelcheers/TypeScriptToCS2

Here is the file generated by the new one with some DuoCode modifications but it still prints template which I don't think is valid in DuoCode. What is the DuoCode equivalent of Bridge's template?

zip

Check out my new project: TypescriptToCS2.

I'm not maintaining this anymore, I'll add you to the collaborators list.

Hi, I haven't been checking this page, sorry.

Hey Michael... Are you still around. I wanna show you my new TypeScript Parser (Compiler) that i was inspired to create after working so much on your ReadTypeScriptFile parsing implementation.


The was REALLY great work. But not being able to work with you, i had no choice but to create my own.

The BIGGEST problem with the ReadTypeScriptFile function is the actual TEXT based parser where you use an index to go up and down the string array look for keywords and characters like '[' or '{' or '(' or ',' and so on... That made it really hard to parse COMPLEX anonymous parameter types when it was NOT the last one... Hard to find the train '}' to end the bracket in '{' when it had a bunch of crap in between.


So i decided to just use typescript and create a typescript compiler using the new TypeScript Compiler API... Very Kool... You can actually compile typescript and do what ever you want with the compiled Abstract Syntax Tree. Take a look a how easy it is to create the compiler:


 

/** Parse Typescript Definitions File(s) */
function parseDefinitions(filenames: string[], outfile:string = "classes.xml", resolve:boolean = true, library:boolean = false, logger:(data:any)=>void = null): void {
    if (logger == null) logger = console.log;
    logger('TypeScript Syntax Tree Compiler - Version: ' + ts.version);
    logger("Version 1.0");
    logger("");

    let output:string = "";
    let modules:ts.Node[] = [];
    let program:ts.Program = null;
    let checker:ts.TypeChecker = null;
    let options:ts.CompilerOptions = { target: ts.ScriptTarget.Latest, module: ts.ModuleKind.CommonJS, declaration: true, noLib: false, noImplicitAny: false, noEmitHelpers:true, allowJs: true, noResolve: !resolve }

    /** Read all module nodes */
    if (filenames!= null && filenames.length > 0) {
        program = ts.createProgram(filenames, options);
        checker = program.getTypeChecker();
        for (let sourceFile of program.getSourceFiles()) {
            const src:string = path.basename(sourceFile.fileName);
            if (src === "lib.d.ts") {
                if (library) {
                    logger("Library: " + src);
                    modules.push(sourceFile);
                    ts.forEachChild(sourceFile, visitModule);
                }
            } else {
                logger("Source: " + src);
                modules.push(sourceFile);
                ts.forEachChild(sourceFile, visitModule);
            }
        }
    }

    /** Parse namespace node items */
    logger("Modules: " + modules.length.toString());
    if (modules != null && modules.length > 0) {
        output += "<Namespaces>\n";
        for (let moduleNode of modules) {
            if (moduleNode.kind === ts.SyntaxKind.SourceFile) {
                const srcNode:ts.SourceFile = <ts.SourceFile>moduleNode;
                const srcName:string = path.basename(srcNode.fileName).toLowerCase() + ".globals";
                logger("Parsing source: " + srcName);
                output += ("\t<Namespace type=\"global\" name=\"" + srcName + "\" comments=\"none\">\n");
                parseModule([srcNode]);
                output += "\t</Namespace>\n";
            } else if (moduleNode.kind === ts.SyntaxKind.ModuleDeclaration) {
                const modSymbol:SymbolInfo = getNodeInfo(<ts.ModuleDeclaration>moduleNode);
                const modName:string = getNodeFullname(moduleNode);
                logger("Parsing module: " + modName);
                output += ("\t<Namespace type=\"module\" name=\"" + modName + "\" comments=\"" + modSymbol.docs + "\">\n");
                parseModule(getModuleBlocks([moduleNode]));
                output += "\t</Namespace>\n";
            }
        }
        output += "</Namespaces>\n";
    }

    /** Write the result file */
    if (outfile == null || outfile === "") outfile = "classes.xml"; 
    fs.writeFileSync(outfile, output);
    logger("Done");
    logger("");
    return;
}

 

That is the main entry point to my compiler... Take a look at the AST for JQuery (Attached)...


EVERY piece of info i could extract about each relevant ts.Node parse into a xml file. Including the JsDoc style comments. Since i am using the compiler to create the AST i can take advantage of things like triple slash file reference resolve... It will basically WALK the chain of WHATEVER is referenced in ANY typescript file in the chain... You can even parse regular typescript source and include any 'strongly' classes native javascript.


Now i can produce FLAWLESS parsed syntax tree xml that includes the entire api BUT formatted in a .NET friendly XML. For example i walk the syntax tree and gather ALL nested typescript modules and fully namespace qualify EACH module into the proper hierarchy ... Namespaces, Classes, Interfaces, Vars, Functions, Calls, Indexers, Types (Include TypeRefernce, FunctionType, UnionType, String UnionType... etc)


NOW ALL YOU GOTTA DO... :)


Is create a ParseSyntaxTreeFile that does the SAME exact thing ReadTypeScriptFile is doing EXCEPT your will parsing the XML file (using System.Xml.XmlDocument to easily traverse the xml nodes) and take EACH 'Type String Value' from the xml node instead for scanner a string look for commas and things. This will create a MUCH cleaner List<NamespaceDefinitions> that contains EVERY thing.


Take a look at the xml... is the whole library ran flawless.

xml
(274 KB)

Note: Im still a little worried about that ENDLESS while loop in the GenericRead.


Its just 'while(true)' always scares me a bit when i see that in code;

You Almost got it... You just need the comma in between generic types:


AnotherWorker<TU> SHOULD BE AnotherWorker<T, U>


Take a look of at this TypeScript:


 

declare namespace Tester { 
	interface TestFx {
	    fx: {
	        tick: () => void;
	        /**
	         * The rate (in milliseconds) at which animations fire.
	         */
	        interval: number;
	        stop: () => void;
	        speeds: { slow: number; fast: number; };
	        /**
	         * Globally disable all animations.
	         */
	        off: boolean;
	        step: any;
	    };
	}
	interface TestWorker {
		test(): string|boolean;
	}
	interface TestWorker<T> {
		test(): T|Object;
	}
	interface AnotherWorker<T, U> {
		another(): U|TestWorker<T>;
	}
}



And this is from your latest Master:


 

using System;
using Bridge;
using Bridge.Html5;
using Bridge.WebGL;
using any = System.Object;
using boolean = System.Boolean;
using Function = System.Delegate;
using RegExp = Bridge.Text.RegularExpressions.Regex;
using number = System.Double;
using Number = System.Double;


namespace Tester
{
	[External]
	public delegate void tickDelegate ();
	[External]
	public delegate void stopDelegate ();
	[ObjectLiteral]
	public class JSONSpeedsInterface : SpeedsInterface
	{
		public extern number Slow { get; set; }
		public extern number Fast { get; set; }
	}
	[External]
	public interface SpeedsInterface
	{
		[FieldProperty]
		number Slow { get; set; }
		[FieldProperty]
		number Fast { get; set; }
	}
	[ObjectLiteral]
	public class JSONFxInterface : FxInterface
	{
		public extern tickDelegate Tick { get; set; }
		public extern number Interval { get; set; }
		public extern stopDelegate Stop { get; set; }
		public extern SpeedsInterface Speeds { get; set; }
		public extern boolean Off { get; set; }
		public extern any Step { get; set; }
	}
	[External]
	public interface FxInterface
	{
		[FieldProperty]
		tickDelegate Tick { get; set; }
		[FieldProperty]
		number Interval { get; set; }
		[FieldProperty]
		stopDelegate Stop { get; set; }
		[FieldProperty]
		SpeedsInterface Speeds { get; set; }
		[FieldProperty]
		boolean Off { get; set; }
		[FieldProperty]
		any Step { get; set; }
	}
	[ObjectLiteral]
	public class JSONTestFx : TestFx
	{
		public extern FxInterface Fx { get; set; }
	}
	[External]
	public interface TestFx
	{
		[FieldProperty]
		FxInterface Fx { get; set; }
	}
	[ObjectLiteral]
	public class JSONTestWorker : TestWorker
	{
	[External]
	public delegate Any<string, boolean> testDelegate ();

		public extern Any<string, boolean> Test ();
		public extern testDelegate test { get; set; }
	}
	[External]
	public interface TestWorker
	{
		Any<string, boolean> Test ();
	}
	[ObjectLiteral]
	public class JSONTestWorker<T> : TestWorker<T>
	{
	[External]
	public delegate Any<T, Object> testDelegate ();

		public extern Any<T, Object> Test ();
		public extern testDelegate test { get; set; }
	}
	[External]
	public interface TestWorker<T>
	{
		Any<T, Object> Test ();
	}
	[ObjectLiteral]
	public class JSONAnotherWorker<TU> : AnotherWorker<TU>
	{
	[External]
	public delegate Any<U, TestWorker<T>> anotherDelegate ();

		public extern Any<U, TestWorker<T>> Another ();
		public extern anotherDelegate another { get; set; }
	}
	[External]
	public interface AnotherWorker<TU>
	{
		Any<U, TestWorker<T>> Another ();
	}

} 

  


Since you ALMOST got Complex Generics working without my code... I will download your latest master (once you fix the comma thing) and apply some of the other fixes to the DuoCode Version and Either Pull request or just send you the whole project and you can do whatever you want with it.

It memory out because of the while loop in GenericRead goes on forever... I have a version (modified from yours of course)... But take a look at my SkipToEndOfWord implementation:


First i let the 1 read of SkipToEndOfWordget BOTH the word and wheres:


  

		internal static SkippedWord SkipToEndOfWord(string tsFile, ref int index)
		{
			var result = new SkippedWord();
			if (!char.IsLetter(tsFile, index))
				SkipEmpty(tsFile, ref index);
			if (tsFile[index] == '[')
				return result;
			for (; index < tsFile.Length; index++) {
				var item = tsFile[index];
				if (char.IsLetterOrDigit(item) || item == '[' || item == ']' || item == '<' || item == '>' || item == '_' || item == '.' || item == '$')
					result.Word += item;
				else {
					if (result.Word.EndsWith("]") && !result.Word.EndsWith("[]")) {
						index--;
						result.Word = result.Word.Substring(0, result.Word.Length - 1);
					}
					result.Wheres = ReadGeneric(tsFile, ref index, ref result.Word);
					return result;
				}
			}
			result.Wheres = ReadGeneric(tsFile, ref index, ref result.Word);
			return result;
		}

		internal static Dictionary<string, string> ReadGeneric(string tsFile, ref int index, ref string word)
		{
			SkipEmpty(tsFile, ref index);
			Dictionary<string, string> whereTypesExt = new Dictionary<string, string>();
			if (word.Contains('<') && !word.Contains('>')) {
				index -= word.Length - word.IndexOf('<');
				word = word.Substring(0, word.IndexOf('<'));
				int marker = CloseGeneric(tsFile, index);
				if (marker > index) {
					string insides = tsFile.Substring(index + 1, (marker - index) - 1);
					word += "<";
					string[] targs = insides.Split(',');
					foreach (string targ in targs) {
						if (targ.IndexOf("extends", StringComparison.Ordinal) >= 0 || targ.IndexOf("implements", StringComparison.Ordinal) >= 0) {
							string buffer = targ.Replace("extends", "|").Replace("implements", "|");
							string[] items = buffer.Split('|');
							string types = items[0].Trim();
							string wheres = items[1].Trim();
							whereTypesExt.Add(types, wheres);
							word += (types.Trim() + ", ");
						} else {
							word += (targ.Trim() + ", ");
						}
					}
					word = word.TrimEnd(',', ' ');
					word += ">";
					index = (marker + 1);
					SkipEmpty(tsFile, ref index);
					// Support Arrays
					char array1 = tsFile[index];
					char array2 = tsFile[index + 1];
					if (array1.Equals('[') && array2.Equals(']')) {
						word += "[]";
						index += 2;
						SkipEmpty(tsFile, ref index);
					}
				} else {
					throw new Exception("Invalid Generic Type Syntax Detected For Word: " + word);
				}
			}
			return whereTypesExt;
		}

		internal static int CloseGeneric(string tsFile, int index)
		{
			int marker = -1;
			for (; index < tsFile.Length; index++) {
				char check = tsFile[index];
				if (check.Equals('>')) {
					marker = index;
					break;
				}
			}
			return marker;
		} 

  

Then used like:


 

							SkippedWord skipName = SkipToEndOfWord(tsFile, ref index); // Mackey Kinard
							string name = skipName.Word;
                            if (string.IsNullOrEmpty(name))
                                goto default;
							var wheres = skipName.Wheres; //GenericRead(tsFile, ref index, ref name);

 

Works pretty good... I made several other small changes and fixes to get working:


Attaches is the entire JQuery API mapped (For DuoCode) with Complex Generics And Everything




cs

I am back at a place where I can get internet freely.

Just commited complex generic fix to master (not NewChanges). (I'm not using your code because I need your changes to the ReadTypeScriptFile as it doesn't currently use SkippedWord.

Simple delegates work in my version it's just that the multiple generics on JQueryPromiseOperator out of memory it.

The no name things sucks... Because some things won't have names like these delegates... Don't know how to tell what should be a delegate and where it should go ... on the class of global:


 

/**
	 * Interface for the JQuery promise/deferred callbacks
	 */
	interface JQueryPromiseCallback<T> {
	    (value?: T, ...args: any[]): void;
	}

	interface JQueryPromiseOperator<T, U> {
	    (callback1: JQueryPromiseCallback<T>|JQueryPromiseCallback<T>[], ...callbacksN: Array<JQueryPromiseCallback<any>|JQueryPromiseCallback<any>[]>): JQueryPromise<U>;
	}

 

Login or Signup to post a comment