VSCode breakpoints unbound when debugging a React app in a docker container

Viewed 48

I'm having issues debugging with VSCode a React app created with Create React App and running in a Docker container.

The app runs fine with docker compose up and it correctly stops its execution until I attach with the debugger. Debugger attached and on hold at the first line, the breakpoint at the following line is active

However, after that, only breakpoints in the start.js file behave correctly, while any other breakpoint in my source files stays unbound. Other breakpoints appear hollow and do not work

Project Setup

package.json

I defined a new script start:debug to run the application in inspect mode.

    {
    "name": "test",
    "version": "0.1.0",
    "private": true,
    "dependencies": {
        "@emotion/react": "^11.8.1",
        "@emotion/styled": "^11.8.1",
        "@mui/icons-material": "^5.4.2",
        "@mui/material": "^5.4.3",
        "@testing-library/jest-dom": "^5.16.1",
        "@testing-library/react": "^12.1.2",
        "@testing-library/user-event": "^13.5.0",
        "axios": "^0.26.0",
        "react": "^17.0.2",
        "react-dom": "^17.0.2",
        "react-scripts": "5.0.0",
        "web-vitals": "^2.1.3"
    },
    "scripts": {
        "start": "react-scripts start",
        "start:debug": "react-scripts --inspect-brk=0.0.0.0:9229 start",
        "build": "react-scripts build",
        "test": "react-scripts test",
        "eject": "react-scripts eject"
    },
    ...
    }

Dockerfile

FROM node:16-alpine

# Create app directory
WORKDIR /usr/src/app

# add `node_modules/.bin` to $PATH
ENV PATH /usr/src/app/node_modules/.bin:$PATH

# Install app dependencies
COPY package.json ./
COPY package-lock.json ./
RUN npm install

# If you are building your code for production
# RUN npm ci --only=production

# Bundle app source
COPY . .

EXPOSE 3000

# Run app
CMD [ "npm", "run", "start:debug" ]

docker-compose.yml

version: '3'

services:
  web:
    image: test
    container_name: test
    build:
      context: .
      dockerfile: Dockerfile
    volumes:
      - ./src:/usr/src/app/src
    env_file:
      - ./.envs/.local/.node
    ports:
      - "3000:3000"
      - "9229:9229"

.vscode/launch.json

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Docker: Attach to Node",
            "type": "node",
            "request": "attach",
            "restart": true,
            "port": 9229,
            "address": "localhost",
            "trace": true,
            "localRoot": "${workspaceFolder}",
            "remoteRoot": "/usr/src/app",
            "sourceMaps": true
        },
    ]
}

I tried every configuration I could find but I did not manage to make it work. Could anyone help me spot where the issue is?


1 Answers

If you are working with a web-based react app - you will need to run a web browser to debug it. Here is a setup that I did:

  1. set up a docker container for React with bind-mounts to my main system
  2. enter container from terminal docker exec -ti react-ptc /bin/bash
  3. create react app inside container in /app directory
  4. cd into app directory
  5. run npm run eject - required to manage webpack config later
  6. run npm run start inside docker container
  7. open react project folder with vscode from bind mount on host system.

Number 7 is necessary because otherwise VSCode will not be able to launch a web browser for debugging. Watch your permissions though. You may need to set bind mounted /react directory to host user permissions.

Here is part of docker-compose.yml I used:

version: '3'
services:
  react-ptc:
    image: 'dmitryr117/node:18.8.0-dev'
    container_name: react-ptc
    restart: unless-stopped
    volumes:
      - ./react:/app
    environment:
      - HOST=0.0.0.0
      - PORT=80
      - WDS_SOCKET_HOST=0.0.0.0
      - WDS_SOCKET_PORT=0
    expose:
      - 80
    ports:
      - 8080:80

I am however running my system with an nginx reverse proxy and using /etc/hosts to emulate real domain name behaviour on local environment.

Now for launch.json and webpack.config.js

launch.json

{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "chrome",
      "request": "launch",
      "name": "Brave Debug",
      "url": "http://client.ptc.test", // can set to http://localhost:8080
      "webRoot": "${workspaceFolder}",
      "timeout": 20000,
      "sourceMaps": true,
      "breakOnLoad": true,
      "runtimeExecutable": "/snap/bin/brave", // I am on Ubuntu using Brave web browser
      "runtimeArgs": [
        "--new-window",
        "--incognito",
        "--user-data-dir=${workspaceFolder}/chrome-profiles/ReactProfile",
        "--remote-debugging-port=9222"
      ],
      "sourceMapPathOverrides": {
        // requires inline-source-map in webpack.config.js for devtool
        // "browser-path": "web-root path"
        // IMPORTANT AREA:
        // Check sources in web browser DeveloperTools and change /app/ptc to 
        // your path above /src directory
        "/app/ptc/*": "${webRoot}/*",
      },
    }
  ]
}

In previously ejected config directory look for webpack.config.js and update as wollows:

    ...
    devtool: 'inline-source-map',
    // isEnvProduction
    //   ? shouldUseSourceMap
    //     ? 'source-map'
    //     : false
    //   : isEnvDevelopment && 'cheap-module-source-map',
    // These are the "entry points" to our application.
    // This means they will be the "root" imports that are included in JS bundle.
    ...

That's it. Also update your .gitignore to avoid committing browser profile:

...
# browser_profile
/chrome-profiles

Should work from here. Hope this helps.

Related