Multipage preference

Draft ECMA-426 / October 21, 2026

소스 맵 형식 명세

이 명세에 대하여

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

이 명세에 기여하기

이 명세는 GitHub에서 개발됩니다. 이 명세의 개발에 기여하는 방법은 여러 가지가 있습니다:

이 문서의 작성 방식에 관한 자세한 내용은 판권기를 참조하십시오.

소개

이 Ecma 표준은 트랜스파일된 소스 코드를 원본 소스에 다시 매핑하는 데 사용하는 소스 맵 형식을 정의합니다.

소스 맵 형식의 목표는 다음과 같습니다:

최초의 소스 맵 형식(v1)은 Joseph Schorr가 최적화된 JavaScript 코드의 소스 수준 디버깅을 지원하기 위해 Closure Inspector에서 사용하도록 만들었습니다(형식 자체는 언어에 독립적입니다). 그러나 소스 맵을 사용하는 프로젝트의 규모가 커지면서 형식의 장황함이 문제가 되기 시작했습니다. v2 형식(소스 맵 개정판 2 제안)은 소스 맵의 전체 크기를 줄이기 위해 단순성과 유연성 일부를 희생하여 만들어졌습니다. 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 핵심 명세.
https://www.w3.org/TR/wasm-core-2/

WHATWG 인코딩.
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 암묵적 완료

이 명세에 선언된 모든 추상 연산은 해당 알고리즘에 선언된 반환 타입의 값을 포함하는 정상 완료 또는 던지기 완료를 반환하는 것으로 암묵적으로 가정합니다. 예를 들어, 다음과 같이 선언된 추상 연산은

4.1.1.1 GetTheAnswer ( input )

추상 연산 GetTheAnswer의 입력은 인수 input (정수)이다. 반환값은 정수이다.

다음과 같습니다:

4.1.1.2 GetTheAnswer2 ( input )

추상 연산 GetTheAnswer2의 입력은 인수 input (정수)이다. 반환값은 정수를 포함하는 정상 완료 또는 throw 완료이다.

완료 레코드를 반환하는 추상 연산에 대한 모든 호출은 명시적인 Completion 호출로 감싸지지 않는 한, ECMA-262의 완료 레코드를 풀어내는 축약 표기인 ?로 감싸진 것으로 암묵적으로 가정합니다. 예를 들면 다음과 같습니다:

  1. result를 GetTheAnswer(value)로 둡니다.
  2. second를 Completion(GetTheAnswer(value))로 둡니다.

다음과 같습니다:

  1. result를 ? GetTheAnswer(value)로 둡니다.
  2. second를 Completion(GetTheAnswer(value))로 둡니다.

4.1.2 선택적 오류

알고리즘이 선택적으로 오류를 보고하도록 되어 있는 경우, 구현은 다음 동작 중 하나를 선택할 수 있습니다:

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

구현은 서로 다른 선택적 오류에 대해 서로 다른 동작을 선택할 수 있습니다.

4.2 문법 표기법

이 명세는 ECMA-262(문법 표기법)에 정의된 것과 동일한 문법 표기 규칙을 따르며, 다음 사항에 유의해야 합니다:

5 용어 및 정의

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

생성된 코드

컴파일러 또는 트랜스파일러가 생성한 코드입니다.

원본 소스

컴파일러 또는 트랜스파일러를 거치지 않은 소스 코드입니다.

소스 맵 URL

생성된 코드에서 소스 맵의 위치를 참조하는 URL입니다.

열

생성된 코드의 한 줄 안에서 0부터 시작하는 인덱스로 나타낸 오프셋이며, JavaScript 및 CSS 소스 맵에서는 UTF-16 코드 단위로, WebAssembly 소스 맵에서는 바이너리 콘텐츠(한 줄로 표현됨)의 바이트 인덱스로 계산합니다.

Note
이는 “A”(LATIN CAPITAL LETTER A)가 1개 코드 단위로, “🔥”(FIRE)가 2개 코드 단위로 계산된다는 의미입니다. 다른 콘텐츠 타입의 소스 맵은 이와 다를 수 있습니다.

6 base64 VLQ

base64 VLQ는 base64로 인코딩된 가변 길이 수량으로, 최상위 비트(6번째 비트)를 연속 비트로 사용하며, “자릿수”는 최하위 자릿수부터 문자열에 인코딩되고, 첫 번째 자릿수의 최하위 비트는 부호 비트로 사용됩니다.

Note 1
base64 VLQ 인코딩으로 표현할 수 있는 값은 더 큰 값의 사용 사례가 제시되기 전까지 32비트 수량으로 제한됩니다. 이는 32비트를 초과하는 값은 유효하지 않으며 구현이 이를 거부할 수 있음을 의미합니다. 부호 비트는 이 제한에 포함되지만, 연속 비트는 포함되지 않습니다.
Note 2
문자열 "iB"는 두 자릿수로 구성된 base64 VLQ를 나타냅니다. 첫 번째 자릿수 "i"는 비트 패턴 0b100010을 인코딩하며, 연속 비트는 1(VLQ가 계속됨), 부호 비트는 0(음수가 아님), 값 비트는 0b0001입니다. 두 번째 자릿수 B는 비트 패턴 0b000001을 인코딩하며, 연속 비트는 0이고, 부호 비트는 없으며, 값 비트는 0b00001입니다. 이 VLQ 문자열을 디코딩한 결과는 숫자 17입니다.
Note 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. unsigned를 VlqDigitList의 VLQUnsignedValue로 둡니다.
  2. unsigned modulo 2 = 1이면, sign을 -1로 둡니다.
  3. 그렇지 않으면, sign을 1로 둡니다.
  4. value를 floor(unsigned / 2)로 둡니다.
  5. value가 0이고 sign이 -1이면, -231을 반환합니다.
  6. value가 ≥ 231이면, 오류를 던집니다.
  7. sign × value를 반환합니다.
Note
6 단계의 검사는 unsigned가 Vlq의 VLQUnsignedValue가 아니라 VlqDigitList의 VLQUnsignedValue이기 때문에 필요합니다.

6.2 VLQUnsignedValue

구문 지향 연산 VLQUnsignedValue: 인수를 받지 않는다. 반환값은 음이 아닌 정수이다. 다음 생성 규칙별로 정의된다:

Vlq :: VlqDigitList
  1. value를 VlqDigitList의 VLQUnsignedValue로 둡니다.
  2. value가 ≥ 232이면, 오류를 던집니다.
  3. value를 반환합니다.
VlqDigitList :: ContinuationDigit VlqDigitList
  1. left를 ContinuationDigit의 VLQUnsignedValue로 둡니다.
  2. right를 VlqDigitList의 VLQUnsignedValue로 둡니다.
  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. value를 IETF RFC 4648에 정의된 base64 인코딩에 따라 digit에 대응하는 정수로 둡니다.
  3. 단언: 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. value를 IETF RFC 4648에 정의된 base64 인코딩에 따라 digit에 대응하는 정수로 둡니다.
  3. 단언: 32 ≤ value < 64입니다.
  4. value - 32를 반환합니다.

7 JSON 값 유틸리티

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

JSON value는 JSON object, JSON array, String, Number, Boolean 또는 null 중 하나입니다.

JSON object는 각 속성이 다음을 만족하는 Object입니다:

JSON array는 다음을 만족하는 JSON object입니다:

7.1 ParseJSON ( string )

추상 연산 ParseJSON의 입력은 인수 string (String)이다. 반환값은 JSON 값이다. 호출되면 다음 단계를 수행한다:

  1. result를 Call(%JSON.parse%, null, « string »)로 둡니다.
  2. 단언: result는 JSON value입니다.
  3. result를 반환합니다.
Editor's Note
이 추상 연산은 tc39/ecma262#3540에서 ECMA-262 자체가 노출하도록 하는 작업이 진행 중입니다.

7.2 JSONObjectGet ( object, key )

추상 연산 JSONObjectGet의 입력은 인수 object (JSON 객체), key (String)이다. 반환값은 JSON 값 또는 missing이다. object에서 지정된 key와 연결된 값을 반환합니다. 호출되면 다음 단계를 수행한다:

  1. object에 키가 key인 자체 속성이 없으면, missing을 반환합니다.
  2. prop을 키가 key인 object의 자체 속성으로 둡니다.
  3. prop의 [[Value]] 어트리뷰트를 반환합니다.

7.3 JSONArrayIterate ( array )

추상 연산 JSONArrayIterate의 입력은 인수 array (JSON 배열)이다. 반환값은 JSON 값의 List이다. 알고리즘에서 “각각에 대해”를 사용하여 순회할 수 있도록 array의 모든 요소를 포함하는 List를 반환합니다. 호출되면 다음 단계를 수행한다:

  1. length를 JSONObjectGet(array, "length")로 둡니다.
  2. 단언: length는 음이 아닌 정숫값의 Number입니다.
  3. list를 새 빈 List로 둡니다.
  4. i를 0으로 둡니다.
  5. i < ℝ(length)인 동안 반복합니다:
    1. value를 JSONObjectGet(array, ToString(𝔽(i)))로 둡니다.
    2. 단언: value는 missing이 아닙니다.
    3. value를 list에 추가합니다.
    4. i를 i + 1로 설정합니다.
  6. list를 반환합니다.

7.4 StringSplit ( string, separators )

추상 연산 StringSplit의 입력은 인수 string (String), separators (비어 있지 않은 String의 List)이다. 반환값은 String의 List이다. separators의 요소 중 하나로 구분되는 부분 문자열로 문자열을 나눕니다. 여러 구분자가 일치하면 separators에서 먼저 나타나는 구분자가 더 높은 우선순위를 가집니다. 호출되면 다음 단계를 수행한다:

  1. parts를 새 빈 List로 둡니다.
  2. strLen을 string의 길이로 둡니다.
  3. lastStart를 0으로 둡니다.
  4. i를 0으로 둡니다.
  5. i < strLen인 동안 반복합니다:
    1. matched를 false로 둡니다.
    2. separators의 각 String sep에 대해 다음을 수행합니다:
      1. sepLen을 sep의 길이로 둡니다.
      2. candidate를 string의 i부터 min(i + sepLen, strLen)까지의 부분 문자열로 둡니다.
      3. candidate = sep이고 matched가 false이면, 다음을 수행합니다:
        1. chunk를 string의 lastStart부터 i까지의 부분 문자열로 둡니다.
        2. chunk를 parts에 추가합니다.
        3. lastStart를 i + sepLen으로 설정합니다.
        4. i를 i + sepLen으로 설정합니다.
        5. matched를 true로 설정합니다.
    3. matched가 false이면, i를 i + 1로 설정합니다.
  6. chunk를 string의 lastStart부터 strLen까지의 부분 문자열로 둡니다.
  7. chunk를 parts에 추가합니다.
  8. parts를 반환합니다.

8 위치 타입

8.1 Position Record

Position Record는 음이 아닌 행 번호와 음이 아닌 열 번호로 구성된 튜플입니다:

Table 1: Position Record 필드
필드 이름 값 타입
[[Line]] 음이 아닌 정숫값의 Number
[[Column]] 음이 아닌 정숫값의 Number

8.2 Original Position Record

Original Position Record는 Decoded Source Record, 음이 아닌 행 번호 및 음이 아닌 열 번호로 구성된 튜플입니다. Position Record와 유사하지만, 특정 원본 소스 파일의 소스 위치를 설명합니다.

Table 2: Original Position Record 필드
필드 이름 값 타입
[[Source]] Decoded Source Record
[[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이다. first가 second보다 앞에 있는지, 같은지, 뒤에 있는지에 따라 각각 lesser, equal 또는 greater를 반환합니다. Original Position Record의 [[Source]] 필드는 무시됩니다. 호출되면 다음 단계를 수행한다:

  1. first.[[Line]] < second.[[Line]]이면, lesser를 반환합니다.
  2. first.[[Line]] > second.[[Line]]이면, greater를 반환합니다.
  3. 단언: 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 소스 맵 디코딩

Decoded Source Map Record에는 다음 필드가 있습니다:

Table 3: Decoded Source Map Record의 필드
필드 이름 값 타입
[[File]] String 또는 null
[[Sources]] Decoded Source Record의 List
[[Mappings]] Decoded Mapping Record의 List

Decoded Source Record에는 다음 필드가 있습니다:

Table 4: Decoded Source Record의 필드
필드 이름 값 타입
[[URL]] URL 또는 null
[[Content]] String 또는 null
[[Ignored]] Boolean

9.1.1 ParseSourceMap ( string, baseURL )

추상 연산 ParseSourceMap의 입력은 인수 string (String), baseURL (URL)이다. 반환값은 Decoded Source Map Record이다. 호출되면 다음 단계를 수행한다:

  1. json을 ParseJSON(string)으로 둡니다.
  2. json이 JSON object가 아니면, 오류를 던집니다.
  3. JSONObjectGet(json, "sections")가 missing이 아니면, 다음을 수행합니다:
    1. DecodeIndexSourceMap(json, baseURL)을 반환합니다.
  4. DecodeSourceMap(json, baseURL)을 반환합니다.

9.1.2 DecodeSourceMap ( json, baseURL )

추상 연산 DecodeSourceMap의 입력은 인수 json (JSON 객체), baseURL (URL)이다. 반환값은 Decoded Source Map Record이다. 호출되면 다음 단계를 수행한다:

  1. JSONObjectGet(json, "version")이 3𝔽가 아니면, 선택적으로 오류를 보고합니다.
  2. mappingsField를 JSONObjectGet(json, "mappings")로 둡니다.
  3. mappingsField가 String이 아니면, 오류를 던집니다.
  4. JSONObjectGet(json, "sources")가 JSON array가 아니면, 오류를 던집니다.
  5. fileField를 GetOptionalString(json, "file")로 둡니다.
  6. sourceRootField를 GetOptionalString(json, "sourceRoot")로 둡니다.
  7. sourcesField를 GetOptionalListOfOptionalStrings(json, "sources")로 둡니다.
  8. sourcesContentField를 GetOptionalListOfOptionalStrings(json, "sourcesContent")로 둡니다.
  9. ignoreListField를 GetOptionalListOfArrayIndexes(json, "ignoreList")로 둡니다.
  10. sources를 DecodeSourceMapSources(baseURL, sourceRootField, sourcesField, sourcesContentField, ignoreListField)로 둡니다.
  11. namesField를 GetOptionalListOfStrings(json, "names")로 둡니다.
  12. mappings를 DecodeMappings(mappingsField, namesField, sources)로 둡니다.
  13. ComparePositions(a.[[GeneratedPosition]], b.[[GeneratedPosition]])가 lesser이면 Decoded Mapping Record a가 Decoded Mapping Record b보다 작은 것으로 하여, mappings를 오름차순으로 정렬합니다.
  14. Decoded Source Map Record { [[File]]: fileField, [[Sources]]: sources, [[Mappings]]: mappings }를 반환합니다.

9.1.2.1 GetOptionalString ( object, key )

추상 연산 GetOptionalString의 입력은 인수 object (JSON 객체), key (String)이다. 반환값은 String 또는 null이다. 호출되면 다음 단계를 수행한다:

  1. value를 JSONObjectGet(object, key)로 둡니다.
  2. value가 String이면, value를 반환합니다.
  3. value가 missing이 아니면, 선택적으로 오류를 보고합니다.
  4. null을 반환합니다.

9.1.2.2 GetOptionalListOfStrings ( object, key )

추상 연산 GetOptionalListOfStrings의 입력은 인수 object (JSON 객체), key (String)이다. 반환값은 String의 List이다. 호출되면 다음 단계를 수행한다:

  1. list를 새 빈 List로 둡니다.
  2. values를 JSONObjectGet(object, key)로 둡니다.
  3. values가 missing이면, list를 반환합니다.
  4. values가 JSON array가 아니면, 다음을 수행합니다:
    1. 선택적으로 오류를 보고합니다.
    2. list를 반환합니다.
  5. JSONArrayIterate(values)의 각 요소 item에 대해 다음을 수행합니다:
    1. item이 String이면, 다음을 수행합니다:
      1. item을 list에 추가합니다.
    2. 그렇지 않으면:
      1. 선택적으로 오류를 보고합니다.
      2. 빈 String을 list에 추가합니다.
  6. list를 반환합니다.

9.1.2.3 GetOptionalListOfOptionalStrings ( object, key )

추상 연산 GetOptionalListOfOptionalStrings의 입력은 인수 object (JSON 객체), key (String)이다. 반환값은 String 또는 null의 List이다. 호출되면 다음 단계를 수행한다:

  1. list를 새 빈 List로 둡니다.
  2. values를 JSONObjectGet(object, key)로 둡니다.
  3. values가 missing이면, list를 반환합니다.
  4. values가 JSON array가 아니면, 다음을 수행합니다:
    1. 선택적으로 오류를 보고합니다.
    2. list를 반환합니다.
  5. JSONArrayIterate(values)의 각 요소 item에 대해 다음을 수행합니다:
    1. item이 String이면, 다음을 수행합니다:
      1. item을 list에 추가합니다.
    2. 그렇지 않으면:
      1. item ≠ null이면, 선택적으로 오류를 보고합니다.
      2. null을 list에 추가합니다.
  6. list를 반환합니다.

9.1.2.4 GetOptionalListOfArrayIndexes ( object, key )

추상 연산 GetOptionalListOfArrayIndexes의 입력은 인수 object (Object), key (String)이다. 반환값은 음이 아닌 정수의 List이다. 호출되면 다음 단계를 수행한다:

  1. list를 새 빈 List로 둡니다.
  2. values를 JSONObjectGet(object, key)로 둡니다.
  3. values가 missing이면, list를 반환합니다.
  4. values가 JSON array가 아니면, 다음을 수행합니다:
    1. 선택적으로 오류를 보고합니다.
    2. list를 반환합니다.
  5. JSONArrayIterate(values)의 각 요소 item에 대해 다음을 수행합니다:
    1. item이 정숫값의 Number이고, item ≥ +0𝔽이면, 다음을 수행합니다:
      1. ℝ(item)을 list에 추가합니다.
    2. 그렇지 않으면:
      1. 선택적으로 오류를 보고합니다.
  6. list를 반환합니다.

9.2 매핑 구조

mappings 필드 데이터는 다음과 같이 나뉩니다:

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

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

  1. 세그먼트가 나타내는 생성된 코드의 줄에서 0부터 시작하는 시작 열입니다. 첫 번째 세그먼트의 첫 번째 필드이거나, 생성된 새 줄(;) 다음에 오는 첫 번째 세그먼트의 첫 번째 필드이면 이 필드는 전체 base64 VLQ를 담습니다. 그렇지 않으면 이 필드는 이전에 나타난 동일한 필드에 대한 상대값인 base64 VLQ를 담습니다. 생성된 각 줄 뒤에 이전 값이 초기화되므로 아래의 후속 필드와 다르다는 점에 유의하십시오.
  2. 존재하는 경우, sources 목록에서 0부터 시작하는 인덱스입니다. 이 필드는 이전에 나타난 동일한 필드에 대한 상대값인 base64 VLQ를 담습니다. 단, 이 필드가 처음 나타나는 경우에는 전체 값을 나타냅니다.
  3. 존재하는 경우, 원본 소스에서 0부터 시작하는 시작 행입니다. 이 필드는 이전에 나타난 동일한 필드에 대한 상대값인 base64 VLQ를 담습니다. 단, 이 필드가 처음 나타나는 경우에는 전체 값을 나타냅니다. 소스 필드가 있으면 반드시 존재해야 합니다.
  4. 존재하는 경우, 원본 소스의 줄에서 0부터 시작하는 시작 열입니다. 이 필드는 이전에 나타난 동일한 필드에 대한 상대값인 base64 VLQ를 담습니다. 단, 이 필드가 처음 나타나는 경우에는 전체 값을 나타냅니다. 소스 필드가 있으면 반드시 존재해야 합니다.
  5. 존재하는 경우, 이 세그먼트와 연결된 names 목록에서 0부터 시작하는 인덱스입니다. 이 필드는 이전에 나타난 동일한 필드에 대한 상대값인 base64 VLQ를 담습니다. 단, 이 필드가 처음 나타나는 경우에는 전체 값을 나타냅니다.
Note 1
이 인코딩의 목적은 소스 맵 크기를 줄이는 것입니다. Google Calendar를 사용하여 수행한 테스트에서 VLQ 인코딩은 소스 맵 개정판 2 제안에 비해 소스 맵 크기를 50% 줄였습니다.
Note 2
필드가 하나인 세그먼트는 컴파일러가 생성한 코드처럼 대응하는 원본 소스 코드가 없어 매핑되지 않은 생성된 코드를 나타내기 위한 것입니다. 필드가 네 개인 세그먼트는 대응하는 이름이 없는 매핑된 코드를 나타냅니다. 필드가 다섯 개인 세그먼트는 매핑된 이름도 있는 매핑된 코드를 나타냅니다.
Note 3
파일 오프셋 사용을 검토했지만, 플랫폼별 줄 끝 문자로 인해 원본과 위치가 어긋나는 것을 방지하기 위해 행/열 데이터를 사용하기로 하여 채택하지 않았습니다.

Decoded Mapping Record에는 다음 필드가 있습니다:

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

9.2.1 매핑 문법

mappings String은 다음 문법을 준수해야 합니다:

MappingsField : LineList LineList : Line Line ; LineList Line : MappingListopt MappingList : Mapping Mapping , MappingList Mapping : GeneratedColumn GeneratedColumn OriginalSource OriginalLine OriginalColumn Nameopt GeneratedColumn : Vlq OriginalSource : Vlq OriginalLine : Vlq OriginalColumn : Vlq Name : Vlq

Decode Mapping State Record에는 다음 필드가 있습니다:

Table 6: Decode Mapping State Record의 필드
필드 이름 값 타입
[[GeneratedLine]] 음이 아닌 정수
[[GeneratedColumn]] 음이 아닌 정수
[[SourceIndex]] 음이 아닌 정수
[[OriginalLine]] 음이 아닌 정수
[[OriginalColumn]] 음이 아닌 정수
[[NameIndex]] 음이 아닌 정수

9.2.1.1 DecodeMappingsField

구문 지향 연산 DecodeMappingsField의 입력은 인수 state (Decode Mapping State Record), mappings (Decoded Mapping Record의 List), names (String의 List), sources (Decoded Source Record의 List)이다. 다음 생성 규칙별로 정의된다:

LineList : Line ; LineList
  1. state, mappings, names 및 sources를 인수로 하여 Line의 DecodeMappingsField를 수행합니다.
  2. state.[[GeneratedLine]]을 state.[[GeneratedLine]] + 1로 설정합니다.
  3. state.[[GeneratedColumn]]을 0으로 설정합니다.
  4. state, mappings, names 및 sources를 인수로 하여 LineList의 DecodeMappingsField를 수행합니다.
Line : [empty]
  1. 반환합니다.
MappingList : Mapping , MappingList
  1. state, mappings, names 및 sources를 인수로 하여 Mapping의 DecodeMappingsField를 수행합니다.
  2. state, mappings, names 및 sources를 인수로 하여 MappingList의 DecodeMappingsField를 수행합니다.
Mapping : GeneratedColumn
  1. state, mappings, names 및 sources를 인수로 하여 GeneratedColumn의 DecodeMappingsField를 수행합니다.
  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. decodedMapping을 mappings에 추가합니다.
Mapping : GeneratedColumn OriginalSource OriginalLine OriginalColumn Nameopt
  1. state, mappings, names 및 sources를 인수로 하여 GeneratedColumn의 DecodeMappingsField를 수행합니다.
  2. state.[[GeneratedColumn]] < 0이면, 다음을 수행합니다:
    1. 선택적으로 오류를 보고합니다.
    2. 반환합니다.
  3. generatedPosition을 새 Position Record { [[Line]]: state.[[GeneratedLine]], [[Column]]: state.[[GeneratedColumn]] }로 둡니다.
  4. state, mappings, names 및 sources를 인수로 하여 OriginalSource의 DecodeMappingsField를 수행합니다.
  5. state, mappings, names 및 sources를 인수로 하여 OriginalLine의 DecodeMappingsField를 수행합니다.
  6. state, mappings, names 및 sources를 인수로 하여 OriginalColumn의 DecodeMappingsField를 수행합니다.
  7. state.[[SourceIndex]] < 0이거나 state.[[SourceIndex]] ≥ sources의 요소 수이거나 state.[[OriginalLine]] < 0이거나 state.[[OriginalColumn]] < 0이면, 다음을 수행합니다:
    1. 선택적으로 오류를 보고합니다.
    2. originalPosition을 null로 둡니다.
  8. 그렇지 않으면:
    1. originalPosition을 새 Original Position Record { [[Source]]: sources[state.[[SourceIndex]]], [[Line]]: state.[[OriginalLine]], [[Column]]: state.[[OriginalColumn]] }로 둡니다.
  9. name을 null로 둡니다.
  10. Name이 존재하면, 다음을 수행합니다:
    1. state, mappings, names 및 sources를 인수로 하여 Name의 DecodeMappingsField를 수행합니다.
    2. state.[[NameIndex]] < 0이거나 state.[[NameIndex]] ≥ names의 요소 수이면, 선택적으로 오류를 보고합니다.
    3. 그렇지 않으면, name을 names[state.[[NameIndex]]]로 설정합니다.
  11. decodedMapping을 새 DecodedMappingRecord { [[GeneratedPosition]]: generatedPosition, [[OriginalPosition]]: originalPosition, [[Name]]: name }로 둡니다.
  12. decodedMapping을 mappings에 추가합니다.
GeneratedColumn : Vlq
  1. relativeColumn을 Vlq의 VLQSignedValue로 둡니다.
  2. state.[[GeneratedColumn]]을 state.[[GeneratedColumn]] + relativeColumn으로 설정합니다.
OriginalSource : Vlq
  1. relativeSourceIndex를 Vlq의 VLQSignedValue로 둡니다.
  2. state.[[SourceIndex]]를 state.[[SourceIndex]] + relativeSourceIndex로 설정합니다.
OriginalLine : Vlq
  1. relativeLine을 Vlq의 VLQSignedValue로 둡니다.
  2. state.[[OriginalLine]]을 state.[[OriginalLine]] + relativeLine으로 설정합니다.
OriginalColumn : Vlq
  1. relativeColumn을 Vlq의 VLQSignedValue로 둡니다.
  2. state.[[OriginalColumn]]을 state.[[OriginalColumn]] + relativeColumn으로 설정합니다.
Name : Vlq
  1. relativeName을 Vlq의 VLQSignedValue로 둡니다.
  2. state.[[NameIndex]]를 state.[[NameIndex]] + relativeName으로 설정합니다.

9.2.2 DecodeMappings ( rawMappings, names, sources )

추상 연산 DecodeMappings의 입력은 인수 rawMappings (String), names (String의 List), sources (Decoded Source Record의 List)이다. 반환값은 Decoded Mapping Record의 List이다. 호출되면 다음 단계를 수행한다:

  1. mappings를 새 빈 List로 둡니다.
  2. mappingsNode를 MappingsField를 목표 기호로 사용하여 rawMappings를 파싱할 때의 루트 Parse Node로 둡니다.
  3. 파싱에 실패하면, 다음을 수행합니다:
    1. 선택적으로 오류를 보고합니다.
    2. mappings를 반환합니다.
  4. state를 모든 필드가 0으로 설정된 새 Decode Mapping State Record로 둡니다.
  5. state, mappings, names 및 sources를 인수로 하여 mappingsNode의 DecodeMappingsField를 수행합니다.
  6. mappings를 반환합니다.

9.2.3 생성된 JavaScript 코드의 매핑

매핑 항목을 가질 수 있는 생성된 코드의 위치는 ECMAScript 어휘 문법에 따라 입력 요소를 기준으로 정의됩니다. 매핑 항목은 다음 중 하나를 가리켜야 합니다:

9.2.4 생성된 JavaScript 코드의 이름

다음 조건을 만족하면 소스 맵 생성기는 JavaScript 토큰에 대해 [[Name]] 필드를 가진 매핑 항목을 생성하는 것이 좋습니다:

  • 원본 소스 언어의 구문 요소가 생성된 JavaScript 코드에 의미적으로 매핑됩니다.
  • 원본 소스 언어의 구문 요소에 이름이 있습니다.

이 경우 매핑 항목의 [[Name]]은 원본 소스 언어 구문 요소의 이름인 것이 좋습니다. [[Name]]이 null이 아닌 매핑을 이름 있는 매핑이라고 합니다.

Note 1
함수와 변수의 이름을 바꾸거나 즉시 호출 함수 표현식에서 함수 이름을 제거하는 코드 축소기입니다.

다음 목록은 ECMAScript 구문 문법의 생성 규칙과, 소스 맵 생성기가 이름 있는 매핑을 출력하는 것이 좋은 해당 토큰 또는 비종단 기호(생성 규칙의 오른쪽에 있음)를 나열합니다. 이러한 토큰에 대해 생성되는 매핑 항목은 9.2.3 절을 따라야 합니다.

이 목록은 “최소한”의 것으로 이해하는 것이 좋습니다. 일반적으로 소스 맵 생성기는 추가적인 이름 있는 매핑을 자유롭게 출력할 수 있습니다.

Note 2
이 목록은 생성기가 이름 있는 매핑을 출력하는 것이 “좋은” 토큰 외에, 출력할 “수 있는” 토큰도 나열합니다. 이는 기존 도구가 이름 있는 매핑을 출력하거나 기대하는 현실을 반영합니다. 중복된 이름 있는 매핑의 비용은 비교적 작습니다: names에 대한 인덱스는 서로에 대한 상대값으로 인코딩되므로 동일한 이름에 대한 후속 매핑은 0(A)으로 인코딩됩니다.

9.3 소스 해석

sourceRoot를 앞에 추가한 후에도 sources가 절대 URL이 아니면, sources는 소스 맵을 기준으로 상대적으로 해석됩니다(HTML 문서에서 스크립트의 src 속성을 해석하는 것과 같습니다).

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

추상 연산 DecodeSourceMapSources의 입력은 인수 baseURL (URL), sourceRoot (String 또는 null), sources (String 또는 null의 List), sourcesContent (String 또는 null의 List), ignoreList (음이 아닌 정수의 List)이다. 반환값은 Decoded Source Record의 List이다. 호출되면 다음 단계를 수행한다:

  1. decodedSources를 새 빈 List로 둡니다.
  2. sourcesContentCount를 sourcesContent의 요소 수로 둡니다.
  3. sourceUrlPrefix를 빈 String으로 둡니다.
  4. sourceRoot ≠ null이면, 다음을 수행합니다:
    1. sourceRoot가 코드 포인트 U+002F (SOLIDUS)로 끝나면, 다음을 수행합니다:
      1. sourceUrlPrefix를 sourceRoot로 설정합니다.
    2. 그렇지 않으면:
      1. sourceUrlPrefix를 sourceRoot와 "/"의 문자열 연결 결과로 설정합니다.
  5. index를 0으로 둡니다.
  6. index < sources의 길이인 동안 반복합니다:
    1. source를 sources[index]로 둡니다.
    2. decodedSource를 Decoded Source Record { [[URL]]: null, [[Content]]: null, [[Ignored]]: false }로 둡니다.
    3. source ≠ null이면, 다음을 수행합니다:
      1. source를 sourceUrlPrefix와 source의 문자열 연결 결과로 설정합니다.
      2. sourceURL을 baseURL을 사용하여 source를 URL 파싱한 결과로 둡니다.
      3. sourceURL이 failure이면, 선택적으로 오류를 보고합니다.
      4. 그렇지 않으면, decodedSource.[[URL]]을 sourceURL로 설정합니다.
    4. ignoreList가 index를 포함하면, decodedSource.[[Ignored]]를 true로 설정합니다.
    5. sourcesContentCount > index이면, decodedSource.[[Content]]를 sourcesContent[index]로 설정합니다.
    6. decodedSource를 decodedSources에 추가합니다.
    7. index를 index + 1로 설정합니다.
  7. decodedSources를 반환합니다.
Note
소스 콘텐츠 표시는 지원하지만 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입니다. 일반 소스 맵의 version 및 file 필드를 공유하지만, 새로운 sections 필드가 추가됩니다.

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

섹션은 시작 위치를 기준으로 정렬되어야 하며, 표현되는 섹션은 서로 겹쳐서는 안 됩니다.

10.1 DecodeIndexSourceMap ( json, baseURL )

추상 연산 DecodeIndexSourceMap의 입력은 인수 json (Object), baseURL (URL)이다. 반환값은 Decoded Source Map Record이다. 호출되면 다음 단계를 수행한다:

  1. sectionsField를 JSONObjectGet(json, "sections")로 둡니다.
  2. 단언: sectionsField는 missing이 아닙니다.
  3. sectionsField가 JSON array가 아니면, 오류를 던집니다.
  4. JSONObjectGet(json, "version")이 3𝔽가 아니면, 선택적으로 오류를 보고합니다.
  5. fileField를 GetOptionalString(json, "file")로 둡니다.
  6. sourceMap을 Decoded Source Map Record { [[File]]: fileField, [[Sources]]: « », [[Mappings]]: « » }로 둡니다.
  7. previousOffsetPosition을 null로 둡니다.
  8. previousLastMapping을 null로 둡니다.
  9. JSONArrayIterate(sectionsField)의 각 JSON value section에 대해 다음을 수행합니다:
    1. section이 JSON object가 아니면, 다음을 수행합니다:
      1. 선택적으로 오류를 보고합니다.
    2. 그렇지 않으면:
      1. offset을 JSONObjectGet(section, "offset")으로 둡니다.
      2. offset이 JSON object가 아니면, 오류를 던집니다.
      3. offsetLine을 JSONObjectGet(offset, "line")으로 둡니다.
      4. offsetColumn을 JSONObjectGet(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. previousOffsetPosition ≠ null이면, 다음을 수행합니다:
        1. ComparePositions(offsetPosition, previousOffsetPosition)이 lesser이면, 선택적으로 오류를 보고합니다.
      9. previousLastMapping ≠ null이면, 다음을 수행합니다:
        1. ComparePositions(offsetPosition, previousLastMapping.[[GeneratedPosition]])이 lesser이면, 선택적으로 오류를 보고합니다.
        2. 참고: 디코딩 알고리즘의 이 부분은 인덱스 소스 맵의 sections 필드 항목이 순서대로 정렬되어 있고 서로 겹치지 않는지 확인합니다. 생성기는 섹션이 겹치는 인덱스 소스 맵을 생성하지 않는 것이 좋지만, 소스 맵 소비자는 예를 들어 섹션 오프셋이 정렬되어 있다는 더 간단한 조건만 확인할 수 있습니다.
      10. mapField를 JSONObjectGet(section, "map")로 둡니다.
      11. mapField가 JSON object가 아니면, 오류를 던집니다.
      12. decodedSectionCompletion을 Completion(DecodeSourceMap(mapField, baseURL))으로 둡니다.
      13. decodedSectionCompletion이 던지기 완료이면, 다음을 수행합니다:
        1. 선택적으로 오류를 보고합니다.
      14. 그렇지 않으면:
        1. decodedSection을 decodedSectionCompletion.[[Value]]로 둡니다.
        2. decodedSection.[[Sources]]의 각 Decoded Source Record additionalSource에 대해 다음을 수행합니다:
          1. sourceMap.[[Sources]]가 additionalSource를 포함하지 않으면, 다음을 수행합니다:
            1. additionalSource를 sourceMap.[[Sources]]에 추가합니다.
        3. offsetMappings를 새 빈 List로 둡니다.
        4. decodedSection.[[Mappings]]의 각 Decoded Mapping Record mapping에 대해 다음을 수행합니다:
          1. mapping.[[GeneratedPosition]].[[Line]] = 0이면, 다음을 수행합니다:
            1. mapping.[[GeneratedPosition]].[[Column]]을 mapping.[[GeneratedPosition]].[[Column]] + offsetColumn으로 설정합니다.
          2. mapping.[[GeneratedPosition]].[[Line]]을 mapping.[[GeneratedPosition]].[[Line]] + offsetLine으로 설정합니다.
          3. mapping을 offsetMappings에 추가합니다.
        5. sourceMap.[[Mappings]]를 sourceMap.[[Mappings]]와 offsetMappings의 목록 연결 결과로 설정합니다.
        6. previousOffsetPosition을 offsetPosition으로 설정합니다.
        7. offsetMappings가 비어 있지 않으면, previousLastMapping을 offsetMappings의 마지막 요소로 설정합니다.
  10. sourceMap을 반환합니다.
Note
구현은 매핑을 함께 이어 붙이지 않고 인덱스 소스 맵 섹션을 표현하도록 선택할 수 있습니다. 예를 들어 각 섹션을 별도로 저장하고 이진 검색을 수행할 수 있습니다.

11 소스 맵 검색

11.1 생성된 코드를 소스 맵에 연결하기

소스 맵 형식은 언어와 플랫폼에 독립적이도록 설계되었지만, 예상되는 사용 사례인 웹 서버에서 호스팅되는 JavaScript를 위해 소스 맵을 참조하는 방법을 정의하는 것이 유용합니다.

소스 맵을 출력에 연결하는 방법은 두 가지가 있습니다. 첫 번째는 HTTP 헤더를 추가하기 위한 서버 지원이 필요하고, 두 번째는 소스에 주석을 추가해야 합니다.

소스 맵은 WHATWG URL에 정의된 URL을 통해 연결됩니다. 특히 URI에 나타날 수 있도록 허용된 문자 집합에 속하지 않는 문자는 퍼센트 인코딩되어야 하며, URL은 데이터 URI일 수 있습니다. 데이터 URI를 sourcesContent와 함께 사용하면 완전히 자체 완결적인 소스 맵을 만들 수 있습니다.

HTTP sourcemap 헤더는 소스 주석보다 우선하며, 둘 다 존재하면 소스 맵 파일을 해석하는 데 헤더 URL을 사용하는 것이 좋습니다.

소스 맵 URL을 검색하는 데 사용한 방법과 관계없이, 이를 해석하는 데는 다음과 같은 동일한 절차가 사용됩니다.

소스 맵 URL이 절대 URL이 아니면 생성된 코드의 소스 출처를 기준으로 한 상대 URL입니다. 소스 출처는 다음 중 하나의 경우에 따라 결정됩니다:

  • 생성된 소스가 src 속성을 가진 스크립트 요소와 연결되어 있지 않고 생성된 코드에 //# sourceURL 주석이 있으면, 해당 주석을 사용하여 소스 출처를 결정하는 것이 좋습니다.

    Note
    이전에는 //@ sourceURL이었으며, //@ sourceMappingURL과 마찬가지로 둘 다 허용하는 것이 합리적이지만 //#이 권장됩니다.
  • 생성된 코드가 스크립트 요소와 연결되어 있고 해당 스크립트 요소에 src 속성이 있으면, 스크립트 요소의 src 속성이 소스 출처가 됩니다.
  • 생성된 코드가 스크립트 요소와 연결되어 있고 해당 스크립트 요소에 src 속성이 없으면, 소스 출처는 페이지의 출처가 됩니다.
  • 생성된 코드가 eval() 함수 또는 new Function()을 통해 문자열로 평가되는 경우, 소스 출처는 페이지의 출처가 됩니다.

11.1.1 HTTP 헤더를 통한 연결

파일이 sourcemap 헤더와 함께 HTTP(S)를 통해 제공되는 경우, 헤더의 값은 연결된 소스 맵의 URL입니다.

sourcemap: <url>
Note
이 문서의 이전 개정판에서는 헤더 이름으로 x-sourcemap을 권장했습니다. 현재는 더 이상 권장되지 않으며, 이제 sourcemap을 사용해야 합니다.

11.1.2 인라인 주석을 통한 연결

생성된 코드는 이름이 sourceMappingURL이고 소스 맵의 URL을 포함하는 주석 또는 언어나 형식에 따른 동등한 구문 요소를 포함하는 것이 좋습니다. 이 명세는 JavaScript, CSS 및 WebAssembly에서 해당 주석이 어떤 형태여야 하는지 정의합니다. 다른 언어도 유사한 규칙을 따르는 것이 좋습니다.

특정 언어에서 sourceMappingURL 주석을 감지하는 방법은 여러 가지일 수 있으며, 이를 통해 각 구현이 자신에게 덜 복잡한 방법을 선택할 수 있습니다. 모든 추출 방법의 결과가 같으면 생성된 코드는 소스 맵에 모호함 없이 연결됩니다.

도구가 소스 맵에 모호함 없이 연결되는 소스 파일을 하나 이상 소비하고 소스 맵에 연결되는 출력 파일을 생성하면, 해당 출력 파일도 모호함 없이 연결되어야 합니다.

Note

다음 JavaScript 코드는 소스 맵에 연결되지만, 모호함 없이 연결되지는 않습니다:

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

이 코드에서 파싱을 통해 소스 맵 URL을 추출하면 foo.js.map을 얻지만, 파싱 없이 추출하면 null을 얻습니다.

11.1.2.1 JavaScriptExtractSourceMapURL ( source )

추상 연산 JavaScriptExtractSourceMapURL의 입력은 인수 source (String)이다. 반환값은 String 또는 null이다. JavaScript 소스에서 소스 맵 URL을 추출합니다. 파싱을 통한 구현과 파싱 없는 구현이라는 두 가지 구현이 가능합니다.

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

  1. tokens를 입력 요소의 List로 두며, 이 요소들은 ECMA-262의 어휘 문법에 따라 source를 파싱하여 얻습니다.
  2. tokens의 각 비종단 기호 token에 대해 역순으로 다음을 수행합니다:
    1. token이 SingleLineComment, WhiteSpace 또는 LineTerminatorSequence가 아니면, null을 반환합니다.
    2. comment를 token의 내용으로 둡니다.
    3. sourceMapURL을 MatchSourceMapURL(comment)로 둡니다.
    4. sourceMapURL이 String이면, sourceMapURL을 반환합니다.
  3. null을 반환합니다.

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

  1. lines를 StringSplit(source, « "\u000D\u000A", "\u000A", "\u000D", "\u2028", "\u2029" »)로 둡니다.
  2. 참고: 위의 문자열 목록은 LineTerminatorSequence 생성 규칙과 일치합니다.
  3. lines의 각 String lineStr에 대해 List의 역순으로 다음을 수행합니다:
    1. line을 StringToCodePoints(lineStr)로 둡니다.
    2. position을 0으로 둡니다.
    3. lineLength를 line의 길이로 둡니다.
    4. position < lineLength인 동안 반복합니다:
      1. first를 line[position]으로 둡니다.
      2. first가 U+002F (SOLIDUS)이고 position + 1 < lineLength이면, 다음을 수행합니다:
        1. position을 position + 1로 설정합니다.
        2. second를 line[position]으로 둡니다.
        3. second가 U+002F (SOLIDUS)이면, 다음을 수행합니다:
          1. position을 position + 1로 설정합니다.
          2. comment를 lineStr의 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. sourceMapURL을 MatchSourceMapURL(comment)로 둡니다.
          6. sourceMapURL이 String이면, sourceMapURL을 반환합니다.
          7. position을 lineLength로 설정합니다.
        4. 그렇지 않으면:
          1. null을 반환합니다.
      3. 그렇지 않고 first가 ECMAScript WhiteSpace이면, 다음을 수행합니다:
        1. position을 position + 1로 설정합니다.
      4. 그렇지 않으면:
        1. null을 반환합니다.
  4. null을 반환합니다.
Note

source를 오류 없이 파싱할 수 있고 여기서 파싱 없이 소스 맵 URL을 추출한 결과가 null이 아니면, 파싱을 통해 추출해도 같은 결과를 얻습니다.

11.1.2.1.1 MatchSourceMapURL ( comment )

추상 연산 MatchSourceMapURL의 입력은 인수 comment (String)이다. 반환값은 none 또는 String이다. 호출되면 다음 단계를 수행한다:

  1. pattern을 RegExpCreate("^[@#]\s*sourceMappingURL=(\S*?)\s*$", "")로 둡니다.
  2. match를 RegExpExec(pattern, comment)로 둡니다.
  3. match가 null이 아니면, Get(match, "1")을 반환합니다.
  4. none을 반환합니다.
Note
이 주석의 접두사는 처음에는 //@였지만, Internet Explorer의 조건부 컴파일과 충돌하여 //#으로 변경되었습니다.

소스 맵 생성기는 //#만 출력해야 하며, 소스 맵 소비자는 //@와 //#을 모두 허용해야 합니다.

11.1.2.2 CSSExtractSourceMapURL ( source )

추상 연산 CSSExtractSourceMapURL의 입력은 인수 source (String)이다. 반환값은 String 또는 null이다. CSS 소스에서 소스 맵 URL을 추출합니다.

CSS에서 소스 맵 URL을 추출하는 방식은 JavaScript와 유사하지만, CSS는 /* ... */ 형식의 주석만 지원한다는 점이 다릅니다.

11.1.2.3 WebAssemblyExtractSourceMapURL ( bytes )

추상 연산 WebAssemblyExtractSourceMapURL의 입력은 인수 bytes (Data Block)이다. 반환값은 String 또는 null이다. WebAssembly 바이너리 소스에서 소스 맵 URL을 추출합니다.

  1. module을 module_decode(bytes)로 둡니다.
  2. module이 WebAssembly 오류이면, null을 반환합니다.
  3. module의 각 사용자 정의 섹션 customSection에 대해 다음을 수행합니다:
    1. name을 customSection의 name으로 둡니다.
    2. CodePointsToString(name)이 "sourceMappingURL"이면, 다음을 수행합니다:
      1. value를 customSection의 bytes로 둡니다.
      2. CodePointsToString(value)를 반환합니다.
  4. null을 반환합니다.

WebAssembly는 텍스트 형식이 아니고 주석을 지원하지 않으므로, 모호함이 없는 단일 추출 방법을 지원합니다. URL은 WebAssembly 이름으로 인코딩되어 사용자 정의 섹션의 내용으로 배치됩니다. WebAssembly 코드를 생성하는 도구가 이름이 sourceMappingURL인 사용자 정의 섹션을 둘 이상 생성하는 것은 유효하지 않습니다.

11.2 소스 맵 가져오기

11.2.1 FetchSourceMap ( url )

추상 연산 FetchSourceMap의 입력은 인수 url (URL)이다. 반환값은 Promise이다. 호출되면 다음 단계를 수행한다:

  1. promiseCapability를 NewPromiseCapability(%Promise%)로 둡니다.
  2. request를 요청 URL이 url인 새 요청으로 둡니다.
  3. processResponseConsumeBody를 매개변수 (response, bodyBytes)를 가지고 promiseCapability와 url을 캡처하며 호출될 때 다음 단계를 수행하는 새 Abstract Closure로 둡니다:
    1. bodyBytes가 null 또는 failure이면, 다음을 수행합니다:
      1. Call(promiseCapability.[[Reject]], undefined, « 새로운 TypeError »)를 수행합니다.
      2. 반환합니다.
    2. url의 스킴이 HTTP(S) 스킴이고 바이트 시퀀스 `)]}'`가 bodyBytes의 바이트 시퀀스 접두사이면, 다음을 수행합니다:
      1. bodyBytes의 바이트 시퀀스 길이 ≠ 0이고 bodyBytes[0]이 HTTP 줄바꿈 바이트가 아닌 동안 반복합니다:
        1. bodyBytes에서 0번째 요소를 제거합니다.
    3. bodyString을 Completion(bodyBytes의 UTF-8 decode)로 둡니다.
    4. IfAbruptRejectPromise(bodyString, promiseCapability).
    5. jsonValue를 Completion(ParseJSON(bodyString))로 둡니다.
    6. IfAbruptRejectPromise(jsonValue, promiseCapability).
    7. Call(promiseCapability.[[Resolve]], undefined, « jsonValue »)를 수행합니다.
  4. processResponseConsumeBody를 processResponseConsumeBody로 설정하여 request에 대한 fetch를 수행합니다.
  5. promiseCapability.[[Promise]]를 반환합니다.
Note

역사적인 이유로, HTTP(S)를 통해 소스 맵을 전달할 때 서버는 소스 맵 앞에 문자열 )]}'로 시작하는 줄을 추가할 수 있습니다.

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

다음과 같이 해석됩니다

{"version": 3, ...}

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

소스 맵을 디코딩한 후, 소스 맵 소비자는 그 결과인 Decoded Source Map Record를 사용하여 디버깅이나 다른 사용 사례를 위한 위치 정보를 조회할 수 있습니다. 이 절은 소스 맵 소비자가 지원할 수 있는 일반적인 연산의 동작을 설명합니다.

GetOriginalPositions 연산은 생성된 코드의 특정 위치에 대응하는 원본 소스의 위치를 질의하는 데 사용할 수 있습니다. 예를 들어, 디버거에서 사용자의 마우스 클릭에 따라 생성된 코드에서 원본 소스로 이동하는 데 사용할 수 있습니다.

12.1 GetOriginalPositions ( sourceMapRecord, generatedPosition )

추상 연산 GetOriginalPositions의 입력은 인수 sourceMapRecord (Decoded Source Map Record), generatedPosition (Position Record)이다. 반환값은 Original Position Record의 List이다. 호출되면 다음 단계를 수행한다:

  1. mappings를 sourceMapRecord.[[Mappings]]로 둡니다.
  2. last를 null로 둡니다.
  3. originalPositions를 새 빈 List로 둡니다.
  4. mappings의 각 요소 mapping에 대해 List의 역순으로 다음을 수행합니다:
    1. last가 null이면, 다음을 수행합니다:
      1. ComparePositions(mapping.[[GeneratedPosition]], generatedPosition)을 수행한 결과가 lesser 또는 equal이면, 다음을 수행합니다:
        1. last를 mapping으로 설정합니다.
  5. last가 null이 아니면, 다음을 수행합니다:
    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

이는 //@ sourceURL로 eval에 이름 지정하기에 설명되어 있습니다.

Annex B (informative) 참고 사항

B.1 언어에 독립적인 스택 매핑

소스 언어에 대한 지식 없이 수행하는 스택 추적 매핑은 이 문서에서 다루지 않습니다.

B.2 다단계 매핑

도구가 특정 DSL(템플릿)에서 소스를 생성하거나 TypeScript → JavaScript → 축소된 JavaScript로 컴파일하여, 최종 소스 맵이 만들어지기 전에 여러 번의 변환이 이루어지는 경우가 점점 더 흔해지고 있습니다. 이 문제는 두 가지 방법 중 하나로 처리할 수 있습니다. 쉽지만 정보 손실이 있는 방법은 디버깅을 위해 처리 과정의 중간 단계를 무시하는 것입니다. 변환 과정의 소스 위치 정보를 무시하거나(중간 변환 결과를 “원본 소스”로 간주), 소스 위치 정보를 끝까지 전달합니다(중간 변환 결과는 숨김). 더 완전한 방법은 여러 단계의 매핑을 지원하는 것입니다: 원본 소스에도 소스 맵 참조가 있으면 사용자에게 그것도 사용할지 선택할 수 있게 합니다.

그러나 JavaScript 이외의 언어에서 “소스 맵 참조”가 어떤 형태인지는 명확하지 않습니다. 더 구체적으로는 JavaScript 형식의 한 줄 주석을 지원하지 않는 언어에서 소스 맵 참조가 어떤 형태인지가 명확하지 않습니다.

