A source map is a JSON file that lets browsers map compiled code to original source code.
We rarely ship raw html/js/css in modern frontends - either because actual code is written in a different language (ts / sass) or uses a library (react) or because we want to reduce file size (minification). Source maps let us get the original source code since this compiled file is not human-readable.
Consider this example from Josh Comeau's CSS transitions post.
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[28274],{28274:(e,s,r)=>{"use strict";r.r(s),r.d(s,{default:()=>n});var t=r(29549);let c=(0,r(24299).I)("button")({name:"Wrapper",class:"w1fyi19c",propsAsIs:!1}),n=function(e){let{children:s}=e;return(0,t.jsx)(c,{children:s})};r(95292)},95292:e=>{e.exports={w1fyi19c:"w1fyi19c"}}}]);
//# sourceMappingURL=28274.8577756433e86cea.js.mapAbove is the compiled file or what's actually received by the browser.
Below is its corresponding source map file. Notice the sourceMappingURL comment line at the bottom - that's how mapping is set.
{
"version": 3,
"file": "static/chunks/28274.8577756433e86cea.js",
"mappings": "2IAMA,IAAMA,EAAUC,CAAAA,EAAAA,EAAH,OAAGA,CAAAA,CAAM,YAAAC,IAAA,WAAAC,KAAA,YAAAC,SAAA,KAiBrB,CAED,EAvBA,SAAoBC,CAAAA,UAAAA,KAAGC,CAAAA,CAAU,CAAZ,CAAc,CACjC,MAAO,UAAC,EAAQ,KAAD,CAAC,IAACA,GACnB,KAD2B,iBCF3B,WAAkB",
"sources": [
"webpack://_N_E/./src/post-helpers/css-transitions/EarlyButton.js",
"webpack://_N_E/./src/post-helpers/css-transitions/EarlyButton.linaria.module.css"
],
"names": ["Wrapper","styled","name","class","propsAsIs","EarlyButton","children"],
"sourcesContent": ["..."],
"sourceRoot": "",
"ignoreList": []
}These are the fields:
version: always 3 idk whyfile: compiled file this map belongs tomappings: base64 VLQ encoded strings (spec), maps lines and locations in compiled file to original filesources: list of original files that went into this chunknames: replacement strings (Wrapper->c)sourcesContent: literal source codesourceRoot: prefix for source pathsignoreList: used for ignoring third party libs and such
For context, this is the actual source file:
import { styled } from '@linaria/react';
function EarlyButton({ children }) {
return <Wrapper>{children}</Wrapper>;
}
const Wrapper = styled.button`
width: 100px;
height: 100px;
border-radius: 50%;
border: none;
background: hsl(345deg, 100%, 47%);
color: white;
font-size: 20px;
font-weight: 500;
line-height: 1;
text-align: center;
transition: transform 250ms;
will-change: transform;
&:hover {
transform: translateY(-10px);
}
`;
export default EarlyButton;Why tho
As I mentioned earlier, for debugging purpose, we want human-readable code. Something like r.r(s),r.d(s,{default:()=>n}) is unuseable. During development we want to be able to set breakpoints and such and this helps with that.
Browser figures out how to do that separating of files into different folders.
Shipping source maps to production is probably not a good idea. One could argue that security through obscurity does not work, but with source maps shipped you can see other information that'd typically not be visible in compiled code - things like comments, folder structure, potential business logic info etc. While you could reverse-engineer any client side logic (AI makes things super easy), having actual code just available like that makes things super easy.
There's one more need for source files, and that's for sharing with logging platforms like NewRelic and Sentry. Seeing a..
TypeError: Cannot read properties of undefined
at n (app.abcd.js:1:42069)
at r (app.abcd.js:1:676767)..is useless (especially in PROD), so we'd want a way to upload source maps to them.
This is typically handled in your build pipeline, where you generate the source maps and then upload them (but not ship to the client / make it available to the public). Example:
curl -H "Api-Key: YOUR_NEW_RELIC_USER_API_KEY" \
-F "sourcemap=@SOURCE_MAP_PATH" \
-F "javascriptUrl=JS_URL" \
-F "releaseId=YOUR_RELEASE_ID" \
-F "releaseName=YOUR_UI_PAGE" \
https://sourcemaps.service.newrelic.com/v2/applications/YOUR_NEW_RELIC_APP_ID/sourcemapsWhere can I see it
A VERY VERY important thing to note is that Source Maps are downloaded only when you open dev tools, a regular user will not get this massive file. Trace it in Chromium's devtools source: devtools-frontend.
[Chrome] It does not appear in your network tab - to see it, you have to open "Developer resources" tab.
More tools -> Developer resources.
Performance Notes
On DEV: there are various options to set the kind of source maps you want (source-map, eval-source-map, cheap-module-source-map etc in webpack), you can mess around with this to improve your HMR speeds.
On PROD: let's say you reeeeeally need source maps on PROD, there's not much in terms of perf. impact, as these fat files (remember, source maps have literal source code) are never downloaded for a normal visitor. It'll take up space in your server, but not really a thing to worry about wrt CDN costs for example - average person does not go open devtools on your site. They will however make builds slower and use more memory.
Thanks to Josh Comeau for leaving his source maps public.