Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Miniflare 3] Serve linked source maps from loopback server (enable breakpoint debugging for TypeScript) #660

Merged
merged 1 commit into from
Aug 16, 2023

Conversation

mrbbot
Copy link
Contributor

@mrbbot mrbbot commented Aug 15, 2023

Hey! 👋 With cloudflare/workerd#710, workerd supports breakpoint debugging! Support for this in Miniflare just worked, assuming you were using a plain JavaScript worker, or you had inline source maps. workerd doesn't know where workers are located on disk, it just knows files' locations relative to each other. This means it's unable to resolve locations of corresponding linked .map files in sourceMappingURL comments.

Miniflare does have this information though. This PR detects linked source maps and rewrites sourceMappingURL comments to http URLs pointing to Miniflare's loopback server. This then looks for the source map relative to the known on-disk source location. Source maps' sourceRoot attributes are updated to ensure correct locations are displayed in DevTools.

This enables breakpoint debugging for compiled TypeScript with linked source maps! 🎉

image
image

Closes DEVX-872

With cloudflare/workerd#710, `workerd` supports breakpoint debugging!
Support for this in Miniflare just worked, assuming you were using a
plain JavaScript worker, or you had inline source maps. `workerd`
doesn't know where workers are located on disk, it just knows files'
locations relative to each other. This means it's unable to resolve
locations of corresponding linked `.map` files in `sourceMappingURL`
comments.

Miniflare _does_ have this information though. This change detects
linked source maps and rewrites `sourceMappingURL` comments to `http`
URLs pointing to Miniflare's loopback server. This then looks for the
source map relative to the known on-disk source location. Source
maps' `sourceRoot` attributes are updated to ensure correct locations
are displayed in DevTools.

**This enables breakpoint debugging for compiled TypeScript with
linked source maps!** 🎉

Closes DEVX-872
@changeset-bot
Copy link

changeset-bot bot commented Aug 15, 2023

⚠️ No Changeset found

Latest commit: 73c06dc

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@mrbbot mrbbot requested a review from a team August 15, 2023 16:50
}
let map: RawSourceMap;
try {
map = JSON.parse(contents);
Copy link
Contributor

@RamIdeas RamIdeas Aug 15, 2023

Choose a reason for hiding this comment

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

This could become a bottleneck. Sourcemap files can get quite large. JSON.parse can become expensive in those cases. Not only is this code loading the whole file into memory, it then parses the object into memory, and then stringifies it again, and then garbage collection might also take longer than normal.

Ideally we would stream the file from disk to the response.

JSON allows keys to be set twice and the last wins so I think we can replace the last } with , sourceRoot: "..." } – ideally in a TransformStream

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Makes sense! 👍 One thing that might complicate this is the new sourceRoot will depend on the existing sourceRoot if it's defined, so we'd also need to "parse" part of the JSON in a streaming fashion. What we're doing here certainly isn't ideal, but it's not on the hot path, and will only be called lazily as DevTools requests source maps. 🤔

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah it should be possible to extract a top-level prop from a stream but that could complicate the transform. I'll leave the decision to you whether we optimise it now or follow up with it later

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think I'm going to leave this as is for now. With the sourcesContent field in source maps containing arbitrary user code, we would effectively need a full streaming JSON parser to do this. https://www.npmjs.com/package/stream-json and https://www.npmjs.com/package/@streamparser/json look like good options, but I suspect this might be less efficient in the case of small-ish source files/maps. JSON.parse() is highly optimised. One other thing to consider is the limited size of Workers in general, currently up to 10MB compressed for paid accounts. This means the source maps we get are unlikely to be massive. If this becomes a problem though, we should definitely revisit this. 👍

@mrbbot mrbbot merged commit 4e2f424 into tre Aug 16, 2023
@mrbbot mrbbot deleted the bcoll/tre-linked-source-maps branch August 16, 2023 09:26
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.

2 participants