From ea466878865f66f88bf845e216cb3ad32c4f95b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aki=20=F0=9F=8C=B9?= Date: Fri, 15 Nov 2024 19:31:28 -0800 Subject: [PATCH] 2024 PDF files --- 2024-PDF/README.md | 16 + 2024-PDF/ecma-print-styles.css | 813 ++++++++++ .../fonts/IBMPlexMono-Bold-SlashedZero.woff2 | Bin 0 -> 10548 bytes .../IBMPlexMono-BoldItalic-SlashedZero.woff2 | Bin 0 -> 11932 bytes .../IBMPlexMono-Italic-SlashedZero.woff2 | Bin 0 -> 11948 bytes .../IBMPlexMono-Regular-SlashedZero.woff2 | Bin 0 -> 10396 bytes 2024-PDF/img/ecma-header.svg | 1 + 2024-PDF/img/print-front-cover.svg | 10 + 2024-PDF/img/print-inside-cover.svg | 59 + 2024-PDF/index.html | 1306 +++++++++++++++++ 2024-PDF/print.js | 104 ++ 11 files changed, 2309 insertions(+) create mode 100644 2024-PDF/README.md create mode 100644 2024-PDF/ecma-print-styles.css create mode 100644 2024-PDF/fonts/IBMPlexMono-Bold-SlashedZero.woff2 create mode 100644 2024-PDF/fonts/IBMPlexMono-BoldItalic-SlashedZero.woff2 create mode 100644 2024-PDF/fonts/IBMPlexMono-Italic-SlashedZero.woff2 create mode 100644 2024-PDF/fonts/IBMPlexMono-Regular-SlashedZero.woff2 create mode 100644 2024-PDF/img/ecma-header.svg create mode 100644 2024-PDF/img/print-front-cover.svg create mode 100644 2024-PDF/img/print-inside-cover.svg create mode 100644 2024-PDF/index.html create mode 100644 2024-PDF/print.js diff --git a/2024-PDF/README.md b/2024-PDF/README.md new file mode 100644 index 0000000..0e5296b --- /dev/null +++ b/2024-PDF/README.md @@ -0,0 +1,16 @@ +# 2024 Edition publication process + +This year's PDF version was manually created by generating an html file from bikeshed and then +manually modifying the markup to be able to re-use the stylesheets from ecmarkup used to generate +various other Ecma programming-related standards. This directory contains: +- ecma-print-styles.css, primarily a subset of ecmarkup css with some additional bespoke style rules to create better "issue" and "example" elements +- fonts/, specifically to support the zero-with-dot character option +- img/, cover background images and header image +- index.html, the final markup used for the PDF +- print.js, a pared-down version of the script used by PrinceXML to generate larger Ecma standards publications + +The command used to generate PDF was the following, run from this directory: + +```bash +prince-books --tagged-pdf --script ./print.js ./index.html -o ECMA-XXX-source-map.pdf +``` \ No newline at end of file diff --git a/2024-PDF/ecma-print-styles.css b/2024-PDF/ecma-print-styles.css new file mode 100644 index 0000000..21e05cb --- /dev/null +++ b/2024-PDF/ecma-print-styles.css @@ -0,0 +1,813 @@ +/** +* TODO: +* - set string variable for year +* - copyright notice letter-spacing: 0.3pt; +* +* +* +* +* +*/ + +@font-face { + font-family: "Arial Plus"; + src: local("Arial"); +} + +@font-face { + font-family: "Arial Plus"; + unicode-range: U+30; + font-style: normal; + font-weight: 400; + src: url("fonts/IBMPlexMono-Regular-SlashedZero.woff2"), local("IBM Plex Mono Regular"), local(IBMPlexMono-Regular); +} + +@font-face { + font-family: "Arial Plus"; + unicode-range: U+221e; + font-style: normal; + font-weight: 400; + src: local("DejaVu Math TeX Gyre"); +} + +@font-face { + font-family: "Arial Plus"; + unicode-range: U+30; + font-style: normal; + font-weight: 700; + src: url("fonts/IBMPlexMono-Bold-SlashedZero.woff2"), local("IBM Plex Mono Bold"), local(IBMPlexMono-Bold); +} + +@font-face { + font-family: "Arial Plus"; + unicode-range: U+221e; + font-style: normal; + font-weight: 700; + src: local("DejaVu Math TeX Gyre"); +} + +@font-face { + font-family: "Arial Plus"; + unicode-range: U+30; + font-style: italic; + font-weight: 400; + src: url("fonts/IBMPlexMono-Italic-SlashedZero.woff2"), local("IBM Plex Mono Italic"), local(IBMPlexMono-Italic); +} + +@font-face { + font-family: "Arial Plus"; + unicode-range: U+221e; + font-style: italic; + font-weight: 400; + src: local("DejaVu Math TeX Gyre"); +} + +@font-face { + font-family: "Arial Plus"; + unicode-range: U+30; + font-style: italic; + font-weight: 700; + src: url("fonts/IBMPlexMono-BoldItalic-SlashedZero.woff2"), local("IBM Plex Mono Bold Italic"), local(IBMPlexMono-BoldItalic); +} + +@font-face { + font-family: "Arial Plus"; + unicode-range: U+221e; + font-style: italic; + font-weight: 700; + src: local("DejaVu Math TeX Gyre"); +} + +:root { + --page-number-style: decimal; +} + +@page { + size: A4; + margin-top: 28mm; + margin-bottom: 20mm; + margin-inside: 19mm; + margin-outside: 13mm; + -prince-page-fill: prefer-fill; + + /* Uncomment when producing WIP versions of final standards */ + /* + @prince-overlay { + color: rgba(0,0,0,0.15); + content: "WORK IN PROGRESS"; + font-family: Arial; + font-weight: bolder; + font-size: 100pt; + transform: rotate(-60deg); + } + */ + + @bottom-left { + font-family: Arial; + } + + @bottom-right { + font-family: Arial; + } +} + +@page :verso { + @top-left { + content: url("img/ecma-header.svg"); + padding-top: 5mm; + } + + @bottom-left { + content: counter(page, var(--page-number-style)); + font-size: 10pt; + } + + @bottom-right { + content: "© Ecma International " string(year, first); + font-size: 8pt; + } +} + +@page :recto { + @top-right { + content: url("img/ecma-header.svg"); + padding-top: 5mm; + } + + @bottom-left { + content: "© Ecma International " string(year, first); + font-size: 8pt; + } + + @bottom-right { + content: counter(page, var(--page-number-style)); + font-size: 10pt; + } +} + +@page :first, :nth(2) { + margin: 0; + + @top-left { + content: none; + } + + @top-right { + content: none; + } + + @bottom-left { + content: none; + } + + @bottom-right { + content: none; + } +} + +@page toc, copyright, intro { + --page-number-style: lower-roman; +} + +@page :blank { + @bottom-left { + content: none; + } + + @bottom-right { + content: none; + } +} + +@page front-cover { + background-image: url("img/print-front-cover.svg"); +} + +@page inside-cover { + background-image: url("img/print-inside-cover.svg"); +} + +@page front-cover, inside-cover { + background-color: transparent; + background-position: center; + background-repeat: no-repeat; + margin: 0; + padding: 0; + page-break-after: always; +} + +html, body { + background-color: initial; +} + +body { + color: #000; + counter-reset: example figure issue; + font-family: "Arial Plus", Arial, Helvetica, sans-serif, "DejaVu Math TeX Gyre", Symbola, monospace; + font-size: 10pt; + line-height: 1.15; + margin: 0; + text-align: justify; +} + +body > emu-clause { + -prince-pdf-tag-type: Part; +} + +h1 { + font-size: 12pt; + line-height: 1.09375; + margin-top: 20pt; + margin-bottom: 19pt; +} + +h2 { + font-size: 11pt; + -prince-bookmark-state: closed; +} + +h3, h4, h5, h6 { + font-size: 10pt; +} + +h1, h2, h3, h4, h5, h6 { + -prince-bookmark-label: content(); +} + +h1.shortname, h1.version, h1.title { + -prince-bookmark-level: none; +} + +h1.shortname { + top: 86mm; + font-weight: 400; + font-size: 21pt; + right: 31mm; + text-align: right; + margin-top: 0; +} + +h1.shortname a:link, +h1.shortname a:visited, +h1.shortname a:hover, +h1.shortname a:active { + color: black; +} + +h1.shortname .status { + display: inline-block; + margin-right: 7em; + text-transform: capitalize; +} + +h1.version { + font-size: 9.7pt; + font-weight: normal; + margin-top: 0; + text-align: right; + top: 96mm; + left: 139mm; + string-set: year attr(data-year); +} + +#front-cover { + page: front-cover; + position: relative; + width: 210mm; + height: 297mm; +} + +#front-cover h1 { + color: black; + display: block; + font-family: Verdana; + position: absolute; +} + +#front-cover h1.title { + font-weight: bold; + font-size: 20pt; + line-height: 1.2; + top: 109mm; + right: 15mm; + width: 95mm; + text-align: left; +} + +#inside-cover { + page: inside-cover; +} + +#inside-cover address { + color: black; + font-size: 12pt; + font-style: normal; + position: relative; + left: 72mm; + top: 82mm; +} + +.copyright-notice + h1.title { + break-before: recto; + counter-reset: page 1; + font-size: 16pt; + margin-bottom: 38pt; +} + +.copyright-notice { /* ecma mandated */ + font-style: italic; + border: 1px solid black; + padding: 1em; + page: copyright; + page-break-before: always; + page-break-after: always; +} + +.full-page-svg { + color: rgba(255, 255, 255, 0); +} + +a { + text-decoration: none; + color: #206ca7; +} + +a:visited { + color: #206ca7; +} + +a:hover { + text-decoration: underline; + color: #239dee; +} + +cite { font-style: italic; } + +p { + text-align: justify; + text-rendering: optimizeLegibility; + text-wrap: pretty; + overflow-wrap: break-word; + hyphens: auto; + orphans: 2; + widows: 2; + margin: 0 0 12pt; +} + +#toc, +#spec-container > emu-intro, +#spec-container > emu-annex { + break-before: recto; + break-after: page; +} + +#toc { + page: toc; + counter-reset: page 1; +} + +#toc h2 { + font-size: 12pt; + margin-bottom: 1.5ex; +} + +#toc > h2 { + -prince-bookmark-level: 1; +} + +#toc > h2::after { + content: "page"; + float: right; + font-size: 10pt; + text-align: right; +} + +#toc a { + color: #000; +} + +#toc a[href]::after { + content: leader(dotted) target-counter(attr(href), page); +} + +#toc > ol.toc { + margin-top: 0; +} + +/* skip the Introduction since it's before the first emu-clause (and therefore doesn't have a proper page number) */ +#toc > ol > li:first-child { + display: none; +} + +#toc > ol > li { + margin-top: 0.75ex; +} + +ol.toc { + font-weight: bold; + list-style: none; + padding-left: 0; + margin-left: 0; + -prince-pdf-tag-type: TOC; +} + +ol.toc .secnum { + display: inline-block; + min-width: 3.25em; +} + +ol.toc li { + text-indent: 35pt hanging; + -prince-pdf-tag-type: TOCI; +} + +ol.toc span.info { + font-weight: normal; +} + +a[data-print-href]::after { + content: " <" attr(href) ">"; + color: initial; +} + +.secnum { + font-family: Arial, Helvetica, sans-serif; +} + +h1 .secnum { + text-decoration: none; + margin-right: 5px; +} + +h1 .secnum:empty { + margin: 0; + padding: 0; +} + +dl { + text-align: left; +} + +dt, dfn { + font-weight: bold; + break-after: avoid; +} + +dd { + break-inside: avoid; + margin: 0 0 .5em 2em; +} + +emu-intro, emu-clause, emu-annex { + display: block; + margin-top: 4ex; +} + +emu-intro { + -prince-pdf-tag-type: Part; + page: intro; +} + +emu-clause { prince-pdf-tag-type: Art; } + +emu-clause p:first-of-type { + margin-bottom: 0; + orphans: 3; + break-after: avoid-page; +} + +emu-clause p:last-child { + break-after: auto; + margin-bottom: 0; +} + +emu-clause > p:only-of-type { + break-after: auto; +} + +emu-clause > p:first-of-type + emu-alg { + break-before: avoid; +} + +emu-intro emu-intro, emu-clause emu-clause, emu-annex emu-annex { + margin-top: 3.5ex; +} + +emu-intro emu-intro emu-intro, +emu-clause emu-clause emu-clause, +emu-annex emu-annex emu-annex, +emu-intro emu-intro emu-intro emu-intro, +emu-clause emu-clause emu-clause emu-clause, +emu-annex emu-annex emu-annex emu-annex, +emu-intro emu-intro emu-intro emu-intro emu-intro, +emu-clause emu-clause emu-clause emu-clause emu-clause, +emu-annex emu-annex emu-annex emu-annex emu-annex { + margin-top: 3.2ex; +} + +emu-intro h1, emu-clause h1 , emu-annex h1 { + break-after: avoid; + font-size: 12pt; + -prince-bookmark-level: 1; + -prince-bookmark-label: content(); +} + +emu-clause emu-clause h1, emu-annex emu-annex h1 { + -prince-bookmark-level: 2; + -prince-bookmark-state: closed; +} + +emu-clause emu-clause h1, emu-annex emu-annex h1, +emu-intro h2, emu-clause h2, emu-annex h2 { + font-size: 11pt; +} + +emu-clause ol, emu-clause ul, emu-clause dl, emu-annex ol, emu-annex ul, emu-annex dl { + margin-left: 0; + padding-left: 1.75em; +} + +emu-clause ol ol, emu-clause ul ul { + padding-left: 2em; +} + +emu-annex header h1, emu-annex header h2, emu-annex header p { + font-size: 14pt; + font-weight: bold; + text-align: center; +} + +emu-annex header em { + font-style: normal; + font-weight: normal; +} + +a[href^="#"]{ + -prince-pdf-tag-type: Reference; +} + +emu-note { + margin: 1em 0; + display: flex; + flex-direction: row; + color: inherit; + padding: 10px 10px 10px 0; + overflow-x: auto; + -prince-pdf-tag-type: Sect; +} + +emu-note { + border-left: 5px solid #52e052; + background: #e9fbe9; + gap: initial; +} + +emu-note::before { + content: "NOTE "; + flex: 0 0 4.5em; + font-size: 9pt; + margin-top: 0.125em; + padding-left: 0.5em; + text-align: left; +} + +emu-note, emu-note p{ + break-inside: avoid; +} + +emu-note p { + text-align: left; + hyphens: manual; + overflow: hidden; +} + +emu-note > span.note { + white-space: nowrap; + flex-grow: 0; + flex-shrink: 1; + text-transform: uppercase; + padding-left: 5px; +} + +emu-note[issue] { + border-left-color: #962626; + background-color: #f1d1d1; + string-set: issues content() +} + +emu-note[issue]::before, emu-note[example]::before { flex-basis: 5.5em; } + +emu-note[issue]::before { + content: "Issue " counter(issue); + counter-increment: issue; +} + +emu-note[example] { + background-color: #ffeedd; + border-left-color: #ff6600; +} + +emu-note[example]::before { + content: "Example " counter(example); + counter-increment: example; +} + +emu-note[example] figure { + margin: 0; +} + +emu-note > div.note-contents { + flex-grow: 1; + flex-shrink: 1; + overflow: auto; +} + +emu-note > div.note-contents > p:first-of-type { + margin-top: 0; +} + +emu-note > div.note-contents > p:last-of-type { + margin-bottom: 0; +} + +emu-figure figure { + display: flex; + flex-direction: column; + align-items: center; + break-inside: avoid; + counter-increment: figure; +} + +figure figcaption { + display: block; + color: #555555; + font-weight: bold; + text-align: center; +} + +/* depth 1 */ +emu-alg ol, + /* depth 4 */ +emu-alg ol ol ol ol, +emu-alg ol.nested-thrice, +emu-alg ol.nested-twice ol, +emu-alg ol.nested-once ol ol { + list-style-type: decimal; +} + +/* depth 2 */ +emu-alg ol ol, +emu-alg ol.nested-once, + /* depth 5 */ +emu-alg ol ol ol ol ol, +emu-alg ol.nested-four-times, +emu-alg ol.nested-thrice ol, +emu-alg ol.nested-twice ol ol, +emu-alg ol.nested-once ol ol ol { + list-style-type: lower-alpha; +} + +/* depth 3 */ +emu-alg ol ol ol, +emu-alg ol.nested-twice, +emu-alg ol.nested-once ol, + /* depth 6 */ +emu-alg ol ol ol ol ol ol, +emu-alg ol.nested-lots, +emu-alg ol.nested-four-times ol, +emu-alg ol.nested-thrice ol ol, +emu-alg ol.nested-twice ol ol ol, +emu-alg ol.nested-once ol ol ol ol, + /* depth 7+ */ +emu-alg ol.nested-lots ol { + list-style-type: lower-roman; +} + +emu-alg ul { + list-style-type: square; +} + +emu-alg li { + orphans: 2; + widows: 2; +} + +emu-alg > ol > li:first-child { + break-after: avoid-page; +} + +emu-alg ol li:last-child { + break-before: avoid; + break-after: initial; /* it's okay to break after the last item in a list, even if it's also the first item in the list */ +} + +emu-clause > p:first-of-type + emu-alg { + break-before: avoid; +} + +.index { + columns: 3; + column-gap: 5%; + text-align: left; + -prince-pdf-tag-type: Index; +} + +.index > li { + margin: 1em 0 .5em; +} + +#issues-index { + counter-reset: issue; +} + +pre, code { + font-family: "Comic Code", "IBM Plex Mono", monospace; + white-space: pre; +} + +code { + font-weight: bold; +} + +code.highlight { + padding: .1em; + border-radius: .3em; + display: inline-block; +} + +code.highlight > * { + display: inline; +} + +pre.highlight, pre > code.highlight { + display: block; + padding: 1em; + margin: .5em 0; + overflow: auto; + border-radius: 0; +} + +.highlight { background: rgba(0, 0, 0, .03); } + +var { + border-radius: 5px; + color: #218379; + transition: all 0.25s ease-out; + cursor: pointer; + padding: 0 4px; + margin: 0 -4px; + mix-blend-mode: multiply; +} + +c-[a] { color: #990055 } /* Keyword.Declaration */ +c-[b] { color: #990055 } /* Keyword.Type */ +c-[c] { color: #708090 } /* Comment */ +c-[d] { color: #708090 } /* Comment.Multiline */ +c-[e] { color: #0077aa } /* Name.Attribute */ +c-[f] { color: #669900 } /* Name.Tag */ +c-[g] { color: #222222 } /* Name.Variable */ +c-[k] { color: #990055 } /* Keyword */ +c-[l] { color: #000000 } /* Literal */ +c-[m] { color: #000000 } /* Literal.Number */ +c-[n] { color: #0077aa } /* Name */ +c-[o] { color: #999999 } /* Operator */ +c-[p] { color: #999999 } /* Punctuation */ +c-[s] { color: #a67f59 } /* Literal.String */ +c-[t] { color: #a67f59 } /* Literal.String.Single */ +c-[u] { color: #a67f59 } /* Literal.String.Double */ +c-[cp] { color: #708090 } /* Comment.Preproc */ +c-[c1] { color: #708090 } /* Comment.Single */ +c-[cs] { color: #708090 } /* Comment.Special */ +c-[kc] { color: #990055 } /* Keyword.Constant */ +c-[kn] { color: #990055 } /* Keyword.Namespace */ +c-[kp] { color: #990055 } /* Keyword.Pseudo */ +c-[kr] { color: #990055 } /* Keyword.Reserved */ +c-[ld] { color: #000000 } /* Literal.Date */ +c-[nc] { color: #0077aa } /* Name.Class */ +c-[no] { color: #0077aa } /* Name.Constant */ +c-[nd] { color: #0077aa } /* Name.Decorator */ +c-[ni] { color: #0077aa } /* Name.Entity */ +c-[ne] { color: #0077aa } /* Name.Exception */ +c-[nf] { color: #0077aa } /* Name.Function */ +c-[nl] { color: #0077aa } /* Name.Label */ +c-[nn] { color: #0077aa } /* Name.Namespace */ +c-[py] { color: #0077aa } /* Name.Property */ +c-[ow] { color: #999999 } /* Operator.Word */ +c-[mb] { color: #000000 } /* Literal.Number.Bin */ +c-[mf] { color: #000000 } /* Literal.Number.Float */ +c-[mh] { color: #000000 } /* Literal.Number.Hex */ +c-[mi] { color: #000000 } /* Literal.Number.Integer */ +c-[mo] { color: #000000 } /* Literal.Number.Oct */ +c-[sb] { color: #a67f59 } /* Literal.String.Backtick */ +c-[sc] { color: #a67f59 } /* Literal.String.Char */ +c-[sd] { color: #a67f59 } /* Literal.String.Doc */ +c-[se] { color: #a67f59 } /* Literal.String.Escape */ +c-[sh] { color: #a67f59 } /* Literal.String.Heredoc */ +c-[si] { color: #a67f59 } /* Literal.String.Interpol */ +c-[sx] { color: #a67f59 } /* Literal.String.Other */ +c-[sr] { color: #a67f59 } /* Literal.String.Regex */ +c-[ss] { color: #a67f59 } /* Literal.String.Symbol */ +c-[vc] { color: #0077aa } /* Name.Variable.Class */ +c-[vg] { color: #0077aa } /* Name.Variable.Global */ +c-[vi] { color: #0077aa } /* Name.Variable.Instance */ +c-[il] { color: #000000 } /* Literal.Number.Integer.Long */ diff --git a/2024-PDF/fonts/IBMPlexMono-Bold-SlashedZero.woff2 b/2024-PDF/fonts/IBMPlexMono-Bold-SlashedZero.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..de464a690845792b076ecd1dcc57c4efcbaddb70 GIT binary patch literal 10548 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 COh9%3 literal 0 HcmV?d00001 diff --git a/2024-PDF/fonts/IBMPlexMono-BoldItalic-SlashedZero.woff2 b/2024-PDF/fonts/IBMPlexMono-BoldItalic-SlashedZero.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..1697592ff25efa8a05abf02058bad708c3394efd GIT binary patch literal 11932 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 literal 0 HcmV?d00001 diff --git a/2024-PDF/fonts/IBMPlexMono-Italic-SlashedZero.woff2 b/2024-PDF/fonts/IBMPlexMono-Italic-SlashedZero.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..48be3cb8ec26ae76762670b0cc78c9e4d445bde9 GIT binary patch literal 11948 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 \ No newline at end of file diff --git a/2024-PDF/img/print-front-cover.svg b/2024-PDF/img/print-front-cover.svg new file mode 100644 index 0000000..aa0ae35 --- /dev/null +++ b/2024-PDF/img/print-front-cover.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/2024-PDF/img/print-inside-cover.svg b/2024-PDF/img/print-inside-cover.svg new file mode 100644 index 0000000..c0f6f02 --- /dev/null +++ b/2024-PDF/img/print-inside-cover.svg @@ -0,0 +1,59 @@ + + + + + + + COPYRIGHT PROTECTED DOCUMENT + + + Ecma International + Rue du Rhone 114 CH-1204 Geneva + Tel: +41 22 849 6000 + Fax: +41 22 849 6001 + Web: + + + https://www.ecma-international.org + + + + + © Ecma International + + + + + + + + is the registered trademark of Ecma International + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/2024-PDF/index.html b/2024-PDF/index.html new file mode 100644 index 0000000..89d3d03 --- /dev/null +++ b/2024-PDF/index.html @@ -0,0 +1,1306 @@ + + + + + Source Map Format Specification + + +
+ + + +

Introduction

+

This Ecma Standard defines the Source Map Format, used for mapping transpiled source code back to the original sources.

+

The source map format has the following goals:

+
    +
  • Support source-level debugging allowing bidirectional mapping

  • +
  • Support server-side stack trace deobfuscation

  • +
+

+ The document at https://tc39.es/source-map/ is the most accurate and up-to-date source map specification. It contains the content of the most recently published snapshot plus any modifications that will be included in the next snapshot. +

+

If you want to get involved you will find more information at the specification repository.

+

+ The original source map format (v1) was created by Joseph Schorr for use by Closure Inspector to enable source-level debugging of optimized JavaScript code (although the format itself is language agnostic). However, as the size of the projects using source maps expanded, the verbosity of the format started to become a problem. The v2 format + [V2Format] was created by trading some simplicity and flexibility to reduce the overall size of the source map. Even with the changes made with the v2 version of the format, the source map file size was limiting its usefulness. The v3 format is based on suggestions made by Pavel Podivilov (Google). +

+

The source map format does not have version numbers anymore, and it is instead hard-coded to always be "3".

+
+ + +

ALTERNATIVE COPYRIGHT NOTICE AND COPYRIGHT LICENSE

+

©2024 Ecma International

+

By obtaining and/or copying this work, you (the licensee) agree that you have read, understood, and will comply with the following terms and conditions.

+

Permission under Ecma's copyright to copy, modify, prepare derivative works of, and distribute this work, with or without modification, for any purpose and without fee or royalty is hereby granted, provided that you include the following on ALL copies of the work or portions thereof, including modifications:

+
    +
  1. The full text of this ALTERNATIVE COPYRIGHT NOTICE AND COPYRIGHT LICENSE in a location viewable to users of the redistributed or derivative work.
  2. +
  3. Any pre-existing intellectual property disclaimers, notices, or terms and conditions. If none exist, the Ecma alternative copyright notice should be included.
  4. +
  5. Notice of any changes or modifications, through a copyright statement on the document such as “This document includes material copied from or derived from [title and URI of the Ecma document]. Copyright © Ecma International.”
  6. +
+

Disclaimers

+

THIS WORK IS PROVIDED “AS IS,” AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE DOCUMENT WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.

+

COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE DOCUMENT.

+

The name and trademarks of copyright holders may NOT be used in advertising or publicity pertaining to the work without specific, written prior permission. Title to copyright in this work will at all times remain with copyright holders.

+
+ +

Source Map Format Specification

+ + +

1 Scope

+ +

This Standard defines the source map format, used by different types of developer tools to improve the debugging experience of code compiled to JavaScript, WebAssembly, and CSS.

+
+ + +

2 Conformance

+ +

A conforming source map document is a JSON document that conforms to the structure detailed in this specification.

+

A conforming source map generator should generate documents which are conforming source map documents, and can be decoded by the algorithms in this specification without reporting any errors (even those which are specified as optional).

+

A conforming source map consumer should implement the algorithms specified in this specification for retrieving (where applicable) and decoding source map documents. A conforming consumer is permitted to ignore errors or report them without terminating where the specification indicates that an algorithm may optionally report an error.

+
+ + +

3 References

+ +

Normative References

+ +
+
[ECMASCRIPT]
+
+ ECMAScript Language Specification. +
+
[ENCODING]
+
+ Anne van Kesteren. Encoding Standard. Living Standard. +
[FETCH]
+
+ Anne van Kesteren. Fetch Standard. Living Standard.
+
[INFRA]
+
+ Anne van Kesteren; Domenic Denicola. Infra Standard. Living Standard. +
+
[URL]
+
+ URL Standard. Living Standard. +
+
[WEBIDL]
+
+ Edgar Chen; Timothy Gu. Web IDL Standard. Living Standard. +
+
+
+ +

Informative References

+
+
[BASE64]
+
+ The Base16, Base32, and Base64 Data Encodings. Standards Track. +
+
[ECMA-262]
+
+ ECMAScript® Language Specification. Standards Track. +
+
[EvalSourceURL]
+
+ Give your eval a name with //@ sourceURL. archive. +
+
[V2Format]
+
+ Source Map Revision 2 Proposal. +
+
[VLQ]
+
+ Variable-length quantity. reference article. +
+
[WasmNamesBinaryFormat]
+
+ WebAssembly Names binary format. Living Standard. +
+
+
+
+ + +

4 Terms and definitions

+ +

For the purposes of this document, the following terms and definitions apply.

+ +
+
4.1 Generated Code
+

The code which is generated by the compiler or transpiler.

+ +
4.2 Original Source
+

The source code which has not been passed through the compiler.

+ +
4.3 Base64 VLQ [VLQ]
+
+

A [base64] value, where the most significant bit (the 6th bit) is used as the continuation bit, and the "digits" are encoded into the string least significant first, and where the least significant bit of the first digit is used as the sign bit.

+ +
+

The values that can be represented by the VLQ Base64 encoded are limited to 32-bit quantities until some use case for larger values is presented. This means that values exceeding 32-bits are invalid and implementations may reject them. The sign bit is counted towards the limit, but the continuation bits are not.

+
+
+ +
+ The string "iB" represents a Base64 VLQ with two digits. The first digit "i" encodes the bit pattern 0x100010, which has a continuation bit of 1 (the VLQ continues), a sign bit of 0 (non-negative), and the value bits 0x0001. + The second digit B encodes the bit pattern 0x000001, which has a continuation bit of 0, no sign bit, and value bits 0x00001. The decoding of this VLQ string is the number 17. +
+
+ +
+ The string "V" represents a Base64 VLQ with one digit. The digit "V" encodes the bit pattern 0x010101, which has a continuation bit of 0 (no continuation), a sign bit of 1 (negative), and the value bits 0x1010. The decoding of this VLQ string is the number -10. +
+
+
+ +
4.4 Source Mapping URL
+
+

The URL referencing the location of a source map from the Generated code.

+
+ +
4.5 Column
+
+

The zero-based indexed offset within a line of the generated code. How this offset is measured can depend on the content type. For JavaScript and CSS based source maps, they are defined to be in UTF-16 code units analogous to JavaScript string indices. That means that "A" (LATIN CAPITAL LETTER A) measures as 1 code unit, and "🔥" (FIRE) measures as 2 code units. For WebAssembly, columns are defined as byte offsets from the beginning of the binary content (and there is only one group representing a line). Source maps for other content types may diverge from this.

+
+
+
+ + +

5 Source Map Format

+ + +
+

A source map is a JSON document containing a top-level JSON object with the following structure:

+
{
+  "version" : 3,
+  "file": "out.js",
+  "sourceRoot": "",
+  "sources": ["foo.js", "bar.js"],
+  "sourcesContent": [null, null],
+  "names": ["src", "maps", "are", "fun"],
+  "mappings": "A,AAAB;;ABCDE"
+  "ignoreList": [0]
+}
+
+
+
+ +
    +
  • + version is the version field which must always be the number + 3 as an integer. The source map may be rejected if the field has any other value. +
  • +
  • + file is an optional name of the generated code that this source map is associated with. It's not specified if this can be a URL, relative path name, or just a base name. Source map generators may choose the appropriate interpretation for their contexts of use. +
  • +
  • + sourceRoot is an optional source root string, used for relocating source files on a server or removing repeated values in the + sources entry. This value is prepended to the individual entries in the + sources field. +
  • +
  • + sources is a list of original sources used by the mappings entry. Each entry is either a string that is a (potentially relative) URL or null if the source name is not known. +
  • +
  • + sourcesContent is an optional list of source content (i.e., the Original Source) strings, used when the source cannot be hosted. The contents are listed in the same order as the sources. Entries may be null if some original sources should be retrieved by name. +
  • +
  • names is an optional list of symbol names which may be used by the mappings entry.
  • +
  • mappings is a string with the encoded mapping data (see § 5.1 Mappings Structure).
  • +
  • + ignoreList is an optional list of indices of files that should be considered third party code, such as framework code or bundler-generated code. This allows developer tools to avoid code that developers likely don't want to see or step through, without requiring developers to configure this beforehand. It refers to the sources array and lists the indices of all the known third-party sources in the source map. Some browsers may also use the deprecated x_google_ignoreList field if ignoreList is not present. +
  • +
+ +

A decoded source map is a struct with the following fields:

+ +
+
file
+
A string or null.
+
sources
+
A list of decoded sources.
+
mappings
+
A list of decoded mappings.
+
+ +

A decoded source is a struct with the following fields:

+ +
+
URL
+
A URL or null.
+
content
+
A string or null.
+
ignored
+
A boolean.
+
+ +

To decode a source map from a JSON string str given a URL baseURL, run the following steps:

+ + +
    +
  1. Let jsonMap be the result of parsing a JSON string to an Infra value str.
  2. +
  3. If jsonMap is not a map, report an error and abort these steps.
  4. +
  5. Decode a source map given jsonMap and baseURL, and return its result if any.
  6. +
+
+ +

To decode a source map given a string-keyed map jsonMap and a URL baseURL, run the following steps:

+ + +
    +
  1. If jsonMap["version"] does not exist or jsonMap["version"] is not 3, optionally report an error.
  2. +
  3. If jsonMap["mappings"] does not exist or jsonMap["mappings"], is not a string, throw an error.
  4. +
  5. If jsonMap["sources"] does not exist or jsonMap["sources"], is not a list, throw an error.
  6. +
  7. Let sourceMap be a new decoded source map.
  8. +
  9. Set sourceMap's file to optionally get a string "file" from jsonMap.
  10. +
  11. + Set sourceMap's sources to the result of decoding source map sources given baseURL with: + +
  12. +
  13. + Set sourceMap's mappings to the result of decoding source map mappings with: + +
  14. +
  15. Return sourceMap.
  16. +
+
+ +

To optionally get a string key from a string-keyed map jsonMap, run the following steps:

+ + +
    +
  1. If jsonMap[key] does not exist, return null.
  2. +
  3. If jsonMap[key] is not a string, optionally report an error and return null.
  4. +
  5. Return jsonMap[key].
  6. +
+
+ +

To optionally get a list of strings key from a string-keyed map jsonMap, run the following steps:

+ + +
    +
  1. If jsonMap[key] does not exist, return a new empty list.
  2. +
  3. If jsonMap[key] is not a list, optionally report an error and return a new empty list.
  4. +
  5. Let list be a new empty list.
  6. +
  7. For each jsonItem of jsonMap[key]: +
      +
    1. If jsonItem is a string, append it to list.
    2. +
    3. Else, optionally report an error and append "" to list.
    4. +
    +
  8. +
  9. Return list.
  10. +
+
+ +

To optionally get a list of optional strings key from a string-keyed map jsonMap, run the following steps:

+ + +
    +
  1. If jsonMap[key] does not exist, return a new empty list.
  2. +
  3. If jsonMap[key] is not a list, optionally report an error and return a new empty list.
  4. +
  5. Let list be a new empty list.
  6. +
  7. For each jsonItem of jsonMap[key]: +
      +
    1. If jsonItem is a string, append it to list.
    2. +
    3. Else, +
        +
      1. If jsonItem is not null, optionally report an error.
      2. +
      3. Append null to list.
      4. +
      +
    4. +
    +
  8. +
  9. Return list.
  10. +
+
+ +

To optionally get a list of array indexes key from a string-keyed map jsonMap, run the following steps:

+ + +
    +
  1. If jsonMap[key] does not exist, return a new empty list.
  2. +
  3. If jsonMap[key] is not a list, optionally report an error and return a new empty list.
  4. +
  5. Let list be a new empty list.
  6. +
  7. For each jsonItem of jsonMap[key]: +
      +
    1. If jsonItem is a non-negative integer number, append it to list.
    2. +
    3. + Else, +
        +
      1. If jsonItem is not null, optionally report an error.
      2. +
      3. Append null to list.
      4. +
      +
    4. +
    +
  8. +
  9. Return list.
  10. +
+
+ +

To optionally report an error, implementations can choose to:

+ + +
    +
  • Do nothing.
  • +
  • Report an error to the user, and continue processing.
  • +
  • Throw an error to abort the running algorithm. (Infra §3.6 Control flow)
  • +
+
+ + +

5.1 Mappings Structure

+ +

The mappings data is broken down as follows:

+ + +
    +
  • each group representing a line in the generated file is separated by a semicolon (;)
  • +
  • each segment is separated by a comma (,)
  • +
  • each segment is made up of 1, 4, or 5 variable length fields.
  • +
+
+ +

The fields in each segment are:

+ + +
    +
  1. The zero-based starting column of the line in the generated code that the segment represents. If this is the first field of the first segment, or the first segment following a new generated line (;), then this field holds the whole Base64 VLQ. Otherwise, this field contains a Base64 VLQ that is relative to the previous occurrence of this field. Note that this is different from the subsequent fields below because the previous value is reset after every generated line.
  2. +
  3. If present, the zero-based index into the sources list. This field contains a Base64 VLQ relative to the previous occurrence of this field, unless it is the first occurrence of this field, in which case the whole value is represented.
  4. +
  5. If present, the zero-based starting line in the original source. This field contains a Base64 VLQ relative to the previous occurrence of this field, unless it is the first occurrence of this field, in which case the whole value is represented. Must be present if there is a source field.
  6. +
  7. If present, the zero-based starting column of the line in the original source. This field contains a Base64 VLQ relative to the previous occurrence of this field, unless it is the first occurrence of this field, in which case the whole value is represented. Must be present if there is a source field.
  8. +
  9. If present, the zero-based index into the names list associated with this segment. This field contains a Base64 VLQ relative to the previous occurrence of this field, unless it is the first occurrence of this field, in which case the whole value is represented.
  10. +
+
+ + +
+

The purpose of this encoding is to reduce the source map size. VLQ encoding reduced source maps by 50% relative to the [V2Format] in tests performed using Google Calendar.

+
+
+ + +
+

Segments with one field are intended to represent generated code that is unmapped because there is no corresponding original source code, such as code that is generated by a compiler. Segments with four fields represent mapped code where a corresponding name does not exist. Segments with five fields represent mapped code that also has a mapped name.

+
+
+ + +
+

Using file offsets was considered but rejected in favor of using line/column data to avoid becoming misaligned with the original due to platform-specific line endings.

+
+
+ +

A decoded mapping is a struct with the following fields:

+ +
+
generatedLine +
A non-negative integer. +
generatedColumn +
A non-negative integer. +
originalSource +
A decoded source or null. +
originalLine +
A non-negative integer or null. +
originalColumn +
A non-negative integer or null. +
name +
A string or null. +
+ +

To decode source map mappings given a string mappings, a list of strings names, and a list of decoded sources sources, run the following steps:

+ + +
    +
  1. Validate base64 VLQ groupings with mappings.
  2. +
  3. Let decodedMappings be a new empty list.
  4. +
  5. Let groups be the result of strictly splitting mappings on ;.
  6. +
  7. Let generatedLine be 0.
  8. +
  9. Let originalLine be 0.
  10. +
  11. Let originalColumn be 0.
  12. +
  13. Let sourceIndex be 0.
  14. +
  15. Let nameIndex be 0.
  16. +
  17. While generatedLine is less than groups's size: +
      +
    1. + If groups[generatedLine] is not the empty string, then: +
        +
      1. Let segments be the result of strictly splitting groups[generatedLine] on ,.
      2. +
      3. Let generatedColumn be 0.
      4. +
      5. For each segment in segments: +
          +
        1. Let position be a position variable for segment, initially pointing at segment's start.
        2. +
        3. Decode a base64 VLQ from segment given position and let relativeGeneratedColumn be the result.
        4. +
        5. If relativeGeneratedColumn is null,optionally report an error and continue with the next iteration.
        6. +
        7. Increase generatedColumn by relativeGeneratedColumn. If the result is negative, optionally report an error and continue with the next iteration.
        8. +
        9. Let decodedMapping be a new decoded mapping whose generatedLine is generatedLine, generatedColumn is generatedColumn, originalSource is null, originalLine is null, originalColumn is null, and name is null.
        10. +
        11. Append decodedMapping to decodedMappings.
        12. +
        13. Decode a base64 VLQ from segment given position and let relativeSourceIndex be the result.
        14. +
        15. Decode a base64 VLQ from segment given position and let relativeOriginalLine be the result.
        16. +
        17. Decode a base64 VLQ from segment given position and let relativeOriginalColumn be the result.
        18. +
        19. + If relativeOriginalColumn is null, then: +
            +
          1. + If relativeSourceIndex is not null, optionally report an error. +
          2. +
          3. Continue with the next iteration.
          4. +
          +
        20. +
        21. Increase sourceIndex by relativeSourceIndex.
        22. +
        23. Increase originalLine by relativeOriginalLine.
        24. +
        25. Increase originalColumn by relativeOriginalColumn.
        26. +
        27. If any of sourceIndex, originalLine, or originalColumn are less than 0, or if sourceIndex is greater than or equal to sources's size, optionally report an error.
        28. +
        29. + Else, +
            +
          1. Set decodedMapping's originalSource to sources[sourceIndex].
          2. +
          3. Set decodedMapping's originalLine to originalLine.
          4. +
          5. Set decodedMapping's originalColumn to originalColumn.
          6. +
          +
        30. +
        31. Decode a base64 VLQ from segment given position and let relativeNameIndex be the result.
        32. +
        33. + If relativeNameIndex is not null, then: +
            +
          1. Increase nameIndex by relativeNameIndex.
          2. +
          3. If nameIndex is negative or greater than names's size, optionally report an error.
          4. +
          5. Else, set decodedMapping's name to names[nameIndex].
          6. +
          +
        34. +
        35. If position does not point to the end of segment, optionally report an error.
        36. +
        +
      6. +
      +
    2. +
    3. Increase generatedLine by 1.
    4. +
    +
  18. +
  19. Return decodedMappings.
  20. +
+
+ +

To validate base64 VLQ groupings from a string groupings, run the following steps:

+ + +
    +
  1. If groupings is not an ASCII string, throw an error. +
  2. + If groupings contains any code unit other than: +
      +
    • U+002C (,) or U+003B (;);
    • +
    • U+0030 (0) to U+0039 (9);
    • +
    • U+0041 (A) to U+005A (Z);
    • +
    • U+0061 (a) to U+007A (z);
    • +
    • U+002B (+), U+002F (/)
    • +
    + +
    +

    These are the valid [base64] characters (excluding the padding character =), together with , and ;. then throw an error.

    +
    +
    +
  3. +
+
+ +

To decode a base64 VLQ from a string segment given a position variable position, run the following steps:

+ +
    +
  1. If position points to the end of segment, return null.
  2. +
  3. + Let first be a byte whose the value is the number corresponding to segment's positionth code unit, according to the [base64] encoding. + +

    The two most significant bits of first are 0.

    +
    +
  4. +
  5. Let sign be 1 if first & 0x01 is 0x00, and -1 otherwise.
  6. +
  7. Let value be (first >> 1) & 0x0F, as a number.
  8. +
  9. Let nextShift be 16.
  10. +
  11. Let currentByte be first.
  12. +
  13. While currentByte & 0x20 is 0x20: +
      +
    1. Advance position by 1.
    2. +
    3. If position points to the end of segment, throw an error.
    4. +
    5. Set currentByte to the byte whose the value is the number corresponding to segment's positionth code unit, according to the [base64] encoding.
    6. +
    7. Let chunk be currentByte & 0x1F, as a number.
    8. +
    9. Add chunk * nextShift to value.
    10. +
    11. If value is greater than or equal to 231, throw an error.
    12. +
    13. Multiply nextShift by 32.
    14. +
    +
  14. +
  15. Advance position by 1.
  16. +
  17. + If value is 0 and sign is -1, return -2147483648. +

    -2147483648 is the smallest 32-bit signed integer.

    +
  18. +
  19. Return value * sign.
  20. +
+
+ + +
+

In addition to returning the decoded value, this algorithm updates the position variable in the calling algorithm.

+
+
+ + +

5.1.1 Mappings for generated JavaScript code

+ +

Generated code positions that may have mapping entries are defined in terms of input elements as per ECMAScript Lexical Grammar. Mapping entries must point to either:

+ + +
    +
  1. the first code point of the source text matched by IdentifierName, PrivateIdentifier, Punctuator, DivPunctuator, RightBracePunctuator, NumericLiteral and RegularExpressionLiteral.
  2. +
  3. any code point of the source text matched by Comment, HashbangComment, StringLiteral, Template, TemplateSubstitutionTail, WhiteSpace and LineTerminator.
  4. +
+
+
+
+ + +

5.2 Resolving Sources

+

+ If the sources are not absolute URLs after prepending the sourceRoot, the sources are resolved relative to the SourceMap (like resolving the script src attribute in an HTML document).

+

To decode source map sources given a URL baseURL, + a string or null sourceRoot, + a list of either strings or nulls sources, + a list of either strings or nulls sourcesContent, + and a list of numbers ignoredSources, + run the following steps:

+ + +
    +
  1. Let decodedSources be a new empty list.
  2. +
  3. Let sourceURLPrefix be "".
  4. +
  5. + If sourceRoot is not null, then: +
      +
    1. + If sourceRoot contains the code point U+002F (/), then: +
        +
      1. Let index be the index of the last occurrence of U+002F (/) in sourceRoot.
      2. +
      3. Set sourceURLPrefix to the substring of sourceRoot from 0 to index + 1.
      4. +
      +
    2. +
    3. Else, set sourceURLPrefix to the concatenation of sourceRoot and "/".
    4. +
    +
  6. +
  7. + For each source of sources with index index: +
      +
    1. Let decodedSource be a new decoded source whose URL is null, content is null, and ignored is false.
    2. +
    3. + If source is not null: +
        +
      1. Set source to the concatenation of sourceURLPrefix and source.
      2. +
      3. Let sourceURL be the result of URL parsing source with baseURL.
      4. +
      5. If sourceURL is failure, optionally report an error.
      6. +
      7. Else, set decodedSource's URL to sourceURL.
      8. +
      +
    4. +
    5. If index is in ignoredSources, set decodedSource's ignored to true.
    6. +
    7. If sourcesContent's size is greater than or equal to index, set decodedSource's content to sourcesContent[index].
    8. +
    9. Append decodedSource to decodedSources.
    10. +
    +
  8. +
  9. Return decodedSources.
  10. +
+
+ + +
+

Implementations that support showing source contents but do not support showing multiple sources with the same URL and different content will arbitrarily choose one of the various contents corresponding to the given URL.

+
+
+ +
+ + +

5.3 Extensions

+

Source map consumers must ignore any additional unrecognized properties, rather than causing the source map to be rejected, so that additional features can be added to this format without breaking existing users.

+
+
+ + +

6 Index Map

+ +

To support concatenating generated code and other common post-processing, an alternate representation of a map is supported:

+ +
{
+  "version" : 3,
+  "file": "app.js",
+  "sections": [
+    {
+      "offset": {"line": 0, "column": 0},
+      "map": {
+        "version" : 3,
+        "file": "section.js",
+        "sources": ["foo.js", "bar.js"],
+        "names": ["src", "maps", "are", "fun"],
+        "mappings": "AAAA,E;;ABCDE"
+      }
+    },
+    {
+      "offset": {"line": 100, "column": 10},
+      "map": {
+        "version" : 3,
+        "file": "another_section.js",
+        "sources": ["more.js"],
+        "names": ["more", "is", "better"],
+        "mappings": "AAAA,E;AACA,C;ABCDE"
+      }
+    }
+  ]
+}
+
+ +

The index map follows the form of the standard map. Like the regular source map, the file format is JSON with a top-level object. It shares the version and file field from the regular source map, but gains a new sections field.

+

sections is an array of Section objects.

+ + +

6.1 Section

+ +

Section objects have the following fields:

+ + +
    +
  • offset is an object with two fields, line and column, that represent the offset into generated code that the referenced source map represents.
  • +
  • map is an embedded complete source map object. An embedded map does not inherit any values from the containing index map.
  • +
+
+ +

The sections must be sorted by starting position and the represented sections must not overlap.

+ +
+
+ + +

7 Retrieving Source Maps

+ + +

7.1 Linking generated code to source maps

+ +

While the source map format is intended to be language and platform agnostic, it is useful to define how to reference to them for the expected use-case of web server-hosted JavaScript.

+

There are two possible ways to link source maps to the output. The first requires server support in order to add an HTTP header and the second requires an annotation in the source.

+

Source maps are linked through URLs as defined in [URL]; in particular, characters outside the set permitted to appear in URIs must be percent-encoded and it may be a data URI. Using a data URI along with sourcesContent allows for a completely self-contained source map.

+

The HTTP sourcemap header has precedence over a source annotation, and if both are present, the header URL should be used to resolve the source map file.

+

Regardless of the method used to retrieve the Source Mapping URL the same process is used to resolve it, which is as follows:

+

When the Source Mapping URL is not absolute, then it is relative to the generated code's source origin. The source origin is determined by one of the following cases:

+ + +
    +
  • + If the generated source is not associated with a script element that has a src attribute and there exists a //# sourceURL comment in the generated code, that comment should be used to determine the source origin. + +
    +

    Previously, this was //@ sourceURL, as with + //@ sourceMappingURL, it is reasonable to accept both but + //# is preferred. +

    +
    +
    +
  • +
  • If the generated code is associated with a script element and the script element has a src attribute, the src attribute of the script element will be the source origin.
  • +
  • If the generated code is associated with a script element and the script element does not have a src attribute, then the source origin will be the page's origin.
  • +
  • If the generated code is being evaluated as a string with the eval() function or via new Function(), then the source origin will be the page's origin.
  • +
+
+ + +

7.1.1 Linking through HTTP headers

+ +

If a file is served through HTTP(S) with a sourcemap header, the value of the header is the URL of the linked source map.

+
sourcemap: <url>
+ + +
+

Previous revisions of this document recommended a header name of x-sourcemap. This is now deprecated; + sourcemap is now expected. +

+
+
+
+ + +

7.1.2 Linking through inline annotations

+ +

The generated code should include a comment, or the equivalent construct depending on its language or format, named sourceMappingURL and that contains the URL of the source map. This specification defines how the comment should look like for JavaScript, CSS, and WebAssembly. Other languages should follow a similar convention.

+

For a given language there can be multiple ways of detecting the sourceMappingURL comment, to allow for different implementations to choose what is less complex for them. The generated code unambiguously links to a source map if the result of all the extraction methods is the same.

+

If a tool consumes one or more source files that unambiguously links to a source map and it produces an output file that links to a source map, it must do so unambiguously.

+ + +
+ The following JavaScript code links to a source map, but it does not do so unambiguously: +
let a = `
+  //# sourceMappingURL=foo.js.map
+  //`;
+    
+

Extracting a Source Map URL from it through parsing gives null, while without parsing gives foo.js.map.

+
+
+ + +
+

Having multiple ways to extract a source map URL, that can lead to different results, can have negative security and privacy implications. Implementations that need to detect which source maps are potentially going to be loaded are strongly encouraged to always apply both algorithms, rather than just assuming that they will give the same result.

+

A fix to this problem is being worked on, and is expected to be included in a future version of the standard. It will likely involve early returning from the below algorithms whenever there is a comment (or comment-like) that contains the characters U+0060 (`), U+0022 ("), or U+0027 ('), or the sequence U+002A U+002F (*/).

+
+
+ + +

7.1.2.1 Extraction methods for JavaScript sources

+ +

To extract a Source Map URL from JavaScript through parsing a string source, run the following steps:

+ + +
    +
  1. Let tokens be the list of tokens obtained by parsing source according to [ECMA-262].
  2. +
  3. + For each token in tokens, in reverse order: +
      +
    1. If token is not a single-line comment or a multi-line comment, return null.
    2. +
    3. Let comment be the content of token.
    4. +
    5. If matching a Source Map URL in comment returns a string, return it.
    6. +
    +
  4. +
  5. Return null.
  6. +
+
+ +

To extract a Source Map URL from JavaScript without parsing a string source, run the following steps:

+ + +
    +
  1. Let lines be the result of strictly splitting source on ECMAScript line terminator code points.
  2. +
  3. Let lastURL be null.
  4. +
  5. + For each line in lines: +
      +
    1. Let position be a position variable for line, initially pointing at the start of line.
    2. +
    3. + While position doesn't point past the end of line: +
        +
      1. + Collect a sequence of code points that are ECMAScript white space code points from line given position. + +

        The collected code points are not used, but position is still updated.

        +
        +
      2. +
      3. If position points past the end of line, break.
      4. +
      5. Let first be the code point of line at position.
      6. +
      7. Increment position by 1.
      8. +
      9. + If first is U+002F (/) and position does not point past the end of line, then: +
          +
        1. Let second be the code point of line at position.
        2. +
        3. Increment position by 1.
        4. +
        5. + If second is U+002F (/), then: +
            +
          1. Let comment be the code point substring from position to the end of line.
          2. +
          3. If matching a Source Map URL in comment returns a string, set lastURL to it.
          4. +
          5. Break.
          6. +
          +
        6. +
        7. + Else if second is U+002A (*), then: +
            +
          1. Let comment be the empty string.
          2. +
          3. + While position + 1 doesn't point past the end of line: +
              +
            1. Let c1 be the code point of line at position.
            2. +
            3. Increment position by 1.
            4. +
            5. Let c2 be the code point of line at position.
            6. +
            7. + If c1 is U+002A (*) and c2 is U+002F (/), then: +
                +
              1. If matching a Source Map URL in comment returns a string, set lastURL to it.
              2. +
              3. Increment position by 1.
              4. +
              +
            8. +
            9. Append c1 to comment.
            10. +
            +
          4. +
          +
        8. +
        9. Else, set lastURL to null.
        10. +
        +
      10. +
      11. Else, set lastURL to null.
      12. +
      + +

      We reset + lastURL to null whenever we find a non-comment code character.

      +
      +
    4. +
    +
  6. +
  7. Return lastURL.
  8. +
+
+ + +
+

The algorithm above has been designed so that the source lines can be iterated in reverse order, returning early after scanning through a line that contains a sourceMappingURL comment.

+
+
+ + +
+

The algorithm above is equivalent to the following JavaScript implementation:

+
const JS_NEWLINE = /^/m;
+        
+// This RegExp will always match one of the following:
+// - single-line comments
+// - "single-line" multi-line comments
+// - unclosed multi-line comments
+// - just trailing whitespaces
+// - a code character
+// The loop below differentiates between all these cases.
+const JS_COMMENT =
+/\s*(?:\/\/(?<single>.*)|\/\*(?<multi>.*?)\*\/|\/\*.*|$|(?<code>[^\/]+))/uym;
+
+const PATTERN = /^[@#]\s*sourceMappingURL=(\S*?)\s*$/;
+
+let lastURL = null;
+for (const line of source.split(JS_NEWLINE)) {
+  JS_COMMENT.lastIndex = 0;
+  while (JS_COMMENT.lastIndex < line.length) {
+    let commentMatch = JS_COMMENT.exec(line).groups;
+    let comment = commentMatch.single ?? commentMatch.multi;
+    if (comment != null) {
+      let match = PATTERN.exec(comment);
+      if (match !== null) lastURL = match[1];
+    } else if (commentMatch.code != null) {
+      lastURL = null;
+    } else {
+      // We found either trailing whitespaces or an unclosed comment.
+      // Assert: JS_COMMENT.lastIndex === line.length
+    }
+  }
+}
+return lastURL;
+
+
+
+ +

To match a Source Map URL in a comment comment (a string), run the following steps:

+ + +
    +
  1. Let pattern be the regular expression /^[@#]\s*sourceMappingURL=(\S*?)\s*$/.
  2. +
  3. Let match be ! RegExpBuiltInExec(pattern, comment).
  4. +
  5. If match is not null, return match[1].
  6. +
  7. Return null.
  8. +
+
+ + +
+

The prefix for this annotation was initially //@ however this conflicts with Internet Explorer's Conditional Compilation and was changed to //#.

+
+
+ +

Source map generators must only emit //# while source map consumers must accept both //@ and //#.

+
+ + +

7.1.2.2 Extraction methods for CSS sources

+ +

Extracting source mapping URLs from CSS is similar to JavaScript, with the exception that CSS only supports /* ... */-style comments.

+
+ + +

7.1.2.3 Extraction methods for WebAssembly binaries

+ +

To extract a Source Map URL from a WebAssembly source given a byte sequence bytes, run the following steps:

+ + +
    +
  1. Let module be module_decode(bytes).
  2. +
  3. If module is error, return null.
  4. +
  5. + For each custom section customSection of module, +
      +
    1. Let name be the name of customSection, decoded as UTF-8.
    2. +
    3. + If name is "sourceMappingURL", then: +
        +
      1. Let value be the bytes of customSection, decoded as UTF-8.
      2. +
      3. If value is failure, return null.
      4. +
      5. Return value.
      6. +
      +
    4. +
    +
  6. +
  7. Return null.
  8. +
+
+ +

+ Since WebAssembly is not a textual format and it does not support comments, it supports a single unambiguous extraction method. The URL is encoded using + [WasmNamesBinaryFormat], and it's placed as the content of the + custom section. It is invalid for tools that generate WebAssembly code to generate two or more + custom sections with the "sourceMappingURL" name. +

+
+
+
+ + +

7.2 Fetching Source Maps

+ +

To fetch a source map given a URL url, run the following steps:

+ + +
    +
  1. Let promise be a new promise.
  2. +
  3. Let request be a new request whose URL is url.
  4. +
  5. + Fetch request with processResponseConsumeBody set to the following steps given response response and null, failure, or a byte sequence bodyBytes: +
      +
    1. If bodyBytes is null or failure, reject promise with a TypeError and abort these steps.
    2. +
    3. + If url's scheme is an HTTP(S) scheme and bodyBytes starts with `)]}'`, then: +
        +
      1. + While bodyBytes's length is not 0 and bodyBytes's 0th byte is not an HTTP newline byte: +
        1. remove the 0th byte from bodyBytes.
        + +
        +

        For historic reasons, when delivering source maps over HTTP(S), servers may prepend a line starting with the string )]}' to the source map.

        +
        )]}'garbage here
        +{"version": 3, ...}
        + is interpreted as +
        {"version": 3, ...}
        +
        +
        +
      2. +
      +
    4. +
    5. Let sourceMap be the result of parsing JSON bytes to a JavaScript value given + bodyBytes. +
    6. +
    7. If the previous step threw an error, reject promise with that error.
    8. +
    9. Otherwise, resolve promise with sourceMap.
    10. +
    +
  6. +
  7. Return promise.
  8. +
+
+
+
+ + +

8 Conventions

+ +

The following conventions should be followed when working with source maps or when generating them.

+ + +

8.1 Source Map Naming

+ +

Commonly, a source map will have the same name as the generated file but with a .map extension. For example, for page.js a source map named page.js.map would be generated.

+
+ + +

8.2 Linking eval'd code to named generated code

+ +

There is an existing convention that should be supported for the use of source maps with eval'd code, it has the following form:

+ +
//# sourceURL=foo.js
+ +

It is described in [EvalSourceURL].

+
+
+ + +

9 Language Neutral Stack Mapping Notes

+ +

Stack tracing mapping without knowledge of the source language is not covered by this document.

+
+ + +

10 Multi-level Mapping Notes

+ +

It is getting more common to have tools generate sources from some DSL (templates) or compile TypeScript -> JavaScript -> minified JavaScript, resulting in multiple translations before the final source map is created. This problem can be handled in one of two ways. The easy but lossy way is to ignore the intermediate steps in the process for the purposes of debugging, the source location information from the translation is either ignored (the intermediate translation is considered the ”Original Source“) or the source location information is carried through (the intermediate translation hidden). The more complete way is to support multiple levels of mapping: if the Original Source also has a source map reference, the user is given the choice of using that as well.

+

However, It is unclear what a "source map reference" looks like in anything other than JavaScript. More specifically, what a source map reference looks like in a language that doesn't support JavaScript-style single-line comments.

+
+ + +

11 License

+ +

This work is licensed under a + Creative Commons Attribution-ShareAlike 3.0 Unported License. +

+
+ + +
+

Annex A
+ (Informative)

+

Index

+
+ + + +

Terms defined by this specification

+ + +
+ + +

Terms defined by reference

+ +
    +
  • + [ECMASCRIPT] defines the following terms: +
      +
    • comment
    • +
    • divpunctuator
    • +
    • ecmascript lexical grammar
    • +
    • hashbangcomment
    • +
    • identifiername
    • +
    • line terminator code points
    • +
    • lineterminator
    • +
    • multi-line comment
    • +
    • numericliteral
    • +
    • privateidentifier
    • +
    • punctuator
    • +
    • regexpbuiltinexec
    • +
    • regularexpressionliteral
    • +
    • rightbracepunctuator
    • +
    • single-line comment
    • +
    • stringliteral
    • +
    • template
    • +
    • templatesubstitutiontail
    • +
    • tokens
    • +
    • white space code points
    • +
    • whitespace
    • +
    +
  • +
  • [WASM] defines the following terms: +
      +
    • custom section
    • +
    • module_decode
    • +
    +
  • +
+
+
+ + +
+

Annex B
+ (Normative)

+

Issues Index

+
+ + +
+

Having multiple ways to extract a source map URL, that can lead to different results, can have negative security and privacy implications. Implementations that need to detect which source maps are potentially going to be loaded are strongly encouraged to always apply both algorithms, rather than just assuming that they will give the same result.

+

A fix to this problem is being worked on, and is expected to be included in a future version of the standard. It will likely involve early returning from the below algorithms whenever there is a comment (or comment-like) that contains the characters U+0060 (`), U+0022 ("), or U+0027 ('), or the sequence U+002A U+002F (*/).

+
+
+ +
+
+ + \ No newline at end of file diff --git a/2024-PDF/print.js b/2024-PDF/print.js new file mode 100644 index 0000000..b0e3968 --- /dev/null +++ b/2024-PDF/print.js @@ -0,0 +1,104 @@ +/** + * Some notes: + * - Prince cant grok trailing commas, so prettier is disabled anywhere it tries to enforce wrapping with trailing + * comma + * - Prince doesn't support template strings yet + * - Set Prince.trackboxes to true for advanced debugging, see + * https://www.princexml.com/doc/cookbook/#how-and-where-is-my-box + * */ + +/* eslint-disable no-undef */ +'use strict'; + +// Prince.trackBoxes = true; + +const specContainer = document.getElementById('spec-container'); + +PDF.pageLayout = 'two-column-right'; +PDF.pageMode = 'show-bookmarks'; +PDF.duplex = 'duplex-flip-long-edge'; +PDF.title = document.title; +PDF.author = 'Ecma International'; +PDF.subject = 'ECMA-XXX, 2024'; + + +Prince.registerPostLayoutFunc(() => { + specContainer.parentNode.insertBefore(generateFrontCover(), specContainer); + specContainer.parentNode.insertBefore(generateInsideCover(), specContainer); +}); + +function generateFrontCover() { + const shortname = document.createElement('h1'); + const version = document.createElement('h1') + const title = document.createElement('h1'); + const year = "2024"; + + shortname.innerHTML = 'Draft Standard ECMA-XXX'; + version.innerHTML = "December, 2024"; + title.innerHTML = 'Source Map Format Specification'; + + shortname.classList.add('shortname'); + title.classList.add('title'); + version.classList.add('version'); + + version.setAttribute('data-year', year); + + const frontCover = document.createElement('div'); + shortname.innerHTML = shortname.innerHTML.replace(/standard/i, ''); + // eslint-disable-next-line prettier/prettier + shortname.innerHTML = shortname.innerHTML.replace(/(draft|proposal)/i, '$1'); + version.innerHTML = "ECMA-XXX, 2024"; + title.innerHTML = title.innerHTML.replace(/(®|®)/, '®'); + + frontCover.classList.add('full-page-svg'); + frontCover.setAttribute('id', 'front-cover'); + + frontCover.appendChild(shortname); + frontCover.appendChild(version); + frontCover.appendChild(title); + + return frontCover; +} + +function generateInsideCover() { + const insideCover = document.createElement('div'); + + insideCover.classList.add('full-page-svg'); + insideCover.setAttribute('id', 'inside-cover'); + insideCover.innerHTML = + '

Ecma International
Rue du Rhone 114 CH-1204 Geneva
Tel: +41 22 849 6000
Fax: +41 22 849 6001
Web: https://www.ecma-international.org
Ecma is the registered trademark of Ecma International.

'; + + return insideCover; +} + + +/** + * @typedef {Object} PrinceBox + * @property {string} type + * @property {number} pageNum + * @property {number} x + * @property {number} y + * @property {number} w + * @property {number} h + * @property {number} baseline + * @property {number} marginTop + * @property {number} marginBottom + * @property {number} marginLeft + * @property {number} marginRight + * @property {number} paddingTop + * @property {number} paddingBottom + * @property {number} paddingLeft + * @property {number} paddingRight + * @property {number} borderTop + * @property {number} borderBottom + * @property {number} borderLeft + * @property {number} borderRight + * @property {string} floatPosition "TOP" or "BOTTOM" + * @property {PrinceBox[]} children + * @property {PrinceBox} parent + * @property {Element|null} element + * @property {string|null} pseudo + * @property {string} text + * @property {string} src + * @property {CSSStyleSheet} style + * */