Pesquisa de site

Como ser capaz de configurar pontos de interrupção corretamente ao usar o VS Code para depurar um aplicativo TypeScript executado usando ts-node em um contêiner Docker?


Nosso aplicativo é escrito em TypeScript e usa Docker, e para evitar a ida e volta através de arquivos .js, estamos executando-o com ts-node para carregar os arquivos .ts diretamente.

Infelizmente, isso parece deixar o VSCode confuso sobre onde estão as linhas de código válidas para definir breakpoints.

Este problema se manifesta com a seguinte configuração:

/package.json

{
  "scripts": {
    "start": "node --inspect=0.0.0.0 --require ts-node/register src/index.ts"
  },
  "dependencies": {
    "@types/node": "^10.1.2",
    "ts-node": "^6.0.3",
    "typescript": "^2.8.3"
  }
}

/tsconfig.json

{
  "compilerOptions": {
    "target": "ES2017",
    "module": "commonjs", 
    "outDir": "./dist",
    "rootDir": "./src",    
    "esModuleInterop": true
  }
}

/Dockerfile

FROM node

RUN mkdir /home/node/app
WORKDIR /home/node/app
COPY package.json /home/node/app
RUN npm install && npm cache clean --force
COPY . /home/node/app
CMD [ "npm", "start" ]

/docker-compose.yml

version: "3.4"

services:
  http:
    build: .
    ports:
      - "8000:8000"
      - "9229:9229"

/.vscode/launch.json

{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "node",
            "request": "attach",
            "name": "Attach",
            "address": "localhost",
            "port": 9229,
            "protocol": "inspector",
            "localRoot": "${workspaceFolder}/src",
            "remoteRoot": "/home/node/app/src"
        }
    ]
}

/src/index.ts

import {createServer} from "http";








const server = createServer((msg, res) => {
    res.writeHead(200, {'Content-Type': 'text/plain'})
    res.end(msg.url)
    debugger
})

server.listen(8000)

(As linhas em branco são significativas por motivos que mostrarei mais tarde, cerca de dez delas são suficientes.)

Você também pode buscar tudo aqui: https://github.com/millimoose/ts-node-breakpoints

Eu executo isso com docker-compose --up, e então me conecto a isso com o depurador usando a configuração de inicialização acima. Quando tento definir pontos de interrupção em /src/index.ts em qualquer uma das linhas dentro da chamada createServer(), eles são reportados como inválidos; enquanto posso definir pontos de interrupção nas linhas em branco. Presumivelmente, isso acontece porque a compilação do TypeScript remove as linhas em branco, e por algum motivo, o VSCode só reconhece os números de linha a partir do JS gerado como válidos:

Este é um exemplo forçado para facilitar a reprodução, mas em geral haverá uma discrepância entre onde eu acho que estou definindo os pontos de interrupção e onde eles estão realmente definidos.

No entanto, quando eu paro na instrução debugger, o VSCode busca o arquivo TypeScript (a aba diz algo como "somente leitura em linha do source map" quando recém-aberto) do servidor, e então posso definir pontos de interrupção corretamente nele:

Esta é uma situação insatisfatória por razões que eu não deveria ter que explicar: lidar com um arquivo local que posso editar e um arquivo remoto onde os pontos de interrupção funcionam é um incômodo, e adicionar declarações debugger envolveria recarregar o aplicativo toda vez que eu precisasse de um novo ponto de interrupção.

Eu procurei sobre o problema, mas as palavras-chave me dão pelo menos dez issues longos no GitHub datando de vários anos atrás. Como não estou intimamente familiarizado com os detalhes internos do ts-node, da transpiração e dos sourcemaps, estou tendo dificuldade em entender o que está acontecendo aqui, quanto mais como consertar. Pelo que eu entendo, o que acontece é que o ts-node compila TS para JS e gera sourcemaps em arquivos temporários dentro do contêiner Docker, onde o VSCode não consegue acessá-los. (É por isso que não faço ideia de como configurar, por exemplo, outFiles.) Também houve algumas menções ao meu cenário já ser suportado se configurado corretamente em issues fechadas, mas sem pistas de como fazer isso.

Existe uma maneira de fazer isso funcionar para que eu possa realmente definir um ponto de interrupção nos meus arquivos locais ao depurar remotamente, e que eles sejam acionados nesses arquivos, sem ter que voltar a pré-compilar TS para JS e mapas de origem para que eu tenha estes últimos disponíveis localmente?

Resposta #1

Você não está gerando mapa de origem para o código. Então eu atualizei seu tsconfig.json da seguinte forma

{
  "compilerOptions": {
    /* Basic Options */
    "target": "ES2017",                          /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
    "module": "commonjs",                     /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
    "outDir": "./dist",                        /* Redirect output structure to the directory. */
    "rootDir": "./src",                       /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
    "sourceMap": true,
    "esModuleInterop": true                   /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
  }
}

e agora funciona como um charme

Artigos relacionados