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

"tsc -w" will compile and watch, then how to run tsc in "watch-only" mode without compiling? #12996

Closed
yaooluu opened this issue Dec 17, 2016 · 42 comments
Labels
Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Suggestion An idea for TypeScript

Comments

@yaooluu
Copy link

yaooluu commented Dec 17, 2016

TypeScript Version: 2.0.3

Hi, I'm finding it confusing when using "tsc -w". From angular quickstart guide and almost every tutorial, I see similar things in package.json as the line below:

"start": "tsc && concurrently \"tsc -w\" \"lite-server\" "

Expected behavior:
The command should compile only once, and then "watch" and "start lite-server" at the same time.

Actual behavior:
The command compiles once first. Then "compile and watch" and "start lite-server" at the same time. The actual result is it compiled twice.

I'm finding this annoying especially when running with karma (which watches for JS file changes).

"test": "tsc && concurrently \"tsc -w\" \"karma start karma.conf.js\""

From the above code, when karma start karma.conf.js runs, tests start running. At the same time (due to concurrently), "tsc -w" starts running and fires another round of TS compilation, which changed the output JS files. Then Karma detected these JS file changes, stopped the currently running (partially or finished) tests, and started re-run the tests again.

I've only find the tsc documentation explaining "tsc -w" as "Run the compiler in watch mode. Watch input files and trigger recompilation on changes.".
https://www.typescriptlang.org/docs/handbook/compiler-options.html

From my understanding, what we wanted from "tsc && concurrently 'tsc -w' 'do-something' " is compile && (watch and do-something concurrently). But we actually got "compile && (compile-then-watch and do-something concurrently. Do we have any options to make "tsc -w" only watch, but don't compile?

@RyanCavanaugh RyanCavanaugh added Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Suggestion An idea for TypeScript labels Dec 21, 2016
@RyanCavanaugh
Copy link
Member

I suppose we could add this, but it'd need to be well-hidden enough that people wouldn't accidently use it and think TS was broken for not compiling the first time around.

I'm assuming they wrote their script that way so that lite-server didn't launch and not see the .js file and panic. It seems like the "right" fix here is to only invoke the first tsc conditionally on the target output file not existing. Adding more switches to tsc to support a scenario like this is perhaps fixing the problem in the wrong place.

@mhegazy
Copy link
Contributor

mhegazy commented Dec 21, 2016

currentlly tsc will update the file if it has changed since the last time it wrote it. we can make it so that it reads the output file the first time if it exists.

@yaooluu
Copy link
Author

yaooluu commented Dec 24, 2016

I think the fact that "tsc && concurrently \"tsc -w\" \"lite-server\" " actually compiles the same code twice is already inefficient due to how tsc -w behaves (compile and watch).

All I'm looking for is something like tsc -w --no-initial-compille.

@emmanuelbuah
Copy link

I run into this as well. It's useful to be able to compile or watch or compile + watch. tsc -w --no-initial-compile should solve the issue or breakout -w to only setup watch (no compilation) and only compile on future changes.

@matthew-0x
Copy link

Couldn't we just remove the first 'tsc' from that line of code?
"start": "concurrently \"tsc -w\" \"lite-server\" "

@avbentem
Copy link

But then, @mhvj, it might still be compiling when the server is already started?

@amir-arad
Copy link

amir-arad commented Jul 22, 2017

IMO the essential features here are:

  • start another process when the initial transpilation is done and watch mode begins.
  • not touch the target files more than once during that process (unless the source actually changed)

I'm affected by this usually when chaining watchers for dev-mode over a complex multy-project workspace. for instance several typescript watchers that produce dependencies for each other, and a webpack-dev-server that bundles all their target files whenever any of them change.

@vyorkin
Copy link

vyorkin commented Sep 17, 2017

TL;DR: This is exactly the same problem @yaooluu described above

It'll be great to have this, could be useful for monorepos (e.g. managed by lerna), where you have multiple packages that you want to test (I mean cross-package / integration testing).
In my case to accomplish this and do TDD I have to watch and rebuild (tsc -w) my packages constantly, at the same time I run jest in a watch-mode. The problem is that when I ran these two tasks in parallel jest is usually starts faster so I have to run just tsc to build dependency sub-packages first, and only then I can run tsc -w and jest in parallel. Thats why I have to run tsc two times.

@vyorkin
Copy link

vyorkin commented Sep 18, 2017

if understand correctly, to start working on this and have a chance that my PR will be accepted this feature needs to

