Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Write compiled output to stdout #1226

Closed
jbowens opened this issue Nov 20, 2014 · 43 comments
Closed

Write compiled output to stdout #1226

jbowens opened this issue Nov 20, 2014 · 43 comments
Labels
Declined The issue was declined as something which matches the TypeScript vision Suggestion An idea for TypeScript

Comments

@jbowens
Copy link

jbowens commented Nov 20, 2014

It doesn't seem possible to compile and output to stdout. There was some discussion on CodePlex before the migration to github: https://typescript.codeplex.com/workitem/600

@mhegazy
Copy link
Contributor

mhegazy commented Nov 20, 2014

Would a node package of the compiler with an API that allows you to pass strings and get strings back do the job?

@jbowens
Copy link
Author

jbowens commented Nov 20, 2014

That'd definitely be useful, but I'd much rather have an option to output to stdout in the cli.

@mhegazy
Copy link
Contributor

mhegazy commented Nov 20, 2014

so what happens if you have multiple outputs? how do you indicate file begin and end? and where do errors go? or should it just be a JSON.stringify output?

@mhegazy mhegazy added the Suggestion An idea for TypeScript label Nov 20, 2014
@jbowens
Copy link
Author

jbowens commented Nov 20, 2014

I think it'd be reasonable to only support --out functionality when printing to stdout, so it would print the concatenation of all outputs. Errors should be written to stderr.

It'd also be nice if it could take input through stdin, so tsc could be used in a series of commands using unix pipes.

@DanielRosenwasser
Copy link
Member

If you're on a Unix system, you can look into mkfifo as a temporary workaround. I _think_ the following might work.

mkfifo -m 600 my_fifo
tsc file1.ts file2.ts --out my_fifo
YOUR_COMMAND_HERE < my_fifo
# or...
cat my_fifo | YOUR_COMMAND_HERE

but I can admittedly see the utility in having standard output as a target.

@RyanCavanaugh RyanCavanaugh added In Discussion Not yet reached consensus Needs More Info The issue still hasn't been fully clarified and removed In Discussion Not yet reached consensus labels Nov 26, 2014
@RyanCavanaugh
Copy link
Member

Open questions:

  • What should we call the flag?
  • What happens to external module emit? We currently emit those to their own file even under --out. It might be desirable to be compiling a single external module under this flag, so we wouldn't want to error, but we may also end up compiling more than one external module (and thus emitting more than one file).

@DanielRosenwasser
Copy link
Member

Another question: how do you differentiate between output and message output? I guess we could use standard error as well.

@mklement0
Copy link

What should we call the flag?

Following @jbowens's suggestion to invariably apply --out behavior, there doesn't have to be a new flag: the Unix (POSIX) convention of using - to represent stdin or stdout (as in this case), could be applied: tsc --out - ....

While there's a hypothetical backward-compatibility concern -- currently, tsc literally creates a file named -, if you do that -- I don't think anyone would miss it.

As for the "Unix-centricness": I wouldn't expect Windows users to object, either.

@mnpenner
Copy link

mnpenner commented Mar 7, 2015

I wanted this functionality for compiling little inline snippets of TypeScript within my templates which I could inject back into <script> tags. I wouldn't use it for anything that generated multi-file output.

@mhegazy
Copy link
Contributor

mhegazy commented Mar 8, 2015

have you considered typestring?

@nicksloan
Copy link

I agree with @mklement0's proposal. This would be very helpful.

@RyanCavanaugh It seems like you should treat stdout the same as if any filename was passed to the out argument. That will cover the majority of use cases anyway.

The rationale for this is wanting to pipe the output to something else, for example a minifier or gzip or ng-annotate or whatever. I think it is likely that people will generally be using this against single files, and using downstream tools to concatenate or whatever.

@ghost
Copy link

ghost commented Apr 25, 2015

I agree. You can see the behavior of related node packages such as node-sass, less, coffeescript. Based on the commonalities, I propose:

tsc blah.ts

to print to stdout instead of carrying out the unwanted/unintended IO op with presumed destination name "blah.js"..

tsc blah.ts anotherBlah.js

should write to anotherBlah.js without requiring the --out flag.

There was a proposal somewhere in node community to provide some kind of standard CLI interface for transpiler and linter packages. That would have prevented everyone inventing their own way.

@DanielRosenwasser
Copy link
Member

I propose:

tsc blah.ts

to print to stdout instead of carrying out the unwanted/unintended IO op with presumed destination name "blah.js"

That is a major breaking change in behavior, so we wouldn't be able to do that.

@nicksloan
Copy link

Perhaps this would be best implemented as a wrapper package.

@mnpenner
Copy link

@nicksloan How would you implement it as an external tool? Have tsc write to a temporary file, read it back in, and dump it to stdout? I could do that myself, but I don't want the overhead of an intermediate file.

I think they should just add an --stdout option. Just have it the throw an error if it needs to generate multiple output files.

If you need further control, a Node package as @mhegazy suggested would be ideal. AFAIK, gulp-typescript and other tools have to invoke a separate process and build up a command-line string to do anything. I've hit the maximum command length with other similar tools on many occasions.

@DanielRosenwasser
Copy link
Member

How would you implement it as an external tool?

@teppeis has made typescript-simple which could be used for this purpose. You could also use our compiler API.

@nicksloan
Copy link

@DanielRosenwasser I assumed there would be a way to get at the compiler API, and it looks like you guys have made it easy enough. Typescript-simple makes it even easier. Either way, this is a perfectly viable approach.

I wonder if the Typescript team would be open to bundling such a thing as an alternative executable within Typescript once it showed some degree of maturity?

@mhegazy
Copy link
Contributor

mhegazy commented Apr 28, 2015

Supporting emitting to stdout by default would have been a good idea a few years back when tsc first came out; but now changing the behavior is a huge breaking change that we can not justify to our customers.

bundling another tool that does mostly what tsc does except slightly different would not be something we would pursue either.

I do not see any harm in taking a dependency on another package that wraps tsc or some of its functionality (e.g. typescript-simple). I think this is a decision you make based on your workflow and other tools you need to support. We have invested in making the API simple and usable to simplify such scenarios, and we will continue to invest into this space moving forward.

@sluijs
Copy link

sluijs commented Jun 28, 2015

Supporting emitting to stdout by default would have been a good idea a few years back when tsc first came out; but now changing the behavior is a huge breaking change that we can not justify to our customers.

@mhegazy Is it possible to add this to the 2.0 release? As npm evolves, I think more people will use npm scripts as a build-tool in the future and for that use-case piping is quite essential to reduce overhead.

@mhegazy
Copy link
Contributor

mhegazy commented Jun 29, 2015

@YoungRoger i would be new typescript-cli tool that does that, or possibly adding a commandline flag to support this if there is enough user demand.

@awerlang
Copy link

I can only see writing to stdout helpful if that works in watch mode too, but I have no idea how. Writing to stdout is a performance optimization, as is watch mode.

Use case:

tsc -out - -w | ng-annotate -a - > release.js

I can't think of an use case of reading from stdin.

Another question: how do you differentiate between output and message output? I guess we could use standard error as well.

Output errors to console: tsc
Redirect errors to a file: tsc 2> err.txt

@jbowens
Copy link
Author

jbowens commented Aug 24, 2015

@awerlang As I said before, reading from stdin and writing to stdout is useful for composing multiple commands to create a pipeline. It's not a performance optimization, it's a usability improvement.

@sebastien
Copy link

If typescript wants to be a good Unix citizen, outputting to stdout is a minimal requirement.

For reference, the following does not work, while it should at the very least be supported.

tsc --out /dev/stdout example.ts
error TS5033: Could not write file '/dev/stdout': ESPIPE, invalid seek

@mhegazy
Copy link
Contributor

mhegazy commented Sep 17, 2015

@sebastien i like this proposal. i think this will resolve the issue and is not a breaking change. i have filed #4841 to track it.

@mhegazy
Copy link
Contributor

mhegazy commented Sep 17, 2015

PRs for #4841 are also welcomed.

@mnpenner
Copy link

@mhegazy This won't resolve the issue for Windows users.

@mhegazy
Copy link
Contributor

mhegazy commented Sep 17, 2015

@mnpenner we have not had a complete proposal for this issue so far.

@mklement0
Copy link