Annex C (informative) 다른 명세에 정의된 용어

이 절은 이 문서에서 사용하는 용어와 알고리즘 중 ECMA-262 이외의 외부 명세에 정의된 모든 항목을 나열합니다.

WebAssembly 핵심 명세 <https://www.w3.org/TR/wasm-core-2/>
사용자 정의 섹션, module_decode, WebAssembly 오류, WebAssembly 이름
WHATWG 인코딩 <https://encoding.spec.whatwg.org/>
UTF-8 decode
WHATWG Fetch <https://fetch.spec.whatwg.org/>
fetch, HTTP 줄바꿈 바이트, processResponseConsumeBody, 요청, 요청 URL
WHATWG Infra <https://infra.spec.whatwg.org/>
바이트 시퀀스, 바이트 시퀀스 접두사, 바이트 시퀀스 길이,
WHATWG URL <https://url.spec.whatwg.org/>
HTTP(S) 스킴, 스킴, URL, URL 파싱

Annex D (informative) 참고 문헌

  1. IETF RFC 4648, Base16, Base32 및 Base64 데이터 인코딩, 열람 위치 <https://datatracker.ietf.org/doc/html/rfc4648>
  2. ECMA-262, ECMAScript® 언어 명세, 열람 위치 <https://tc39.es/ecma262/>
  3. ECMA-404, JSON 데이터 교환 형식, 열람 위치 <https://www.ecma-international.org/publications-and-standards/standards/ecma-404/>
  4. WebAssembly 핵심 명세, 열람 위치 <https://www.w3.org/TR/wasm-core-2/>
  5. WHATWG 인코딩, 열람 위치 <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. //@ sourceURL로 eval에 이름 지정하기, Firebug (2009), 열람 위치 <http://blog.getfirebug.com/2009/08/11/give-your-eval-a-name-with-sourceurl/>
  10. 소스 맵 개정판 2 제안, John Lenz (2010), 열람 위치 <https://docs.google.com/document/d/1xi12LrcqjqIHTtZzrzZKmQ3lbTv9mKrN076UB-j3UZQ/>
  11. 가변 길이 수량, 위키백과, 열람 위치 <https://en.wikipedia.org/wiki/Variable-length_quantity>

Annex E (informative) 판권기

이 명세는 GitHub에서 Ecmarkup이라는 일반 텍스트 소스 형식으로 작성됩니다. Ecmarkup은 ECMAScript 명세를 일반 텍스트로 작성하고, 이 문서의 편집 규칙을 따르는 완전한 기능의 HTML 렌더링으로 처리하기 위한 프레임워크와 도구 모음을 제공하는 HTML 및 Markdown의 변형입니다. Ecmarkup은 구문 정의를 위한 Grammarkdown과 알고리즘 단계 작성을 위한 Ecmarkdown을 비롯한 여러 형식과 기술을 기반으로 하며 이를 통합합니다. 이 명세의 PDF 렌더링은 HTML 렌더링을 PDF로 인쇄하여 생성됩니다.

이 명세의 초판은 HTML과 Markdown을 기반으로 하는 또 다른 일반 텍스트 소스 형식인 Bikeshed를 사용하여 작성되었습니다.

이 문서의 표준화 이전 버전은 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.