초안 ECMA-426 / 2026년 2월 20일

소스맵 형식 사양

이 사양서에 대하여

https://tc39.es/ecma426/에 있는 문서가 가장 정확하고 최신의 소스맵 사양서입니다. 이 문서는 가장 최근에 발행된 스냅샷의 내용과 다음 스냅샷에 포함될 예정인 모든 변경 내역을 포함합니다.

사양서에 기여하기

이 사양서는 GitHub에서 개발됩니다. 여러 방법으로 이 사양서 개발에 기여할 수 있습니다:

이 문서가 어떻게 작성되는지에 대한 더 많은 정보는 간행 정보를 참고하세요.

소개

이 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 참고 문헌

다음 문서들은 이 문서에서 일부 또는 전체 내용이 요구사항을 구성하는 방식으로 참조됩니다. 날짜가 명시된 경우 해당 판만 적용됩니다. 날짜가 없으면, 참조된 문서의 최신 판(개정 포함)을 적용합니다.

3.1 기준 문헌

ECMA-262, ECMAScript® 언어 사양.
https://tc39.es/ecma262/

ECMA-404, JSON 데이터 교환 형식.
https://www.ecma-international.org/publications-and-standards/standards/ecma-404/

3.2 참고 문헌

IETF RFC 4648, Base16, Base32, Base64 데이터 인코딩.
https://datatracker.ietf.org/doc/html/rfc4648

WebAssembly Core Specification.
https://www.w3.org/TR/wasm-core-2/

WHATWG Encoding.
https://encoding.spec.whatwg.org/

WHATWG Fetch.
https://fetch.spec.whatwg.org/

WHATWG Infra.
https://infra.spec.whatwg.org/

WHATWG URL.
https://url.spec.whatwg.org/

4 표기 규칙

이 사양서는 ECMA-262 (표기 규칙)에서 정의된 동일한 표기 규칙을 따르며, 이 절에서 정의된 확장사항을 적용합니다.

4.1 알고리즘 규칙

4.1.1 암시적 완료

이 사양에서 선언된 모든 추상 연산은 알고리즘에서 선언된 반환 타입을 포함한 정상적인 완료 또는 throw completion을 반환하는 것으로 간주합니다. 예를 들면, 다음과 같이 선언된 추상 연산:

4.1.1.1 GetTheAnswer ( input )

추상 연산 GetTheAnswer는 input ( integer 타입 )을 인수로 받고 integer를 반환합니다.

는 다음과 동일합니다:

4.1.1.2 GetTheAnswer2 ( input )

추상 연산 GetTheAnswer2는 input ( integer 타입 )을 인수로 받고, 정상적인 완료integer 또는 throw completion을 반환합니다.

모든 추상 연산 호출 중 completion 레코드를 반환하는 것은 암시적으로 ReturnIfAbrupt 매크로로 감싸는 것으로 간주하며, 명시적으로 Completion 호출로 감싸는 경우는 예외입니다. 예시:

  1. Let result be GetTheAnswer(value).
  2. Let second be Completion(GetTheAnswer(value)).

다음과 동일합니다:

  1. Let result be ReturnIfAbrupt(GetTheAnswer(value)).
  2. Let second be Completion(GetTheAnswer(value)).

4.1.2 선택적 오류

알고리즘이 선택적으로 오류를 보고할 때 구현체는 다음 중 하나의 동작을 선택할 수 있습니다:

  • 알고리즘의 나머지 부분을 계속 실행한다.
  • 사용자(예: 브라우저 콘솔)에게 오류를 보고한 후 알고리즘을 계속 실행한다.
  • ThrowCompletion을 반환한다.

구현체는 선택적 오류마다 서로 다른 동작을 선택해도 됩니다.

4.2 문법 표기법

이 사양서는 ECMA-262 (문법 표기법)에서 정의된 문법 표기 규칙을 따르며, 다음의 주의사항이 있습니다:

5 용어 및 정의

본 문서에서 다음 용어와 정의를 적용합니다.

생성된 코드

컴파일러 또는 트랜스파일러에 의해 생성된 코드.

원본 소스

컴파일러 또는 트랜스파일러에 의해 처리되지 않은 소스 코드.

소스맵 URL

URL로, 생성된 코드에서 소스맵 위치를 참조함.

컬럼

생성된 코드의 한 줄 내에서 0부터 시작하는 인덱스 오프셋. JavaScript 및 CSS 소스맵에서는 UTF-16 코드 유닛 수로, WebAssembly 소스맵에서는 바이너리 콘텐츠의 바이트 인덱스(단일 줄로 표현됨)로 계산됨.

참고
즉, "A"(LATIN CAPITAL LETTER A)는 코드 유닛 1로 계산되고, "🔥"(FIRE)는 코드 유닛 2로 계산됩니다. 다른 콘텐츠 타입의 소스맵에서는 이 규칙이 다를 수 있습니다.

6 base64 VLQ

base64 VLQbase64로 인코딩된 가변 길이 정수로, 가장 큰 비트(6번째 비트)가 연속 비트로 사용되며, "숫자"들은 문자열로 최하위부터 순서대로 인코딩되고, 첫 숫자의 최하위 비트가 부호 비트로 사용됩니다.

노트 1
base64 VLQ 인코딩으로 표현할 수 있는 값은 32비트까지만 제한되며, 더 큰 값이 필요한 경우가 있을 때 확장될 수 있습니다. 즉, 32비트를 초과하는 값은 유효하지 않으며 구현체는 이를 거부할 수 있습니다. 부호 비트는 제한에 포함되지만 연속 비트는 포함되지 않습니다.
노트 2
문자열 "iB"는 두 자리의 base64 VLQ를 의미합니다. 첫 자리 "i"0b100010 비트 패턴을 인코딩하며, 연속 비트 1(VLQ가 계속됨), 부호 비트 0(음수가 아님), 값 비트 0b0001을 가집니다. 두 번째 자리 B0b000001을 인코딩하며, 연속 비트 0, 부호 비트 없음, 값 비트 0b00001을 가집니다. 이 VLQ 문자열을 디코딩하면 숫자 17이 됩니다.
노트 3
문자열 "V"는 한 자리의 base64 VLQ를 의미합니다. "V"0b010101 비트 패턴을 인코딩하며, 연속 비트 0(계속 없음), 부호 비트 1(음수), 값 비트 0b1010을 가집니다. 이 VLQ 문자열을 디코딩하면 숫자 -10이 됩니다.

base64 VLQ는 다음의 렉시컬 문법을 따릅니다:

Vlq :: VlqDigitList VlqDigitList :: TerminalDigit ContinuationDigit VlqDigitList TerminalDigit :: A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e f ContinuationDigit :: g h i j k l m n o p q r s t u v w x y z 0 1 2 3 4 5 6 7 8 9 + /

6.1 VLQSignedValue

구문 지향 연산 VLQSignedValue는 인수를 받지 않고 정수를 반환합니다. 다음 생성 규칙에 따라 단계적으로 정의됩니다:

Vlq :: VlqDigitList
  1. unsignedVLQUnsignedValue VlqDigitList의 값으로 한다.
  2. unsigned modulo 2 = 1이라면, sign을 -1로 한다.
  3. 그렇지 않으면, sign을 1로 한다.
  4. valuefloor(unsigned / 2)으로 한다.
  5. value가 0이고 sign이 -1이면, -231을 반환한다.
  6. value가 231 이상이면, 오류를 발생시킨다.
  7. sign × value를 반환한다.
참고
6번 단계의 확인은 unsignedVLQUnsignedValue VlqDigitList의 값이지, Vlq의 값이 아니기 때문에 필요합니다.

6.2 VLQUnsignedValue

구문 지향 연산 VLQUnsignedValue는 인수를 받지 않으며, 음수가 아닌 정수를 반환합니다. 다음 생성 규칙에 따라 단계적으로 정의됩니다:

Vlq :: VlqDigitList
  1. valueVLQUnsignedValue VlqDigitList의 값으로 한다.
  2. value가 232 이상이면 오류를 발생시킨다.
  3. value를 반환한다.
VlqDigitList :: ContinuationDigit VlqDigitList
  1. leftVLQUnsignedValueContinuationDigit 값으로 한다.
  2. rightVLQUnsignedValueVlqDigitList 값으로 한다.
  3. left + right × 25를 반환한다.
TerminalDigit :: A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e f
  1. 이 생성 규칙에 의해 매치된 문자를 digit에 할당한다.
  2. digit에 대응하는 정수를 IETF RFC 4648에 정의된 base64 인코딩에 따라 value에 할당한다.
  3. Assert: value < 32.
  4. value를 반환한다.
ContinuationDigit :: g h i j k l m n o p q r s t u v w x y z 0 1 2 3 4 5 6 7 8 9 + /
  1. 이 생성 규칙에 의해 매치된 문자를 digit에 할당한다.
  2. digit에 대응하는 정수를 IETF RFC 4648에 정의된 base64 인코딩에 따라 value에 할당한다.
  3. Assert: 32 ≤ value < 64.
  4. value - 32를 반환한다.

7 JSON 값 유틸리티

이 명세의 알고리즘은 ECMA-262 내부 동작을 기반으로 정의되어 있지만, JavaScript가 아닌 플랫폼에서도 쉽게 구현할 수 있도록 설계되었습니다. 이 절에서는 JSON 값을 다뤄 ECMA-262의 세부 사항을 문서의 다른 부분과 분리하여 추상화합니다.

JSON 값JSON 객체, JSON 배열, 문자열(String), 숫자(Number), 불리언(Boolean) 또는 null 중 하나입니다.

JSON 객체객체(Object)로, 각 프로퍼티가 다음을 만족해야 합니다:

JSON 배열JSON 객체로서 다음을 만족해야 합니다:

7.1 ParseJSON ( string )

추상 연산 ParseJSON는 string(문자열)을 받아 JSON 값을 반환합니다. 호출 시 다음 단계를 수행합니다:

  1. resultCall(%JSON.parse%, null, « string »)로 한다.
  2. Assert: resultJSON 값이다.
  3. result를 반환한다.
편집자 주
이 추상 연산은 ECMA-262에서 직접 공개되는 작업 중에 있습니다. 관련 논의는 tc39/ecma262#3540에서 볼 수 있습니다.

7.2 JSONObjectGet ( object, key )

추상 연산 JSONObjectGet은 object(JSON 객체)와 key(문자열)를 받아 JSON 값 또는 missing을 반환합니다. object에서 지정된 key에 대응하는 값을 반환합니다. 호출 시 다음 단계를 수행합니다:

  1. objectkey로 된 자체 프로퍼티가 없다면, missing을 반환한다.
  2. propobjectkey에 해당하는 자체 프로퍼티를 할당한다.
  3. prop[[Value]] 속성을 반환한다.

7.3 JSONArrayIterate ( array )

추상 연산 JSONArrayIterate는 array(JSON 배열)를 받아 리스트(List) 형태의 JSON 값들을 반환합니다. 이는 "For each" 알고리즘 등에서 반복할 수 있도록 array 내의 모든 요소를 포함한 리스트를 만듭니다. 호출 시 다음 단계를 수행합니다:

  1. lengthJSONObjectGet(array, "length")로 한다.
  2. Assert: length는 음수가 아닌 정수(Number)이다.
  3. list를 새로운 빈 리스트로 한다.
  4. i를 0으로 한다.
  5. i < (length)인 동안 반복하여,
    1. valueJSONObjectGet(array, ToString(𝔽(i))))로 한다.
    2. Assert: valuemissing이 아니다.
    3. valuelist에 추가한다.
    4. ii + 1로 한다.
  6. list를 반환한다.

7.4 StringSplit ( string, separators )

추상 연산 StringSplit은 인수 string (문자열)과 separators (비어 있지 않은 문자열들의 리스트)를 받아 문자열들의 리스트를 반환합니다. 이 연산은 separators의 요소 중 하나로 구분된 부분 문자열로 입력 문자열을 나눕니다. 여러 구분자가 일치할 경우, separators에서 먼저 등장하는 구분자가 더 높은 우선순위를 갖습니다. 호출 시 다음 단계를 수행합니다:

  1. parts를 새로운 빈 리스트로 한다.
  2. strLenstring의 길이로 한다.
  3. lastStart를 0으로 한다.
  4. i를 0으로 한다.
  5. i < strLen인 동안 반복하여,
    1. matchedfalse로 한다.
    2. separators의 각 문자열 sep에 대해,
      1. sepLensep의 길이로 한다.
      2. candidatestringi에서 min(i+sepLen, strLen)까지의 부분 문자열로 한다.
      3. candidate = sep이고 matchedfalse이면,
        1. chunkstringlastStart에서 i까지의 부분 문자열로 한다.
        2. chunkparts에 추가한다.
        3. lastStarti + sepLen로 한다.
        4. ii + sepLen로 한다.
        5. matchedtrue로 한다.
    3. matchedfalse이면, ii + 1로 한다.
  6. chunkstringlastStart에서 strLen까지의 부분 문자열로 한다.
  7. chunkparts에 추가한다.
  8. parts를 반환한다.

8 위치 타입

8.1 위치 레코드

위치 레코드는 음수가 아닌 행 번호와 음수가 아닌 컬럼 번호의 쌍입니다:

표 1: 위치 레코드 필드
필드 이름 값 타입
[[Line]] 음수가 아닌 정수(Number)
[[Column]] 음수가 아닌 정수(Number)

8.2 원본 위치 레코드

원본 위치 레코드디코드된 소스 레코드, 음수가 아닌 행 번호, 음수가 아닌 컬럼 번호의 쌍입니다. 이는 위치 레코드와 유사하지만, 구체적인 원본 소스 파일 안의 소스 위치를 나타냅니다.

표 2: 원본 위치 레코드 필드
필드 이름 값 타입
[[Source]] 디코드된 소스 레코드
[[Line]] 음수가 아닌 정수(Number)
[[Column]] 음수가 아닌 정수(Number)

8.3 ComparePositions ( first, second )

추상 연산 ComparePositions는 first ( Position Record 또는 Original Position Record)와 second ( Position Record 또는 Original Position Record)를 인수로 받아 lesser, equal 또는 greater를 반환한다. firstsecond보다 앞에 있으면 lesser를, 두 값이 같으면 equal를, second보다 뒤에 있으면 greater를 각각 반환한다. Original Position Record[[Source]] 필드는 무시한다. 다음 단계들을 수행한다:

  1. first.[[Line]] < second.[[Line]]이면, lesser를 반환합니다.
  2. first.[[Line]] > second.[[Line]]이면, greater를 반환합니다.
  3. Assert: first.[[Line]]second.[[Line]]은 같습니다.
  4. first.[[Column]] < second.[[Column]]이면, lesser를 반환합니다.
  5. first.[[Column]] > second.[[Column]]이면, greater를 반환합니다.
  6. equal를 반환합니다.

9 소스맵 형식

소스맵은 아래와 같이 최상위 JSON 객체 구조를 갖는 JSON 문서입니다:

{
  "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]
}

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) 을 받아 해독된 소스 맵 레코드를 반환한다. 호출 시 다음 단계를 수행한다:

  1. jsonParseJSON(string)의 결과로 둔다.
  2. jsonJSON 객체가 아니면 에러를 던진다.
  3. JSONObjectGet(json, "sections")가 missing이 아니라면, 다음을 수행한다:
    1. DecodeIndexSourceMap(json, baseURL)을 반환한다.
  4. DecodeSourceMap(json, baseURL)을 반환한다.

9.1.2 DecodeSourceMap ( json, baseURL )

추상 연산 DecodeSourceMap은 인수 json (JSON 객체)과 baseURL (URL)을 받아 해독된 소스 맵 레코드 를 반환한다. 호출 시 다음 단계를 수행한다:

  1. JSONObjectGet(json, "version")이 3𝔽이 아니면, 에러를(선택적으로) 보고한다.
  2. mappingsFieldJSONObjectGet(json, "mappings")의 결과로 둔다.
  3. mappingsField문자열이 아니면, 에러를 던진다.
  4. JSONObjectGet(json, "sources")가 JSON 배열이 아니면, 에러를 던진다.
  5. fileFieldGetOptionalString(json, "file")의 결과를 둔다.
  6. sourceRootFieldGetOptionalString(json, "sourceRoot")의 결과를 둔다.
  7. sourcesFieldGetOptionalListOfOptionalStrings(json, "sources")의 결과를 둔다.
  8. sourcesContentFieldGetOptionalListOfOptionalStrings(json, "sourcesContent")의 결과를 둔다.
  9. ignoreListFieldGetOptionalListOfArrayIndexes(json, "ignoreList")의 결과를 둔다.
  10. sourcesDecodeSourceMapSources(baseURL, sourceRootField, sourcesField, sourcesContentField, ignoreListField)의 결과로 둔다.
  11. namesFieldGetOptionalListOfStrings(json, "names")의 결과로 둔다.
  12. mappingsDecodeMappings(mappingsField, namesField, sources)의 결과로 둔다.
  13. mappings를 오름차순으로 정렬한다. Decoded Mapping Record aDecoded Mapping Record b보다 작으려면 ComparePositions(a.[[GeneratedPosition]], b.[[GeneratedPosition]])이 lesser여야 한다.
  14. 해독된 소스 맵 레코드 { [[File]]: fileField, [[Sources]]: sources, [[Mappings]]: mappings } 를 반환한다.

9.1.2.1 GetOptionalString ( object, key )

추상 연산 GetOptionalString은 인수 object (JSON 객체)와 key(문자열)을 받아 문자열 또는 null을 반환한다. 호출 시 다음 단계를 수행한다:

  1. valueJSONObjectGet(object, key)의 결과로 둔다.
  2. value문자열이면, value를 반환한다.
  3. valuemissing이 아니면, 에러를(선택적으로) 보고한다.
  4. null을 반환한다.

9.1.2.2 GetOptionalListOfStrings ( object, key )

추상 연산 GetOptionalListOfStrings는 인수 object (JSON 객체) 및 key (문자열)을 받아 문자열의 리스트를 반환한다. 호출 시 다음 단계를 수행한다:

  1. list를 새로운 비어 있는 리스트로 둔다.
  2. valuesJSONObjectGet(object, key)의 결과로 둔다.
  3. valuesmissing이면, list를 반환한다.
  4. valuesJSON 배열이 아니면, 다음을 수행한다:
    1. 에러를(선택적으로) 보고한다.
    2. list를 반환한다.
  5. JSONArrayIterate(values)의 각 요소 item에 대하여, 다음을 수행한다:
    1. item문자열이면, 다음을 수행한다:
      1. itemlist에 추가한다.
    2. 그 외의 경우,
      1. 에러를(선택적으로) 보고한다.
      2. ""list에 추가한다.
  6. list를 반환한다.

9.1.2.3 GetOptionalListOfOptionalStrings ( object, key )

추상 연산 GetOptionalListOfOptionalStrings는 인수 object (JSON 객체)와 key(문자열)을 받아 문자열 또는 null 값을 요소로 갖는 리스트를 반환한다. 호출 시 다음 단계들을 수행한다:

  1. list를 새로운 비어 있는 리스트로 둔다.
  2. valuesJSONObjectGet(object, key)의 결과로 둔다.
  3. valuesmissing이면, list를 반환한다.
  4. valuesJSON 배열이 아니면, 다음을 수행한다:
    1. 에러를(선택적으로) 보고한다.
    2. list를 반환한다.
  5. JSONArrayIterate(values)의 각 요소 item에 대하여, 다음을 수행한다:
    1. item문자열이면, 다음을 수행한다:
      1. itemlist에 추가한다.
    2. 그 외의 경우,
      1. itemnull 이면, 에러를(선택적으로) 보고한다.
      2. nulllist에 추가한다.
  6. list를 반환한다.

9.1.2.4 GetOptionalListOfArrayIndexes ( object, key )

추상 연산 GetOptionalListOfArrayIndexes는 인수 object (객체)와 key (문자열)를 받아, 0 이상의 정수 들의 리스트를 반환한다. 호출 시 다음 단계를 수행한다:

  1. list를 새로운 빈 리스트로 둔다.
  2. valuesJSONObjectGet(object, key)의 결과로 둔다.
  3. valuesmissing이면 list를 반환한다.
  4. valuesJSON 배열이 아니면, 다음을 수행한다:
    1. 에러를(선택적으로) 보고한다.
    2. list를 반환한다.
  5. JSONArrayIterate(values)의 각 요소 item에 대해, 다음을 수행한다:
    1. item정수(Number)이고, item+0𝔽이며 item+0𝔽이면, 다음을 수행한다:
      1. (item)을 list에 추가한다.
    2. 그 외의 경우,
      1. itemnull 이면 에러를(선택적으로) 보고한다.
      2. nulllist에 추가한다.
  6. list를 반환한다.

9.2 매핑 구조

mappings 필드 데이터는 다음과 같이 분해된다:

  • 생성된 파일의 각 줄을 나타내는 각 그룹은 세미콜론 (;)으로 구분된다
  • 각 세그먼트는 콤마 (,)로 구분된다
  • 각 세그먼트는 1개, 4개, 또는 5개의 가변 길이 필드로 구성된다.

각 세그먼트의 필드는 다음과 같다:

  1. 세그먼트가 나타내는 생성된 코드의 줄에서 열(column)의 0 기준 시작값. 이 필드가 첫 세그먼트의 첫 필드이거나, 새로운 생성된 줄(;) 뒤의 첫 세그먼트라면, 이 필드는 전체 base64 VLQ 값을 가진다. 그렇지 않으면, 이 필드는 이전에 나온 값 기준의 base64 VLQ를 가진다. 이 값은 이후의 필드와 다르게, 이전 값이 매 생성 줄마다 초기화됨에 주의.
  2. 존재하는 경우, 소스 리스트의 0 기준 인덱스. 이 필드는 이전 값 기준의 base64 VLQ를 가지지만, 해당 필드의 첫 등장에서는 전체 값을 나타낸다.
  3. 존재하는 경우, 원본 소스의 0 기준 시작 줄. 이 필드는 이전 값 기준의 base64 VLQ를 가지며, 해당 필드의 처음 등장시 전체 값이 사용된다. 소스 필드가 있으면 반드시 존재해야 한다.
  4. 존재하는 경우, 원본 소스의 줄에서 0 기준 시작값. 이 필드는 이전 값 기준의 base64 VLQ를 가지며, 해당 필드의 첫 등장시 전체 값을 의미한다. 소스 필드가 있다면 반드시 나타난다.
  5. 존재하는 경우, 이 세그먼트와 연관된 이름 리스트의 0 기준 인덱스. 이 필드는 이전 값 기준의 base64 VLQ를 가지며, 해당 필드의 첫 등장시 전체 값을 의미한다.
Note 1
이 인코딩의 목적은 소스 맵의 크기를 줄이기 위함이다. VLQ 인코딩은 Google Calendar에서 수행한 테스트에서 Source Map Revision 2 Proposal 대비 소스 맵을 50% 줄였다.
Note 2
한 필드만 있는 세그먼트는 원본 소스 코드와 매핑되지 않는, 예를 들어 컴파일러가 생성한 코드와 같이 생성된 코드를 나타낸다. 4개의 필드가 있는 세그먼트는 매핑된 코드이지만, 해당 이름이 존재하지 않는 경우를 나타낸다. 5개의 필드는 이름 매핑까지 있는 경우를 의미한다.
Note 3
file 오프셋 사용도 고려되었으나, 플랫폼별 줄 끝 처리에 따라 원본과 어긋날 위험이 있어, 줄/ column 데이터 사용이 채택되었다.

Decoded Mapping Record는 다음 필드를 가진다:

Table 5: Decoded Mapping Records의 필드
필드 이름 값 타입
[[GeneratedPosition]] Position Record
[[OriginalPosition]] Original Position Record 또는 null
[[Name]] 문자열 또는 null

9.2.1 매핑 문법

매핑스필드(MappingsField) : 줄목록(LineList) 줄목록(LineList) : 줄(Line) 줄(Line) ; 줄목록(LineList) 줄(Line) : 매핑목록(MappingList)opt 매핑목록(MappingList) : 매핑(Mapping) 매핑(Mapping) , 매핑목록(MappingList) 매핑(Mapping) : 생성열(GeneratedColumn) 생성열(GeneratedColumn) 원본소스(OriginalSource) 원본줄(OriginalLine) 원본열(OriginalColumn) 이름(Name)opt 생성열(GeneratedColumn) : VLQ 원본소스(OriginalSource) : VLQ 원본줄(OriginalLine) : VLQ 원본열(OriginalColumn) : VLQ 이름(Name) : VLQ

매핑 해독 상태 레코드(Decode Mapping State Record)는 다음 필드를 가진다:

표 6: 매핑 해독 상태 레코드(Decode Mapping State Records)의 필드
필드 이름 값 타입
[[GeneratedLine]] 0 이상의 정수
[[GeneratedColumn]] 0 이상의 정수
[[SourceIndex]] 0 이상의 정수
[[OriginalLine]] 0 이상의 정수
[[OriginalColumn]] 0 이상의 정수
[[NameIndex]] 0 이상의 정수

9.2.1.1 DecodeMappingsField

구문 지시 연산인 DecodeMappingsField는 state(매핑 해독 상태 레코드), mappings(Decoded Mapping Record 리스트), names(문자열 리스트), sources(Decoded Source Record 리스트)를 인자로 받는다. 이 연산은 다음 프로덕션별로 정의된다:

LineList : Line ; LineList
  1. DecodeMappingsFieldLine에 대해 state, mappings, names, sources 인자로 수행한다.
  2. state.[[GeneratedLine]]state.[[GeneratedLine]] + 1을 할당한다.
  3. state.[[GeneratedColumn]]에 0을 할당한다.
  4. DecodeMappingsFieldLineList에 대해 state, mappings, names, sources 인자로 수행한다.
Line : [empty]
  1. 리턴한다.
MappingList : Mapping , MappingList
  1. DecodeMappingsFieldMapping에 대해 state, mappings, names, sources 인자로 수행한다.
  2. DecodeMappingsFieldMappingList에 대해 state, mappings, names, sources 인자로 수행한다.
Mapping : GeneratedColumn
  1. DecodeMappingsFieldGeneratedColumn에 대해 state, mappings, names, sources 인자로 수행한다.
  2. 만약 state.[[GeneratedColumn]] < 0 이라면,
    1. (선택적으로) 에러를 보고한다.
    2. 리턴한다.
  3. position을 새로운 Position Record { [[Line]]: state.[[GeneratedLine]], [[Column]]: state.[[GeneratedColumn]] }로 둔다.
  4. decodedMapping을 새로운 DecodedMappingRecord { [[GeneratedPosition]]: position, [[OriginalPosition]]: null, [[Name]]: null }로 둔다.
  5. decodedMappingmappings에 append 한다.
Mapping : GeneratedColumn OriginalSource OriginalLine OriginalColumn Nameopt
  1. DecodeMappingsFieldGeneratedColumn에 대해 state, mappings, names, sources 인자로 수행한다.
  2. 만약 state.[[GeneratedColumn]] < 0 이라면,
    1. 선택적으로 에러를 보고.
    2. 리턴한다.
  3. generatedPosition을 새로운 Position Record { [[Line]]: state.[[GeneratedLine]], [[Column]]: state.[[GeneratedColumn]] }로 둔다.
  4. DecodeMappingsFieldOriginalSource에 대해 state, mappings, names, sources 인자로 수행한다.
  5. DecodeMappingsFieldOriginalLine에 대해 state, mappings, names, sources 인자로 수행한다.
  6. DecodeMappingsFieldOriginalColumn에 대해 state, mappings, names, sources 인자로 수행한다.
  7. 만약 state.[[SourceIndex]] < 0 이거나 state.[[SourceIndex]]sources의 요소 개수이거나, state.[[OriginalLine]] < 0이거나 state.[[OriginalColumn]] < 0이라면,
    1. 선택적으로 에러를 보고.
    2. originalPositionnull 할당.
  8. 그 외의 경우,
    1. originalPosition을 새로운 Original Position Record { [[Source]]: sources[state.[[SourceIndex]]], [[Line]]: state.[[OriginalLine]], [[Column]]: state.[[OriginalColumn]] }로 둔다.
  9. namenull 할당.
  10. Name이 있는 경우,
    1. DecodeMappingsFieldName에 대해 state, mappings, names, sources 인자로 수행한다.
    2. state.[[NameIndex]] < 0이거나 state.[[NameIndex]]names의 요소 개수라면, 선택적으로 에러 보고한다.
    3. 그 외의 경우, namenames[state.[[NameIndex]]] 을 셋팅.
  11. decodedMapping을 새로운 DecodedMappingRecord { [[GeneratedPosition]]: generatedPosition, [[OriginalPosition]]: originalPosition, [[Name]]: name }로 둔다.
  12. decodedMappingmappings에 append 한다.
GeneratedColumn : Vlq
  1. relativeColumnVLQSignedValue of Vlq 결과를 둔다.
  2. state.[[GeneratedColumn]]state.[[GeneratedColumn]] + relativeColumn를 할당한다.
OriginalSource : Vlq
  1. relativeSourceIndexVLQSignedValue of Vlq 결과를 둔다.
  2. state.[[SourceIndex]]state.[[SourceIndex]] + relativeSourceIndex를 할당한다.
OriginalLine : Vlq
  1. relativeLineVLQSignedValue of Vlq 결과를 둔다.
  2. state.[[OriginalLine]]state.[[OriginalLine]] + relativeLine을 할당한다.
OriginalColumn : Vlq
  1. relativeColumnVLQSignedValue of Vlq 결과를 둔다.
  2. state.[[OriginalColumn]]state.[[OriginalColumn]] + relativeColumn를 할당한다.
Name : Vlq
  1. relativeNameVLQSignedValue of Vlq 결과를 둔다.
  2. state.[[NameIndex]]state.[[NameIndex]] + relativeName을 할당한다.

9.2.2 DecodeMappings ( rawMappings, names, sources )

추상 연산 DecodeMappings는 rawMappings (문자열), names (문자열 리스트), sources (Decoded Source Record 리스트) 를 입력받아, Decoded Mapping Record 리스트 를 반환한다. 호출 시 다음 단계를 수행한다:

  1. mappings를 새로운 빈 리스트로 둔다.
  2. mappingsNodeMappingsField목표 기호로 하여 rawMappings를 파싱할 때의 루트 파싱 노드로 둔다.
  3. 파싱에 실패했다면,
    1. 선택적으로 에러를 보고한다.
    2. mappings를 반환한다.
  4. 모든 필드가 0으로 채워진 새로운 매핑 해독 상태 레코드state로 둔다.
  5. DecodeMappingsFieldmappingsNode에 대하여 state, mappings, names, sources 인자로 수행한다.
  6. mappings를 반환한다.

9.2.3 생성된 자바스크립트 코드의 매핑

생성 코드의 위치에서 매핑 항목이 있을 수 있는 경우를 입력 요소로 정의하며, ECMAScript 어휘 문법을 따릅니다. 매핑 항목은 다음 중 하나를 가리켜야 합니다:

9.2.4 생성된 자바스크립트 코드의 이름

소스맵 생성기는 자바스크립트 토큰에 대해 매핑 항목과 [[Name]] 필드를 생성해야 하는데, 다음 조건을 만족할 때입니다:

  • 원본 소스 언어 구조가 생성된 자바스크립트 코드에 의미적으로 매핑되는 경우
  • 원본 소스 언어 구조가 이름을 갖는 경우

이때 매핑 항목의 [[Name]] 값은 원본 소스 언어 구조의 이름이 되어야 합니다. 매핑 중에서 [[Name]] 필드가 null이 아닌 값을 가진 것을 이름이 있는 매핑이라고 부릅니다.

참고 1
즉시 실행 함수식에서 함수와 변수명을 바꾸거나 함수명을 제거하는 미니파이어

다음 열거는 ECMAScript 구문 문법의 생성식과, 소스맵 생성기가 이름이 있는 매핑을 생성해야 하는 해당 토큰 혹은 비터미널(생성식의 오른쪽 항)을 나열합니다. 이때 생성되는 매핑 항목은 9.2.3 절을 따라야 합니다.

이 열거는 반드시 포함해야 하는 "최소한"의 항목으로 이해되어야 하며, 일반적으로 소스맵 생성기는 추가적인 이름 매핑을 자유롭게 생성할 수 있습니다.

참고 2
이 목록에는 생성기가 "추가적으로" 이름 매핑을 생성할 수 있는 토큰도 포함되어 있습니다. 이는 기존 도구가 이름 매핑을 생성하거나 필요로 하는 현실을 반영합니다. 같은 이름에 대해 중복된 이름 매핑을 생성하는 것은 비교적 저렴합니다: 이름 인덱스는 서로 상대적으로 인코딩되므로 이후 같은 이름으로 매핑할 경우 0(A)로 인코딩됩니다.

9.3 소스 해결

sourceRoot를 앞에 붙인 후에도 소스가 절대 URL이 아니면, 소스는 소스맵 기준으로 상대적으로 해결됩니다(HTML 문서에서 script src 속성을 해석하는 것과 유사).

9.3.1 DecodeSourceMapSources ( baseURL, sourceRoot, sources, sourcesContent, ignoreList )

추상 연산 DecodeSourceMapSources는 baseURL(URL), sourceRoot(문자열 또는 null), sources(문자열 또는 null을 포함하는 리스트), sourcesContent(문자열 또는 null을 포함하는 리스트), ignoreList(0 이상의 정수로 구성된 리스트)를 인자로 받아서 디코딩된 소스 레코드를 반환합니다. 호출 시 다음 절차를 수행합니다:

  1. decodedSources를 새로운 빈 리스트로 설정합니다.
  2. sourcesContentCountsourcesContent의 요소 개수로 설정합니다.
  3. sourceUrlPrefix""로 설정합니다.
  4. sourceRootnull이면,
    1. sourceRoot가 코드 포인트 U+002F (슬래시)로 끝나면,
      1. sourceUrlPrefixsourceRoot로 설정합니다.
    2. 그렇지 않으면,
      1. sourceUrlPrefixsourceRoot"/"문자열 연결로 설정합니다.
  5. index를 0으로 설정합니다.
  6. index < sources의 길이인 동안 반복,
    1. sourcesources[index]로 설정합니다.
    2. decodedSource디코딩된 소스 레코드 { [[URL]]: null, [[Content]]: null, [[Ignored]]: false}로 설정합니다.
    3. sourcenull이면,
      1. sourcesourceUrlPrefixsource문자열 연결로 설정합니다.
      2. sourceURLURL 파싱 결과로 sourcebaseURL로 설정합니다.
      3. sourceURLfailure이면 선택적으로 오류를 보고합니다.
      4. 그 외에는 decodedSource.[[URL]] 값을 sourceURL로 설정합니다.
    4. ignoreListindex가 포함되어 있으면 decodedSource.[[Ignored]]true로 설정합니다.
    5. sourcesContentCount > index이면 decodedSource.[[Content]]sourcesContent[index]로 설정합니다.
    6. decodedSourcedecodedSources에 추가합니다.
  7. decodedSources를 반환합니다.
참고
소스 내용을 표시할 수 있고 동일한 URL에 대한 서로 다른 내용을 동시에 표시할 수 없는 구현체는, 주어진 URL에 해당하는 내용 중 임의의 하나를 선택해 보여줄 수 있습니다.

9.4 확장

소스맵 소비자는 이 포맷에 추가 기능이 추가될 수 있도록, 인식되지 않는 추가 프로퍼티를 무시해야 하며, 그렇지 않고 소스맵을 거부하면 안 됩니다. 기존 사용자들이 깨지지 않도록 하기 위함입니다.

10 인덱스 소스맵

생성 코드의 연결 및 기타 일반적인 후처리를 지원하기 위해 대체 소스맵 표현이 지원됩니다:

{
  "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"
      }
    }
  ]
}

인덱스 맵은 표준 맵의 형태를 따릅니다. 일반 소스맵과 같이 파일 포맷은 최상위 객체를 가진 JSON입니다. 버전 및 file 필드는 일반 소스맵과 동일하며, 새로운 sections 필드가 추가됩니다.

sections 필드는 다음 필드를 가진 객체 배열입니다:

sections는 시작 위치로 정렬되어 있어야 하며, 나타내는 영역이 겹치면 안 됩니다.

10.1 DecodeIndexSourceMap ( json, baseURL )

추상 연산 DecodeIndexSourceMap은 json(객체)와 baseURL(URL)을 인자로 받고 디코딩된 소스맵 레코드를 반환합니다. 호출 시 다음을 수행합니다:

  1. sectionsFieldJSONObjectGet(json, "sections")의 결과를 할당한다.
  2. Assert: sectionsFieldmissing이 아니다.
  3. sectionsFieldJSON 배열이 아니면 에러를 발생시킨다.
  4. JSONObjectGet(json, "version") 결과가 3𝔽이 아니면, (선택적으로) 에러를 보고한다.
  5. fileFieldGetOptionalString(json, "file")의 결과를 할당한다.
  6. sourceMap해독된 소스 맵 레코드 { [[File]]: fileField, [[Sources]]: « », [[Mappings]]: « » }를 할당한다.
  7. previousOffsetPositionnull로 둔다.
  8. previousLastMappingnull로 둔다.
  9. JSON 값 section에 대해 JSONArrayIterate(sectionsField)를 순회하며 반복한다:
    1. sectionJSON 객체가 아니면,
      1. (선택적으로) 에러를 보고한다.
    2. 그 외의 경우,
      1. offsetJSONObjectGet(section, "offset")를 할당한다.
      2. offsetJSON 객체가 아니면 에러를 발생시킨다.
      3. offsetLineJSONObjectGet(offset, "line")를 할당한다.
      4. offsetColumnJSONObjectGet(offset, "column")을 할당한다.
      5. offsetLine정수(Number)가 아니면:
        1. (선택적으로) 에러를 보고한다.
        2. offsetLine+0𝔽을 할당한다.
      6. offsetColumn정수(Number)가 아니면:
        1. (선택적으로) 에러를 보고한다.
        2. offsetColumn+0𝔽을 할당한다.
      7. offsetPosition에 새로운 Position Record { [[Line]]: offsetLine, [[Column]]: offsetColumn }를 할당한다.
      8. previousOffsetPositionnull인 경우
        1. ComparePositions(offsetPosition, previousOffsetPosition) 결과가 lesser라면, (선택적으로) 에러를 보고한다.
      9. previousLastMappingnull인 경우
        1. ComparePositions(offsetPosition, previousLastMapping.[[GeneratedPosition]]) 결과가 lesser라면, (선택적으로) 에러를 보고한다.
        2. 참고: 이 부분은 인덱스 소스 맵의 sections 필드 항목이 오름차순이며 겹치지 않는지 확인한다. 생성기는 겹치는 섹션이 없는 인덱스 소스 맵만 생성해야 하지만, 소비자는 단순히 offset만 검사할 수도 있다.
      10. mapFieldJSONObjectGet(section, "map")을 할당한다.
      11. mapFieldJSON 객체가 아니면 에러를 발생시킨다.
      12. decodedSectionCompletionCompletion(DecodeSourceMap(json, baseURL)) 결과를 할당한다.
      13. decodedSectionCompletionthrow completion이면:
        1. (선택적으로) 에러를 보고한다.
      14. 그 외인 경우,
        1. decodedSectiondecodedSectionCompletion.[[Value]]를 할당한다.
        2. Decoded Source Record additionalSource에 대해 decodedSection.[[Sources]]를 반복하면서,
          1. sourceMap.[[Sources]]additionalSource가 없다면,
            1. additionalSourcesourceMap.[[Sources]]에 추가한다.
        3. offsetMappings에 새로운 빈 리스트를 할당한다.
        4. Decoded Mapping Record mapping에 대해 decodedSection.[[Mappings]]를 반복하면서,
          1. mapping.[[GeneratedPosition]].[[Line]] = 0이면,
            1. mapping.[[GeneratedPosition]].[[Column]]mapping.[[GeneratedPosition]].[[Column]] + offsetColumn을 할당한다.
          2. mapping.[[GeneratedPosition]].[[Line]]mapping.[[GeneratedPosition]].[[Line]] + offsetLine을 할당한다.
          3. mappingoffsetMappings에 추가한다.
        5. sourceMap.[[Mappings]]리스트 연결sourceMap.[[Mappings]]offsetMappings를 할당한다.
        6. previousOffsetPositionoffsetPosition을 할당한다.
        7. offsetMappings이 비어있지 않으면, previousLastMappingoffsetMappings의 마지막 원소를 할당한다.
    3. sourceMap을 반환한다.
참고
구현체는 인덱스 소스맵의 각 섹션을 합치지 않고 따로 저장하거나, 예를 들어 이진 탐색을 사용할 수도 있습니다.

11 소스맵 가져오기

11.1 생성 코드와 소스맵 연결

소스 맵 형식은 언어와 플랫폼에 구애받지 않게 설계되었으나, 웹 서버에 호스팅된 자바스크립트라는 일반적인 사용 사례에 대해 어떻게 참조해야 하는지 정의하는 것은 유용하다.

소스 맵을 결과물에 연결하는 방법에는 두 가지가 있다. 첫 번째는 서버에서 HTTP 헤더를 추가하는 것이고, 두 번째는 소스 내부에 주석(annotation)을 넣는 것이다.

소스 맵은 WHATWG URL에 정의된 URL을 통해 연결된다. 특히, URI에서 허용되지 않는 문자는 퍼센트 인코딩되어야 하며, data URI일 수도 있다. data URI를 sourcesContent와 함께 사용할 경우 완전히 자기완결적인 소스 맵을 만들 수 있다.

HTTP sourcemap 헤더는 소스 내부 주석보다 우선한다. 만약 둘 다 존재할 경우, 소스 맵 파일을 해결할 때 헤더의 URL을 사용해야 한다.

소스 맵 URL을 가져오는 방식과는 상관없이, 모두 다음과 같은 방법으로 소스 맵을 해석해야 한다.

소스 맵 URL이 절대 경로가 아니면, 이 URL은 생성 코드source origin을 기준으로 해석된다. source origin은 다음 중 한 가지 방식으로 결정된다:

  • 생성된 소스가 src 속성이 있는 스크립트 요소와 연결되어 있지 않고, 생성 코드 안에 //# sourceURL 주석이 존재한다면, 해당 주석을 사용해서 source origin을 결정한다.

    Note
    과거에는 //@ sourceURL이었으며, //@ sourceMappingURL처럼 두 방식 모두 허용하지만 //#가 우선시된다.
  • 만약 생성 코드가 스크립트 요소와 연결돼 있고, 그 요소가 src 속성을 가진다면, 해당 src 속성이 source origin이 된다.
  • 만약 생성 코드가 스크립트 요소와 연결돼 있지만 src 속성이 없다면, source origin은 페이지의 origin이 된다.
  • 만약 생성 코드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 주석을 감지할 수 있는 방법은 여러 가지 있을 수 있으며, 이는 다양한 구현체가 자신에게 가장 복잡도가 낮은 방법을 선택할 수 있게 하기 위함이다. 모든 추출 방법의 결과가 동일하다면 생성된 코드명확하게 소스 맵을 연결한다고 본다.

도구가 명확하게 소스 맵을 연결하는 하나 이상의 소스 파일을 받아, 그 결과물로 소스 맵을 링크하는 파일을 생성하는 경우, 반드시 명확하게 링크해야 한다.

Note

다음 자바스크립트 코드는 소스 맵을 연결하지만, 명확하게 연결하지 않는다:

let a = `
//# sourceMappingURL=foo.js.map
// `

여기서 소스 맵 URL파싱을 통해 추출하면 foo.js.map이 나오지만, 파싱 없이 추출하면 null이 된다.

11.1.2.1 JavaScriptExtractSourceMapURL ( source )

추상 연산 JavaScriptExtractSourceMapURL은 인자 source(문자열)를 받아 문자열 또는 null을 반환한다. 이 연산은 자바스크립트 소스에서 소스 맵 URL을 추출한다. 두 가지 구현 방법이 있다: 파싱을 통한 방법과, 파싱 없이 추출하는 방법이다.

소스 맵 URL파싱을 통해 추출하려면:

  1. tokenssourceECMA-262의 렉시컬 문법에 따라 파싱하여 얻은 입력 요소리스트를 할당한다.
  2. tokens의 각 넌터미널 token에 대해, 역순으로, 다음을 수행:
    1. tokenSingleLineComment, WhiteSpace, 또는 LineTerminatorSequence가 아니라면, null을 반환한다.
    2. commenttoken의 내용을 할당한다.
    3. sourceMapURLMatchSourceMapURL(comment)의 결과를 할당한다.
    4. sourceMapURL문자열이면, sourceMapURL을 반환한다.
  3. null을 반환한다.

소스 맵 URL파싱 없이 추출하려면:

  1. linesStringSplit(source, « "\u000D\u000A", "\u000A", "\u000D", "\u2028", "\u2029" ») 결과를 할당한다.
  2. 참고: 위 문자열 목록은 LineTerminatorSequence 프로덕션과 일치한다.
  3. lines의 각 문자열 lineStr에 대하여, 역순(리스트 역순)으로 다음을 수행:
    1. lineStringToCodePoints(lineStr) 결과를 할당한다.
    2. position에 0을 할당한다.
    3. lineLengthline의 길이를 할당한다.
    4. position < lineLength인 동안 반복:
      1. firstline[position]을 할당.
      2. first가 U+002F(SOLIDUS)이고 position + 1 < lineLength라면:
        1. positionposition + 1을 할당.
        2. secondline[position]을 할당.
        3. second가 U+002F(SOLIDUS)라면:
          1. positionposition + 1을 할당.
          2. commentlineStrsubstring (position부터 lineLength까지)를 할당.
          3. comment에 코드포인트 U+0022(QUOTATION MARK), U+0027(APOSTROPHE), U+0060(GRAVE ACCENT) 중 하나라도 있으면:
            1. null을 반환.
          4. comment에 코드포인트 U+002A(ASTERISK) 바로 뒤에 U+002F(SOLIDUS)가 오면:
            1. null을 반환한다.
          5. sourceMapURLMatchSourceMapURL(comment) 결과를 할당.
          6. sourceMapURL문자열이면 sourceMapURL을 반환한다.
          7. positionlineLength로 설정한다.
        4. 그 외:
          1. null을 반환한다.
      3. 그 외 first가 ECMAScript WhiteSpace이면:
        1. positionposition + 1을 할당.
      4. 그 외의 경우:
        1. null을 반환.
  4. null을 반환한다.
Note

source를 에러 없이 파싱할 수 있고, 소스 맵 URL파싱 없이 추출하여 non-null 값을 얻는다면, 파싱을 통해 추출해도 결과는 같다.

11.1.2.1.1 MatchSourceMapURL ( comment )

추상 연산 MatchSourceMapURL은 comment(문자열) 인자를 받아 none 또는 문자열을 반환합니다. 호출 시 다음을 실행합니다:

  1. patternRegExpCreate("^[@#]\s*sourceMappingURL=(\S*?)\s*$", "")로 설정
  2. matchRegExpExec(pattern, comment)로 설정
  3. matchnull이 아니면 Get(match, "1")을 반환
  4. none 반환
참고
이 주석 접두사는 원래 //@였으나, Internet Explorer의 조건부 컴파일과 충돌하여 //#로 변경되었습니다.

소스맵 생성기는 반드시 //#만 내보내야 하며, 소스맵 소비자는 //@//# 모두 허용해야 합니다.

11.1.2.2 CSSExtractSourceMapURL ( source )

추상 연산 CSSExtractSourceMapURL은 source (문자열)를 인자로 받아 문자열 또는 null을 반환한다. 이 연산은 CSS 소스에서 소스 맵 URL을 추출한다.

CSS에서 소스 맵 URL을 추출하는 방법은 자바스크립트와 비슷하지만, CSS는 /* ... */ 형태의 주석만 지원한다는 점이 다르다.

11.1.2.3 WebAssemblyExtractSourceMapURL ( bytes )

추상 연산 WebAssemblyExtractSourceMapURL은 bytes(Data Block) 인자를 받아 문자열 또는 null을 반환합니다. WebAssembly 바이너리 소스에서 소스맵 URL을 추출합니다.

  1. modulemodule_decode(bytes)의 결과로 둔다.
  2. moduleWebAssembly 오류라면, null을 반환한다.
  3. module의 모든 커스텀 섹션 customSection에 대해, 다음을 수행한다:
    1. namecustomSectionname으로 둔다.
    2. CodePointsToString(name)이 "sourceMappingURL"이면, 다음을 수행한다:
      1. valuecustomSectionbytes로 둔다.
      2. CodePointsToString(value)를 반환한다.
  4. null을 반환한다.

WebAssembly는 텍스트 형식이 아니고 주석을 지원하지 않으므로 오직 하나의 명확한 추출 방법만 지원한다. URLWebAssembly name으로 인코딩되어 커스텀 섹션의 콘텐츠에 배치된다. WebAssembly 코드를 생성하는 도구가 sourceMappingURL 이름을 가진 커스텀 섹션을 2개 이상 생성하는 것은 올바르지 않다.

11.2 소스맵 가져오기

11.2.1 FetchSourceMap ( url )

추상 연산 FetchSourceMap은 url (URL)을 인자로 받고 Promise를 반환한다. 이 연산을 호출하면 다음 단계를 수행한다:

  1. promiseCapabilityNewPromiseCapability(%Promise%) 값을 할당한다.
  2. requesturlrequest URL로 갖는 새로운 request를 할당한다.
  3. processResponseConsumeBody에 (response, bodyBytes)를 매개변수로 하고, promiseCapabilityurl을 캡처하는 새로운 Abstract Closure를 할당한다. 아래 단계를 수행한다:
    1. bodyBytesnull 또는 failure이면:
      1. Call(promiseCapability.[[Reject]], undefined, « a new TypeError »)를 수행한다.
      2. 리턴한다.
    2. urlschemeHTTP(S) scheme이고, 바이트 시퀀스 `)]}'`가 bodyBytes접두사이면:
      1. bodyBytes길이가 0이 아니고 bodyBytes[0]이 HTTP 줄바꿈 바이트가 아닐 때까지 반복:
        1. bodyBytes의 0번째 요소를 지운다.
    3. bodyStringCompletion(UTF-8 decode(bodyBytes)) 값을 할당한다.
    4. IfAbruptRejectPromise(bodyString, promiseCapability)를 수행한다.
    5. jsonValueCompletion(ParseJSON(bodyString)) 값을 할당한다.
    6. IfAbruptRejectPromise(jsonValue, promiseCapability)를 수행한다.
    7. Call(promiseCapability.[[Resolve]], undefined, « jsonValue »)를 수행한다.
  4. fetch request를, processResponseConsumeBodyprocessResponseConsumeBody로 설정하여 실행한다.
  5. promiseCapability.[[Promise]]를 반환한다.
Note

과거의 이유로, 소스 맵을 HTTP(S)로 전송할 때 서버에서 )]}'로 시작하는 라인을 소스 맵 앞에 붙일 수 있다.

)]}'garbage here
{"version": 3, ...}

이렇게 오면 다음과 같이 해석된다:

{"version": 3, ...}

12 소스맵 레코드에 대한 연산

소스 맵을 해독한 이후, 소스 맵 소비자는 결과로 얻은 해독된 소스 맵 레코드들를 활용하여 디버깅 또는 기타 용도의 위치 정보를 조회할 수 있습니다. 본 절에서는 소스 맵 소비자가 지원할 수 있는 일반적인 연산의 동작을 설명합니다.

GetOriginalPositions 연산은 생성된 코드 상의 위치에 해당하는 원본 소스에서의 위치를 질의하는데 사용할 수 있습니다. 예를 들어, 이는 디버거에서 사용자가 생성된 코드를 클릭하면, 원본 소스로 이동할 수 있게 해줍니다.

12.1 GetOriginalPositions ( sourceMapRecord, generatedPosition )

추상 연산 GetOriginalPositions는 sourceMapRecord (디코드된 소스 맵 레코드)와 generatedPosition (위치 레코드)를 인자로 받아, 리스트(List) 형태의 원본 위치 레코드들을 반환한다. 호출 시 다음 단계를 수행한다:

  1. mappingssourceMapRecord.[[Mappings]]를 할당한다.
  2. lastnull을 할당한다.
  3. originalPositions에 새로운 비어있는 리스트(List)를 할당한다.
  4. mappings의 각 요소 mapping에 대해, 리스트 역순으로 다음을 수행한다:
    1. lastnull인 경우:
      1. ComparePositions(mapping.[[GeneratedPosition]], generatedPosition)의 결과가 lesser 또는 equal라면:
        1. lastmapping으로 설정한다.
  5. lastnull이 아니라면,
    1. mappings의 각 요소 mapping에 대해 다음을 수행:
      1. ComparePositions(last.[[GeneratedPosition]], mapping.[[GeneratedPosition]])의 결과가 equal이면:
        1. mapping.[[OriginalPosition]]originalPositions에 추가한다.
  6. originalPositions을 반환한다.

Annex A (informative) 관례

소스맵을 다루거나 생성할 때 다음과 같은 관례를 따라야 합니다.

A.1 소스맵 네이밍

일반적으로 소스맵은 생성된 파일과 동일한 이름에 .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 이외의 외부 명세에서 정의된 모든 용어와 알고리즘을 나열합니다.

WebAssembly Core Specification <https://www.w3.org/TR/wasm-core-2/>
custom section, module_decode, WebAssembly error, WebAssembly names
WHATWG Encoding <https://encoding.spec.whatwg.org/>
UTF-8 decode
WHATWG Fetch <https://fetch.spec.whatwg.org/>
fetch, HTTP newline byte, processResponseConsumeBody, request, request URL
WHATWG Infra <https://infra.spec.whatwg.org/>
byte sequence, byte-sequence-prefix, byte-sequence-length,
WHATWG URL <https://url.spec.whatwg.org/>
HTTP(S) scheme, scheme, URL, URL parsing

Annex D (informative) 참고문헌

  1. IETF RFC 4648, The Base16, Base32, and Base64 Data Encodings, <https://datatracker.ietf.org/doc/html/rfc4648>
  2. ECMA-262, ECMAScript® 언어 스펙, <https://tc39.es/ecma262/>
  3. ECMA-404, The JSON Data Interchange Format, <https://www.ecma-international.org/publications-and-standards/standards/ecma-404/>
  4. WebAssembly Core Specification, <https://www.w3.org/TR/wasm-core-2/>
  5. WHATWG Encoding, <https://encoding.spec.whatwg.org/>
  6. WHATWG Fetch, <https://fetch.spec.whatwg.org/>
  7. WHATWG Infra, <https://infra.spec.whatwg.org/>
  8. WHATWG URL, <https://url.spec.whatwg.org/>
  9. Give your eval a name with //@ sourceURL, Firebug (2009), <http://blog.getfirebug.com/2009/08/11/give-your-eval-a-name-with-sourceurl/>
  10. Source Map Revision 2 Proposal, John Lenz (2010), <https://docs.google.com/document/d/1xi12LrcqjqIHTtZzrzZKmQ3lbTv9mKrN076UB-j3UZQ/>
  11. 가변 길이 quantity, 위키백과, <https://en.wikipedia.org/wiki/Variable-length_quantity>

Annex E (informative) 간기

이 명세서는 GitHub에서 평문 소스 포맷인 Ecmarkup으로 작성되었습니다. Ecmarkup은 HTML과 Markdown의 방언으로, ECMAScript 명세를 평문으로 작성하고, 본 문서의 편집 규약을 따르면서 완전한 기능의 HTML 렌더링으로 가공할 수 있는 프레임워크 및 도구 세트를 제공합니다. Ecmarkup은 Grammarkdown을 통한 문법 정의와 Ecmarkdown을 통한 알고리즘 기술 등 다양한 형식과 기술을 통합합니다. 이 명세서의 PDF 버전은 HTML 렌더링을 PDF로 인쇄하여 생성됩니다.

이 명세서의 첫 에디션은 Bikeshed라는 또 다른 평문 소스 포맷(HTML과 Markdown 기반)으로 작성되었습니다.

표준화 이전 버전의 문서는 Google Docs로 작성되었습니다.

Copyright & Software License

Ecma International

Rue du Rhone 114

CH-1204 Geneva

Tel: +41 22 849 6000

Fax: +41 22 849 6001

Web: https://ecma-international.org/

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:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  2. 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.
  3. 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.