0

I am trying to figure out how to setup VS Code to debug mocha tests in a TypeScript module npm project.

When I combined these solutions I hit a wall:

TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension "" for C:\Users\peter\Projects\TEMP\node_modules\mocha\bin\_mocha
    at new NodeError (node:internal/errors:400:5)
    at Object.getFileProtocolModuleFormat [as file:] (node:internal/modules/esm/get_format:74:9)
    at defaultGetFormat (node:internal/modules/esm/get_format:114:38)
    at defaultLoad (node:internal/modules/esm/load:81:20)
    at nextLoad (node:internal/modules/esm/loader:161:28)
    at C:\Users\peter\Projects\TEMP\node_modules\ts-node\src\esm.ts:255:45
    at async addShortCircuitFlag (C:\Users\peter\Projects\TEMP\node_modules\ts-node\src\esm.ts:409:15)
    at async nextLoad (node:internal/modules/esm/loader:161:22)
    at async ESMLoader.load (node:internal/modules/esm/loader:596:20)
    at async ESMLoader.moduleProvider (node:internal/modules/esm/loader:448:11) {
  code: 'ERR_UNKNOWN_FILE_EXTENSION'
}

It reads like it can't load the \node_modules\mocha\bin\_mocha extensionless binary when the experimental-specifier-resolution=node option is set.

A workaround would be to remove the --experimental-specifier-resolution=node runtimeArgs from launch.json and add the .js extension to all imports but it's a rather hacky solution I would rather not do.

My Environment

  • node: v19.3.0
  • npm: 9.2.0

Files

https://github.com/pariesz/vscode-debug-issue

.vscode/launch.json

{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "node",
            "request": "launch",
            "name": "Mocha",
            "program": "${workspaceRoot}/node_modules/mocha/bin/_mocha",
            "args": [
                "--no-timeouts",
                "--colors",
                "--inspect-brk",
                "*.test.ts"
            ],
            "runtimeArgs": [
                "--experimental-specifier-resolution=node",
                "--loader", "ts-node/esm"
            ]
        }
    ]
}

package.json

{
  "type": "module",
  "scripts": {
    "test": "mocha --node-option loader=ts-node/esm --node-option experimental-specifier-resolution=node *.test.ts"
  },
  "devDependencies": {
    "@types/chai": "^4.3.5",
    "@types/mocha": "^10.0.1",
    "chai": "^4.3.7",
    "mocha": "^10.2.0",
    "ts-node": "^10.9.1",
    "tslib": "^2.5.0",
    "typescript": "^5.0.4"
  }
}

tsconfig.json

{
  "compilerOptions": {
    "types": ["mocha", "node" ],
    "typeRoots": [ "./node_modules/@types" ],
    "module": "ESNext",
    "moduleResolution": "node"
  },
  "exclude": [
    "node_modules",
  ]
}

project.ts

export const TRUE = true;

project.test.ts

import { assert } from "chai"
import { TRUE } from "./project"

describe("project", () => {
    it("TRUE === true", () => {
        assert.isTrue(TRUE);
    })
})

UPDATE 1

Some googling led me to make experimental-specifier-resolution=node support extension-less files

I have tried adding a custom loader (loader.mjs) and commited to the repo:

let is_main = true;

export const load = (url, context, loadNext) => {
    if (is_main) {
        is_main = false;
        if (!context.format) {
            context.format = "commonjs";
        }
    }
    return loadNext(url, context, loadNext);
};

Now I have a new error:

Error: Failed to load raw source: Format was 'null' and url was 'file:///C:/Users/peter/Projects/TEMP/node_modules/mocha/bin/_mocha''.
    at C:\Users\peter\Projects\TEMP\node_modules\ts-node\src\esm.ts:265:17
    at async addShortCircuitFlag (C:\Users\peter\Projects\TEMP\node_modules\ts-node\src\esm.ts:409:15)
    at async nextLoad (node:internal/modules/esm/loader:161:22)
    at async ESMLoader.load (node:internal/modules/esm/loader:596:20)
    at async ESMLoader.moduleProvider (node:internal/modules/esm/loader:448:11)
    at async link (node:internal/modules/esm/module_job:68:21)

I have use console.log to verify that ./loader.js is run and format is set for node_modules/mocha/bin/_mocha and had a look at esm.ts but I still don't understand how it gets to this state.

Peter Riesz
  • 3,091
  • 29
  • 33

1 Answers1

0

I realized I can do better than console.log to see what the loaders are doing as I am already running the debugger, duh ‍♂️.

Debugging showed the experimental-loader hooks are run in the reverse order that they are listed in the runtimeArgs.

Here is the final launch.json to debug Mocha + Typescript + ESM in VS Code:

{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "node",
            "request": "launch",
            "name": "Mocha",
            "program": "${workspaceRoot}/node_modules/mocha/bin/_mocha",
            "args": [
                "--no-timeouts",
                "--colors",
                "--inspect-brk",
                "${relativeFile}"
            ],
            "runtimeArgs": [
                "--experimental-specifier-resolution=node",
                "--experimental-loader", "ts-node/esm",
                "--experimental-loader", "./loader.mjs",
                "--no-warnings"
            ]
        }
    ]
}

See the full solution on https://github.com/pariesz/vscode-debug-issue

Peter Riesz
  • 3,091
  • 29
  • 33