Visitar URL original
Source maps do not account for U+2028 and U+2029 line terminators · Issue #12454 · swc-project/swc · GitHub
Skip to content

Source maps do not account for U+2028 and U+2029 line terminators #12454

Open

Description

@gb-jos

Describe the bug

SWC parses U+2028 (LINE SEPARATOR) and U+2029 (PARAGRAPH SEPARATOR) as JavaScript line terminators, but source-map position tracking does not consistently recognize them.

This affects:

  • Original positions when the input contains either separator.
  • Generated positions when either separator is preserved in emitted block comments or tagged templates.

The simplest case is an actual U+2028 character followed by debugger;. The debugger statement starts on line 2, column 0, but its source mapping points to line 1, column 1.

All positions below use 1-based lines and 0-based columns.

Input code

The input is an actual U+2028 character followed immediately by debugger;.

To construct the exact source string:

const source = "\u2028debugger;";

Pass the value of source to SWC, not this variable declaration. The playground link contains the actual character.

Config

The verified reproduction uses SWC's Rust APIs to parse and emit JavaScript with source maps enabled, rather than a .swcrc file.

The playground fixture uses:

{
  "jsc": {
    "parser": {
      "syntax": "ecmascript"
    },
    "target": "esnext"
  },
  "sourceMaps": true,
  "minify": false
}

Link to the code that reproduces this issue

https://play.swc.rs/?version=1.16.2&code=H4sIAAAAAAAC%2F3vUsCIlNak0PT21yBoAFwSqowwAAAA%3D&config=H4sIAAAAAAAC%2FyWJXQqAIAyArxJ77gTdoUOMMcNIk22CIt69YW%2Ffz4BbCY5tQEFRloXas2FzBKaEShKLwdw3MJSLbQ3N3P6obxXiE4v6MKnsLcUcQ3cP%2BCjPD%2BCeYrlmAAAA

Choose Cancel in the “Detected unusual line terminators” dialog to preserve the U+2028 character. Choosing OK removes it and changes the reproduction.

The link contains the minimal input and a source-map-enabled configuration. The measured mapping results below come from the Rust pipeline; the playground's output has not been independently verified.

SWC Info output

SWC is consumed through its Rust APIs rather than @swc/core.

Tested crate versions:

  • swc_common 16.0.0
  • swc_ecma_codegen 19.0.0

The reproduction was observed in a local Rust-based JavaScript instrumentation pipeline. Source-map lookups were checked using both @jridgewell/trace-mapping and source-map-js, and generated debugger positions were independently checked in Chrome.

Expected behavior

Source-map positions should account for U+2028 and U+2029 as JavaScript line terminators.

For an actual U+2028 or U+2029 immediately followed by debugger;, the generated debugger statement should map to original line 2, column 0.

When these separators are preserved in generated comments or tagged templates, subsequent generated positions should also advance to the next line.

Actual behavior

The following inputs are shown using JavaScript string notation to make the separator characters visible. Each string's value is the source supplied to SWC, not the quoted string expression itself.

Input as a JavaScript string Expected original position Observed
"\u2028debugger;" Line 2, column 0 Line 1, column 1
"\u2029debugger;" Line 2, column 0 Line 1, column 1
"//x\u2028debugger;" Line 2, column 0 Line 1, column 4

Generated-position failures also occur with the source strings constructed below:

const source = "/*\u2028*/debugger;";
const source = "String.raw`\u2028`;debugger;";

In those cases, looking up the actual generated debugger statement location returned no original mapping in our pipeline. Equivalent U+2029 cases also failed.

LF-based control cases mapped correctly. Both independent source-map consumers agreed on the results.

Version

swc_common 16.0.0; swc_ecma_codegen 19.0.0 (Rust APIs).

Additional context

Suspected cause

  1. Original-source line tracking in swc_common's analyze_source_file_generic() recognizes CR/LF, but does not add line starts for U+2028/U+2029 in its multibyte-character handling:

    https://github.com/swc-project/swc/blob/29679adab06310fd97e1701fa3f7c7b7efd4ae22/crates/swc_common/src/syntax_pos/analyze_source_file.rs

  2. Generated-text tracking in swc_ecma_codegen's compute_line_starts_from_bytes() likewise recognizes only CR/LF:

    https://github.com/swc-project/swc/blob/107c0108acb518179b821d537849ff0471bcfc34/crates/swc_ecma_codegen/src/text_writer/basic_impl.rs

Writer-generated line breaks handled through JsWriter.write_line() track correctly. The issue concerns separators encountered within original source or emitted text.

Source inspection shows the relevant omission remains in swc_common 26.0.0, swc_ecma_codegen 32.0.3, and upstream main. Those newer versions were not executed for this reproduction.

Replacing these characters globally with LF is not a safe workaround because doing so can change string and tagged-template values.

Disclosure

This report was generated by GPT-6 Astra, an AI coding agent, based on code inspection and local reproduction results.

Activity

  1. magic-akari commented on Oct 1, 2026

    @magic-akari
    Member

    Thanks for the report. How LS/PS should affect source-location line numbers is still under discussion in tc39/ecma426#212, and implementations currently differ.

    I tested esbuild and Babel and inspected SWC's source-map position tracking:

    • esbuild 0.28.2 counts LS/PS as line breaks in both original and generated positions.
    • Babel 7.29.7 and 8.0.6 count them in original positions, but not in generated positions when they are preserved in block comments or tagged templates.
    • SWC, based on source inspection, does not count them as line breaks in either original or generated positions.

    I also reproduced a difference within Chrome 154: runtime stack traces and debugger pause locations count LS/PS as line breaks, while the Sources editor does not. VS Code's text model also does not count them as line breaks.

    There is therefore no consistent convention across these tools at present. The expected positions in this report match some implementations, but the underlying line-counting convention remains an open question.

  2. gb-jos commented on Oct 1, 2026

    @gb-jos
    Author

    magic-akari interesting, was not aware of this. In light of this i would not mind if this issue is closed, i leave it up to you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions