microsoft / TypeScript

TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
https://www.typescriptlang.org
Apache License 2.0
101.34k stars 12.54k forks source link

How do you do a straight TS emit? #1651

Closed ctaggart closed 9 years ago

ctaggart commented 9 years ago

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?

DanielRosenwasser commented 9 years ago

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:

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.

ctaggart commented 9 years ago

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

DanielRosenwasser commented 9 years ago

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

ctaggart commented 9 years ago

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);
DanielRosenwasser commented 9 years ago

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.

ctaggart commented 9 years ago

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.

ctaggart commented 9 years ago

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

mhegazy commented 9 years ago

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!

ctaggart commented 9 years ago

@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

DanielRosenwasser commented 9 years ago

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.

ctaggart commented 9 years ago

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.

/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.

DanielRosenwasser commented 9 years ago

@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.

ctaggart commented 9 years ago

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