Skip to content

Introduce Sourcemap#8393

Open
mununki wants to merge 3 commits intomasterfrom
sourcemap
Open

Introduce Sourcemap#8393
mununki wants to merge 3 commits intomasterfrom
sourcemap

Conversation

@mununki
Copy link
Copy Markdown
Member

@mununki mununki commented Apr 27, 2026

Summary

Introduce linked Source Map v3 generation for ReScript JavaScript output.

This adds per-file .js.map output, appends //# sourceMappingURL=... to generated JavaScript, and wires source map options through rescript.json, bsc, and rewatch. The compiler printer now tracks generated line/column positions and records mappings back to .res source locations.

Basic rescript.json usage:

{
  "sourceMap": true,
  "sourceMapSourcesContent": true
}
  • sourceMap: true or "linked" generates *.js.map files next to generated JavaScript and links them from the JS output.
  • sourceMapSourcesContent: true embeds original .res source text in sourcesContent.
  • sourceMapRoot is parsed and forwarded to the map sourceRoot field, but most local/Vite workflows do not need it.
  • inline, hidden, and external are intentionally not included in this MVP.

This PR also preserves source locations through function output, call expressions, pipe expressions, and pattern-match branch output so browser breakpoints and Node stack traces can resolve back to .res files more usefully.

Validation

From the repository root:

git fetch origin
git checkout sourcemap
yarn install
opam exec -- make test
cargo test source_map --lib --manifest-path rewatch/Cargo.toml

The build test suite now includes tests/build_tests/source_map, which verifies that sourceMap: true emits a .js.map file, writes a sourceMappingURL comment, includes non-empty mappings, and includes source contents.

For manual browser and Node sourcemap behavior, see the external Vite test project:
https://github.com/mununki/rescript-sourcemap-test

The test project README explains how to check out the sourcemap branch of a local ReScript compiler clone, link it from package.json, and verify sourcemaps in Vite/Chrome DevTools and Node.

Next To Do

  • Investigate bundler integration so Vite/Rollup/Webpack can consume ReScript input source maps without custom user configuration.
  • Add additional source map modes after the MVP, especially inline and hidden.
  • Consider names mappings for better symbol-level debugging.
  • Improve JSX/helper-code mapping granularity.

@cknitt
Copy link
Copy Markdown
Member

cknitt commented Apr 27, 2026

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 68c516ba9d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread compiler/core/js_source_map.ml Outdated
Comment on lines +183 to +186
(match (!current, Hashtbl.find_opt marker_locs id) with
| Some builder, Some loc ->
let generated_line, generated_column = Ext_pp.position fmt in
add_mapping builder ~generated_line ~generated_column loc
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Clear consumed source-map markers from global cache

comment_of_loc stores every marker ID in the process-global marker_locs, but mark_comment only does a find_opt and never removes the entry. In long-lived processes (notably rewatch watch mode), each rebuild/file compile adds more IDs and this table grows without bound, which steadily increases memory use and retained location data even after maps are emitted.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Good catch. The rewatch-specific leak is limited today because rewatch spawns bsc as a separate process, so the OCaml global table does not live for the whole watch session. Still, consumed markers should be removed from marker_locs; they are one-shot internal markers and keeping them unnecessarily retains location data for the rest of the compiler process. I’ll update mark_comment to remove marker entries after lookup.

Comment thread compiler/core/js_source_map.ml Outdated
Comment on lines +85 to +89
| from_root :: _, to_root :: _ when from_root = to_root ->
let from_rest, to_rest = drop_common from_parts to_parts in
let parts = repeat ".." (List.length from_rest) @ to_rest in
if parts = [] then Filename.basename to_file else String.concat "/" parts
| _ -> Filename.basename to_file
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve full relative source paths across different roots

When the first normalized path segment differs, relative_path falls back to Filename.basename, which drops directory context entirely. If generated outputs and source files resolve through different absolute prefixes (for example symlink/canonicalized roots), the map will emit only bare filenames, causing ambiguous or unresolvable sources entries when sourcesContent is disabled.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Good catch. Falling back to Filename.basename can lose useful source path context and make sources ambiguous when sourcesContent is disabled. I updated the path logic to preserve relative paths whenever possible, even when the first normalized path segment differs. It now only falls back to a normalized absolute path for cross-drive Windows paths, where a filesystem-relative path is not representable.

Comment thread rewatch/src/config.rs

pub namespace: Option<NamespaceConfig>,
pub jsx: Option<JsxSpecs>,
#[serde(rename = "sourceMap")]
Copy link
Copy Markdown
Member

@fhammerschmidt fhammerschmidt Apr 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe these new options need to be nested in another "sourceMap": {}
object? You only use them together anyway, right?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That’s a fair point. I kept the MVP shape as top-level fields to keep the implementation simple, but nesting the source map-specific options under a sourceMap object also looks like a good direction, especially if we add more options later.

{
  "sourceMap": {
    "mode": "linked",
    "sourcesContent": true,
    "sourceRoot": "",
    ...
  }
}

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Apr 27, 2026

Open in StackBlitz

rescript

npm i https://pkg.pr.new/rescript@8393

@rescript/darwin-arm64

npm i https://pkg.pr.new/@rescript/darwin-arm64@8393

@rescript/darwin-x64

npm i https://pkg.pr.new/@rescript/darwin-x64@8393

@rescript/linux-arm64

npm i https://pkg.pr.new/@rescript/linux-arm64@8393

@rescript/linux-x64

npm i https://pkg.pr.new/@rescript/linux-x64@8393

@rescript/runtime

npm i https://pkg.pr.new/@rescript/runtime@8393

@rescript/win32-x64

npm i https://pkg.pr.new/@rescript/win32-x64@8393

commit: b021c2f

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants