이 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 참조 문헌
다음 문서는 그 내용의 일부 또는 전부가 이 문서의 요구 사항을 구성하는 방식으로 본문에서 참조됩니다. 날짜가 명시된 참조에는 인용된 판만 적용됩니다. 날짜가 명시되지 않은 참조에는 참조 문서의 최신판(모든 개정 사항 포함)이 적용됩니다.
생성된 코드의 한 줄 안에서 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입니다.
value를 IETF RFC 4648에 정의된 base64 인코딩에 따라 digit에 대응하는 정수로 둡니다.
단언: 32 ≤ value < 64입니다.
value - 32를 반환합니다.
7 JSON 값 유틸리티
이 명세의 알고리즘은 ECMA-262의 내부 동작을 기반으로 정의되어 있지만, JavaScript가 아닌 플랫폼에서도 쉽게 구현할 수 있도록 설계되었습니다. 이 절에는 JSON 값을 다루기 위한 유틸리티가 포함되어 있으며, 문서의 나머지 부분에서 ECMA-262의 세부 사항을 추상화합니다.
추상 연산 JSONObjectGet의 입력은 인수 object (JSON 객체), key (String)이다. 반환값은 JSON 값 또는 missing이다. object에서 지정된 key와 연결된 값을 반환합니다. 호출되면 다음 단계를 수행한다:
object에 키가 key인 자체 속성이 없으면, missing을 반환합니다.
prop을 키가 key인 object의 자체 속성으로 둡니다.
prop의 [[Value]] 어트리뷰트를 반환합니다.
7.3 JSONArrayIterate ( array )
추상 연산 JSONArrayIterate의 입력은 인수 array (JSON 배열)이다. 반환값은 JSON 값의 List이다. 알고리즘에서 “각각에 대해”를 사용하여 순회할 수 있도록 array의 모든 요소를 포함하는 List를 반환합니다. 호출되면 다음 단계를 수행한다:
추상 연산 StringSplit의 입력은 인수 string (String), separators (비어 있지 않은 String의 List)이다. 반환값은 String의 List이다. separators의 요소 중 하나로 구분되는 부분 문자열로 문자열을 나눕니다. 여러 구분자가 일치하면 separators에서 먼저 나타나는 구분자가 더 높은 우선순위를 가집니다. 호출되면 다음 단계를 수행한다:
ignoreList 필드는 프레임워크 코드나 번들러가 생성한 코드처럼 서드 파티 코드로 간주해야 하는 파일의 인덱스를 담은 선택적 목록입니다. 이를 통해 개발자 도구는 개발자가 미리 설정하지 않아도 보고 싶지 않거나 단계별로 실행하고 싶지 않을 가능성이 높은 코드를 건너뛸 수 있습니다. 이 필드는 sources 필드를 참조하며, 소스 맵에서 알려진 모든 서드 파티 소스의 인덱스를 나열합니다. 일부 브라우저는 ignoreList가 없는 경우 더 이상 사용되지 않는 x_google_ignoreList 필드도 사용할 수 있습니다.
세그먼트가 나타내는 생성된 코드의 줄에서 0부터 시작하는 시작 열입니다. 첫 번째 세그먼트의 첫 번째 필드이거나, 생성된 새 줄(;) 다음에 오는 첫 번째 세그먼트의 첫 번째 필드이면 이 필드는 전체 base64 VLQ를 담습니다. 그렇지 않으면 이 필드는 이전에 나타난 동일한 필드에 대한 상대값인 base64 VLQ를 담습니다. 생성된 각 줄 뒤에 이전 값이 초기화되므로 아래의 후속 필드와 다르다는 점에 유의하십시오.
존재하는 경우, sources 목록에서 0부터 시작하는 인덱스입니다. 이 필드는 이전에 나타난 동일한 필드에 대한 상대값인 base64 VLQ를 담습니다. 단, 이 필드가 처음 나타나는 경우에는 전체 값을 나타냅니다.
존재하는 경우, 원본 소스에서 0부터 시작하는 시작 행입니다. 이 필드는 이전에 나타난 동일한 필드에 대한 상대값인 base64 VLQ를 담습니다. 단, 이 필드가 처음 나타나는 경우에는 전체 값을 나타냅니다. 소스 필드가 있으면 반드시 존재해야 합니다.
존재하는 경우, 원본 소스의 줄에서 0부터 시작하는 시작 열입니다. 이 필드는 이전에 나타난 동일한 필드에 대한 상대값인 base64 VLQ를 담습니다. 단, 이 필드가 처음 나타나는 경우에는 전체 값을 나타냅니다. 소스 필드가 있으면 반드시 존재해야 합니다.
존재하는 경우, 이 세그먼트와 연결된 names 목록에서 0부터 시작하는 인덱스입니다. 이 필드는 이전에 나타난 동일한 필드에 대한 상대값인 base64 VLQ를 담습니다. 단, 이 필드가 처음 나타나는 경우에는 전체 값을 나타냅니다.
Note 1
이 인코딩의 목적은 소스 맵 크기를 줄이는 것입니다. Google Calendar를 사용하여 수행한 테스트에서 VLQ 인코딩은 소스 맵 개정판 2 제안에 비해 소스 맵 크기를 50% 줄였습니다.
Note 2
필드가 하나인 세그먼트는 컴파일러가 생성한 코드처럼 대응하는 원본 소스 코드가 없어 매핑되지 않은 생성된 코드를 나타내기 위한 것입니다. 필드가 네 개인 세그먼트는 대응하는 이름이 없는 매핑된 코드를 나타냅니다. 필드가 다섯 개인 세그먼트는 매핑된 이름도 있는 매핑된 코드를 나타냅니다.
Note 3
파일 오프셋 사용을 검토했지만, 플랫폼별 줄 끝 문자로 인해 원본과 위치가 어긋나는 것을 방지하기 위해 행/열 데이터를 사용하기로 하여 채택하지 않았습니다.
originalPosition을 새 Original Position Record { [[Source]]: sources[state.[[SourceIndex]]], [[Line]]: state.[[OriginalLine]], [[Column]]: state.[[OriginalColumn]] }로 둡니다.
이 경우 매핑 항목의 [[Name]]은 원본 소스 언어 구문 요소의 이름인 것이 좋습니다. [[Name]]이 null이 아닌 매핑을 이름 있는 매핑이라고 합니다.
Note 1
함수와 변수의 이름을 바꾸거나 즉시 호출 함수 표현식에서 함수 이름을 제거하는 코드 축소기입니다.
다음 목록은 ECMAScript 구문 문법의 생성 규칙과, 소스 맵 생성기가 이름 있는 매핑을 출력하는 것이 좋은 해당 토큰 또는 비종단 기호(생성 규칙의 오른쪽에 있음)를 나열합니다. 이러한 토큰에 대해 생성되는 매핑 항목은 9.2.3 절을 따라야 합니다.
이 목록은 “최소한”의 것으로 이해하는 것이 좋습니다. 일반적으로 소스 맵 생성기는 추가적인 이름 있는 매핑을 자유롭게 출력할 수 있습니다.
Note 2
이 목록은 생성기가 이름 있는 매핑을 출력하는 것이 “좋은” 토큰 외에, 출력할 “수 있는” 토큰도 나열합니다. 이는 기존 도구가 이름 있는 매핑을 출력하거나 기대하는 현실을 반영합니다. 중복된 이름 있는 매핑의 비용은 비교적 작습니다: names에 대한 인덱스는 서로에 대한 상대값으로 인코딩되므로 동일한 이름에 대한 후속 매핑은 0(A)으로 인코딩됩니다.
추상 연산 DecodeSourceMapSources의 입력은 인수 baseURL (URL), sourceRoot (String 또는 null), sources (String 또는 null의 List), sourcesContent (String 또는 null의 List), ignoreList (음이 아닌 정수의 List)이다. 반환값은 Decoded Source Record의 List이다. 호출되면 다음 단계를 수행한다:
참고: 디코딩 알고리즘의 이 부분은 인덱스 소스 맵의 sections 필드 항목이 순서대로 정렬되어 있고 서로 겹치지 않는지 확인합니다. 생성기는 섹션이 겹치는 인덱스 소스 맵을 생성하지 않는 것이 좋지만, 소스 맵 소비자는 예를 들어 섹션 오프셋이 정렬되어 있다는 더 간단한 조건만 확인할 수 있습니다.
sourceMap.[[Mappings]]를 sourceMap.[[Mappings]]와 offsetMappings의 목록 연결 결과로 설정합니다.
previousOffsetPosition을 offsetPosition으로 설정합니다.
offsetMappings가 비어 있지 않으면, previousLastMapping을 offsetMappings의 마지막 요소로 설정합니다.
sourceMap을 반환합니다.
Note
구현은 매핑을 함께 이어 붙이지 않고 인덱스 소스 맵 섹션을 표현하도록 선택할 수 있습니다. 예를 들어 각 섹션을 별도로 저장하고 이진 검색을 수행할 수 있습니다.
11 소스 맵 검색
11.1 생성된 코드를 소스 맵에 연결하기
소스 맵 형식은 언어와 플랫폼에 독립적이도록 설계되었지만, 예상되는 사용 사례인 웹 서버에서 호스팅되는 JavaScript를 위해 소스 맵을 참조하는 방법을 정의하는 것이 유용합니다.
소스 맵을 출력에 연결하는 방법은 두 가지가 있습니다. 첫 번째는 HTTP 헤더를 추가하기 위한 서버 지원이 필요하고, 두 번째는 소스에 주석을 추가해야 합니다.
소스 맵은 WHATWG URL에 정의된 URL을 통해 연결됩니다. 특히 URI에 나타날 수 있도록 허용된 문자 집합에 속하지 않는 문자는 퍼센트 인코딩되어야 하며, URL은 데이터 URI일 수 있습니다. 데이터 URI를 sourcesContent와 함께 사용하면 완전히 자체 완결적인 소스 맵을 만들 수 있습니다.
HTTP sourcemap 헤더는 소스 주석보다 우선하며, 둘 다 존재하면 소스 맵 파일을 해석하는 데 헤더 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 주석을 감지하는 방법은 여러 가지일 수 있으며, 이를 통해 각 구현이 자신에게 덜 복잡한 방법을 선택할 수 있습니다. 모든 추출 방법의 결과가 같으면 생성된 코드는 소스 맵에 모호함 없이 연결됩니다.
도구가 소스 맵에 모호함 없이 연결되는 소스 파일을 하나 이상 소비하고 소스 맵에 연결되는 출력 파일을 생성하면, 해당 출력 파일도 모호함 없이 연결되어야 합니다.
추상 연산 JavaScriptExtractSourceMapURL의 입력은 인수 source (String)이다. 반환값은 String 또는 null이다. JavaScript 소스에서 소스 맵 URL을 추출합니다. 파싱을 통한 구현과 파싱 없는 구현이라는 두 가지 구현이 가능합니다.
WebAssembly는 텍스트 형식이 아니고 주석을 지원하지 않으므로, 모호함이 없는 단일 추출 방법을 지원합니다. URL은 WebAssembly 이름으로 인코딩되어 사용자 정의 섹션의 내용으로 배치됩니다. WebAssembly 코드를 생성하는 도구가 이름이 sourceMappingURL인 사용자 정의 섹션을 둘 이상 생성하는 것은 유효하지 않습니다.
11.2 소스 맵 가져오기
11.2.1 FetchSourceMap ( url )
추상 연산 FetchSourceMap의 입력은 인수 url (URL)이다. 반환값은 Promise이다. 호출되면 다음 단계를 수행한다:
도구가 특정 DSL(템플릿)에서 소스를 생성하거나 TypeScript → JavaScript → 축소된 JavaScript로 컴파일하여, 최종 소스 맵이 만들어지기 전에 여러 번의 변환이 이루어지는 경우가 점점 더 흔해지고 있습니다. 이 문제는 두 가지 방법 중 하나로 처리할 수 있습니다. 쉽지만 정보 손실이 있는 방법은 디버깅을 위해 처리 과정의 중간 단계를 무시하는 것입니다. 변환 과정의 소스 위치 정보를 무시하거나(중간 변환 결과를 “원본 소스”로 간주), 소스 위치 정보를 끝까지 전달합니다(중간 변환 결과는 숨김). 더 완전한 방법은 여러 단계의 매핑을 지원하는 것입니다: 원본 소스에도 소스 맵 참조가 있으면 사용자에게 그것도 사용할지 선택할 수 있게 합니다.
그러나 JavaScript 이외의 언어에서 “소스 맵 참조”가 어떤 형태인지는 명확하지 않습니다. 더 구체적으로는 JavaScript 형식의 한 줄 주석을 지원하지 않는 언어에서 소스 맵 참조가 어떤 형태인지가 명확하지 않습니다.
Annex C (informative) 다른 명세에 정의된 용어
이 절은 이 문서에서 사용하는 용어와 알고리즘 중 ECMA-262 이외의 외부 명세에 정의된 모든 항목을 나열합니다.
이 명세는 GitHub에서 Ecmarkup이라는 일반 텍스트 소스 형식으로 작성됩니다. Ecmarkup은 ECMAScript 명세를 일반 텍스트로 작성하고, 이 문서의 편집 규칙을 따르는 완전한 기능의 HTML 렌더링으로 처리하기 위한 프레임워크와 도구 모음을 제공하는 HTML 및 Markdown의 변형입니다. Ecmarkup은 구문 정의를 위한 Grammarkdown과 알고리즘 단계 작성을 위한 Ecmarkdown을 비롯한 여러 형식과 기술을 기반으로 하며 이를 통합합니다. 이 명세의 PDF 렌더링은 HTML 렌더링을 PDF로 인쇄하여 생성됩니다.
이 명세의 초판은 HTML과 Markdown을 기반으로 하는 또 다른 일반 텍스트 소스 형식인 Bikeshed를 사용하여 작성되었습니다.
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:
(i) The full text of this COPYRIGHT NOTICE AND COPYRIGHT LICENSE in a location
viewable to users of the redistributed or derivative work.
(ii) Any pre-existing intellectual property disclaimers, notices, or terms and
conditions. If none exist, the Ecma alternative copyright notice should be
included.
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.
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.