草案 ECMA-426 / 2025年11月13日

源映射格式规范

关于本规范

https://tc39.es/ecma426/ 的文档是最准确且最新的源映射规范。它包含最近一次发布快照的内容以及将包含在下一次快照中的任何修改。

如何贡献本规范

本规范在 GitHub 上开发。有多种方式可以为规范的制定做出贡献:

更多关于本文件创建方式的信息请参考 后记

简介

本 Ecma 标准定义了源映射格式,用于将转译后的源代码映射回原始源代码。

源映射格式有以下目标:

原始源映射格式(v1)由 Joseph Schorr 创建,用于 Closure Inspector 以实现优化的 JavaScript 代码的源级调试(但格式本身与语言无关)。然而,随着使用源映射的项目规模扩大,格式的冗长性逐渐成为问题。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 范围

本标准定义了源映射格式,被多种开发者工具用于改善编译到 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(一个 整数),返回正常补全,其值为整数,或一个抛出补全

所有返回补全记录的抽象操作调用,默认使用ReturnIfAbrupt宏包裹,除非已经被Completion显式包裹。例如:

  1. resultGetTheAnswer(value)。
  2. secondCompletion(GetTheAnswer(value))。

等价于:

  1. resultReturnIfAbrupt(GetTheAnswer(value))。
  2. secondCompletion(GetTheAnswer(value))。

4.1.2 可选错误

当某算法可选择性报告错误时,实现可以选择以下行为之一:

  • 继续执行算法的剩余部分。
  • 向用户报告错误(例如在浏览器控制台),并继续执行算法剩余部分。
  • 返回一个ThrowCompletion

针对不同的可选错误,实现可选择不同的行为。

4.2 语法表示

本规范采用 ECMA-262(语法表示)中定义的语法表示惯例,并有以下注意事项:

5 术语与定义

在本文件范围内,下列术语与定义适用。

生成代码

通过编译器或转译器生成的代码。

原始源

未经过编译器或转译器处理的源代码。

源映射URL

URL,用于从生成代码引用源映射的位置。

生成代码的一行内,以零为起始的偏移量。对于 JavaScript 和 CSS 源映射按 UTF-16 码元计算,对于 WebAssembly 源映射的二进制内容(被表示为单行)则按字节索引计算。

这意味着 "A"(拉丁大写字母A)计为1个码元,而 "🔥"()计为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。

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 of VlqDigitList
  2. 如果 unsigned 取模 2 = 1,令 sign 为 -1。
  3. 否则,令 sign 为 1。
  4. value向下取整(unsigned / 2)。
  5. 如果 value 是 0 且 sign 是 -1,返回 -231
  6. 如果 value ≥ 231,抛出错误。
  7. 返回 sign × value
步骤6中的检查是必要的,因为 unsignedVLQUnsignedValue of VlqDigitList,而不是 Vlq

6.2 VLQUnsignedValue

语法指引操作 VLQUnsignedValue 无参数,返回一个非负整数。其在以下产生式上分段定义:

Vlq :: VlqDigitList
  1. valueVLQUnsignedValue of VlqDigitList
  2. 如果 value ≥ 232,抛出错误。
  3. 返回 value
VlqDigitList :: ContinuationDigit VlqDigitList
  1. leftVLQUnsignedValue of ContinuationDigit
  2. rightVLQUnsignedValue of VlqDigitList
  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 为对应 digit整数,根据 IETF RFC 4648 所定义的base64编码。
  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 为对应 digit整数,根据 IETF RFC 4648 所定义的base64编码。
  3. 断言:32 ≤ value < 64。
  4. 返回 value - 32。

7 JSON值工具

虽然本规范的算法基于ECMA-262内部实现,但其目的是方便非JavaScript平台也能实现。本节包含用于处理JSON值的工具,将ECMA-262的细节从本文件的其他部分中抽象出来。

JSON值可以是JSON对象JSON数组字符串数字布尔值,或null

JSON对象是一个对象,其中每个属性:

JSON数组是一个JSON对象,满足:

7.1 ParseJSON ( string )

抽象操作ParseJSON接受参数string(字符串),返回一个JSON值。调用时按如下步骤执行:

  1. resultCall(%JSON.parse%, null, « string »)。
  2. 断言resultJSON值
  3. 返回result
编者注
该抽象操作正在ECMA-262规范自身中公开,参见tc39/ecma262#3540

7.2 JSONObjectGet ( object, key )

抽象操作JSONObjectGet接受参数objectJSON对象)和key(字符串),并返回JSON值missing。返回该keyobject中对应的值。执行时步骤如下:

  1. object没有名为key的自有属性,则返回missing
  2. propobject中名为key的自有属性。
  3. 返回prop[[Value]]属性。

7.3 JSONArrayIterate ( array )

抽象操作JSONArrayIterate接受参数arrayJSON数组),返回一个列表,元素为JSON值。返回array的所有元素,便于算法 "For each" 遍历。执行步骤如下:

  1. lengthJSONObjectGet(array, "length")。
  2. 断言length是非负整数
  3. list为新的空列表
  4. i为0。
  5. 重复,条件为i < (length),
    1. valueJSONObjectGet(array, ToString(𝔽(i))))。
    2. 断言value不为missing
    3. value加入list
    4. i加1。
  6. 返回list

7.4 StringSplit ( string, separators )

抽象操作StringSplit接受参数string(字符串)和separators(字符串列表),返回一个字符串列表。将stringseparators中任意分隔符分割为子串。如果有多个分隔符匹配,则以separators中较前的优先。执行步骤如下:

  1. parts为新的空列表。
  2. strLenstring长度。
  3. lastStart为0。
  4. i为0。
  5. 重复,当i < strLen
    1. matchedfalse
    2. separators中的每个字符串sep,执行:
      1. sepLensep长度。
      2. candidatestringimin(i + sepLen, strLen)的子串。
      3. 如果candidate等于sepmatchedfalse,则:
        1. chunkstringlastStarti的子串。
        2. chunk加入parts
        3. lastStart设为i + sepLen
        4. i设为i + sepLen
        5. matched设为true
    3. 如果matchedfalse,则i加1。
  6. chunkstringlastStartstrLen的子串。
  7. chunk加入parts
  8. 返回parts

8 位置类型

8.1 位置记录

位置记录是一个由非负行号和非负列号组成的元组:

表1:位置记录字段
字段名 值类型
[[Line]] 非负整数
[[Column]] 非负整数

8.2 原始位置记录

原始位置记录是一个由解码源记录、非负行号和非负列号组成的元组。它类似于位置记录,但用于描述具体原始源文件中的源位置。

表2:原始位置记录字段
字段名 值类型
[[Source]] 解码源记录
[[Line]] 非负整数
[[Column]] 非负整数

8.3 ComparePositions ( first, second )

抽象操作ComparePositions接受参数first(一个位置记录原始位置记录)和second(一个位置记录原始位置记录),返回lesserequalgreater。取值依赖于first是发生在second之前、等于或之后。原始位置记录中的[[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 源映射解码

解码后源映射记录包含如下字段:

表3:解码后源映射记录字段
字段名称 值类型
[[File]] 字符串或null
[[Sources]] List,其中每项为解码后源码记录
[[Mappings]] List,其中每项为解码后映射记录

解码后源码记录包含如下字段:

表4:解码后源码记录字段
字段名称 值类型
[[URL]] URLnull
[[Content]] 字符串或null
[[Ignored]] 布尔值

9.1.1 ParseSourceMap ( string, baseURL )

抽象操作 ParseSourceMap 接收参数 string(字符串)和 baseURLURL),返回一个解码后源映射记录。执行下列步骤:

  1. jsonParseJSON(string)。
  2. 如果 json 不是 JSON 对象,则抛出错误。
  3. 如果 JSONObjectGet(json, "sections") 不是 missing,则
    1. 返回 DecodeIndexSourceMap(json, baseURL)。
  4. 返回 DecodeSourceMap(json, baseURL)。

9.1.2 DecodeSourceMap ( json, baseURL )

抽象操作 DecodeSourceMap 接收参数 jsonJSON 对象)和 baseURLURL),返回解码后源映射记录。执行下列步骤:

  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,若 解码后映射记录 a解码后映射记录 b 小,则 ComparePositions(a.[[GeneratedPosition]], b.[[GeneratedPosition]]) 为 lesser
  14. 返回 解码后源映射记录 { [[File]]: fileField, [[Sources]]: sources, [[Mappings]]: mappings }。

9.1.2.1 GetOptionalString ( object, key )

抽象操作 GetOptionalString 接收参数 objectJSON 对象)和 key(字符串),返回字符串或 null。执行下列步骤:

  1. valueJSONObjectGet(object, key)。
  2. 如果 value 为字符串,返回 value
  3. 如果 value 不是 missing可选地报告错误
  4. 返回 null

9.1.2.2 GetOptionalListOfStrings ( object, key )

抽象操作 GetOptionalListOfStrings 接收参数 objectJSON 对象)和 key(字符串),返回字符串的List。执行下列步骤:

  1. list 为新的空List
  2. valuesJSONObjectGet(object, key)。
  3. 如果 valuesmissing,返回 list
  4. 如果 values 不是 JSON 数组,则
    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 接收参数 objectJSON 对象)和 key(字符串),返回(字符串或 null)的List。执行下列步骤:

  1. list 为新的空List
  2. valuesJSONObjectGet(object, key)。
  3. 如果 valuesmissing,返回 list
  4. 如果 values 不是 JSON 数组,则
    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(字符串),返回非负整数List。执行下列步骤:

  1. list 为新的空List
  2. valuesJSONObjectGet(object, key)。
  3. 如果 valuesmissing,返回 list
  4. 如果 values 不是 JSON 数组,则
    1. 可选地报告错误
    2. 返回 list
  5. 对于 JSONArrayIterate(values) 的每个元素 item,执行:
    1. 如果 item整数类型,item+0𝔽,且 item+0𝔽,则
      1. 添加 (item) 到 list
    2. 否则,
      1. 如果 itemnull可选地报告错误
      2. 添加 nulllist
  6. 返回 list

9.2 映射结构

mappings字段的数据分解如下:

  • 每组代表生成文件中的一行,由分号 (;) 分隔
  • 每个片段由逗号 (,) 分隔
  • 每个片段由1、4或5个可变长度字段组成。

每个片段中的字段为:

  1. 该行在生成代码中的零基起始。如果这是首个片段的第一个字段,或在新生成行(;)后首个片段,则该字段表示完整的base64 VLQ。否则,此字段为相对于前一次出现的base64 VLQ注意这个字段的前值会在每行生成代码后重置,这与下方其他字段不同。
  2. 如果存在,表示sources列表中的零基索引。此字段为相对于前一次出现的base64 VLQ,除非是首次出现,则表示完整值。
  3. 如果存在,表示原始源码中的零基起始行号。此字段为相对于前一次出现的base64 VLQ,除非为首次出现则为完整值。有source字段时必须出现。
  4. 如果存在,表示原始源码中该行的零基起始。此字段为相对于前一次出现的base64 VLQ,首次出现则为完整值。有source字段时必须出现。
  5. 如果存在,表示与该片段相关的names列表中的零基索引。此字段为相对于前一次出现的base64 VLQ,首次出现则为完整值。
注意 1
此编码目的是减小源映射的体积。VLQ编码相比Source Map Revision 2 Proposal在Google Calendar测试中缩小约50%。
注意 2
仅有一个字段的片段用于表示没有对应原始源码代码的生成代码(如编译器自动生成),四字段片段表示有对应源码但无对应名称的片段,五字段片段表示既有源码又有名称的映射。
注意 3
曾考虑使用文件偏移,但最终采用行/数据,以避免因平台行结束符不同导致和原文件不一致的问题。

解码后映射记录具有以下字段:

表5:解码后映射记录字段
字段名称 值类型
[[GeneratedPosition]] 位置记录
[[OriginalPosition]] 原始位置记录null
[[Name]] 字符串或null

9.2.1 映射语法

映射字符串必须遵循如下语法:

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

解码映射状态记录包含以下字段:

表6:解码映射状态记录字段
字段名称 值类型
[[GeneratedLine]] 非负整数
[[GeneratedColumn]] 非负整数
[[SourceIndex]] 非负整数
[[OriginalLine]] 非负整数
[[OriginalColumn]] 非负整数
[[NameIndex]] 非负整数

9.2.1.1 DecodeMappingsField

语法驱动操作DecodeMappingsField,接收参数state解码映射状态记录)、mappingsList,每项为解码后映射记录)、names(字符串List)、sources(每项为解码后源码记录List)。定义如下:

LineList : Line ; LineList
  1. Line执行DecodeMappingsField,参数为statemappingsnamessources
  2. 设置state.[[GeneratedLine]]state.[[GeneratedLine]]+1。
  3. 设置state.[[GeneratedColumn]]为0。
  4. LineList执行DecodeMappingsField,参数为statemappingsnamessources
Line : [empty]
  1. 返回。
MappingList : Mapping , MappingList
  1. Mapping执行DecodeMappingsField,参数为statemappingsnamessources
  2. MappingList执行DecodeMappingsField,参数为statemappingsnamessources
