1 of 40

An introduction to�Source Maps

NICOLÒ RIBAUDO

@nicolo-ribaudo

@NicoloRibaudo

https://nicr.dev

https://nicr.dev

2 of 40

2

NICOLÒ RIBAUDO

  • Working at Igalia on web standards
  • TC39 delegate
  • Maintaining Babel, the JavaScript compiler

in collaboration with

https://nicr.dev

3 of 40

What are Source Maps?

3

https://nicr.dev

4 of 40

JavaScript is a compiled programming language

4

https://nicr.dev

5 of 40

5

User.ts

export class User {

@signal name;

constructor(name) {

this.name = name;

}

}

App.tsx

import { User } from "./User.ts";

export function App(

props: {user: User}

) {

return <div>

Hello {props.user.name}

</div>;

}

import _applyDecs from "@babel/runt…

�class User {

static {

[_init_name, _init_extra_name] …

}

name = _init_name(this);

constructor(name) {

_init_extra_name(this);

this.name = name;

}

}

import { User } from "./User.ts";

import { jsxs as _jsxs } from "preact/jsx-runtime";

export function App(props) {

return /*#__PURE__*/_jsxs("div", {

children: [

"Hello ",

props.user.name

]

});

}

function _applyDecs(targetClass, clas…

var symbolMetadata = Symbol.metadata ||

var defineProperty = Object.defineProperty;

var create = Object.create;

var metadata;

// Use both as and satisfies to ensure tha…

var existingNonFields = [create(null), cret…

var hasClassDecs = classDecs.length;

// This is a temporary variable for smaller…

var _;

function createRunInitializers(initializers

return function (thisArg, value) {

if (useStaticThis) {

value = thisArg;

thisArg = targetClass;

}

for (var i = 0; i < initializers.length

value = initializers[i].apply(thisArg

bundle.js

class e{static{[_init_name,_init_extra_name

]=function(e,t,n,r,i,o){var a,c,s,u,l,f,d,h=

Symbol.metadata||Symbol.for("Symbol.metadata"),m=Object.defineProperty,p=Object.create,v=[p(null),p(null)],g=t.length;function b(t,n,r)

{return function(i,o){n&&(o=i,i=e);for(var a=0;a<t.length;a++)o=t[a].apply(i,r?[o]:[]);return r?o:i}}function y(e,t,n,r){if("function

"!=typeof e&&(r||void 0!==e))throw new TypeEr

ror(t+" must "+(n||"be")+" a function"+(r?"":

" or undefined"));return e}function _(e,t,n,

r,i,o,s,u,l,f,d){function h(e){if(!d(e))throw new TypeError("Attempted to access private el

ement on non-instance")}var p=[].concat(t[0])

,g=t[3],_=!s,w=1===i,j=3===i,E=4===i,x=2===i;function O(t,n,r){return function(i,o){retu…

https://nicr.dev

6 of 40

The code you write is not the code you run

6

https://nicr.dev

7 of 40

How do you debug it?

7

https://nicr.dev

8 of 40

8

1 class State {

2 get isLoggedIn() {

3 return true;

4 }

5 get isAdmin() {

6 return false;

7 }

8 }

9

10 function canReadNames(state) {

11 console.log("Is logged in?", state.isLoggedIn);

12 return state.isLoggedIn && state.isAdmin;

13 }

14

15 const state = new State();

16 console.log("Can read names?", canReadNames());

https://nicr.dev

9 of 40

9

1 class S {

2 get isLoggedIn() {return true;}

3 get isAdmin() {return false;}

4 }

5 function n(state) {console.log("Is logged in?", state.isLoggedIn);ret…

6 const s = new S();

7 console.log("Can read names?", n());

https://nicr.dev

10 of 40

Source maps let devtools map the running code to the original code

10

https://nicr.dev

11 of 40

11

1 class State {

2 get isLoggedIn() {

3 return true;

4 }

5 get isAdmin() {

6 return false;

7 }

8 }

9

10 function canReadNames(state) {

11 console.log("Is logged in?", state.isLoggedIn);

12 return state.isLoggedIn && state.isAdmin;

13 }

14

15 const state = new State();

16 console.log("Can read names?", canReadNames());

https://nicr.dev

12 of 40

Not just for JavaScript!

12

https://nicr.dev

13 of 40

Anatomy of a�Source Map

13

https://nicr.dev

14 of 40

14

1 {

2 "version": 3,

3 "file": "main.js",

4 "names": ["AmazingPet","name","title","class_Pet","prototype","sayHi","console…

5 "sources": [

6 "../src/main.ts"

7 ],

8 "sourcesContent": [

9 "interface PetConfig {\n name: string;\n title: string;\n}\n\nclass Amazin…

10 ],

11 "mappings": "AAKA,SAAMA,gBAAUA,CAIF;EAAEC,IAAI;EAAEC;AAAiB,CAAC,EAAE;EACtC,IAA…

12 }

https://nicr.dev

15 of 40

15

1 {

2 "version": 3,

3 "file": "main.js",

4 "names": ["AmazingPet","name","title","class_Pet","prototype","sayHi","console…

5 "sources": [

6 "../src/main.ts"

7 ],

8 "sourcesContent": [

9 "interface PetConfig {\n name: string;\n title: string;\n}\n\nclass Amazin…

10 ],

11 "mappings": "AAKA,SAAMA,gBAAUA,CAIF;EAAEC,IAAI;EAAEC;AAAiB,CAAC,EAAE;EACtC,IAA…

12 }

{

"version": 3,

"file": "main.js",

"names": ["AmazingPet","name","title","class_Pet","prototype","sayHi","console…

"sources": [

"../src/main.ts"

],

"sourcesContent": [

"interface PetConfig {\n name: string;\n title: string;\n}\n\nclass Amazin…

],

"mappings": "AAKA,SAAMA,gBAAUA,CAIF;EAAEC,IAAI;EAAEC;AAAiB,CAAC,EAAE;EACtC,IAA…

}

https://nicr.dev

16 of 40

16

1 {

2 "version": 3,

3 "file": "main.js",

4 "names": ["AmazingPet","name","title","class_Pet","prototype","sayHi","console…

5 "sources": [

6 "../src/main.ts"

7 ],

8 "sourcesContent": [

9 "interface PetConfig {\n name: string;\n title: string;\n}\n\nclass Amazin…

10 ],

11 "mappings": "AAKA,SAAMA,gBAAUA,CAIF;EAAEC,IAAI;EAAEC;AAAiB,CAAC,EAAE;EACtC,IAA…

12 }

{

"file": "main.js",

"names": ["AmazingPet","name","title","class_Pet","prototype","sayHi","console…

"sources": [

"../src/main.ts"

],

"sourcesContent": [

"interface PetConfig {\n name: string;\n title: string;\n}\n\nclass Amazin…

],

"mappings": "AAKA,SAAMA,gBAAUA,CAIF;EAAEC,IAAI;EAAEC;AAAiB,CAAC,EAAE;EACtC,IAA…

}

https://nicr.dev

17 of 40

17

1 {

2 "version": 3,

3 "file": "main.js",

4 "names": ["AmazingPet","name","title","class_Pet","prototype","sayHi","console…

5 "sources": [

6 "../src/main.ts"

7 ],

8 "sourcesContent": [

9 "interface PetConfig {\n name: string;\n title: string;\n}\n\nclass Amazin…

10 ],

11 "mappings": "AAKA,SAAMA,gBAAUA,CAIF;EAAEC,IAAI;EAAEC;AAAiB,CAAC,EAAE;EACtC,IAA…

12 }

{

"version": 3,

"names": ["AmazingPet","name","title","class_Pet","prototype","sayHi","console…

"sources": [

"../src/main.ts"

],

"sourcesContent": [

"interface PetConfig {\n name: string;\n title: string;\n}\n\nclass Amazin…

],

"mappings": "AAKA,SAAMA,gBAAUA,CAIF;EAAEC,IAAI;EAAEC;AAAiB,CAAC,EAAE;EACtC,IAA…

}

https://nicr.dev

18 of 40

18

1 {

2 "version": 3,

3 "file": "main.js",

4 "names": ["AmazingPet","name","title","class_Pet","prototype","sayHi","console…

5 "sources": [

6 "../src/main.ts"

7 ],

8 "sourcesContent": [

9 "interface PetConfig {\n name: string;\n title: string;\n}\n\nclass Amazin…

10 ],

11 "mappings": "AAKA,SAAMA,gBAAUA,CAIF;EAAEC,IAAI;EAAEC;AAAiB,CAAC,EAAE;EACtC,IAA…

12 }

{

"version": 3,

"file": "main.js",

"AmazingPet","name","title","class_Pet","prototype","sayHi","console…

"sources": [

"../src/main.ts"

],

"sourcesContent": [

"interface PetConfig {\n name: string;\n title: string;\n}\n\nclass Amazin…

],

"mappings": "AAKA,SAAMA,gBAAUA,CAIF;EAAEC,IAAI;EAAEC;AAAiB,CAAC,EAAE;EACtC,IAA…

}

https://nicr.dev

19 of 40

19

1 {

2 "version": 3,

3 "file": "main.js",

4 "names": ["AmazingPet","name","title","class_Pet","prototype","sayHi","console…

5 "sources": [

6 "../src/main.ts"

7 ],

8 "sourcesContent": [

9 "interface PetConfig {\n name: string;\n title: string;\n}\n\nclass Amazin…

10 ],

11 "mappings": "AAKA,SAAMA,gBAAUA,CAIF;EAAEC,IAAI;EAAEC;AAAiB,CAAC,EAAE;EACtC,IAA…

12 }

{

"version": 3,

"file": "main.js",

"names": ["AmazingPet","name","title","class_Pet","prototype","sayHi","console…

"sourcesContent": [

"interface PetConfig {\n name: string;\n title: string;\n}\n\nclass Amazin…

],

"mappings": "AAKA,SAAMA,gBAAUA,CAIF;EAAEC,IAAI;EAAEC;AAAiB,CAAC,EAAE;EACtC,IAA…

}

https://nicr.dev

20 of 40

sources

20

dist/main.js

function class_AmazingPet({

name,

title

}) {

this.name = name;

this.title = title;

}

class_AmazingPet.prototype.sayHi = fu…

console.log(`Hello, I'm ${this.titl…

};

const timmy = new class_AmazingPet({

name: "Timmy",

… … …

src/main.ts

"sources": [

"../src/main.ts"

],

https://nicr.dev

21 of 40

sources

21

dist/main.js

function class_AmazingPet({

name,

title

}) {

this.name = name;

this.title = title;

}

class_AmazingPet.prototype.sayHi = fu…

console.log(`Hello, I'm ${this.titl…

};

const timmy = new class_AmazingPet({

name: "Timmy",

… … …

src/main.ts

"sources": [

"../src/main.ts",

"../src/other.js"

],

src/other.js

https://nicr.dev

22 of 40

22

1 {

2 "version": 3,

3 "file": "main.js",

4 "names": ["AmazingPet","name","title","class_Pet","prototype","sayHi","console…

5 "sources": [

6 "../src/main.ts"

7 ],

8 "sourcesContent": [

9 "interface PetConfig {\n name: string;\n title: string;\n}\n\nclass Amazin…

10 ],

11 "mappings": "AAKA,SAAMA,gBAAUA,CAIF;EAAEC,IAAI;EAAEC;AAAiB,CAAC,EAAE;EACtC,IAA…

12 }

{

"version": 3,

"file": "main.js",

"names": ["AmazingPet","name","title","class_Pet","prototype","sayHi","console…

"sources": [

"../src/main.ts"

],

"mappings": "AAKA,SAAMA,gBAAUA,CAIF;EAAEC,IAAI;EAAEC;AAAiB,CAAC,EAAE;EACtC,IAA…

}

https://nicr.dev

23 of 40

sourcesContent

23

dist/main.js

function class_AmazingPet({

name,

title

}) {

this.name = name;

this.title = title;

}

class_AmazingPet.prototype.sayHi = fu…

console.log(`Hello, I'm ${this.titl…

};

const timmy = new class_AmazingPet({

name: "Timmy",

… … …

src/main.ts

interface PetConfig {

name: string;

title: string;

}

class AmazingPet {

declare name: string;

declare title: string;

constructor({ name, title }: PetCon…

this.name = name;

this.title = title;

… … …

"sourcesContent": [

"interface PetConfig {\n

],

https://nicr.dev

24 of 40

sourcesContent

24

dist/main.js

function class_AmazingPet({

name,

title

}) {

this.name = name;

this.title = title;

}

class_AmazingPet.prototype.sayHi = fu…

console.log(`Hello, I'm ${this.titl…

};

const timmy = new class_AmazingPet({

name: "Timmy",

… … …

src/main.ts

src/other.js

"sourcesContent": [

"interface PetConfig {\n

"// some other file\n// c…

],

interface PetConfig {

name: string;

title: string;

}

class AmazingPet {

… … …

// some other file

// containing many

// interesting things

console.log("Hi!");

https://nicr.dev

25 of 40

sources and sourcesContent let devtools show the original files

25

https://nicr.dev

26 of 40

How to they know that

this point

was originally this one?

26

8 class_AmazingPet.prototype.sayHi = function sayHi() {

9 console.log(`Hello, I'm ${this.title} ${this.name}`);

10 };

15 sayHi() {

16 console.log(`Hello, I'm ${this.title} ${this.name}`);

17 }

https://nicr.dev

27 of 40

27

1 {

2 "version": 3,

3 "file": "main.js",

4 "names": ["AmazingPet","name","title","class_Pet","prototype","sayHi","console…

5 "sources": [

6 "../src/main.ts"

7 ],

8 "sourcesContent": [

9 "interface PetConfig {\n name: string;\n title: string;\n}\n\nclass Amazin…

10 ],

11 "mappings": "AAKA,SAAMA,gBAAUA,CAIF;EAAEC,IAAI;EAAEC;AAAiB,CAAC,EAAE;EACtC,IAA…

12 }

{

"version": 3,

"file": "main.js",

"names": ["AmazingPet","name","title","class_Pet","prototype","sayHi","console…

"sources": [

"../src/main.ts"

],

"sourcesContent": [

"interface PetConfig {\n name: string;\n title: string;\n}\n\nclass Amazin…

],

}

https://nicr.dev

28 of 40

28

"mappings":"AAKA,SAAMA,gBAAUA,CAIF;EAAEC,IAAI;EAAEC;AAAiB,CAAC,EAAE;EACtC,IAAI,CAACD,IAAI,GAAGA,IAAI;EAChB,IAAI,CAACC,KAAK,GAAGA,KAAK;AACpB;AAACC,SAAA,CAAAC,SAAA,CAAAC,KAAA,GAED,SAAAA,MAAA,EAAQ;EACNC,OAAO,CAACC,GAAG,CAAC,cAAc,IAAI,CAACL,KAAK,IAAI,IAAI,CAACD,IAAI,EAAE,CAAC;AACtD,CAAC;AAGH,MAAMO,KAAK,GAAG,IAAIR,SAAG,CAAC;EAAEC,IAAI,EAAE,OAAO;EAAEC,KAAK,EAAE;AAAK,CAAC,CAAC;AACrDM,KAAK,CAACH,KAAK,CAAC,CAAC"

https://nicr.dev

29 of 40

29

1 function class_AmazingPet({

2 name,

3 title

4 }) {

5 this.name = name;

6 this.title = title;

7 }

8 class_AmazingPet.prototype.sayHi = function sayH…

9 console.log(`Hello, I'm ${this.title} ${this.n…

10 };

11 const timmy = new class_AmazingPet({

12 name: "Timmy",

13 title: "Dr"

14 });

15 timmy.sayHi();

AAKA,SAAMA,gBAAUA,CAIF;�EAAEC,IAAI;�EAAEC;�AAAiB,CAAC,EAAE;�EACtC,IAAI,CAACD,IAAI,GAAGA…;

EAChB,IAAI,CAACC,KAAK,GAAGA…;

AACpB;

AAACC,SAAA,CAAAC,SAAA,CAAAC…;

EACNC,OAAO,CAACC,GAAG,CAAC,…;

AACtD,CAAC;

AAGH,MAAMO,KAAK,GAAG,IAAIR,…;

EAAEC,IAAI,EAAE,OAAO;

EAAEC,KAAK,EAAE;

AAAK,CAAC,CAAC;

AACrDM,KAAK,CAACH,KAAK,CAAC,…

https://nicr.dev

30 of 40

30

https://nicr.dev

31 of 40

The Source Map standard

31

https://nicr.dev

32 of 40

32

2011–2023

https://nicr.dev

33 of 40

33

2024

https://nicr.dev

34 of 40

Why a standard?

34

https://nicr.dev

35 of 40

Source Map in TC39: goals

  • Ensure compatibility between implementations
  • Ensure that the specification is complete
  • Ensure that the specification matches implementations

35

https://nicr.dev

36 of 40

New Source Map features

36

https://nicr.dev

37 of 40

"ignoreList": […]

37

1 {

2 "version": 3,

3 "file": "bundle.js",

4 "names": [],

5 "sources": [

6 "../src/main.ts",

7 "../src/utils.ts",

8 "../node_modules/some-lib/index.js",

9 "bundler-iternal://helpers"

10 ],

11 "ignoreList": [2, 3],

… … …

Ignore some source files in stack traces and debuggers

https://nicr.dev

38 of 40

Scopes proposal

Source maps allow mapping locations and� variable names from the final code to the original source

38

Is this enough?

https://nicr.dev

39 of 40

Scopes proposal

39

1 function logIfBig(num) {

2 const treeshold = 2;

3 if (num > treeshold) {

4 console.log("big: " + num);

5 }

6 }

7 export function run(val) {

8 for (let i = 0; i < val; i++) {

9 logIfBig(i);

10 }

11 }

src/main.js

1 export function run(v) {

2 for(let i=0;i<v;i++) {

3 if (i>2) {

4 console.log("big: " + i);

5 }

6 }

7 }

dist/main.min.js

logIfBig is inlined inside run

treeshold is replaced with 2

https://nicr.dev

40 of 40

An introduction�Source Maps

NICOLÒ RIBAUDO

@nicolo-ribaudo

@NicoloRibaudo

https://nicr.dev

https://nicr.dev