@mnpenner, @mhegazy: Note that going with the POSIX convention of using - to represent stdout and/or stdin - would also work on Windows.

For instance, reading from stdin and writing to stdout would then take the form

tsc - --out -   # 1st '-' is stdin, 2nd '-' is stdout.

There is a hypothetical backward-compatibility issue, if there are people out there currently using files literally named -, but I don't think that's a real-world concern.
(Going forward, you could still use that filename by specifying ./- to disambiguate.)

As an added convenience, if the input comes from stdin, you could consider outputting to stdout by default, given that no meaningful output filename can be derived; this would:

  • allow convenient use in pipes; e.g., ... | tsc - | ...
  • enable the following shortcut for tsc foo.js --out -: tsc < foo.js

@sebastien
Copy link

Using - would be desirable, but in any case you should be able to write to/read from named pipes as well. I don't get why tsc is seeking on the output file, opening with write mode should be enough.

@mklement0
Copy link

@sebastien Agreed. Fortunately, it looks like the behavior is an incidental byproduct of tsc using Node.js's fs.writeFileSync() function to write output files. (Why the Node.js function behaves that way, I don't know).

A naïve fix that implements - as representing stdout would be (file tsc.js):

        function writeFile(fileName, data, writeByteOrderMark) {
            if (writeByteOrderMark) {
                data = "\uFEFF" + data;
            }
            if (fileName === '-') {
                process.stdout.write(data, "utf8");
            } else {
                _fs.writeFileSync(fileName, data, "utf8");
            }
        }

A potential problem is that the Node.js documentation states that process.stdout is non-blocking when writing to pipes on Windows, unlike on Unix-like systems - I'm not sure what the implications are.

P.S.: Here's a simple test that you can run in the Node.js REPL to reproduce the problem:

require('fs').writeFileSync('/dev/stdout', 'hello');   # breaks as of Node.js v0.12.7

Interestingly, Linux reports error 'ESPIPE, invalid seek', while OSX reports 'ENXIO, no such device or address'.

@stroborobo
Copy link

So this issue is not really about writing the compiled output to stdout anymore, that was fixed by #5454. This is about making it nice to use, so reading from stdin, writing errors to stderr and accepting - shortcuts, is that right?

stroborobo added a commit to stroborobo/TypeScript that referenced this issue Dec 16, 2015
Following the POSIX guideline 13:

> For utilities that use operands to represent files to be opened for
> either reading or writing, the '-' operand should be used only to
> mean
> standard input (or standard output when it is clear from context
> that
> an output file is being specified).

http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap12.html#tag_12_02

See issue microsoft#1226
@sebastien
Copy link

That would be a nice addition, given that it's standard practice in UNIX . So yes, - should be aliased to stdin for input and stdout for output.

@mhegazy
Copy link
Contributor

mhegazy commented Jan 5, 2016

@sebastien with #5454 --outFile /dev/stdout works on *NIX systems.

@mhegazy mhegazy added Needs Proposal This issue needs a plan that clarifies the finer details of how it could be implemented. and removed Needs More Info The issue still hasn't been fully clarified labels Feb 20, 2016
@josh-endries
Copy link

👍

--outFile - on Windows please (neither - nor CON work with 1.8.9).

@pitaj
Copy link

pitaj commented Nov 27, 2017

This issue hasn't had any activity for quite some time. I think that Windows should be supported better, as *NIX can only output to stdout at this point (by specifying --out /dev/stdout).

It seems like the best solution to this, which has been proposed in a few locations (1, 2), is to allow a special value - to --out and --outFile which instructs tsc to, instead of writing to a file, writes to stdout.

Example usage:

tsc src/compiler/binder.ts --out -
tsc src/compiler/binder.ts --outFile -

If a compiler error occurs, the errors will be appended after the emitted output.

I've worked on a very simple implementation for this issue which is essentially just the following in compiler/sys/writeFile

// If `-` is provided as the fileName, output to stdout
if (fileName === '-') {
  process.stdout.write(data, "utf8");
  return;
}

I'm wondering how to author tests for a change like this, and what kind of documentation changes need to happen, and where those go. Thanks.

@sebastien
Copy link

