privatenumber / tsx

⚡️ TypeScript Execute | The easiest way to run TypeScript in Node.js
https://tsx.is
MIT License
9.36k stars 142 forks source link

Watch not working with docker #266

Open azhar1038 opened 1 year ago

azhar1038 commented 1 year ago

Bug description

I have created one file named main.ts and am using tsx watch ./main.ts as my start script to watch the file. Locally everything is working fine (Node 20) but when I try to run in a docker container, it does not watch changes in main.ts. I have mounted my local files to container. Screenshot 2023-07-12 220303

I was expecting it to reload when I make changes in main.ts locally.

Nothing happens. tsx does not reload anything.

I did not get any error

Reproduction

This is my main.ts

console.log('Hello world!');

This is my Dockerfile (I have tried with node:alpine too)

FROM node:18-alpine
WORKDIR /app
COPY package.json .
COPY package-lock.json .
RUN npm install
COPY . .
CMD [ "npm", "run", "start"]

This is the docker-compose.yml

version: '3.8'
services:
  tsx-test:
    build: .
    volumes:
      - .:/app
      - nodeModules:/app/node_modules

volumes:
  nodeModules:

Environment

System:
    OS: Windows 10 10.0.22621
    CPU: (8) x64 Intel(R) Core(TM) i5-9300H CPU @ 2.40GHz
    Memory: 1.23 GB / 7.85 GB
  Binaries:
    Node: 20.1.0 - C:\Program Files\nodejs\node.EXE
    Yarn: 3.5.1 - C:\Program Files\nodejs\yarn.CMD
    npm: 9.6.4 - C:\Program Files\nodejs\npm.CMD
    pnpm: 8.6.7 - C:\Program Files\nodejs\pnpm.CMD
  npmPackages:
    tsx: ^3.12.7 => 3.12.7 

BTW, I am using Windows 11 not 10

Can you work on a fix?

privatenumber commented 1 year ago

I don't use Docker but if you want to investigate and open a PR, feel free.

BTW, the watcher used is chokidar, so perhaps it's a bug in that.

azhar1038 commented 1 year ago

Okay, I will check and see if I can find the issue.

azhar1038 commented 1 year ago

Hi @privatenumber, I found the problem!

It is mentioned in Chokidar documentation that we need to set usePolling to true to be able to watch files over a network.

I tested locally and it seems to work but the problem is setting it to true can cause performance issue.

Then I checked Nodemon to see how they have handled this and found that they use a flag --legacy-watch or -L to set usePolling to true.

Shall I add a similar flag to set usePolling in tsx?

privatenumber commented 1 year ago

Thanks for the investigation. For reference, here's the issue in Chokidar: https://github.com/paulmillr/chokidar/issues/1051

Ideally no new flags... Node.js already added watch mode, and I don't want to end up maintaining a watch feature as it's not the main point of this project.

How does Node.js's watch handle this?

azhar1038 commented 1 year ago

Thanks for the reply. I have checked node experimental watch mode already and it does not handle this either.

privatenumber commented 1 year ago

Okay, lets add a --poll flag that accepts a interval value.

privatenumber commented 1 year ago

Actually, it may not be necessary if we change watchers: https://github.com/esbuild-kit/tsx/issues/246

Is Parcel's watcher compatible with network files?

azhar1038 commented 1 year ago

I am not sure. I will check.

azhar1038 commented 1 year ago

I checked @parcel/watcher and it also does not work with Docker on windows.

In fact, no watcher can actually handle this. The problem is in Windows, Docker uses WSL2 which uses its own file system (ext4 for Ubuntu) instead of windows NTFS. So, when I change something locally in windows, change happens in NTFS but container running in ext4 never gets notified of the change because of this bug in WSL: [WSL2] File changes made by Windows apps on Windows filesystem don't trigger notifications for Linux apps.

So, until that issue is fixed (which will definitely take a long time) we have a few options:

I think switching to @parcel/watcher is actually a better long term option as Chokidar is a bit less active now.

matt-sanders commented 1 year ago

I'm having a similar issue here which is potentially related. If not, let me know and I can open a new issue. But when using this with Docker on an M2 Mac, changes to files are not being picked up. Interestingly if I use nodemon it works as expected. Example repo here.

azhar1038 commented 1 year ago

Both tsx and nodemon uses Chokidar to watch file changes. So, I don't think it is same issue. However, it is possible that nodemon is setting usePolling or useFsEvents for mac somehow. Unfortunately I don't have access to any mac, can you please try to see if chokidar is directly working in docker or not? If it does then there is some other issue in tsx.

matt-sanders commented 1 year ago

Can confirm that Chokidar works fine without TSX. I've pushed another script to the above repo as an example.

azhar1038 commented 1 year ago

Then your issue is probably related to #246

matt-sanders commented 1 year ago

Hmm, we're actually using Node 18. Interestingly it seems to work with colima but not with Docker Desktop. For now I'm just either continuing with colima or if I really need it, I use Chokidar's polling.

adam-beck commented 6 months ago

I have a similar issue but I don't know if it's related. I'm using Compose Watch instead of volumes. For the first few file changes, it works great. But after like 4-5 it just stops. I'm not sure how to debug either.

In the meantime -- and the reason I'm posting this comment -- is that I resolved to using polling with chokidar. I'm still using tsx but I'm also passing in the environment variable CHOKIDAR_USEPOLLING=true to my container that is running tsx. Seems to "fix" the issue. Well, as good as any other alternative at the moment.

My host OS is PopOS and I'm using a version of Alpine Linux in the container. I'm using Docker Engine (not Docker for Linux).

azhar1038 commented 5 months ago

@adam-beck Yes, I just tried compose watch and it is behaving weirdly. When I use:

Not sure why tsx watch stops working after some time. Here is my compose.yml in case anyone is interested.

# compose.yml

services:
  server:
    build:
      context: .
    ports:
      - 3000:3000
    environment:
      - CHOKIDAR_USEPOLLING=true
    develop:
      watch:
        - action: sync
          path: ./src
          target: /app/src
        - action: rebuild
          path: package.json
          target: /app
adam-beck commented 4 months ago

I wrote a simple script to determine if chokidar was picking up the changes via compose watch

const chokidar = require('chokidar');

// One-liner for current directory
chokidar.watch('.').on('all', (event, path) => {
  console.log(event, path);
});

Every change seemed to be picked up. So I'm starting to think it's something within tsx itself. Granted, this is a JS script but I don't think that would matter.

privatenumber commented 4 months ago

I'm likely going to migrate to using Parcel watcher in the near future.

Can one of you give me a reproduction in a GitHub Action environment (or something similar) where I can reproduce this bug? This would help me cover this use-case.

insanity54 commented 1 month ago

Hi hi, thank you for tsx. I've been using it for a month or two and I really enjoy it.

I've been bit by this issue so I wanted to see if there's anything I can do to help figure it out. I've made a reproduction repo along with a couple Github actions to test the --watch behavior. I'm not sure it's correct but maybe it's a start.

https://github.com/insanity54/tsx-issue-266/blob/master/.github/workflows/tests.yaml

socketopp commented 1 month ago
  • tsx with CHOKIDAR_USEPOLLING=true works fine as expected

Any progress on this? I've tried that Currently runnig tsx like this: "dev": "xvfb-run -a tsx src/index.ts",

But I guess I should make the command like this if I want hot-reload? "dev": "xvfb-run -a tsx watch src"

I've also added CHOKIDAR_USEPOLLING=true to my environment in .env and in my docker-compose file.