Mapping : GeneratedColumn
  1. GeneratedColumn执行DecodeMappingsField,参数为statemappingsnamessources
  2. 如果state.[[GeneratedColumn]]<0,则
    1. 可选地报告错误
    2. 返回。
  3. position为新的位置记录 { [[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. GeneratedColumn执行DecodeMappingsField,参数为statemappingsnamessources
  2. 如果state.[[GeneratedColumn]]<0,则
    1. 可选地报告错误
    2. 返回。
  3. generatedPosition为新的位置记录 { [[Line]]: state.[[GeneratedLine]], [[Column]]: state.[[GeneratedColumn]] }。
  4. OriginalSource执行DecodeMappingsField,参数为statemappingsnamessources
  5. OriginalLine执行DecodeMappingsField,参数为statemappingsnamessources
  6. OriginalColumn执行DecodeMappingsField,参数为statemappingsnamessources
  7. 如果state.[[SourceIndex]]<0或state.[[SourceIndex]]sources元素数,或state.[[OriginalLine]]<0,或state.[[OriginalColumn]]<0,则
    1. 可选地报告错误
    2. originalPositionnull
  8. 否则,
    1. originalPosition为新的原始位置记录 { [[Source]]: sources[state.[[SourceIndex]]],[[Line]]: state.[[OriginalLine]][[Column]]: state.[[OriginalColumn]] }。
  9. namenull
  10. 如果Name存在,则
    1. Name执行DecodeMappingsField,参数为statemappingsnamessources
    2. 如果state.[[NameIndex]]<0或state.[[NameIndex]]names元素数,可选地报告错误
    3. 否则,设置namenames[state.[[NameIndex]]]。
  11. decodedMapping为新的DecodedMappingRecord { [[GeneratedPosition]]: generatedPosition, [[OriginalPosition]]: originalPosition, [[Name]]: name }。
  12. decodedMapping加入mappings
GeneratedColumn : Vlq
  1. relativeColumnVLQSignedValueVlq结果。
  2. 设置state.[[GeneratedColumn]]state.[[GeneratedColumn]]+relativeColumn
OriginalSource : Vlq
  1. relativeSourceIndexVLQSignedValueVlq结果。
  2. 设置state.[[SourceIndex]]state.[[SourceIndex]]+relativeSourceIndex
OriginalLine : Vlq
  1. relativeLineVLQSignedValueVlq结果。
  2. 设置state.[[OriginalLine]]state.[[OriginalLine]]+relativeLine
OriginalColumn : Vlq
  1. relativeColumnVLQSignedValueVlq结果。
  2. 设置state.[[OriginalColumn]]state.[[OriginalColumn]]+relativeColumn
Name : Vlq
  1. relativeNameVLQSignedValueVlq结果。
  2. 设置state.[[NameIndex]]state.[[NameIndex]]+relativeName

9.2.2 DecodeMappings ( rawMappings, names, sources )

抽象操作 DecodeMappings 接收参数 rawMappings(字符串),names(字符串List),sources解码后源码记录List),并返回解码后映射记录List。调用时执行以下步骤:

  1. mappings 为新的空List
  2. mappingsNode 为解析 rawMappings 且以 MappingsField目标符号时的根解析节点
  3. 如果解析失败,则
    1. 可选地报告错误
    2. 返回 mappings
  4. state 为所有字段均设为0的新解码映射状态记录
  5. mappingsNode 执行 DecodeMappingsField,参数为 statemappingsnamessources
  6. 返回 mappings

9.2.3 生成 JavaScript 代码的映射

生成代码 的位置可能具有 映射条目,这些位置根据 ECMAScript 词法语法定义为 输入元素。映射条目应指向以下任一:

  • 源文本匹配的 IdentifierNamePrivateIdentifierPunctuatorDivPunctuatorRightBracePunctuatorNumericLiteralRegularExpressionLiteral 的第一个码点。
  • 源文本匹配的 CommentHashbangCommentStringLiteralTemplateTemplateSubstitutionTailWhiteSpaceLineTerminator 的任意码点。

9.2.4 生成 JavaScript 代码的名称

若 JavaScript 代码符号满足下列条件,源映射生成器应为该符号生成带 [[Name]] 字段的 映射 条目:

  • 原始源码语言结构在语义上映射到生成的 JavaScript 代码。
  • 原始源码语言结构有名称。

此时,该 映射 条目的 [[Name]] 应为 原始源码语言结构的名称。 [[Name]] 非 null 的 映射 被称为 命名映射

注意 1
例如混淆器会重命名函数和变量,或从立即调用函数表达式中移除函数名。

下列枚举列出了 ECMAScript 句法语法中的产生式,以及源映射生成器为其生成命名映射的对应 token 或非终结符(即产生式右侧项)。创建这些 token 的 映射条目应遵循 9.2.3

该枚举应理解为“最低标准”。一般情况下,源映射生成器可自由生成其他命名映射。

注意 2
枚举也包含生成器“可”生成命名映射的 token,而不仅仅是“应”生成的那些。这反映了现有工具实际生成或期望命名映射。重复的命名映射开销很低:names 的索引是相对编码的,因此连续映射同一名称时编码为 0(A)。
  • BindingIdentifier,用于 LexicalDeclarationVariableStatementFormalParameterList

  • BindingIdentifier,用于 FunctionDeclarationFunctionExpressionAsyncFunctionDeclarationAsyncFunctionExpressionGeneratorDeclarationGeneratorExpressionAsyncGeneratorDeclarationAsyncGeneratorExpression(如果存在),否则为紧接 FormalParameters 的左括号 (

    无论 BindingIdentifier 是否存在,源映射生成器可选择在左括号为位置生成命名映射。

  • 对于 ArrowFunctionAsyncArrowFunction

    • ArrowFunction 由单一 BindingIdentifier 参数生成,或 AsyncArrowFunctionAsyncArrowBindingIdentifier 生成时,=> token。

      注意 3
      这描述了(异步)箭头函数有单一参数且该参数未被圆括号包裹的情况。
    • ArrowFunctionAsyncArrowFunctionArrowFormalParameters 生成时的左括号 (

      源映射生成器亦可选择在 => token 处生成命名映射,以与前述情况一致。

  • ClassElementName,用于 MethodDefinition。包含生成器、异步方法、异步生成器和访问器。若 ClassElementName"constructor",则 [[Name]] 应为原始类名(如果适用)。

    源映射生成器亦可选择在左括号 ( 处生成命名映射。

  • 源映射生成器可为包含在 ExpressionIdentifierReference 生成命名映射。

9.3 解析源码

在拼接 sourceRoot 后,如果 sources 不是绝对 URL,则应相对于 source map 解析 sources(就像在 HTML 文档中解析 script 的 src 属性)。

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

抽象操作 DecodeSourceMapSources 接收参数 baseURLURL)、sourceRoot(字符串或 null)、sources(字符串或 nullList)、sourcesContent(字符串或 nullList)、ignoreList(非负 整数List),返回 解码后源码记录。调用时执行以下步骤:

  1. decodedSources 为新的空 List
  2. sourcesContentCountsourcesContent 的元素个数。
  3. sourceUrlPrefix""
  4. 如果 sourceRootnull,则
    1. 如果 sourceRoot 以码点 U+002F (SOLIDUS) 结尾,则
      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. source 设置为 sourceUrlPrefixsource字符串拼接
      2. sourceURLURL 解析 source (以 baseURL 作为基础)。
      3. 如果 sourceURLfailure可选地报告错误
      4. 否则,将 decodedSource.[[URL]] 设置为 sourceURL
    4. 如果 ignoreList 包含 index,则将 decodedSource.[[Ignored]] 设置为 true
    5. 如果 sourcesContentCount > index,则将 decodedSource.[[Content]] 设置为 sourcesContent[index]。
    6. decodedSource 添加到 decodedSources
  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,顶层为对象。它共享常规源映射的 version 以及file字段,但增加了新的sections字段

sections字段是一个对象数组,每个对象包含如下字段:

sections 应按起始位置排序,并且所表示的区块不能重叠。

10.1 DecodeIndexSourceMap ( json, baseURL )

抽象操作 DecodeIndexSourceMap 接收参数 json(对象)和 baseURLURL),返回解码后源映射记录。调用时执行以下步骤:

  1. sectionsFieldJSONObjectGet(json, "sections")。
  2. 断言sectionsField 不是 missing
  3. 如果 sectionsField 不是 JSON数组,抛出错误。
  4. 如果 JSONObjectGet(json, "version") 不是 3𝔽可选地报告错误
  5. fileFieldGetOptionalString(json, "file")。
  6. sourceMap解码后源映射记录 { [[File]]: fileField, [[Sources]]: « », [[Mappings]]: « » }。
  7. previousOffsetPositionnull
  8. previousLastMappingnull
  9. 对于 JSON值 sectionJSONArrayIterate(sectionsField) 中,执行:
    1. 如果 section 不是 JSON对象,则
      1. 可选地报告错误
    2. 否则,
      1. offsetJSONObjectGet(section, "offset")。
      2. 如果 offset 不是 JSON对象,抛出错误。
      3. offsetLineJSONObjectGet(offset, "line")。
      4. offsetColumnJSONObjectGet(offset, "column")。
      5. 如果 offsetLine 不是 整数,则
        1. 可选地报告错误
        2. 设置 offsetLine+0𝔽
      6. 如果 offsetColumn 不是 整数,则
        1. 可选地报告错误
        2. 设置 offsetColumn+0𝔽
      7. offsetPosition 为新的 位置记录 { [[Line]]: offsetLine, [[Column]]: offsetColumn }。
      8. 如果 previousOffsetPositionnull,则
        1. 如果 ComparePositions(offsetPosition, previousOffsetPosition) 为 lesser可选地报告错误
      9. 如果 previousLastMappingnull,则
        1. 如果 ComparePositions(offsetPosition, previousLastMapping.[[GeneratedPosition]]) 为 lesser可选地报告错误
        2. 注意:该部分解码算法确保 index 源映射的sections字段的条目按顺序排列且不重叠。虽然应保证生成器不会生成重叠 section 的 index 源映射,但源映射消费方可能只检查较简单的 offset 顺序条件。
      10. mapFieldJSONObjectGet(section, "map")。
      11. 如果 mapField 不是 JSON对象,抛出错误。
      12. decodedSectionCompletionCompletion(DecodeSourceMap(json, baseURL))。
      13. 如果 decodedSectionCompletion抛出完成,则
        1. 可选地报告错误
      14. 否则,
        1. decodedSectiondecodedSectionCompletion.[[Value]]
        2. 对于 解码后源码记录 additionalSourcedecodedSection.[[Sources]],执行:
          1. 如果 sourceMap.[[Sources]] 不包含 additionalSource,则
            1. additionalSource 加入 sourceMap.[[Sources]]
        3. offsetMappings 为新的空 List
        4. 对于 解码后映射记录 mappingdecodedSection.[[Mappings]],执行:
          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. 设置 previousOffsetPositionoffsetPosition
        7. 如果 offsetMappings 不为空,设置 previousLastMappingoffsetMappings 的最后一个元素。
    3. 返回 sourceMap
注意
实现可以选择不将所有 section 的映射拼接在一起,例如可以分别存储每个 section 并使用二分查找来定位。

11 获取源映射

11.1 关联生成代码与源映射

虽然源映射格式旨在兼容多语言和多平台,但对于典型的 web 服务器托管 JavaScript 的情况,定义如何引用源映射是有用的。

有两种方式可以将源映射链接到输出文件。第一种需要服务器支持,添加 HTTP 头部;第二种则是在源码中添加注释。

源映射通过 URL 进行链接,参见WHATWG URL的定义;特别是,URI 规范外的字符应进行百分号编码,并且 URL 可以为 data URI。结合 sourcesContent 使用 data URI 可以实现完全自包含的源映射。

HTTP sourcemap 头部优先生效,如果同时存在注释和头部,优先使用头部中的 URL 解析源映射文件。

无论采用哪种方法获取 源映射URL,解析流程一致,过程如下:

源映射URL 不是绝对路径时,其路径相对于 生成代码源起始位置(source origin)。源起始位置 的确定方式如下:

  • 如果生成代码不属于带有 src 属性的 script 元素,并且代码中存在 //# sourceURL 注释,则应通过该注释确定 源起始位置

    注意
    之前使用 //@ sourceURL,与 //@ sourceMappingURL 类似,两者都可以接受,但建议使用 //#
  • 如果 生成代码 属于 script 元素且该元素有 src 属性,则 script 的 src 即为 源起始位置
  • 如果 生成代码 属于 script 元素且没有 src 属性,则 源起始位置 是页面的 origin。
  • 如果 生成代码 是通过 eval()new Function() 执行的字符串,则 源起始位置 是页面的 origin。

11.1.1 通过 HTTP 头部关联

如果文件通过 HTTP(S) 服务器返回 sourcemap 头部,头部的值就是关联源映射的 URL

sourcemap: <url>
注意
文档先前版本推荐名为 x-sourcemap 的头部。现在已弃用,应使用 sourcemap

11.1.2 通过源码内注释关联

生成代码 应该包含一个名为 sourceMappingURL 的注释(或者依语言/格式而定的等价结构),其中包含源映射的 URL。本规范定义了 JavaScript、CSS 和 WebAssembly 中注释的格式,其他语言可参照类似约定。

一种语言可有多种方式检测到 sourceMappingURL 注释,以便不同实现选择较简单方式。生成代码若经过所有方法提取的结果一致,则称为无歧义关联到源映射

如果工具处理的一或多个源文件 无歧义关联到源映射,并输出文件也关联源映射,则输出也必须是无歧义的关联。

注意 1

如下 JavaScript 代码关联了源映射,但方式不是无歧义:

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

若通过解析提取源映射URL会得到 null,但不解析直接提取则会得到 foo.js.map

注意 2

多种方式提取源映射URL,可能得到不同结果,会带来安全和隐私风险。建议实现始终同时采用两种算法,而不假定其结果一致。

标准的未来版本会修正此问题,可能包括:只要注释(或类注释)包含字符 U+0060 (`)、U+0022 (")、U+0027 ('),或序列 U+002A U+002F (*/) 就直接提前返回。

11.1.2.1 JavaScriptExtractSourceMapURL ( source )

抽象操作 JavaScriptExtractSourceMapURL 接收参数 source(字符串),返回字符串或 null。它从 JavaScript 源码中提取 源映射URL。可通过 解析不解析两种方式实现。

如下为解析方式

  1. tokens 为根据ECMA-262词法语法source 解析后得到的 token List
  2. 对于 tokens 中非终结符 token,逆序遍历,执行:
    1. 如果 token 不是 SingleLineCommentMultiLineComment,返回 null
    2. commenttoken 的内容。
    3. sourceMapURLMatchSourceMapURL(comment)。
    4. 如果 sourceMapURL 是字符串,返回 sourceMapURL
  3. 返回 null

如下为不解析方式

  1. linesStringSplit(source, « "\u000D\u000A", "\u000A", "\u000D", "\u2028", "\u2029" »)。
  2. 注:上方分隔符正好匹配 LineTerminatorSequence 产生式。
  3. lastURLnull
  4. 遍历 lines 中每个字符串 lineStr,执行:
    1. lineStringToCodePoints(lineStr)。
    2. position 为 0。
    3. lineLengthline 长度。
    4. position < lineLength,重复:
      1. firstline[position]。
      2. position 加 1。
      3. 如果 first 为 U+002F (SOLIDUS) 且 position < lineLength,则
        1. secondline[position]。
        2. position 加 1。
        3. 如果 second 为 U+002F (SOLIDUS),则
          1. commentsubstring(lineStr, position, lineLength)。
          2. sourceMapURLMatchSourceMapURL(comment)。
          3. 如果 sourceMapURL 是字符串,则 lastURL 赋值为 sourceMapURL
          4. position 赋值为 lineLength
        4. 否则若 second 为 U+002A (ASTERISK),则
          1. commentCp 为新的空 List
          2. position + 1 < lineLength,重复:
            1. c1line[position]。
            2. position 加 1。
            3. c2line[position]。
            4. 如果 c1 是 U+002A 且 c2 是 U+002F,则
              1. position 加 1。
              2. sourceMapURLMatchSourceMapURL(CodePointsToString(commentCp))。
              3. 如果 sourceMapURL 是字符串,则 lastURL 赋值为 sourceMapURL
            5. c1 加入 commentCp
        5. 否则,
          1. lastURL 赋值为 null
      4. 否则若 first 不是 ECMAScript WhiteSpace,则
        1. lastURL 赋值为 null
      5. 注:遇到非注释代码字符时,将 lastURL 重置为 null
  5. 返回 lastURL
注意 1
上述算法允许反向遍历源码行,遇到包含 sourceMappingURL 的注释后即可提前返回。
注意 2

上述算法等价于以下 JavaScript 实现:

const JS_NEWLINE = /^/m;

// 此正则能匹配以下类型:
// - 单行注释
// - “单行”多行注释
// - 未闭合的多行注释
// - 仅有尾随空白
// - 代码字符
// 循环体会区分这些情况。
const JS_COMMENT =
  /\s*(?:\/\/(?<single>.*)|\/\*(?<multi>.*?)\*\/|\/\*.*|$|(?<code>[^\/]+))/uym;

const PATTERN = /^[@#]\s*sourceMappingURL=(\S*?)\s*$/;

let lastURL = null;
for (const line of source.split(JS_NEWLINE)) {
  JS_COMMENT.lastIndex = 0;
  while (JS_COMMENT.lastIndex < line.length) {
    let commentMatch = JS_COMMENT.exec(line).groups;
    let comment = commentMatch.single ?? commentMatch.multi;
    if (comment != null) {
      let match = PATTERN.exec(comment);
      if (match !== null) lastURL = match[1];
    } else if (commentMatch.code != null) {
      lastURL = null;
    } else {
      // 找到尾部空白或未闭合注释。
      // 断言:JS_COMMENT.lastIndex === line.length
    }
  }
}
return lastURL;

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
注意
该注释前缀最初为 //@,但与 IE 的条件编译冲突,已改为 //#

源映射生成器只需输出 //#,而源映射消费方需同时接受 //@//#

11.1.2.2 CSSExtractSourceMapURL ( source )

抽象操作 CSSExtractSourceMapURL 接收参数 source(字符串),返回字符串或 null。它从 CSS 源码中提取 源映射URL

从 CSS 提取 源映射URL 的方式与 JavaScript 类似,但 CSS 只支持 /* ... */ 样式注释。

11.1.2.3 WebAssemblyExtractSourceMapURL ( bytes )

抽象操作 WebAssemblyExtractSourceMapURL 接收参数 bytes数据块),返回字符串或 null。它从 WebAssembly 二进制源码中提取 源映射URL

  1. modulemodule_decode(bytes)。
  2. 如果 moduleWebAssembly error,返回 null
  3. 遍历 modulecustom section customSection,执行:
    1. namecustomSectionname
    2. 如果 CodePointsToString(name) 为 "sourceMappingURL",则
      1. valuecustomSectionbytes
      2. 返回 CodePointsToString(value)。
  4. 返回 null

由于 WebAssembly 不是文本格式且不支持注释,只支持唯一的无歧义提取方法。该 URLWebAssembly 名称编码,并放置于 custom section 内容中。工具生成 WebAssembly 代码时,不允许生成两个或更多 sourceMappingURL 名称的 custom section

11.2 获取源映射文件

11.2.1 FetchSourceMap ( url )

抽象操作 FetchSourceMap 接收参数 urlURL),返回一个 Promise。调用时执行如下步骤:

  1. promiseCapabilityNewPromiseCapability(%Promise%)。
  2. request 为新的 request,其 request URLurl
  3. processResponseConsumeBody 为新的 Abstract Closure,参数 (response, bodyBytes),捕获 promiseCapabilityurl,步骤如下:
    1. 如果 bodyBytesnullfailure,则
      1. 执行 Call(promiseCapability.[[Reject]], undefined, « 新 TypeError »)。
      2. 返回。
    2. 如果 urlschemeHTTP(S) 且字节序列 )]}'byte-sequence-prefix,则
      1. 重复,当 bodyBytes 长度 ≠ 0 且 bodyBytes[0] 非 HTTP newline byte
        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]]
注意

历史原因下,通过 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) 的结果为 lesserequal,则
        1. last 设为 mapping
  5. 如果 last 不为 null,则
    1. 遍历 mappings 中每个 mapping,执行:
      1. 如果 ComparePositions(last.[[GeneratedPosition]], mapping.[[GeneratedPosition]]) 的结果为 equal,则
        1. mapping.[[OriginalPosition]] 加入 originalPositions
  6. 返回 originalPositions

附录A (资料性) 约定

在使用或生成源映射时,应遵循以下约定。

A.1 源映射命名

通常,源映射会和生成文件名称相同,只是文件扩展名为 .map。例如 page.js 的源映射会命名为 page.js.map

A.2 关联eval代码与有名生成代码

在 eval 代码使用源映射时有一种现有约定,格式如下:

//# sourceURL=foo.js

详细描述见 Give your eval a name with //@ sourceURL

附录B (资料性) 说明

B.1 语言无关的堆栈映射

无需源码语言知识的堆栈追踪映射,未在本文档中涉及

B.2 多级映射

目前常见工具会通过一些DSL(模板)或编译流程(TypeScript → JavaScript → 压缩 JavaScript)生成源文件,导致最终源映射在此过程中经过多次转换。此问题有两种解决方式。一种是简单但有信息损失的方法,即为调试目的忽略中间步骤,将中间产物视为“原始源码”,或者只传递源映射位置信息,隐藏上述过程。更完整的方法是支持多级映射:如果原始源码也有源映射引用,允许用户继续解析下一级源映射。

但尚不清楚除 JavaScript 外,其他语言的“源映射引用”应如何表达,特别是在不支持 JavaScript 风格单行注释的语言中。

附录C (资料性) 外部规范中定义的术语

本节列出了本文档中所用、由 ECMA-262 以外的外部规范定义的所有术语和算法。

WebAssembly 核心规范 <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

附录D (资料性) 参考文献

  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 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. 给 eval 起名字 //@ 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. Variable-length quantity,维基百科,见 <https://en.wikipedia.org/wiki/Variable-length_quantity>

附录E (资料性) 后记

本规范在 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.