9

I'm building a (simple) command line tool that writes a temporary typescript file to disk and then performs type checking on the file via tsc /tmp/foo/bar.ts --noEmit.

I would like to send the code to tsc via STDIN instead of writing a file in /tmp.

How can I run tsc --noEmit on code that is sent to STDIN?

Rick
  • 8,366
  • 8
  • 47
  • 76
  • 2
    can you use [ts-node](https://github.com/TypeStrong/ts-node#usage) (from https://stackoverflow.com/questions/44752995/transpile-a-typescript-file-in-memory/44753766#44753766)? – vike Feb 17 '20 at 22:26

3 Answers3

1

I tried ts-node as vike suggested. It worked fine but there's about a 1s startup cost to both ts-node and tsc. Creating all the files and compiling (tsc --noEmit *.ts) them all at once allowed me to pay startup cost that only once.

tldr;

I wanted to test the conformance of the 437 ShExJ JSON tests against @types/shexj. I cloned the tests and constructed trivial ts files that tested the type.

for f in *.json; do time (
  (
    echo -en "import {Schema} from 'shexj'\nconst x:Schema = " &&
    cat $f
  ) |
  ../node_modules/.bin/ts-node
); done

real    0m1,336s
user    0m2,919s
sys     0m0,121s

real    0m1,233s
user    0m2,586s
sys     0m0,101s

real    0m1,374s
user    0m2,979s
sys     0m0,127s

real    0m1,257s
user    0m2,692s
sys     0m0,096s
…

It was taking > 1.2s/file. I bypassed ts-node and went straight to tsc:

for f in *.json; do time (
  (
    echo -en "import {Schema} from 'shexj'\nconst x:Schema = " &&
    cat $f
  ) > ../ts/$f.ts &&
  tsc --noEmit ../ts/$f.ts
); done

real    0m2,909s
user    0m7,356s
sys     0m0,171s

real    0m2,735s
user    0m7,077s
sys     0m0,182s

real    0m2,669s
user    0m6,822s
sys     0m0,173s

real    0m2,670s
user    0m6,952s
sys     0m0,123s
…

This was taking > 2.6s/file. As slow was invoking ts-node individually was, it was still faster than invoking tsc individually. (I wonder how they managed that.)

I wrote them all out at once:

time (
  for f in 0*.json; do (
    echo -en "import {Schema} from 'shexj'\nconst x:Schema = " &&
    cat $f
  ) > ../ts/$f.ts;
  done
)

real    0m0,021s
user    0m0,013s
sys     0m0,009s

and compiled them at once:

time tsc --noEmit ../ts/*.ts

real    0m3,198s
user    0m8,424s
sys     0m0,148s

Extrapolating, this saved me 9.5 mins of full CPU.

ericP
  • 1,675
  • 19
  • 21
1

So I came across this problem today and found a decent way to fix it!

You do need to do some simple post-processing of the output, and it doesn't really use tsc directly but rather deno as a way to compile (without running the code! don't worry!), but it's certainly usable and decently fast.

Simply pipe the output to deno run -, a Deno command that takes input from stdin and runs it directly.

The trick is to wrap it in a function to keep it from being ran. I've added console.log((() => { and }).toString()) around the code I wish to transpile.

Here's what that looks like as Python code:

import subprocess
import json

# Just pass your TypeScript code in one end, and get compiled JavaScript!
def transpile(code: str) -> str:

    # Add padding to code
    padded_code: str = "console.log((() => {" + code + "}).toString())"

    # Compile with `deno run -`
    out_code: str = subprocess.getoutput(f'echo "{json.dumps(padded_code)}" | deno run -')

    # Remove arrow function padding
    final_code: str = out_code[6:-2]

    return final_code

Note: Turning type-checking on makes things a bit difficult for Deno's compiler, so it's very important to keep it off.

Abb
  • 11
  • 1
0

If type checking is not as important, you can also use swc with stdin: swc -f filename.ts.

Here's how to install & use:

pnpm install @swc/cli
echo "const a: number = 1" | swc -f index.ts

Output:

var a = 1;

Which can be piped to node:

$ echo "const a: number = 1; console.log(a)" | swc -f index.ts | node -
1
Duane J
  • 1,615
  • 1
  • 15
  • 22