---
title: What are source maps?
subhead: >
  Improve web debugging experience with source maps.
date: 2023-03-31
hero: image/dPDCek3EhZgLQPGtEG3y0fTn4v82/pHEJbtfbTxbY0UJCa7IX.jpg
alt: Tomato sauce
authors:
  - jecelynyeen
tags:
  - blog
  - sourcemap
  - javascript
  - css
---

{% YouTube 'FIYkjjFYvoI' %}

Today, we are talking about source maps, a crucial tool in modern web development that makes debugging significantly easier. In this article, we will explore the basics of source maps, how they are generated, and how they improve the debugging experience.

## The need for source maps

Back in the good old days, we built web applications with pure HTML, CSS, and JavaScript and deployed the same files to the web. 

However, as we are building more complex web applications nowadays, your development workflow may involve using various tools. For example:

- Templating languages and HTML preprocessors: [Pug](https://pugjs.org/), [Nunjucks](https://mozilla.github.io/nunjucks/), [Markdown](https://daringfireball.net/projects/markdown/).
- CSS preprocessors: [SCSS](https://sass-lang.com/), [LESS](https://lesscss.org/), [PostCSS](https://postcss.org/).
- JavaScript frameworks: Angular, React, Vue, Svelte.
- JavaScript meta frameworks: [Next.js](https://nextjs.org/), [Nuxt](https://nuxt.com/), [Astro](https://astro.build/).
- High-level programming languages: [TypeScript](https://www.typescriptlang.org/), [Dart](https://dart.dev/tools/dart2js), [CoffeeScript](https://coffeescript.org/).
- And more. The list can go on and on!

{% Img src="image/dPDCek3EhZgLQPGtEG3y0fTn4v82/DBPCct3WcUhRuA9qvu1i.png", alt="A brief overview of various tooling.", width="800", height="450" %}

These tools require a build process to transpile your code into standard HTML, JavaScript, and CSS that browsers can understand. Additionally, to optimize performance, it's common practice to compress (for example, using [Terser](https://github.com/terser/terser) to minify and mangle JavaScript) and combine these files, reducing their size and making them more efficient for the web.

For example, using build tools, we can transpile and compress the following TypeScript file into a single line of JavaScript. You can play with the [demo in my GitHub repository](https://github.com/jecfish/parcel-demo).

```ts
/* A TypeScript demo: example.ts */

document.querySelector('button')?.addEventListener('click', () => {
  const num: number = Math.floor(Math.random() * 101);
  const greet: string = 'Hello';
  (document.querySelector('p') as HTMLParagraphElement).innerText = `${greet}, you are no. ${num}!`;
  console.log(num);
});
```
A compressed version would be:

```js
/* A compressed JavaScript version of the TypeScript demo: example.min.js  */

document.querySelector("button")?.addEventListener("click",(()=>{const e=Math.floor(101*Math.random());document.querySelector("p").innerText=`Hello, you are no. ${e}!`,console.log(e)}));
```

However, this optimization can make debugging more challenging. Compressed code with everything in a single line and shorter variable names can make it difficult to pinpoint the source of an issue. That's where source maps come in—they map your compiled code back to the original code.

## Generating source maps

Source maps are files with names ending with `.map` (for example, `example.min.js.map` and `styles.css.map`). They can be generated by most build tools, for example, [Vite](https://vitejs.dev/), [webpack](https://webpack.js.org/), [Rollup](https://rollupjs.org/), [Parcel](https://parceljs.org/), [esbuild](https://esbuild.github.io/), and more. 

Some tools include source maps by default, while others may need additional configuration to produce them.

```js
/* Example configuration: vite.config.js */
/* https://vitejs.dev/config/ */

export default defineConfig({
  build: {
    sourcemap: true, // enable production source maps
  },
  css: {
    devSourcemap: true // enable CSS source maps during development
  }
})
```

## Understanding the source map

These source map files contain essential information about how the compiled code maps to the original code, enabling developers to debug with ease. Here is an example of a source map.

```js
{
  "mappings": "AAAAA,SAASC,cAAc,WAAWC, ...",
  "sources": ["src/script.ts"],
  "sourcesContent": ["document.querySelector('button')..."],
  "names": ["document","querySelector", ...],
  "version": 3,
  "file": "example.min.js.map"
}
```

To understand each of these fields, you can read the [source map specification](https://bit.ly/sourcemap) or this classic article on [the anatomy of a source map](https://developer.chrome.com/blog/sourcemaps/#the-anatomy-of-a-source-map).

The most critical aspect of a source map is the `mappings` field. It uses a [VLQ base 64 encoded string](https://developer.chrome.com/blog/sourcemaps/#base64-vlq-and-keeping-the-source-map-small) to map lines and locations in the compiled file to the corresponding original file. This mapping can be visualized using a source map visualizer like [source-map-visualization](https://sokra.github.io/source-map-visualization/) and [Source Map Visualization](https://evanw.github.io/source-map-visualization/).

<figure>
  {% Img src="image/dPDCek3EhZgLQPGtEG3y0fTn4v82/luYuSy7CYuB3ZdcCgM6A.png", alt="A source map visualization.", width="800", height="483" %}
  <figcaption>
    The image shows the visualization of our previous code example above, generated by a <a href="https://sokra.github.io/source-map-visualization/">visualizer</a>.
  </figcaption>
</figure>
 
The **generated** column on the left shows the compressed content and the **original** column shows the original source.

The visualizer color codes each line in the **original** column and its corresponding code in  the **generated** column.

The **mappings** section shows the decoded mappings of the code. For example, the entry `65-> 2:2` means:

- **Generated** code:  The word `const` starts at position 65 in the compressed content.
- **Original** code: The word `const` starts at line 2 and column 2 in the original content.

{% Img src="image/dPDCek3EhZgLQPGtEG3y0fTn4v82/VlYPU6oeB2OVcqfRHFbw.png", alt="Mapping entry.", width="800", height="483" %}

This way, developers can quickly identify the relationship between the minified code and the original code, making debugging a smoother process.

Browser Developer Tools apply these source maps to help you pinpoint your debugging issues quicker, right in the browsers.

{% Img src="image/dPDCek3EhZgLQPGtEG3y0fTn4v82/rfMBvs6g6bZ1Bxblj7cL.png", alt="Developer Tools applies a source map.", width="800", height="439" %}

The image shows how browser Developer Tools applies source maps and shows the mappings between files.

## Source map extensions

Source maps support extensions. Extensions are custom fields that start with the `x_` naming convention. One example is the `x_google_ignoreList` extension field proposed by Chrome DevTools. See [x_google_ignoreList](https://developer.chrome.com/articles/x-google-ignore-list) to learn more on how these extensions help you focus on your code.

## It's not perfect

In our example, the variable `greet` was optimized away during the build process. The value was directly embedded into the final string output.

{% Img src="image/dPDCek3EhZgLQPGtEG3y0fTn4v82/2HCLJ3TEHPi3DoNiiTp4.png", alt="Variaqble greet is not map.", width="800", height="483" %}

In this case, when you debug the code, developer tools might not be able to infer and display the actual value. This isn't just the challenges for browser's developer tools. It also makes code monitoring and analysis harder.

{% Img src="image/dPDCek3EhZgLQPGtEG3y0fTn4v82/BH1kRkjkjPAdFmeNqKbr.png", alt="Variable greet is undefined.", width="800", height="457" %}

This of course is a solvable problem. One of the ways is to include the scope information in the source maps like other programming languages do with their debug information.

However, this requires the whole ecosystem to work together to improve the source maps specification and implementation. There is an [active discussion](https://github.com/source-map/source-map-rfc/issues/12) on improving the debuggability with source maps.

We look forward to improving source maps and making debugging even less overwhelming!
