microsoft/TypeScript

How do you do a straight TS emit?

ctaggart opened this issue · 13 comments

I want to be able to programmatically create a TS file and emit it.

@DanielRosenwasser mentioned last week:

It honestly may be better to do a straight TS emit if what you're looking for is definition file emit, if you're looking to use it as a verification pass for an intermediate language, or if you're looking to take advantage of our downlevel emit functionality.

How do do a straight TS emit?

In the Using the Compiler API it says:

Emitter: Output generated from a set of inputs (.ts and .d.ts) files can be one of: JavaScript (.js), definitions (.d.ts), or source maps (.js.map)

That is cool that you can emit a .d.ts definition. Is there any way to emit a .ts file? Is there anything that can "pretty print" a TS AST?

What I meant is that you shouldn't use our trees as a direct target, you should simply find a way to transform FunScript code to TypeScript, or emit straight to JavaScript.

Is there any way to emit a .ts file?

I'm not sure what you're trying to achieve. It depends what your input is. If your input is TypeScript, then yes, we have a formatter.

Is there anything that can 'pretty print' a TS AST?

Yes, but you'll need an actual TypeScript source file as input. As part of our services API, we provide the following functions:

  • getFormattingEditsForRange
  • getFormattingEditsForDocument
  • getFormattingEditsAfterKeystroke

I think what you might be looking for getFormattingEditsForDocument. Once you've gotten the appropriate edit ranges, you can easily apply them in reverse and fix up the original source text.

For this issue, I'm just interested in emitting TypeScript code using Node.js & TypeScript. services\formatting\formatting.ts does indeed look like it could be helpful. I'd really like to use:

function formatDocument(sourceFile: SourceFile, rulesProvider: RulesProvider, options: FormatCodeOptions): TextChange[];

It looks like you can pass in a SourceFile and FormatCodeOptions to get back a list of TextChange edits. Each TextChange has a span and the newText. I'm hoping I would then have the newText for the SourceFile. What could I add to typescript.d.ts to expose that function? In Jakefile definitionRoots I tried adding services/formatting/formatting.d.ts, but it showed up invalid and I'm not sure what the valid TypeScript should be instead of:

image

Why do you need that function? getFormattingEditsForDocument internally just calls that function and that is what we've chosen to expose.

Is there any easy way to apply the TextChanges returned by getFormattingEditsForDocument in order to get a string back with it formatted. A clearly wrong and poor attempt is:

https://github.com/ctaggart/TsAst/blob/format/app.ts#L67-L76

    var sf = services.getSourceFile("file1.ts");
    textChanges.forEach(tc => {
        var tcr: ts.TextChangeRange = {
            span: tc.span,
            newLength: tc.newText.length,
        }
        var b = sf.update(tc.newText, tcr);
        console.log('b.text: ' + b.text);
    });
    console.log('sf.text: ' + sf.text);

The most simple way is just to apply the changes in reverse:

function formatCode(orig: string, changes: TextChange[]): string {
    var result = orig;

    for (var i = changes.length - 1; i >= 0; i--) {
        var change = changes[i];
        var head = result.slice(0, change.span.start);
        var tail = result.slice(change.span.start + change.span.length)
        result = head + change.newText + tail;
    }

    return result;
}

Let me know if you have any more questions.

PS: I thought about it a bit more and I can see why you probably wanted to use formatDocument.

Thanks! Formatting works now.

My original goal was to be able to create the nodes from scratch and have a function that writes out the TypeScript source to a string. I reviewed src/compiler/emitter.fs and src/services/formatting/formatting.ts yesterday and witnessed what was mentioned:

I've spoken with others on the team; there are difficulties associated with dynamically creating an AST and using our emitter. For one thing, there are times in which we look back at the source text for certain operations, so you need to have a valid TypeScript corpus to base some checking/emit off of. I don't think an AST-to-AST transformation is going to work as very ideally.

Indeed, there are a few calls to getSourceTextOfNodeFromSourceFile which are problematic in emitter.fs for this. Emitter currently only supports .d.ts definition files and not regular .ts source files. I just thought I would ensure that it wasn't done already before trying to build something. I don't think it will be too hard to create a similar function to emitNode that returns formatted a string representing the Node passed in. Thoughts? I could be wrong. I would love to see this built in.

Cleaned up for the formatting code and put the example here. :)
http://blog.ctaggart.com/2015/01/format-typescript-with-v14-language.html

thanks @ctaggart for sharing. i have left a few comments. i am also working on cleaning up the host API so it can be easier to use. your feedback would be highly appreciated!

@mhegazy I made the corrections that you recommended. I also have a new blog post that shows how to call the format function from F#. :)
http://blog.ctaggart.com/2015/01/format-typescript-with-f.html

Hey, very cool! We appreciate you working on this and sharing it with us.

Completely unrelated, I realize you could write TypeScript code in your Edge app and use the compiler at runtime to generate the appropriate JavaScript. Huh.

Thank you guys! I really appreciate the move to GitHub. It is fun watching how quickly things progress on this project. The Compiler API is a great addition. My immediate need is to be able to generate TypeScript code for web clients when there is a defined web API contract (not necessary .NET Web API). I've created such clients in the past using printf style hacks, but I think using the AST is a more robust solution. The thing that I'm missing is a pretty printer from the TypeScript AST, but I'm hoping that isn't to hard for me to write.

Yes, there are several very cool possibilities when combining F# + Edge.js + TypeScript.

  • An F# Type Provider could be written that exposes types from .d.ts files. FunScript does this today via FParsec parsing of the file, but I think using TypeScript Compiler API would be a better solution.
  • An F# Type Provider could support inline TypeScript like Edge.js inlines JavaScript, but everything would be type safe.
  • F# programs can create TypeScipt code in order to create JavaScript code. I'd like to projects like FunScript more to this.

/cc @tjanczuk @alfonsogarciacaro

Hopefully this thread won't get too off track. :) The goal of this thread being how to emit or pretty print .ts code.

@ctaggart would you feel comfortable with us adapting your pretty printer for use on Using the Compiler API? Of course, we'd give attribution and link to your blog post as the original source.

Sure, feel free to adapt stuff from my blog. I don't have anything on pretty printing yet. My last blog was about using the Compiler API to do formatting:
http://blog.ctaggart.com/2015/01/format-typescript-with-v14-language.html