This would definitely be the expected behaviour from a Unix perspective.

@sploders101
Copy link

Using --outfile=/dev/stdout works now, but if there is an error on compilation, the error is printed in-place of/alongside the compiled output. This, again, is not standard practice and can cause issues when scripting. To work around this, I use the following.

(tsc -m system ./path/to/file.ts --outfile /dev/stderr 3>&2 2>&1 1>&3-) | someOtherCommand

Because the errors are printed on stdout, I'm telling tsc to print code on stderr, and then switching the file descriptors on it, resulting in errors on stderr, and code on stdout.

Hope this helps someone!

@RyanCavanaugh RyanCavanaugh added Declined The issue was declined as something which matches the TypeScript vision and removed Needs Proposal This issue needs a plan that clarifies the finer details of how it could be implemented. labels Aug 21, 2019
@RyanCavanaugh
Copy link
Member

Declining this one - outFile compilations (which is the only place this makes sense) are definitely getting increasingly rare and it's straightforward enough to use ts.transpileModule if you need this in a scripting context of some sort.

@timreichen
Copy link

timreichen commented Mar 24, 2020

This becomes a necessity again. I am currently experimenting with deno. As you know deno and typescript go hand in hand. Node modules on the other hand not really. So ts.transpileModule is not an option at the moment. This issue stops some creative workflow rn.
Why would the result code not be piped to stdout if no --outFile is declared?

@gh-andre
Copy link

gh-andre commented Apr 5, 2021

Printing to stdout is a basic function for most command line tools. I'm not quite sure why there's such resistance to implement this. It's nice to have DevOps scripts written in TypeScript, but having to set up their own build directories for a CI pipeline is cumbersome and unnecessary because generated JS files are transient and are only needed to complete whatever they are intended to do. On Linux this works:

tsc --module none --outFile /dev/stdout some-script.ts | node

, but it is not portable. Yes, ts.transpileModule works, but I have to copy this same script from a project to a project and maintain any changes to this file across projects.

Mind boggling.

hlizard pushed a commit to hlizard/QuickJS that referenced this issue Dec 2, 2021
…mment);

用F:\i686-4.9.2-release-win32-dwarf-rt_v4-rev4\mingw32\bin编译出可正常执行的tsc.exe, F:\i686-8.1.0-release-posix-dwarf-rt_v6-rev0\mingw32\bin编译的不行
@k0pernikus
Copy link

Given a ts file:

var foo = {
    "id": 13,
    "name": "horst",
    "cars": [{
            "brand": "VW",
            "maxSpeed": 120,
            "isWastingGazoline": true
        }]
};

one can get the generated JavaScript to stdout as @gh-andre pointed out:

tsc --module none --outFile /dev/stdout sample.json.ts
var foo = {
    "id": 13,
    "name": "horst",
    "cars": [{
            "brand": "VW",
            "maxSpeed": 120,
            "isWastingGazoline": true
        }]
}

Unfortunately, it doesn't work for getting the declarations onto stdout:

tsc --emitDeclarationOnly --declaration --module none --outFile /dev/stdout sample.json.ts                                                                                                                                                                                error TS5033: Could not write file '/dev/stdout.d.ts': EACCES: permission denied, open '/dev/stdout.d.ts'.


Found 1 error.

I am interested in getting the typescript declaration from a json file

declare const foo: {
    id: number;
    name: string;
    cars: {
        brand: string;
        maxSpeed: number;
        isWastingGazoline: boolean;
    }[];
};

@Soberia
Copy link

Soberia commented May 19, 2022

@gh-andre

tsc --module none --outFile /dev/stdout some-script.ts | node

This doesn't work if script.ts contains ES Modules import/export syntax

npx tsc --target esnext --outFile /dev/stdout --module none --moduleResolution node script.ts | node --input-type module

@gh-andre
Copy link

@Soberia I struggled with this and other things TypeScript for a bit and then moved my DevOps scripts to JavaScript. Far from ideal, but I cut down DevOps scripts enough to justify some duplication between the main TypeScript app and DevOps scripts. Will probably need to revisit it this year, as both codebases grow, but so far it's simpler to manage for my use cases.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Declined The issue was declined as something which matches the TypeScript vision Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests