Skip to content

Assertions yield incorrect message when source is transpiled by loader #59612

@DASPRiD

Description

@DASPRiD

Version

v24.5.0

Platform

Linux dasprid-desktop 6.12.10-76061203-generic #202412060638~1753385872~22.04~dc2e00d SMP PREEMPT_DYNAMIC Thu J x86_64 x86_64 x86_64 GNU/Linux

Subsystem

internal/assert

What steps will reproduce the bug?

Create a test file in TypeScript with a test and an assertion in it:

import assert from "node:assert/strict";
import { it } from "node:test";

it("line issue", () => {
    assert.ok(false);
});

Running it through node test (through internal type stripping) will yield the correct result:

  AssertionError [ERR_ASSERTION]: The expression evaluated to a falsy value:
  
    assert.ok(false)

Running it with a loader like tsx will incorrectly identify the assertion source:

  AssertionError [ERR_ASSERTION]: The expression evaluated to a falsy value:
  
    it("line issue", () => {
    assert.ok(false);
    })

How often does it reproduce? Is there a required condition?

Reproduces every time.

What is the expected behavior? Why is that the expected behavior?

Running through a TS loader should yield the same result as node with type stripping.

What do you see instead?

Assertion reports wrong source location.

Additional information

Previous finding can be found here: privatenumber/tsx#732

To summarize:

  • To generate the assertion message, Node.js captures a stack trace in its interal getErrMessage(). That stack trace references the transpiled location though; in this case, line 0, column 74.
  • It then loads the active file from disk (so test.ts). For line 0 it has a special handling in getCode(), where it will use findColumn() directly. Note that the column is the absolute column value, so a line break is simply considered a character in this case.
  • The parser then uses acorn internally to parse the TypeScript source. This search will most of the time yield partially or completely wrong results.

tsx (through esbuild) does include a source map in the transpiled code. I'm not sure how Node.js could handle this best. Ideally it'd scan the transpiled code to find the assertion source and then reference the original file through the source map.

But looking at the current code which always loads the executed file from disk, I'm not sure if Node.js even has access to the executed code at runtime.

Metadata

Metadata

Assignees

No one assigned

    Labels

    assertIssues and PRs related to the assert subsystem.source mapsIssues and PRs related to source map support.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions