이 Ecma 표준은 트랜스파일된 소스코드를 원본 소스코드로 맵핑하기 위해 사용되는 소스맵 형식을 정의합니다.
소스맵 형식의 주요 목표는 다음과 같습니다:
양방향 매핑을 통해 소스 수준 디버깅 지원
서버 측 스택 추적 복원 지원
원본 소스 맵 형식(v1)은 최적화된 JavaScript 코드의 소스 수준 디버깅을 위해 Closure Inspector에서 Joseph
Schorr가 만들었으며, 형식 자체는 언어에 구애받지 않습니다. 하지만 소스맵을 사용하는 프로젝트의 규모가 커짐에 따라 형식의 장황함이 문제가 되기 시작했습니다. v2
형식(Source Map
Revision 2 Proposal)은 전체 소스맵 크기를 줄이기 위해 일부 단순성과 유연성을 희생하여 만들어졌습니다. v2에서도 파일 크기가
여전히 한계에 봉착하여 v3 형식은 Pavel Podivilov(Google)의 제안을 기반으로 만들어졌습니다.
소스맵 형식은 더 이상 버전 번호를 사용하지 않으며, 항상 "3"으로 고정되어 있습니다.
2023~2024년 동안 소스맵 형식은 Ecma 표준으로 발전되었고, 많은 사람들의 의미 있는 기여가 있었습니다. 소스맵 형식의 추가적인 개선은 TC39-TG4에서 이어질 예정입니다.
Asumu Takikawa, Nicolò Ribaudo, Jon Kuperman
ECMA-426, 제1판, 프로젝트 편집자
1 범위
이 표준은 다양한 종류의 개발자 도구들이 JavaScript, WebAssembly, 그리고 CSS로 컴파일된 코드의 디버깅 경험을 향상시키기 위해 사용하는 소스맵 형식을 정의합니다.
2 적합성
적합한 소스맵 문서는 이 사양에서 자세히 설명하는 구조를 따른 JSON 문서입니다.
적합한 소스맵 생성기는 적합한 소스맵 문서를 생성해야 하며, 이 사양서의 알고리즘으로 어떤 오류도 보고받지 않고(명시적으로 선택적 오류로 지정된 경우조차) 디코드가 가능해야 합니다.
적합한 소스맵 소비자는 소스맵 문서의 검색(해당하는 경우) 및 디코딩을 위해 이 사양서에 지정된 알고리즘을 구현해야 합니다. 적합한 소비자는 오류를 무시하거나, 사양서가 알고리즘에서
선택적으로 오류를 보고해도
됨을 명시한 경우 종료하지 않고 오류를 보고할 수 있습니다.
3 참고 문헌
다음 문서들은 이 문서에서 일부 또는 전체 내용이 요구사항을 구성하는 방식으로 참조됩니다. 날짜가 명시된 경우 해당 판만 적용됩니다. 날짜가 없으면, 참조된 문서의 최신 판(개정 포함)을
적용합니다.
생성된 코드의 한 줄 내에서 0부터 시작하는
인덱스 오프셋. JavaScript 및 CSS 소스맵에서는 UTF-16 코드 유닛 수로, WebAssembly 소스맵에서는 바이너리 콘텐츠의 바이트 인덱스(단일 줄로
표현됨)로 계산됨.
참고
즉, "A"(LATIN CAPITAL LETTER A)는 코드 유닛 1로 계산되고,
"🔥"(FIRE)는 코드 유닛 2로 계산됩니다. 다른 콘텐츠 타입의 소스맵에서는 이 규칙이 다를 수 있습니다.
6 base64 VLQ
base64 VLQ는 base64로 인코딩된 가변 길이 정수로, 가장 큰 비트(6번째 비트)가 연속
비트로 사용되며, "숫자"들은 문자열로 최하위부터 순서대로 인코딩되고, 첫 숫자의 최하위 비트가 부호 비트로 사용됩니다.
노트 1
base64 VLQ 인코딩으로 표현할 수 있는 값은 32비트까지만 제한되며, 더 큰 값이 필요한 경우가 있을 때 확장될 수 있습니다. 즉, 32비트를 초과하는 값은 유효하지 않으며
구현체는 이를 거부할 수 있습니다. 부호 비트는 제한에 포함되지만 연속 비트는 포함되지 않습니다.
노트 2
문자열 "iB"는 두 자리의 base64 VLQ를 의미합니다. 첫 자리 "i"는 0b100010 비트 패턴을
인코딩하며, 연속 비트 1(VLQ가 계속됨), 부호 비트 0(음수가 아님), 값 비트 0b0001을 가집니다.
두 번째 자리 B는 0b000001을 인코딩하며, 연속 비트 0, 부호 비트 없음, 값 비트
0b00001을 가집니다. 이 VLQ 문자열을 디코딩하면 숫자 17이 됩니다.
노트 3
문자열 "V"는 한 자리의 base64 VLQ를 의미합니다. "V"는 0b010101 비트 패턴을 인코딩하며,
연속 비트 0(계속 없음), 부호 비트 1(음수), 값 비트 0b1010을 가집니다. 이 VLQ 문자열을
디코딩하면 숫자 -10이 됩니다.
이 추상 연산은 ECMA-262에서 직접 공개되는 작업 중에 있습니다. 관련 논의는 tc39/ecma262#3540에서 볼 수 있습니다.
7.2 JSONObjectGet ( object, key )
추상 연산 JSONObjectGet은 object(JSON 객체)와 key(문자열)를 받아 JSON 값 또는
missing을 반환합니다. object에서 지정된 key에 대응하는 값을 반환합니다. 호출 시
다음 단계를 수행합니다:
object에 key로 된 자체 프로퍼티가 없다면, missing을 반환한다.
prop에 object의 key에 해당하는 자체 프로퍼티를 할당한다.
prop의 [[Value]] 속성을 반환한다.
7.3 JSONArrayIterate ( array )
추상 연산 JSONArrayIterate는 array(JSON 배열)를 받아 리스트(List)
형태의 JSON
값들을 반환합니다. 이는 "For each" 알고리즘 등에서 반복할 수 있도록 array 내의 모든 요소를 포함한
리스트를 만듭니다. 호출 시 다음 단계를 수행합니다:
추상 연산 StringSplit은 인수 string (문자열)과 separators (비어 있지 않은
문자열들의 리스트)를
받아
문자열들의 리스트를
반환합니다.
이 연산은 separators의 요소 중 하나로 구분된 부분 문자열로 입력 문자열을 나눕니다. 여러
구분자가 일치할 경우, separators에서 먼저 등장하는 구분자가 더 높은 우선순위를 갖습니다.
호출 시 다음 단계를 수행합니다:
version 필드는 항상 정수 3이어야 합니다. 만약 값이 다를 경우 소스맵이 거부될 수 있습니다.
file 필드는 이 소스맵이 연결되는 생성된 코드의 선택적 이름입니다.
URL이거나, 상대 경로 혹은 단순 파일명일 수 있으며, 소스맵
생성기는 활용 환경에 맞는 해석을 할 수 있습니다.
sourceRoot 필드는 서버에서 소스파일 경로를 재지정하거나
sources 항목 내 반복값을 제거할 때 쓰는 선택적 소스 루트 문자열입니다. 이 값은 sources 필드 각 항목 앞에 붙습니다.
sources 필드는 mappings 필드에서 사용하는 원본 소스 목록입니다. 각 항목은 (상대일 수도 있는) URL 문자열이거나, 소스 이름을 알 수 없을 경우
null입니다.
sourcesContent 필드는 (예를 들어 원본 소스)를, 소스 호스팅이 불가할 때
사용하는 선택적 콘텐츠 문자열 목록입니다. sources 필드와 동일한 순서이며,
일부 원본 소스는 이름으로
조회하여야 하기에 null일 수 있습니다.
names 필드는 mappings 필드에서 사용 가능한 선택적 심볼 이름 목록입니다.
mappings 필드는 인코딩된 매핑 데이터가 담긴 문자열입니다
(9.2 참고).
ignoreList 필드는 프레임워크 코드, 번들러 등과 같은 서드파티
코드로 간주되는 파일의 인덱스 목록(선택적)입니다. 이 필드를 통해 개발자 도구는 사용자가 보고 싶지 않은 코드나 디버깅 시 넘길 코드들을 미리 걸러낼 수 있습니다.
sources 필드의 인덱스를 참조해서 소스맵상의 모든 서드파티 소스 인덱스를
나열하며, 일부 브라우저는 ignoreList가 없을 경우 이전 필드인 x_google_ignoreList도 사용할 수 있습니다.
9.1 소스맵 디코딩
디코딩된 소스맵
레코드는 다음 필드들을 가집니다:
표 3: 디코딩된 소스맵
레코드의 필드
필드 이름
값 타입
[[File]]
문자열 또는 null
[[Sources]]
List의
디코딩된 소스 레코드
[[Mappings]]
List의
디코딩된 매핑 레코드
디코딩된 소스 레코드는 다음
필드들을 가집니다:
표 4: 디코딩된 소스 레코드의 필드
필드 이름
값 타입
[[URL]]
URL 또는
null
[[Content]]
문자열 또는 null
[[Ignored]]
Boolean
9.1.1 ParseSourceMap ( string, baseURL )
추상 연산 ParseSourceMap은 인수 string(문자열)과 baseURL
(URL)
을 받아 해독된 소스 맵 레코드를 반환한다. 호출 시 다음 단계를 수행한다:
세그먼트가 나타내는 생성된 코드의 줄에서
열(column)의 0 기준 시작값. 이 필드가 첫
세그먼트의 첫 필드이거나, 새로운 생성된 줄(;) 뒤의 첫 세그먼트라면, 이 필드는 전체 base64 VLQ 값을 가진다. 그렇지 않으면, 이 필드는 이전에
나온 값 기준의 base64
VLQ를 가진다. 이 값은 이후의 필드와 다르게, 이전 값이 매 생성 줄마다 초기화됨에 주의.
존재하는 경우, 소스 리스트의 0 기준 인덱스. 이 필드는 이전 값 기준의 base64 VLQ를 가지지만, 해당 필드의 첫 등장에서는 전체 값을 나타낸다.
존재하는 경우, 원본 소스의 0 기준 시작 줄. 이 필드는
이전 값 기준의 base64
VLQ를 가지며, 해당 필드의 처음 등장시 전체 값이 사용된다. 소스 필드가 있으면 반드시 존재해야 한다.
존재하는 경우, 원본 소스의 줄에서 0 기준
열 시작값. 이 필드는 이전 값 기준의 base64 VLQ를
가지며, 해당 필드의 첫 등장시 전체 값을 의미한다. 소스 필드가 있다면 반드시 나타난다.
존재하는 경우, 이 세그먼트와 연관된 이름 리스트의 0 기준 인덱스. 이 필드는 이전 값 기준의 base64 VLQ를 가지며, 해당 필드의 첫 등장시 전체 값을
의미한다.
한 필드만 있는 세그먼트는 원본 소스 코드와 매핑되지 않는, 예를
들어 컴파일러가 생성한 코드와 같이 생성된 코드를 나타낸다. 4개의 필드가 있는
세그먼트는 매핑된 코드이지만, 해당 이름이 존재하지 않는 경우를 나타낸다. 5개의 필드는 이름 매핑까지 있는 경우를 의미한다.
Note 3
file 오프셋 사용도 고려되었으나, 플랫폼별 줄 끝 처리에 따라 원본과 어긋날
위험이 있어, 줄/ column 데이터 사용이 채택되었다.
originalPosition을 새로운 Original Position
Record { [[Source]]:
sources[state.[[SourceIndex]]], [[Line]]:
state.[[OriginalLine]], [[Column]]: state.[[OriginalColumn]] }로 둔다.
이때 매핑 항목의 [[Name]]
값은 원본 소스 언어 구조의 이름이 되어야
합니다. 매핑 중에서 [[Name]]
필드가 null이 아닌 값을 가진 것을 이름이 있는 매핑이라고 부릅니다.
참고 1
즉시 실행 함수식에서 함수와 변수명을 바꾸거나 함수명을 제거하는 미니파이어
다음 열거는 ECMAScript 구문 문법의 생성식과, 소스맵
생성기가 이름이 있는 매핑을 생성해야 하는 해당 토큰 혹은 비터미널(생성식의 오른쪽 항)을 나열합니다. 이때 생성되는 매핑 항목은 9.2.3 절을 따라야 합니다.
이 열거는 반드시 포함해야 하는 "최소한"의 항목으로 이해되어야 하며, 일반적으로 소스맵 생성기는 추가적인 이름 매핑을 자유롭게 생성할 수 있습니다.
참고 2
이 목록에는 생성기가 "추가적으로" 이름 매핑을 생성할 수 있는 토큰도 포함되어 있습니다. 이는 기존 도구가 이름 매핑을 생성하거나 필요로 하는 현실을 반영합니다.
같은 이름에 대해 중복된 이름 매핑을 생성하는 것은 비교적 저렴합니다: 이름 인덱스는 서로 상대적으로 인코딩되므로 이후 같은 이름으로 매핑할 경우
0(A)로 인코딩됩니다.
추상 연산 DecodeSourceMapSources는 baseURL(URL), sourceRoot(문자열
또는 null), sources(문자열 또는 null을 포함하는 리스트),
sourcesContent(문자열 또는 null을 포함하는 리스트),
ignoreList(0 이상의 정수로 구성된 리스트)를
인자로 받아서 디코딩된 소스 레코드를 반환합니다. 호출 시 다음 절차를 수행합니다:
offsetMappings이 비어있지 않으면, previousLastMapping에
offsetMappings의 마지막 원소를 할당한다.
sourceMap을 반환한다.
참고
구현체는 인덱스 소스맵의 각 섹션을 합치지 않고 따로 저장하거나, 예를 들어 이진 탐색을 사용할 수도 있습니다.
11 소스맵 가져오기
11.1 생성 코드와 소스맵 연결
소스 맵 형식은 언어와 플랫폼에 구애받지 않게 설계되었으나, 웹 서버에 호스팅된 자바스크립트라는 일반적인 사용 사례에 대해 어떻게 참조해야 하는지 정의하는 것은 유용하다.
소스 맵을 결과물에 연결하는 방법에는 두 가지가 있다. 첫 번째는 서버에서 HTTP 헤더를 추가하는 것이고, 두 번째는 소스 내부에 주석(annotation)을 넣는 것이다.
소스 맵은 WHATWG URL에 정의된 URL을 통해 연결된다. 특히, URI에서
허용되지 않는 문자는 퍼센트 인코딩되어야 하며, data URI일 수도 있다. data URI를 sourcesContent와 함께 사용할 경우 완전히 자기완결적인 소스 맵을 만들
수 있다.
HTTP sourcemap 헤더는 소스 내부 주석보다 우선한다. 만약 둘 다 존재할 경우, 소스 맵 파일을 해결할 때 헤더의 URL을
사용해야 한다.
소스 맵 URL을 가져오는 방식과는
상관없이, 모두 다음과 같은 방법으로 소스 맵을 해석해야 한다.
만약 생성 코드가
eval() 함수나 new Function()으로 문자열로 평가되고 있다면, source origin은
페이지의 origin이 된다.
11.1.1 HTTP 헤더로 연결
파일이 HTTP(S)로 제공되며 sourcemap 헤더를 포함한 경우, 헤더 값이 연결된 소스맵의 URL입니다.
sourcemap: <url>
참고
이전 버전에서는 헤더 이름으로 x-sourcemap을 추천했지만
이는 이제 폐지되었고 현재는 sourcemap을 사용합니다.
11.1.2 인라인 주석으로 연결
생성된 코드에는
sourceMappingURL이라는 이름의 주석이나, 언어 또는 형식에 따라 대등한 구조가 포함되어야 하며
그 안에 소스 맵의 URL이 들어가야 한다. 이 명세는
자바스크립트, CSS, 그리고 WebAssembly에 대해 해당 주석이 어떻게 보여야 하는지 정의한다. 다른 언어들도 유사한 관례를 따라야 한다.
하나의 언어에 대해 sourceMappingURL 주석을 감지할 수 있는 방법은 여러 가지 있을 수 있으며,
이는 다양한 구현체가 자신에게 가장 복잡도가 낮은 방법을 선택할 수 있게 하기 위함이다.
모든 추출 방법의 결과가 동일하다면 생성된 코드는 명확하게 소스 맵을 연결한다고 본다.
도구가 명확하게 소스 맵을
연결하는 하나 이상의 소스 파일을 받아,
그 결과물로 소스 맵을 링크하는 파일을 생성하는 경우, 반드시 명확하게 링크해야 한다.
추상 연산 JavaScriptExtractSourceMapURL은 인자 source(문자열)를 받아
문자열 또는 null을 반환한다. 이 연산은 자바스크립트 소스에서
소스 맵 URL을 추출한다.
두 가지 구현 방법이 있다: 파싱을 통한
방법과,
파싱 없이 추출하는
방법이다.
WebAssembly는 텍스트 형식이 아니고 주석을 지원하지 않으므로 오직 하나의 명확한 추출 방법만 지원한다. URL은
WebAssembly
name으로 인코딩되어 커스텀 섹션의 콘텐츠에
배치된다. WebAssembly 코드를 생성하는 도구가 sourceMappingURL 이름을 가진 커스텀 섹션을 2개 이상 생성하는 것은 올바르지
않다.
11.2 소스맵 가져오기
11.2.1 FetchSourceMap ( url )
추상 연산 FetchSourceMap은 url (URL)을 인자로 받고
Promise를 반환한다. 이 연산을 호출하면 다음 단계를 수행한다:
일반적으로 소스맵은 생성된 파일과 동일한 이름에 .map 확장자를 붙여 사용합니다. 예를 들어, page.js의 소스맵은
page.js.map로 생성됩니다.
A.2 eval된 코드와 명명된 생성 코드 연결
eval된 코드에 소스맵을 사용하는 경우 지원해야 하는 기존 관례가 있으며, 다음과 같은 형식을 갖습니다:
//# sourceURL=foo.js
Give your eval a name with //@
sourceURL에서 서술되어 있습니다.
Annex B (informative) 참고사항
B.1 언어 중립적 스택 매핑
소스 언어에 대한 지식 없이 수행하는 스택 트레이싱 매핑은
covered되지 않습니다.
B.2 다단계 매핑
최근에는 도구가 일부 DSL(템플릿)이나 TypeScript → JavaScript → minified JavaScript 등 여러 단계를 거쳐 소스를 생성한 뒤 최종 소스맵을
만드는 경우가 많아지고 있습니다.
이 문제는 두 가지 방법 중 하나로 처리할 수 있습니다. 쉬운 방법이지만 정보가 손실되는 방식은 과정 중간의 단계를 디버깅 목적상 무시하는 것이고, 변환의 소스 위치 정보가
무시되거나(중간 변환이 “원본 소스”로 간주됨) 혹은 위치 정보가 이어지도록(중간 변환은 숨김) 구현하는 것입니다.
보다 완전한 방식은 다단계 매핑을 지원하는 것입니다. 즉, 원본 소스에도 소스맵 참조가 있다면, 사용자가 그 참조도 사용할 수 있게 하는 것입니다.
단, 자바스크립트 외의 언어에서는 "소스맵 참조"가 어떻게 생겼는지 명확하지 않습니다. 특히 자바스크립트 스타일의 단일행 주석을 지원하지 않는 언어에서는 소스맵 참조가 어떻게
제공되는지 불분명합니다.
Annex C (informative) 외부 명세에서 정의된 용어
본 문서에서 사용한 ECMA-262 이외의 외부 명세에서 정의된 모든 용어와 알고리즘을 나열합니다.
이 명세서는 GitHub에서 평문 소스 포맷인 Ecmarkup으로 작성되었습니다. Ecmarkup은 HTML과 Markdown의 방언으로,
ECMAScript 명세를 평문으로 작성하고, 본 문서의 편집 규약을 따르면서 완전한 기능의 HTML 렌더링으로 가공할 수 있는 프레임워크 및 도구 세트를 제공합니다. Ecmarkup은
Grammarkdown을 통한 문법 정의와 Ecmarkdown을 통한 알고리즘 기술 등 다양한 형식과 기술을 통합합니다. 이 명세서의
PDF 버전은 HTML 렌더링을 PDF로 인쇄하여 생성됩니다.
이 명세서의 첫 에디션은 Bikeshed라는 또 다른 평문 소스 포맷(HTML과 Markdown
기반)으로 작성되었습니다.
This draft document may be copied and furnished to others, and derivative works that comment on or
otherwise explain it or assist in its implementation may be prepared, copied, published, and
distributed, in whole or in part, without restriction of any kind, provided that the above copyright
notice and this section are included on all such copies and derivative works. However, this document
itself may not be modified in any way, including by removing the copyright notice or references to
Ecma International, except as needed for the purpose of developing any document or deliverable
produced by Ecma International.
This disclaimer is valid only prior to final version of this document. After approval all rights on
the standard are reserved by Ecma International.
The limited permissions are granted through the standardization phase and will not be revoked by Ecma
International or its successors or assigns during this time.
This document and the information contained herein is provided on an "AS IS" basis and ECMA
INTERNATIONAL DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY
WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY OWNERSHIP RIGHTS OR ANY
IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
Software License
All Software contained in this document ("Software") is protected by copyright and is being made
available under the "BSD License", included below. This Software may be subject to third party rights
(rights from parties other than Ecma International), including patent rights, and no licenses under such
third party rights are granted under this license even if the third party concerned is a member of Ecma
International. SEE THE ECMA CODE OF CONDUCT IN PATENT MATTERS AVAILABLE AT https://ecma-international.org/memento/codeofconduct.htm
FOR INFORMATION REGARDING THE LICENSING OF PATENT CLAIMS THAT ARE REQUIRED TO IMPLEMENT ECMA
INTERNATIONAL STANDARDS.
Redistribution and use in source and binary forms, with or without modification, are permitted provided
that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and
the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions
and the following disclaimer in the documentation and/or other materials provided with the
distribution.
Neither the name of the authors nor Ecma International may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE ECMA INTERNATIONAL "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ECMA INTERNATIONAL BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.