be approved (marked as "Milestone == Community" by a TypeScript coordinator with the message "Approved") in the suggestion issue

according to CONTRIBUTING

what should be done to make it approved? does it need just more feedback or I can start working on it, submit a PR and only then it could be approved?

@RyanCavanaugh
Copy link
Member

@mhegazy thoughts on taking a PR for a flag to skip the initial watch cycle?

@itsUndefined
Copy link

This needs to be done.

@mhegazy
Copy link
Contributor

mhegazy commented Nov 17, 2017

Sure, we would consider a PR for it.

@avbentem
Copy link

avbentem commented Dec 5, 2017

Just as an aside, I'm using https://github.com/gilamran/tsc-watch which extends the built-in compiler with an --onSuccess option, which can then be used like:

"start": "tsc-watch --project . --outDir ./dist --onSuccess \"nodemon ./dist/bin/www.js\""

(Another aside: it seems the above does not work with projects in subfolders, such as "tsc-watch --project ./server --outDir ./server/dist --onSuccess \"nodemon ./server/dist/bin/www.js\"", for which the first compilation is fine, but compilation results of changes do somehow not end up in ./server/dist.)

@amir-arad
Copy link

@gilamran and I both work at the same place, and I've been waiting for this feature for months
🤦‍♂️

@vyorkin
Copy link

vyorkin commented Dec 5, 2017

sorry guys, I'm unable to work on the issue right now (not using ts in the current project), hope someone else will take it over

@gilamran
Copy link

gilamran commented Dec 5, 2017

@amir-arad, @avbentem You should use node and not nodemon as the tsc-watch is doing the watch for you. The "Big" thing about this is that tsc is transpiling ONLY the changed modules (fast). 😉

@gilamran
Copy link

gilamran commented Dec 5, 2017

@itsUndefined That's the whole point of tsc-watch to do just that. Start and re-start a process on compilation success (onSuccess param)

@lukasnagl
Copy link

In my opinion this should be part of tsc itself instead of requiring tsc-watch.

@Lian1230
Copy link

Lian1230 commented Apr 6, 2018

You can temporarily run a delay.js between the two scripts.
"start": "tsc && concurrently \"tsc -w\" \"node delay.js 3000 && lite-server\" "
And the delay.js:
`
const args = process.argv.slice(2);
const delay = args[0];

if (delay !== parseInt(delay, 10).toString()) {
console.error(Incorrect delay value: ${delay});
process.exit(1);
}

setTimeout(() => { process.exit(0); }, delay);
`

@SteppeEagle
Copy link

SteppeEagle commented Dec 11, 2018

Guys, I really need this feature.
Because without it I cannot create incrementive building by proper way I have to use delay, but it is awful.
Chokidar for example has ignoreInitial property for that goal.

@gilamran
Copy link

@Lian-LF, @SteppeEagle You can still use tsc-watch instead of delay or some ignoreInitial.
tsc-watch is using the installed version of typescript, so you're always getting the latest...

@SteppeEagle
Copy link

the problem of tsc-watch is
I cannot pass in --onFirstSuccess some service because on next compile phase service will be killed.
Workflow with tsc-watch assumes that I have to restart service every time, but I don't want to.
It was first point.
Second if tsc provide watching it should provide and arguments for different scenarios.

@gilamran
Copy link

@SteppeEagle Look at this. Here you can do what ever you want when the compilation succeeded of failed

@dorin131
Copy link

this is how I do it
"start-dev": "concurrently \"tsc -w\" \"sleep 3; nodemon\""

this will start nodemon 3 seconds after tsc -w

@Himujjal
Copy link

Himujjal commented Jan 9, 2019

  1. Install the ts-node dependency as a global or dev dependency.
    npm i -g ts-node.

  2. Create a ts-run.bat file in Windows. (Add it to PATH if required). Write the following in the file.

@echo off
set filename=%1
nodemon --watch %filename%.ts -e ts --exec "cls && ts-node %filename%.ts"

^^ts-run.bat file.

  1. Then run:
    .\tsc-run index for running a file named index.ts. Careful not to use the extension.

@kirill-konshin
Copy link

This is very much needed. Babel CLI has --skip-initial-build option for example. Flag --no-initial-compille would perfectly fit.

My use case is a Lerna monorepo with libraries and demos, you need to build all libs as phase 1 and then run demos + watch on phase 2, so the more libs you have the more inefficient it becomes. Currently the watch procedure looks like this:

{
  "scripts": {
    "build": "lerna run build --scope=@xxx/lib-* --concurrency=1 --stream",
    "start": "npm run build && lerna run start --parallel"
  }
}

Since Lerna's --parallel completely disregards topology this phased approach is a must, demos are independent of each other while libs have dependencies on each other and demos depend on libs.

@pronebird
Copy link

Any resolution on this? Our app is freaky twitchy, each time I run a watch script it reloads the whole process 3 times before the changes stop coming from the initial build. It's insane.

@gilamran
Copy link

gilamran commented Feb 6, 2019

@pronebird have you considered using tsc-watch? Only AFTER a successful build your app will get restarted.

@pronebird
Copy link

@gilamran yeah that's definitely one thing I consider to use. But AFAIK it's less elegant than having a first class support in tsc.

@kirill-konshin
Copy link

tsc-watch won't work for the monorepo with libs case because there are many libs and many demos, so the only way (as I see it now) is to first build everything, then start some magical watch procedure which will do nothing until any file changes.

@ericbf
Copy link
Contributor

ericbf commented Mar 22, 2019

Any update here?

@dziegelbein
Copy link

I created a package called runmon to work around this very issue.

@sheetalkamat
Copy link
Member

--incremental should allow you to build incrementally all the time.

@pronebird
Copy link

pronebird commented Apr 26, 2019

@sheetalkamat can you elaborate how that enables us to use tsc -w directly without using tsc-watch?

@sheetalkamat
Copy link
Member

@pronebird You can read more about --incremental at https://github.com/Microsoft/TypeScript/wiki/What's-new-in-TypeScript#faster-subsequent-builds-with-the---incremental-flag

@kirill-konshin
Copy link

@sheetalkamat perfecto, seems to be working! tsc && tsc -w runs much faster.

@pronebird
Copy link

@sheetalkamat now I got it. Thank you!

@lukywong
Copy link

+1 any update on this?

@RyanCavanaugh
Copy link
Member

I think --incremental actually addresses this to a high enough degree - with that flag turned on, the next watch invocation after a full build will be effectively free. Please do comment (with specifics) if you disagree and we can re-evaluate

@michaelfarrell76
Copy link

michaelfarrell76 commented Aug 23, 2019

I disagree with the above statement. here is the workflow I have setup:

I am running a node server inside of a docker container with the folder structure

/src (ts files)
/build (js files)

I want to run a single start command inside of the docker container, i.e.

npm run start

with the following package.json scripts:

"build": "tsc && copy ./src/{**/*.hbs,**/*.json,**/*.png} ./build",
"start": "npm run build && concurrently 'npm run build:watch' 'npm run server:start'",
"server:start": "nodemon --delay 2 -e '*' --watch build 'node build/server.js'",
"build:watch": "tsc --watch-only"

with this setup, i have a tsc watching for any files changes in src and rebuilding them into build, simultaneously i have nodemon restarting the node server whenever the build folder changes.

On start, I have to perform a fresh build and copy since tsc watch does not allow for post-compile scripts to run (such as copying over non-ts files), and then I have tsc:watch setup to rebuild, and thus restart the server anytime ts files change.

What tends to happen here is that my server will startup right away, but then since build:watch always performs a rebuild, this causes my server to autoreload everytime I start it up. This is quite annoying since the app will work, and then suddenly a network error is thrown after the ~3 second tsc rebuild. Since the server startup takes about 3-5 seconds, this makes the server start time go from 3 seconds to about 10-15 seconds everytime the docker image is booted.

This was so annoying that I had to start my server with two commands and i just run build:watch outside of the docker container. it;s quite easy to forget to run both commands and really tricky bugs have come out of just not having the build:watch command running in another window.

This workflow could be fixed by either allowing:

  • a command to be run every time tsc rebuilds
  • allowing watch-only mode so there is optionally no initial re-compilation

@pronebird
Copy link

pronebird commented Aug 23, 2019

@michaelfarrell76

watch-only mode so there is optionally no initial re-compilation

Try --incremental, it does exactly that, i.e it won't rebuild the same stuff all over again. You should use task automation if you need to copy files or do any other kind of pre-processing, expecting tsc to do this makes no sense.

a command to be run every time tsc rebuilds

Some way of knowing when tsc rebuilds files is a good idea. I use tsc-watch for that, but it parses the CLI output which is not optimal.

@ghost
Copy link

ghost commented Aug 30, 2020

Is this added? I run TS in watch mode and it doesn't compile. I would like to turn this on
TS 4.0.0 dev
I use this feature with VSCode, VSCode does that "debugging" work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests