nrwl / nx

Smart Monorepos · Fast CI
https://nx.dev
MIT License
23.59k stars 2.36k forks source link

Issues with `nx serve` auto-reloading for a nodejs server #27919

Open LukeWood opened 1 month ago

LukeWood commented 1 month ago

Current Behavior

Background

Hello! Thanks for all of your hard work on nx. I really enjoy using it to develop various projects. I'm working in an integrated-monorepo, setup using tsconfig paths in a tsconfig.base.json at the root.

I'm struggling with the reloading feature for one of my nodejs servers. In particular, I watch my monorepo by running nx server bulletz-server. The project.json for bulletz-server is as follow:

  {    
    "name": "bulletz-server",    
    "$schema": "../../../node_modules/nx/schemas/project-schema.json",    
    "sourceRoot": "apps/bulletz/server/src",    
    "projectType": "application",    
    "tags": [],    
    "targets": {    
      "build": {    
        "executor": "@nx/esbuild:esbuild",    
        "outputs": ["{options.outputPath}"],    
        "defaultConfiguration": "production",    
        "options": {    
          "platform": "node",    
          "outputPath": "dist/apps/bulletz/server",    
          "format": ["cjs"],    
          "bundle": false,    
          "main": "apps/bulletz/server/src/main.ts",    
          "tsConfig": "apps/bulletz/server/tsconfig.app.json",    
          "assets": ["apps/bulletz/server/src/assets"],    
          "generatePackageJson": true,    
          "esbuildOptions": {    
            "sourcemap": true,    
            "outExtension": {    
              ".js": ".js"    
            }    
          }    
        },    
        "configurations": {    
          "development": {},    
          "production": {    
            "esbuildOptions": {    
              "sourcemap": false,    
              "outExtension": {    
                ".js": ".js"    
              }    
            }    
          }    
        }    
      },    
      "serve": {    
        "executor": "@nx/js:node",    
        "defaultConfiguration": "development",    
        "options": {    
          "buildTarget": "bulletz-server:build"    
        },    
        "configurations": {    
          "development": {    
            "buildTarget": "bulletz-server:build:development"    
          },    
          "production": {    
            "buildTarget": "bulletz-server:build:production"    
          }    
        }    
      }    
    }    
  }    

I believe that the serve command is coming from @nx/js:node.


The issue

Whenever I'm developing, I will occasionally push a change that causes compilation to fail. This causes the server to die - ok totally expected. where the behavior becomes unexpected is after I fix the error. Regardless of how many saves/loads I trigger, after a single breakage the server NEVER gets re-launched! It simply sits waiting forever.

Here's an example: I intentionally assign patchRate to a string, an invalid assignment. As such, tsc fails once:

aapps/bulletz/server/src/QuickplayRoom.ts:26:5 - error TS2322: Type 'string' is not assignable to type 'number'.

  24 |   getClient(id: string) {
  25 | 
> 26 |     this.patchRate = 'hleloworkdklagdjkla';
     |     ^
  27 |     for (let client of this.clients) {
  28 |       if (client.sessionId === id) {
  29 |         return client;

Found 1 error.
[ watch ] build finished with errors (see above), watching for changes...
> Removing stale rooms by processId: n92KiWZFJ (1 rooms found)
Build failed, waiting for changes to restart...

But, let's go fix it - after fixing it, we should see the server reload! This works when you modify the files in the server itself, and usually one of the dependent packages - but in some subset of circumstances the reload is simply never triggered! The app will wait forever.

Expected Behavior

I'd expect it to reload on any dependency update, including in a tsconfig specified path.

GitHub Repo

No response

Steps to Reproduce

Unfortunately, I still don't know exactly how to do this :( I'd appreciate some help in narrowing the root cause.

Nx Report

Node   : 18.19.1
OS     : linux-x64
pnpm   : 9.9.0

nx (global)        : 19.7.3
nx                 : 19.4.2
@nx/js             : 19.4.2
@nx/jest           : 19.4.2
@nx/eslint         : 19.4.2
@nx/workspace      : 19.4.2
@nx/cypress        : 19.4.2
@nx/devkit         : 19.4.2
@nx/esbuild        : 19.4.2
@nx/eslint-plugin  : 19.4.2
@nx/next           : 19.4.2
@nx/node           : 19.4.2
@nx/playwright     : 19.4.2
@nx/react          : 19.4.2
@nx/vite           : 19.4.2
@nx/web            : 19.4.2
typescript         : 5.4.5
---------------------------------------
Registered Plugins:
@nx/vite/plugin
@nx/eslint/plugin
@nx/next/plugin

Failure Logs

Posted above

Package Manager Version

pnpm=9.10.0

Operating System

Additional Information

Maybe this is relevant - but I simultaneously server numerous apps at once. I do this from within tmux. They all run concurrently - so maybe something weird is going on! Maybe they concurrently write to the cache, so something is not detected.

LukeWood commented 1 month ago

Some other information: Sometimes the project fails to detect things from the tsconfig's paths, and I have no idea why! I just tried to serve and got:

Error: Cannot find module '@yeti/ecs'
Require stack:
- /home/luke/workspace/yeti/dist/apps/bulletz/server/apps/bulletz/common/src/BulletzGame.js
- /home/luke/workspace/yeti/dist/apps/bulletz/server/apps/bulletz/common/src/index.js
- /home/luke/workspace/yeti/dist/apps/bulletz/server/apps/bulletz/server/src/ServerBulletzGame.js
- /home/luke/workspace/yeti/dist/apps/bulletz/server/apps/bulletz/server/src/QuickplayRoom.js
- /home/luke/workspace/yeti/dist/apps/bulletz/server/apps/bulletz/server/src/app.js
- /home/luke/workspace/yeti/dist/apps/bulletz/server/apps/bulletz/server/src/main.js
- /home/luke/workspace/yeti/dist/apps/bulletz/server/main.js
    at Function.Module._resolveFilename (node:internal/modules/cjs/loader:1134:15)
    at Function.Module._resolveFilename (/home/luke/workspace/yeti/tmp/bulletz-server/main-with-require-overrides.js:41:36)
    at Function.Module._load (node:internal/modules/cjs/loader:975:27)
    at Function.Module._load (/home/luke/workspace/yeti/node_modules/.pnpm/@nx+js@19.4.2_@babel+traverse@7.25.6_@swc-node+register@1.9.2_@swc+core@1.5.7_@swc+helpers@0._iuj2vrzr57d2ibwkf2mknv75wi/node_modules/@nx/js/src/executors/node/node-with-require-overrides.js:18:31)
    at Module.require (node:internal/modules/cjs/loader:1225:19)
    at require (node:internal/modules/helpers:177:18)
    at Object.<anonymous> (/home/luke/workspace/yeti/apps/bulletz/common/src/BulletzGame.ts:16:69)
    at Module._compile (node:internal/modules/cjs/loader:1356:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1414:10)
    at Module.load (node:internal/modules/cjs/loader:1197:32)

Yet my tsconfig is:

  {                                                                                                                                  
    "extends": "../../../tsconfig.base.json",                                                                                        
    "files": [],                                                                                                                     
    "include": [],                                                                                                                   
    "references": [                                                                                                                  
      {                                                                                                                              
        "path": "./tsconfig.app.json"                                                                                                
      }                                                                                                                              
    ]                                                                                                                                
  }    

And the base:

  {                                                                                                                                  
    "compileOnSave": false,                                                                                                          
    "compilerOptions": {                                                                                                             
      "rootDir": ".",                                                                                                                
      "sourceMap": true,                                                                                                             
      "resolveJsonModule": true,                                                                                                     
      "moduleResolution": "node",                                                                                                    
      "declaration": true,                                                                                                           
      "emitDecoratorMetadata": true,                                                                                                 
      "experimentalDecorators": true,                                                                                                
      "esModuleInterop": true,                                                                                                       
      "importHelpers": true,                                                                                                         
      "target": "es2015",                                                                                                            
      "module": "esnext",                                                                                                            
      "lib": ["es2020", "dom"],                                                                                                      
      "skipLibCheck": true,                                                                                                          
      "skipDefaultLibCheck": true,                                                                                                   
      "baseUrl": ".",                                                                                                                
      "paths": {                                                                                                                     
        ...                                                     
        "@yeti/ecs": ["lib/ecs/src/index.ts"]
        ... # more modules                                                                    
      }                                                                                                                              
    },                                                                                                                               
    "exclude": ["node_modules", "tmp"],                                                                                              
    "includes": ["apps/**/*.ts", "lib/**/*.ts"]                                                                                      
  }        
LukeWood commented 1 month ago

Here's an example of the failure state:

Screenshot 2024-09-15 at 6 57 07 PM

Notice the server never restarts

hco commented 1 month ago

Is this related to https://github.com/nrwl/nx/issues/17070?