"tsc -w" will compile and watch, then how to run tsc in "watch-only" mode without compiling?
yaooluu opened this issue ยท 42 comments
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?
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.
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.
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
.
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.
Couldn't we just remove the first 'tsc' from that line of code?
"start": "concurrently \"tsc -w\" \"lite-server\" "
But then, @mhvj, it might still be compiling when the server is already started?
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.
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.
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?
@mhegazy thoughts on taking a PR for a flag to skip the initial watch cycle?
This needs to be done.
Sure, we would consider a PR for it.
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
.)
@gilamran and I both work at the same place, and I've been waiting for this feature for months
๐คฆโโ๏ธ
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
@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). ๐
@itsUndefined That's the whole point of tsc-watch
to do just that. Start and re-start a process on compilation success (onSuccess
param)
In my opinion this should be part of tsc
itself instead of requiring tsc-watch
.
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);
`
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.
@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...
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.
@SteppeEagle Look at this. Here you can do what ever you want when the compilation succeeded of failed
this is how I do it
"start-dev": "concurrently \"tsc -w\" \"sleep 3; nodemon\""
this will start nodemon 3 seconds after tsc -w
-
Install the ts-node dependency as a global or dev dependency.
npm i -g ts-node
. -
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.
- Then run:
.\tsc-run index
for running a file namedindex.ts
. Careful not to use the extension.
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.
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.
@pronebird have you considered using tsc-watch
? Only AFTER a successful build your app will get restarted.
@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
.
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.
Any update here?
I created a package called runmon to work around this very issue.
--incremental
should allow you to build incrementally all the time.
@sheetalkamat can you elaborate how that enables us to use tsc -w
directly without using tsc-watch
?
@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
@sheetalkamat perfecto, seems to be working! tsc && tsc -w
runs much faster.
@sheetalkamat now I got it. Thank you!
+1 any update on this?
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
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
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.
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.