语法分析和语义分析

2022-10-7 About 43 min

# 语法分析和语义分析

语法分析(syntactic analysis)的作用是在词法分析的基础上,遍历 token,从而识别出程序的抽象语法树(AST),它是一个树形结构。这个树形结构主要描述了源码的结构,暂时还缺少语义信息,比如作用域、类型检查等,我们将在语义分析的环节详细介绍。

以英文句子 "today is comfortable." 为例,对其词法分析就是将句子分离为各个单词,而语法分析关注的是单词和单词之间的关系,这些关系就是所谓的语法。

https://www.tr0y.wang/2021/04/04/%E7%BC%96%E8%AF%91%E5%8E%9F%E7%90%86%EF%BC%88%E5%9B%9B%EF%BC%89%EF%BC%9A%E8%AF%AD%E4%B9%89%E5%88%86%E6%9E%90/

语义分析负责分析上下文相关的情况,和具体语言相关。

# 相关概念

# 抽象语法树和具象语法树

  • 具象语法树(concrete syntax tree, CST)

    具象语法树(concrete syntax tree, CST),又称为语法树(syntax tree),它是源码的树状表示,会展示各种语法细节。

    TODO(画图)

  • 抽象语法树(abstract syntax tree, AST)

    抽象语法树也是树形结构,只不过相对于具象语法树,其不会展示源码的各种语法细节。

    TODO(画图)

  • 不同点

    • AST 使用操作符作为根节点和内部节点,并用操作数作为子节点

      TODO(画图)

    • 与 CST 不同,AST 不适用内部节点表示语法规则

      TODO(画图)

    • AST 不代表真实语法的每个细节,比如,它省略了括号等细节

      TODO(画图)

    • 与 CST 相比,AST 更密集

      TODO(画图)

  • 案例

    TODO(画图)

# 语法分析器

语法分析器负责接收 token 列表,生成源码的抽象语法树,此时的抽象语法树,重点在描述源码的程序结构信息。

# 上下文无关文法

  • 文法

    以英文句子 "today is comfortable." 为例,文法用于定义该句子的结构,对于编程语言,文法用户定义编程语言的结构。

  • 乔姆斯基文法体系

    一位叫乔姆斯基的教授准备利用数学来研究人类自然语言的规律,在此期间,他总结了很多数学工具和方法,称为"乔姆斯基文法体系"。但这套体系在计算机领域进行了发扬光大,用于对程序语言进行结构分析。

    乔姆斯基文法体系有 4 类文法:

    • 0 型文法: 任意文法,在程序设计语言中不常用
    • 1 型文法: 上下文相关文法,在程序设计语言中不常用
    • 2 型文法: 上下文无关文法(Context-free Grammar,CFG)在程序设计中用于描述程序语言的语法结构
    • 3 型文法: 正则文法,就是词法分析中的正则表达式,可以用来描述程序语言的词法结构

    所有文法中,0 型文法的表达能力最强,3 型文法最弱,如图:

    TODO(画图)

  • 上下文无关文法(Context-free Grammar,CFG)

    上文无关文法,指的是该文法定义的所有句子结构之间是没有任何关系的,比如 ID = ID + ID,我们不需要关注 ID 是怎么来的,上下文有什么和它有关的内容,我们只需要关心一个字符是否是 ID,并且 ID 的等价形式是什么。

    https://www.cnblogs.com/jacksplwxy/p/10052752.html

    一个上下文无关文法包括四个部分: 终结符号集合,非终结符号集合,起始符号,产生式集合。

  • 用上下文无关文法描述算术表达式

    TODO

# 生成 AST 的算法

有两种方式可以生成 AST: 自顶向下分析、自底向上分析。

  • 自顶向下分析

    在自顶向下分析中,"递归下降"算法是常用的一种算法,"递归下降"算法包括两个要点: "下降"和"递归"。

    • 下降

    • 递归

    • 左递归

  • 自底向上分析

    TODO

    • 自顶向下分析: 根据形式语法规则,在语法分析树的自顶向下展开中搜索输入符号串可能的最左推导。单词按从左到右的顺序依次使用。
    • 自底向上分析: 语法分析器从现有的输入符号串开始,尝试将其根据给定的形式语法规则进行改写,最终改写为语法的起始符号。

# 引用消解

引用消解解决的是如何找到引用的定义的问题。比如下述案例:

1> const a = 1;
2>
3> const run = () => {
4>     const a = 2;
5>     return a;
6> };
7> 
8> console.log(run());
1
2
3
4
5
6
7
8

在外层环境和 run() 内部都定义了变量 a,无论是编辑器快速跳到 a 变量的定义,还是在运行时,都能准确发现第五行的变量 a 的值是第四行的 2。这个发现定义的过程就是引用消解。

# 左值和右值

左值和右值是开发编译器、解释器时绕不开的一个话题,什么是左值和右值呢?

a + 1
1

上面这个例子中,很显然取的是变量 a 的值。

a = 5
1

这个例子中,取的是变量 a 的地址,并将 5 赋值到该地址对应的值中,当然这部分是 JavaScript 内部的处理。

对于 Babel 这样的转译器而言,需要区分源码中,哪些属于赋值操作,哪些属于取值操作。

简单而言,左值指的就是赋值的目标,右值指的就是取值的目标。

满足左值,自然要满足一些条件:

  • 节点位于赋值表达式的左边
  • 节点位于函数的形参
  • 一元操作符: ++--
  • 其他需要改变变量值的情况

对于像 a + 3a = 5 这样的代码,对应到语法树上,都叫做 xxxx 节点,那么用什么方式区分它们是左值还是右值呢,就需要在 xxxx 节点上添加一些属性进行标记。

获取这些标记属性的过程,叫"属性计算"。

# 属性计算

  • 属性文法

    在词法分析中涉及到了正则文法,在语法分析中涉及到了上下文无关文法。而在语义分析中,涉及到了属性文法。

    属性文法计算的是语义分析中,某个节点的上下文信息,比如某个节点的值(value)就是一种典型的属性计算。

    用语法分析中涉及的上下文无关文法来描述加法计算,描述如下:

    add → add + mul
    add → mul
    mul → mul * primary
    mul → primary
    primary → "(" add ")"
    primary → integer
    
    1
    2
    3
    4
    5
    6

    在上述上下文无关文法的描述基础上,针对节点的 value 属性,用属性文法描述如下:

    dd1 → add1 + mul [ add1.value = add2.value + mul.value ]
    add → mul [ add.value = mul.value ]
    mul1 → mul2 * primary [ mul1.value = mul2.value * primary.value ]
    mul → primary [ mul.value = primary.value ]
    primary → "(" add ")" [ primary.value = add.value ]
    primary → integer [ primary.value = strToInt(integer.str) ]
    
    1
    2
    3
    4
    5
    6

    这就是属性文法定义的针对 value 的规则,规则确定后,就能通过工具计算出该属性的值。

    这种属性文法定义的规则,叫做语法制导的定义(Syntax directed definition,SDD),如果变成计算动作,就叫做语法制导的翻译(Syntax directed translation,SDT)。

  • 综合属性和继承属性

    • 综合属性(S 属性,Synthesized Attribute)

      如果一种属性能够由下级节点推导出来,那么这种属性就叫 S 属性(Synthesized Attribute),也就是综合属性,能够从下级节点的属性归纳、计算为本节点的属性。

      比如:

      a = '1' + 2 
      
      1

      在语义分析阶段,Babel 等转译器已经可以知道变量 a 的类型是 string 了,而这个类型是由"类型推导"而来,变量 a 的类型由其两个子节点(字符串 '1' 和数字 2 )决定,这两个子节点的类型推导出了 a 的类型。

    • 继承属性(I 属性,Inherited Attribute)

      与 S 属性相对应,I 属性(Inherited Attribute)意为继承属性,它可以通过当前节点的上级节点、兄弟节点、自身节点来决定。

      TODO

  • 属性计算的时机

    属性计算逻辑上位于语义分析环节,但是在实际转译逻辑执行中,进行语法分析生成 AST 的同时,属性计算等语义分析也可以同步进行。

    比如,在语法分析的时候,如果某个节点的属性可以通过其他节点(父节点、兄弟节点)计算而来,那么该属性可以在构建 AST 的时候顺便执行了计算。

    另外,语法分析生成 AST 后,语义分析可以根据需要,对 AST 进行多次遍历,为其进行不同的属性计算。

  • 属性计算在语义分析中的应用

    • 作用域解析
    • 引用消解
    • 类型推导
    • 类型检查
    • 类型转换
    • 语义合法性检查

# 语法分析和语义分析过程

# 第1步: 确定文法规则

TODO

# 第2步: 自顶向下分析

TODO

# Babel 的语法分析和语义分析

语法分析的目的是生成源码的结构化表示,即抽象语法树(AST),此时并不需要特别关注语义方面的问题,专注生成 AST。

# 源码结构

文件 babel/packages/babel-parser/src/parser/index.js 既包含了词法分析模块,也包含了语法和语义分析模块。其定义了 Parser 类,在内部实现上有一系列的继承关系:

继承顺序 作用
Parser 对外提供语法分析能力
StatementParser 解析声明
ExpressionParser 解析表达式
LValParser 解析左值
NodeUtils AST节点的各种属性和方法
UtilParser
Tokenizer 词法分析器
ParserErrors 错误处理模块
CommentsParser 对注释进行解析
BaseParser 提供基础属性和关于插件的工具方法

通过观察 Babel Parser 中的继承链,我们也可以了解完整的语法分析可以分解为哪些模块。

这里先介绍一些基础模块的功能,如 BaseParser/ErrorParser,其他模块将在介绍 Babel 的语法分析、语义分析时分别详细介绍。

# BaseParser

BaseParser 类作为继承链的基础类,提供了一些基础属性和方法,为继承链的其他类使用。

由此可以看出,"继承链"也是一种良好的代码复用的方式。

BaseParser 定义了一些属性和方法。

  • 属性

    • options

      babel-parser 对外暴露方法的配置项,参数类型定义在 babel/packages/babel-parser/src/options.js

      • sourceType: SourceType

      • sourceFilename: string

      • startLine: number

      • allowAwaitOutsideFunction: boolean

      • allowReturnOutsideFunction: boolean

      • allowImportExportEverywhere: boolean

      • allowSuperOutsideMethod: boolean

      • plugins: PluginList

      • strictMode: boolean

      • ranges: boolean

      • tokens: boolean

      • createParenthesizedExpressions: boolean

      • errorRecovery: boolean

    • inModule

    • scope

    • classScope

    • expressionScope

    • prodParam

    • plugins

    • filename

    • exportedIdentifiers

    • sawUnambiguousESM

    • ambiguousScriptDifferentAst

    • state

    • input

    • length

      用于获取 input 的长度,因为 input 会比较长,考虑到性能优化,要获取其长度,Babel 会尽量避免直接获取其长度,而是单独维护一个 length。

  • 方法

    • hasPlugin()

    • getPluginOption()

# CommentsParser

提供针对注释的各种处理工具。

# ParserErrors

提供了对错误的统一处理工具,并集成了词法分析、语法分析过程中的所有可能出现的错误情况。

  • ParserErrors 对外提供了一些工具对象

    • ErrorCodes

      export const ErrorCodes = Object.freeze({
          SyntaxError: "BABEL_PARSER_SYNTAX_ERROR",
          SourceTypeModuleError: "BABEL_PARSER_SOURCETYPE_MODULE_REQUIRED",
      });
      
      1
      2
      3
      4
    • ParsingError

      type ErrorContext = {
          pos: number,
          loc: Position,
          missingPlugin?: Array<string>,
          code?: string,
          reasonCode?: String,
      };
      export type ParsingError = SyntaxError & ErrorContext;
      
      1
      2
      3
      4
      5
      6
      7
      8
    • ErrorTemplate

      export type ErrorTemplate = {
          code: ErrorCode,
          template: string,
          reasonCode: string,
      };
      
      1
      2
      3
      4
      5
    • makeErrorTemplates()

      export function makeErrorTemplates(
          messages: {
              [key: string]: string,
          },
          code: ErrorCode,
      ): ErrorTemplates 
      
      1
      2
      3
      4
      5
      6
    • Errors / SourceTypeModuleErrors

      export {
          ErrorMessages as Errors,
          SourceTypeModuleErrorMessages as SourceTypeModuleErrors,
      } from "./error-message";
      
      // error-message
      export const SourceTypeModuleErrorMessages = makeErrorTemplates(
          {
              ImportMetaOutsideModule: `import.meta may appear only with 'sourceType: "module"'`,
              ImportOutsideModule: `'import' and 'export' may appear only with 'sourceType: "module"'`,
          },
          /* code */ ErrorCodes.SourceTypeModuleError,
      );
      
      export const ErrorMessages = makeErrorTemplates(
      {
          AccessorIsGenerator: "A %0ter cannot be a generator.",
          ArgumentsInClass: "'arguments' is only allowed in functions and class methods."
          // ...
      }, SyntaxError);
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
  • ParserErrors 集成了词法分析、语法分析过程的所有错误描述

    相较于 ParserError 对外暴露了哪些对象,更容易帮助我们理解转译过程的,是转译过程中会关注哪些错误。

    ParserError 集成了如下错误:

    TODO

# 位置信息

文件 babel/packages/babel-parser/src/util/location.js 定义了一系列和代码位置相关的对象和方法。

  • Position

    描述了目标字符在源码的行列信息(line/column),它是一个基础的位置类。

  • SourceLocation 类(依赖 Position 类)

    描述了目标字符串在源码的开始位置(start: Position)和结束位置(end: Position)等信息。

  • getLineInfo(input: string, offset: number): Position

    该方法用于临时获取目标字符串的位置信息。比如出于性能考虑关闭语法分析时检测代码的位置信息后,仍可以通过该方法临时获取目标字符在源码的位置信息。

# 工具方法

  • 节点处理类(node

    • startNode()
    • startNodeAt()
    • startNodeAtNode()
    • finishNode()
    • finishNodeAt()
    • resetStartLocation()
    • resetEndLocation()
    • resetStartLocationFromNode()
  • 校验类(util

    • isRelational(op: "<" | ">"): boolean

      <> 节点。

    • isContextual(name: string): boolean

    • isUnparsedContextual

    • isLookaheadContextual

    • expectRelational

    • expectContextual

    • canInsertSemicolon

    • hasPrecedingLineBreak

    • hasFollowingLineBreak

    • isLineTerminator

    • semicolon

    • expect

    • assertNoSpace

    • unexpected

    • expectPlugin

    • expectOnePlugin

    • tryParse

    • checkExpressionErrors

    • isLiteralPropertyName

    • isPrivateName

    • hasPropertyAsPrivateName

    • isOptionalChain

    • isObjectProperty

    • isObjectMethod

  • 操作类(util

    • addExtra(node: Node, key: string, val: any)

      nodeextra 添加属性: key/val

    • eatContextual

    • getPrivateNameSV

    • initializeScopes

    • enterInitialScopes

# 保留字识别

文件 babel/packages/babel-parser/src/util/identifier.js 定义了关于标识符的相关对象和方法。

该文件描述了以下单词会被认为是 JavaScript 保留字:

  • 无条件保留字

    break / case / catch / continue / debugger / default / do / else / finally / for / function / if / return / switch / throw / try / var / const / while / with / new / this / super / class / extends / export / import / null / true / false / in / instanceof / typeof / void / delete
    
    1
  • 严格模式下的新增保留字

    implements / interface / let / package / private / protected / public / static / yield
    
    1
  • strictBind 下的新增保留字

    eval / arguments
    
    1
  • 可能是保留字

    enum / await
    
    1

工具包 @babel/helper-validator-identifier 也定义了一系列工具方法,在程序中判断保留字信息。

# 作用域处理(scope

  • 什么是作用域

    作用域是当前的执行上下文,也就是值和表达式在其中"可见"或可被访问到的上下文。如果一个值或表达式不在"当前的作用域中",那么它就是不可用的。

    我们知道,JavaScript 中存在三种作用域: 全局作用域、函数作用域、块级作用域。并且,JavaScript 是静态作用域,也就是在静态代码解析的时候,作用域已经确认了,因此,Babel 这样的工具也可以针对作用域做一些转译工作。

    Babel 针对这几种作用域做了更细致的划分,在文件 babel/packages/babel-parser/src/util/scopeflags.js 定义了一系列的作用域:

    export type ScopeFlags =
    | typeof SCOPE_OTHER
    | typeof SCOPE_PROGRAM
    | typeof SCOPE_FUNCTION
    | typeof SCOPE_VAR
    | typeof SCOPE_ARROW
    | typeof SCOPE_SIMPLE_CATCH
    | typeof SCOPE_SUPER
    | typeof SCOPE_DIRECT_SUPER
    | typeof SCOPE_CLASS
    | typeof SCOPE_STATIC_BLOCK
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    笔者整理了 Babel 定义的作用域和 JavaScript 作用域之间的关系:

    • 全局作用域: SCOPE_PROGRAM

    • 函数作用域: SCOPE_FUNCTION

      箭头函数和普通函数。

    • 块级作用域

      • SCOPE_VAR

      • SCOPE_ARROW

        箭头函数

      • SCOPE_SIMPLE_CATCH

        try {
        
        } catch(err) {
            // 这个区域
        }
        
        1
        2
        3
        4
        5
      • SCOPE_SUPER

      • SCOPE_DIRECT_SUPER

      • SCOPE_CLASS

      • SCOPE_STATIC_BLOCK

      • SCOPE_OTHER

        • ForStatement
        • SwitchStatement
        • TryStatement
        • Block
  • 作用域栈

    文件 babel/packages/babel-parser/src/util/scope.js 描述了 Babel 对作用域的各种处理。

    其中,该文件定义的 Scope 类维护了一个"作用域栈"(scopeStack),并提供了入栈方法 enter() 和出栈方法 exit(),如图:

    TODO

    当 Babel 解析表达式(expression)、语句(statement)等节点时,会频繁地通过递归方式进行节点解析的工作,TODO

    每进入一个新的节点解析时,一个新的 scope 作用域对象会入栈,在该新节点解析的过程中的上下文均以入栈的这个作用域对象为准,待新节点解析完毕,之前入栈的作用域对象就出栈。

  • 3 种作用域

    • this.scope
    • this.classScope
    • this.expressionScope
  • scope.js

    文件 babel/packages/babel-parser/src/util/scope.js 描述了 Babel 对作用域的各种工具属性和方法。

    不同的应用场景有不同的工具对应:

    • inFunction

      当前作用域处于某个函数体中。

    • allowSuper

      当前作用域允许使用 super() 方法。

    • allowDirectSuper

      当前作用有允许直接使用 super() 方法。

    • inClass

      当前作用域处于 class 声明中。

    • inClassAndNotInNonArrowFunction

      当前作用域位于 class 且是非箭头函数体中。

    • inStaticBlock

      当前作用域位于 statiBlock 中。

    • inNonArrowFunction

      当前作用域位于非箭头函数体中。

    • treatFunctionsAsVar

      当前作用域位于中,函数是否可以被认为是 var 定义的函数对象。

      从顶层作用域的角度看,函数声明可以被视为 var 定义的函数对象。

    • createScope()

      创建一个 scope 实例。

    • enter()

      创建一个 scope 实例,并推入到 scope 栈。

    • exit()

      scope 栈推出。

    • treatFunctionsAsVarInScope()

      treatFunctionsAsVar()

    • declareName()

      TODO

    • maybeExportDefined()

      TODO

    • checkRedeclarationInScope()

      检查是否重复定义,如果有,会抛出错误。

    • isRedeclaredInScope()

      判断是否重复定义,如果有,不会抛出错误。

    • checkLocalExport()

      TODO

    • currentScope()

      获取 scope 栈顶项。

    • currentVarScopeFlags()

      TODO

    • currentThisScopeFlags()

      TODO

# 语句处理(statement

文件 babel/packages/babel-parser/src/statement.js 描述了 Babel 对语句(statement)的各种处理。

  • 生成语句节点

    首先看一下 statement 部分,解析了哪些 AST 节点:

    parseProgram --> parseBlockBody --> parseBlockOrModuleBlockBody --> while body afterBlockParse

    这里列举了 statement.js 可以解析的节点列表:

    分类 节点名称 定义 描述
    File
    Program interface Program <: Node {
      type: "Program";
      interpreter: InterpreterDirective | null;
      sourceType: "script" | "module";
      body: [ Statement | ModuleDeclaration ];
      directives: [ Directive ];
    }
    根节点,AST顶部节点

    body 是一个数组,包括语句和模块声明

    如果是 ES6 模块,必须指定 sourceTypemodule,否则指定为 script
    InterpreterDirective interface InterpreterDirective <: StringLiteral {
      type: "InterpreterDirective";
    }
    #!字面量
    MemberExpression interface MemberExpression <: Expression, Pattern {
      type: "MemberExpression";
      object: Expression | Super;
      property: Expression | PrivateName;
      computed: boolean;
    }
    成员表达式节点

    表示引用对象成员,如 obj.name 或 obj["name"]

    1. object: 表示对象表达式节点,如上例中的 obj
    2. property: 表示属性名称,如上例中的 name
    3. computed: 若为 true,表示 obj["name"] 形式,property 需要为 Expression 节点;若为 false,表示 obj.name 形式,property 需要为 Identifier 节点
    Decorator interface Decorator <: Node {
      type: "Decorator";
      expression: Expression;
    }
    装饰器
    CallExpression interface CallExpression <: Expression {
      type: "CallExpression";
      callee: Expression | Super | Import;
      arguments: [ Expression | SpreadElement ];
    }
    函数调用表达式

    如 run(1, 2) 这样的表达式。

    1. callee: 表示函数,是表达式节点
    2. arguments: 表示参数,为 Expression 或 SpreadElement
    BreakStatement interface BreakStatement <: Statement {
      type: "BreakStatement";
      label: Identifier | null;
    }
    Break 语句break [label?];
    ContinueStatement interface ContinueStatement <: Statement {
      type: "ContinueStatement";
      label: Identifier | null;
    }
    Continue 语句

    continue [label?];
    DebuggerStatement interface DebuggerStatement <: Statement {
      type: "DebuggerStatement";
    }
    调试语句

    debugger;
    DoWhileStatement interface DoWhileStatement <: Statement {
      type: "DoWhileStatement";
      body: Statement;
      test: Expression;
    }
    DoWhile语句

    do {[test]} while ([body])
    VariableDeclaration interface VariableDeclaration <: Declaration {
      type: "VariableDeclaration";
      declarations: [ VariableDeclarator ];
      kind: "var" | "let" | "const";
    }
    变量声明

    let a = 1;

    1. kind: 表示是什么类型的声明
    2. declarations: 表示声明的多个描述,比如 let a = 1, b = 2;

    例如:

    1. const a = 1
    2. let a = 1, b = 2
    IfStatement interface IfStatement <: Statement {
        type: "IfStatement";
        test: Expression;
        consequent: Statement;
        alternate: Statement | null;
    }
    if语句

    if ([test]) {[consequent]} else {[alternate]}

    1. test表示if (...)括号中的表达式
    2. consequent表示条件为 true 时的执行语句,通常是一个块语句
    3. alternate 用来表示 else 后跟随的语句,通常也会是块语句,也可以又是一个 if 语句,例如 else if (...),也可以是 null
    ReturnStatement interface ReturnStatement <: Statement {
      type: "ReturnStatement";
      argument: Expression | null;
    }
    返回语句

    return [argument]

    argument 为表达式或 null
    SwitchCase interface SwitchCase <: Node {
      type: "SwitchCase";
      test: Expression | null;
      consequent: [ Statement ];
    }
    SwitchCase节点

    case: [test]: [consequent]
    SwitchStatement interface SwitchStatement <: Statement {
      type: "SwitchStatement";
      discriminant: Expression;
      cases: [ SwitchCase ];
    }
    switch语句

    switch ([discriminant]) {[cases]}

    1. discriminant: switch 后紧跟的表达式
    2. cases: switchCase 节点数组
    ThrowStatement interface ThrowStatement <: Statement {
      type: "ThrowStatement";
      argument: Expression;
    }
    Throw语句

    throw [argument]
    CatchClause interface CatchClause <: Node {
      type: "CatchClause";
      param?: Pattern;
      body: BlockStatement;
    }
    Catch从句节点

    ES2019 允许 catch 从句为空:try {} catch {}
    TryStatement interface TryStatement <: Statement {
      type: "TryStatement";
      block: BlockStatement;
      handler: CatchClause | null;
      finalizer: BlockStatement | null;
    }
    Try语句

    try {[block]} catch {[handler]} finally {[finalizer]}
    WhileStatement interface WhileStatement <: Statement {
      type: "WhileStatement";
      test: Expression;
      body: Statement;
    }
    While语句

    while ([test] {[body]}
    WithStatement interface WithStatement <: Statement {
      type: "WithStatement";
      object: Expression;
      body: Statement;
    }
    with 语句

    with ([object]) {[body]}
    EmptyStatement interface EmptyStatement <: Statement {
      type: "EmptyStatement";
    }
    空语句

    ;
    LabeledStatement interface LabeledStatement <: Statement {
      type: "LabeledStatement";
      label: Identifier;
      body: Statement;
    }
    标签语句

    loop: … break loop;
    ExpressionStatement interface ExpressionStatement <: Statement {
      type: "ExpressionStatement";
      expression: Expression;
    }
    表达式语句

    如 1 + 1
    BlockStatement interface BlockStatement <: Statement {
      type: "BlockStatement";
      body: [ Statement ];
      directives: [ Directive ];
    }
    块语句

    {[body]}

    例如:if (...) { // 块语句部分 }

    1. body: 内容节点列表
    2. directives: ???
    ForStatement interface ForStatement <: Statement {
      type: "ForStatement";
      init: VariableDeclaration | Expression | null;
      test: Expression | null;
      update: Expression | null;
      body: Statement;
    }
    for 语句

    for ([init];[test];[update]) {[body]}

    1. init/test/update: 分别表示 for 语句括号中的三部分,分别为初始化值、循环判断条件、变量更新操作;三个均可以为 null,即 for(;;){...}
    2. body: 循环执行的语句
    ForInStatement interface ForInStatement <: Statement {
      type: "ForInStatement";
      left: VariableDeclaration |  Expression;
      right: Expression;
      body: Statement;
    }
    ForIn 语句

    for ([left] in [right]) {[body]}
    ForOfStatement interface ForOfStatement <: ForInStatement {
      type: "ForOfStatement";
      await: boolean;
    }
    ForOf 语句

    for (let [left] of [right])

    await 表示是否为异步迭代器 for-await-of: for await (const x of obj) {}
    VariableDeclarator interface VariableDeclarator <: Node {
        type: "VariableDeclarator";
        id: Pattern;
        init: Expression | null;
    }
    变量声明的描述

    1. id 表示变量名称节点
    2. init 表示初始值的表达式,可以为 null

    例如:

    a = 1
    ClassDeclaration interface ClassDeclaration <: Class, Declaration {
      type: "ClassDeclaration";
      id: Identifier;
    }
    类声明

    class [name] [extends] {[body]}
    ClassExpression interface ClassExpression <: Class, Expression {
      type: "ClassExpression";
    }
    类表达式

    const MyClass = class [className] [extends otherClassName] { ... }
    ClassBody interface ClassBody <: Node {
      type: "ClassBody";
      body: [ 
        ClassMethod | 
        ClassPrivateMethod | 
        ClassProperty | 
        ClassPrivateProperty | 
        StaticBlock 
      ];
    }
    ClassPrivateProperty interface ClassPrivateProperty <: Node {
      type: "ClassPrivateProperty";
      key: PrivateName;
      value: Expression;
      static: boolean;
    }
    ClassProperty interface ClassProperty <: Node {
      type: "ClassProperty";
      key: Expression;
      value: Expression;
      static: boolean;
      computed: boolean;
    }
    ExportAllDeclaration interface ExportAllDeclaration <: ModuleDeclaration {
      type: "ExportAllDeclaration";
      source: StringLiteral;
      assertions?: [ ImportAttribute ];
    }
    全部导出声明

    export * from './a'
    ExportNamedDeclaration interface ExportNamedDeclaration <: ModuleDeclaration {
      type: "ExportNamedDeclaration";
      declaration: Declaration | null;
      specifiers: [ ExportSpecifier ];
      source: StringLiteral | null;
      assertions?: [ ImportAttribute ];
    }
    导出具名声明

    如:export { x, y as z }export var a = 1export { x } from 'a'
    ExportDefaultDeclaration interface ExportDefaultDeclaration <: 
    ModuleDeclaration {
      type: "ExportDefaultDeclaration";
      declaration: 
        OptFunctionDeclaration | 
        OptClassDeclaration | 
        Expression;
    }
    默认导出声明

    export default a
    ExportDefaultSpecifier
    ExportNamespaceSpecifier
    ExportSpecifier interface ExportSpecifier <: ModuleSpecifier {
      type: "ExportSpecifier";
      exported: Identifier | StringLiteral;
      local?: Identifier | StringLiteral;
    }
    导出说明符

    export { a }export { a as b }
    ImportDeclaration interface ImportDeclaration <: ModuleDeclaration {
      type: "ImportDeclaration";
      importKind: null | "type" | "typeof" | "value";
      specifiers: [ 
        ImportSpecifier | 
        ImportDefaultSpecifier | 
        ImportNamespaceSpecifier 
      ];
      source: StringLiteral;
      assertions?: [ ImportAttribute ];
    }
    import 声明

    如 import a from "./a"
    ImportDefaultSpecifier interface ImportDefaultSpecifier <: ModuleSpecifier {
      type: "ImportDefaultSpecifier";
    }
    默认导入说明符

    import a from "./a"
    ImportNamespaceSpecifier interface ImportNamespaceSpecifier <: ModuleSpecifier {
      type: "ImportNamespaceSpecifier";
    }
    命名空间导入说明符

    import * as a from "./a"
    ImportAttribute interface ImportAttribute <: Node {
      type: "ImportAttribute";
      key: Identifier;
      value: StringLiteral;
    }
    ImportSpecifier interface ImportSpecifier <: ModuleSpecifier {
      type: "ImportSpecifier";
      imported: Identifier | StringLiteral;
    }
    导入说明符

    import { a as b } from "./a"
  • 校验AST节点

    • isLet
    • isLetKeyword
    • isValidDirective
    • isExportDefaultSpecifier
    • isThisParam
    • canHaveLeadingDecorator
    • assertModuleNodeAllowed
    • verifyBreakContinue
    • shouldParseExportDeclaration
    • checkExport
    • checkDeclaration
    • checkDuplicateExports

# 表达式处理(expression

文件 babel/packages/babel-parser/src/statement.js 描述了 Babel 对语句(statement)的各种处理。

  • 生成表达式节点

    分类 节点名称 定义 描述
    SequenceExpression interface SequenceExpression <: Expression {
      type: "SequenceExpression";
      expressions: [ Expression ];
    }
    Sequence 表达式

    1, 2, 3
    AssignmentExpression interface AssignmentExpression <: Expression {
      type: "AssignmentExpression";
      operator: AssignmentOperator;
      left: Pattern | Expression;
      right: Expression;
    }
    赋值表达式节点

    1. operator: 赋值运算符
    2. left/right: 赋值运算符左右的表达式
    3. left: ???
    ConditionalExpression interface ConditionalExpression <: Expression {
      type: "ConditionalExpression";
      test: Expression;
      alternate: Expression;
      consequent: Expression;
    }
    条件表达式

    就是三元运算表达式,即 a > b ? c : d
    LogicalExpression interface LogicalExpression <: Expression {
      type: "LogicalExpression";
      operator: LogicalOperator;
      left: Expression;
      right: Expression;
    }
    逻辑运算表达式

    a && b
    BinaryExpression interface BinaryExpression <: Expression {
      type: "BinaryExpression";
      operator: BinaryOperator;
      left: Expression | PrivateName;
      right: Expression;
    }
    二元运算表达式

    a > b

    1. left/right: 运算符左右的表达式
    2. operator: 二元运算符节点
    3. left: 还可以是 PrivateName 节点
    UnaryExpression interface UnaryExpression <: Expression {
      type: "UnaryExpression";
      operator: UnaryOperator;
      prefix: boolean;
      argument: Expression;
    }
    一元表达式

    typeof 1
    UpdateExpression interface UpdateExpression <: Expression {
      type: "UpdateExpression";
      operator: UpdateOperator;
      argument: Expression;
      prefix: boolean;
    }
    Update 表达式

    ++a

    1. operatorUpdateOperator 节点,即 ++/--
    2. argument: 参数
    3. prefix: 表示 operator 是否在前
    OptionalMemberExpression interface OptionalMemberExpression <: Expression {
      type: "OptionalMemberExpression";
      object: Expression;
      property: Expression | PrivateName;
      computed: boolean;
      optional: boolean;
    }
    可选链表达式

    a?.b?.c
    MemberExpression interface MemberExpression <: Expression, Pattern {
      type: "MemberExpression";
      object: Expression | Super;
      property: Expression | PrivateName;
      computed: boolean;
    }
    成员表达式节点

    表示引用对象成员,如 obj.name 或 obj["name"]

    1. object: 表示对象表达式节点,如上例中的 obj
    2. property: 表示属性名称,如上例中的 name
    3. computed: 若为 true,表示 obj["name"] 形式,property 需要为 Expression 节点;若为 false,表示 obj.name 形式,property 需要为 Identifier 节点
    BindExpression interface BindExpression <: Expression {
        type: "BindExpression";
        object: Expression | null;
        callee: Expression;
    }
    TaggedTemplateExpression interface TaggedTemplateExpression <: Expression {
      type: "TaggedTemplateExpression";
      tag: Expression;
      quasi: TemplateLiteral;
    }
    带标签的模板字符串表达式

    function tagFunc(...args) {
      return args;
    }
    const setting = 'dark mode';
    console.log(tagFunc`Setting ${ setting } is ${ value }!`);
    OptionalCallExpression interface OptionalCallExpression <: Expression {
      type: "OptionalCallExpression";
      callee: Expression;
      arguments: [ Expression | SpreadElement ];
      optional: boolean;
    }
    可选执行方法节点
    f?.()()中的?.()
    CallExpression interface CallExpression <: Expression {
      type: "CallExpression";
      callee: Expression | Super | Import;
      arguments: [ Expression | SpreadElement ];
    }
    函数调用表达式

    如 run(1, 2) 这样的表达式。

    1. callee: 表示函数,是表达式节点
    2. arguments: 表示参数,为 Expression 或 SpreadElement
    Import interface Import <: Node {
        type: "Import";
    }
    Import 表达式

    import * from './a'
    ThisExpression interface ThisExpression <: Expression {
      type: "ThisExpression";
    }
    This表达式

    this
    PipelinePrimaryTopicReference
    DoExpression interface DoExpression <: Expression {
      type: "DoExpression";
      body: BlockStatement;
    }
    do表达式

    do&nbsp;{&nbsp;...&nbsp;}
    Super interface Super <: Node {
        type: "Super";
    }
    Super 表达式

    super([arguments])
    PrivateName interface PrivateName <: Node {
      type: "PrivateName";
      id: Identifier;
    }
    表示私有的标识符
    MetaProperty interface MetaProperty <: Expression {
      type: "MetaProperty";
      meta: Identifier;
      property: Identifier;
    }
    元属性

    new.target
    StringLiteral interface StringLiteral <: Literal {
      type: "StringLiteral";
      value: string;
    }
    字符串字面量
    NumericLiteral interface NumericLiteral <: Literal {
      type: "NumericLiteral";
      value: number;
    }
    数字字面量
    BigIntLiteral interface BigIntLiteral <: Literal {
      type: "BigIntLiteral";
      value: string;
    }
    BigInt 字面量
    DecimalLiteral interface DecimalLiteral <: Literal {
      type: "DecimalLiteral";
      value: string;
    }
    RegExpLiteral interface RegExpLiteral <: Literal {
      type: "RegExpLiteral";
      pattern: string;
      flags: string;
    }
    正则字面量

    1. pattern: 正则表达式内容字符,如 /a/i 中的"a"
    2. flagsig等标志
    BooleanLiteral interface BooleanLiteral <: Literal {
      type: "BooleanLiteral";
      value: boolean;
    }
    布尔值字面量
    NullLiteral interface NullLiteral <: Literal {
      type: "NullLiteral";
    }
    null 字面量
    SequenceExpression interface SequenceExpression <: Expression {
      type: "SequenceExpression";
      expressions: [ Expression ];
    }
    Sequence 表达式

    1, 2, 3
    ParenthesizedExpression interface ParenthesizedExpression <: Expression {
        type "ParenthesizedExpression";
        expression: Expression;
    }
    括号表达式
    NewExpression interface NewExpression <: CallExpression {
      type: "NewExpression";
    }
    new 表达式

    new Date()
    TemplateElement interface TemplateElement <: Node {
      type: "TemplateElement";
      tail: boolean;
      value: {
        cooked: string | null;
        raw: string;
      };
    }
    模板元素
    TemplateLiteral interface TemplateLiteral <: Expression {
      type: "TemplateLiteral";
      quasis: [ TemplateElement ];
      expressions: [ Expression ];
    }
    模板字面量

    Hello ${name}
    ObjectExpression interface ObjectExpression <: Expression {
      type: "ObjectExpression";
      properties: [ 
        ObjectProperty | 
        ObjectMethod | 
        SpreadElement 
      ];
    }
    Object表达式

    "{ a: 1 }"

    properties 为数组,每个元素可以是: 
    1. ObjectProperty 节点
    2. ObjectMethod 节点
    3. SpreadElement 节点
    ObjectPattern interface AssignmentProperty <: ObjectProperty {
      value: Pattern;
    }

    interface ObjectPattern <: Pattern {
      type: "ObjectPattern";
      properties: [ AssignmentProperty | RestElement ];
    }
    RecordExpression interface RecordExpression <: Expression {
      type: "RecordExpression";
      properties: [ 
        ObjectProperty | 
        ObjectMethod | 
        SpreadElement 
      ];
    }
    Record表达式,如#{ a: 1, b: '2' }
    RestElement interface RestElement <: Pattern {
      type: "RestElement";
      argument: Pattern;
    }
    fun(…args) {}
    ObjectProperty interface ObjectProperty <: ObjectMember {
      type: "ObjectProperty";
      shorthand: boolean;
      value: Expression;
    }
    属性节点
    TupleExpression interface TupleExpression <: Expression {
      type: "TupleExpression";
      elements: [ Expression | SpreadElement | null ];
    }
    ArrayExpression interface ArrayExpression <: Expression {
      type: "ArrayExpression";
      elements: [ Expression | SpreadElement | null ];
    }
    数组表达式

    "[1, 2, 3]"

    elements 为数组,表示数组的多个元素,每个元素为表达式或 null
    ArrowFunctionExpression interface ArrowFunctionExpression <: 
    Function, Expression {
      type: "ArrowFunctionExpression";
      body: BlockStatement | Expression;
    }
    箭头函数表达式

    () => {[body]}
    ArgumentPlaceholder interface ArgumentPlaceholder <: Node {
        type: "ArgumentPlaceholder";
    }
    Identifier interface Identifier <: Expression, Pattern {
        type: "Identifier";
        name: string;
    }
    标识符

    即自定义名称,如变量名、属性名、函数名

    一个标识符可以是一个表达式(Expression),或者是解构(Pattern)
    AwaitExpression interface AwaitExpression <: Expression {
      type: "AwaitExpression";
      argument: Expression | null;
    }
    Await 表达式

    await [argument]
    YieldExpression interface YieldExpression <: Expression {
      type: "YieldExpression";
      argument: Expression | null;
      delegate: boolean;
    }
    Yield 表达式

    yield [argument]
    PipelineBareFunction
    PipelineTopicExpression
    ModuleExpression
  • 校验AST节点

    • checkProto()
    • checkGetterSetterParams()
    • checkParams()
    • checkReservedWord()
    • atPossibleAsyncArrow()
    • shouldParseAsyncArrow()
    • maybeAsyncOrAccessorProp()
    • isSimpleParamList()
    • isAwaitAllowed()
    • isAmbiguousAwait()
    • isSimpleReference()
    • withTopicPermittingContext()
  • 转换AST节点

    • toReferencedArguments()

# 左值处理(lval

文件 babel/packages/babel-parser/src/lvel.js 描述了 Babel 对左值( lval )的各种处理。

  • 生成的左值节点

    分类 节点名称 定义 描述
    SpreadElement interface SpreadElement <: Node {
      type: "SpreadElement";
      argument: Expression;
    }
    Spread 表达式

    [a, …b]
    RestElement interface RestElement <: Pattern {
      type: "RestElement";
      argument: Pattern;
    }
    fun(…args) {}
    ArrayPattern interface ArrayPattern <: Pattern {
      type: "ArrayPattern";
      elements: [ Pattern | null ];
    }
    [ name, age ] = [ 'Jack', 13 ]
    AssignmentPattern interface AssignmentPattern <: Pattern {
      type: "AssignmentPattern";
      left: Pattern;
      right: Expression;
    }
    fun(a = 10) {}
  • 校验AST节点

    • isAssignable()
    • checkLVal()
    • checkToRestConversion()
    • checkCommaAfterRest()
  • 转换AST节点

    • toAssignable()
    • toAssignableObjectExpressionProp()
    • toAssignableList()
    • toReferencedList()
    • toReferencedListDeep()

# 错误处理

Babel 除了能够正确的进行语法分析和语义分析之外,错误扫描也是必不可少的工作。如果难以直观地梳理出 Babel 在语法分析和语义分析的各种细节逻辑的话,通过分析源码中的错误扫描部分,也可以帮助我们理解其分析逻辑。

笔者梳理了 babel/packages/babel-parser/ 下出现过的报错,以文件为单位分别介绍各自定义的错误。

另外,所有错误的描述,定义为在 3 个对象上:

  • babel-parser/src/parser/error-message.jsErrorMessages,使用时重命名为 Errors
  • babel-parser/src/parser/error-message.jsSourceTypeModuleErrorMessages,使用时重命名为 SourceTypeModuleErrors
  • babel-parser/src/plugins/jsx/index.jsJsxErrors,使用时继续用 JsxErrors

以下是 Babel 在解析 AST 节点过程中会抛出的错误。

# 语句类错误(statement.js
  • export 的对象未定义

    Errors.ModuleExportUndefined

    Export '%0' is not defined.
    
    1

    案例:

    babel.transformSync('export { a }', {
        parserOpts: {
            allowUndeclaredExports: false
        }
    })
    
    1
    2
    3
    4
    5

    会抛出错误:

    SyntaxError: unknown: Export 'a' is not defined.
    
    > 1 | export { a }
        |          ^
    
    1
    2
    3
    4
  • 在严格模式下,函数必须声明在顶层作用域或者块级作用域下

    Errors.StrictFunction

    In strict mode code, functions can only be declared at top level or inside a block.
    
    1
    if (true) function run() {}
    
    1

    Babel 在解析该代码时,默认会转译为严格模式,也就会在转译时就抛出此类错误。

    如果调整转译配置:

    babel.transformSync(code, {
        parserOpts: {
            strictMode: false // striceMode 默认是 true
        }
    })
    
    1
    2
    3
    4
    5

    那么在转译时就不会抛错了。

  • 非严格模式下,函数必须声明在顶层作用域、块级作用域或者作为 if 语句的 body 区域

    Errors.SloppyFunction

    In non-strict mode code, functions can only be declared at top level, inside a block, or as the body of an if statement.
    
    1

    这些写法都是合法的:

    if(true) function run() {}
    
    {
        function run() {}
    }
    
    1
    2
    3
    4
    5

    另外,在 statement.js 中,也允许函数声明出现在标签语句中,即: label: function run() {}

    但这样的写法是不合法的:

    while(true) function run() {}
    
    1
  • TODO

    Errors.UnexpectedLexicalDeclaration

    Lexical declaration cannot appear in a single-statement context.
    
    1

    以下案例中:

    if (true) let a = 1
    
    1

    这个案例的转译报错并不是这个,而是 Unexpected reserved word 'let'

  • importexport 必须出现在顶层作用域。

    Errors.UnexpectedImportExport

    'import' and 'export' may only appear at the top level.
    
    1
    {
        export { x }
    }
    
    1
    2
    3

    在转译时就会抛出以下错误:

    SyntaxError: unknown: 'import' and 'export' may only appear at the top level. (2:4)
    
    1 | {
    > 2 |     export { x }
        |     ^
    3 | }
    
    1
    2
    3
    4
    5
    6
  • async 标记的函数必须定义在顶层作用域或块级作用域

    Errors.AsyncFunctionInSingleStatementContext

    Async functions can only be declared at the top level or inside a block.
    
    1

    这种写法的源码在转译时会触发该报错:

    if (true) async function f() { }
    
    1
  • 装饰器必须作用于类的声明中

    Errors.UnexpectedLeadingDecorator

    Leading decorators must be attached to a class declaration.
    
    1

    举例:

    @track greeting = 'World';
    
    1
    SyntaxError: unknown: Leading decorators must be attached to a class declaration. (1:7)
    
    > 1 | @track greeting = 'World';
        |        ^
    
    1
    2
    3
    4
  • 非法的 breakcontinue

    Errors.IllegalBreakContinue

    Unsyntactic %0.
    
    1

    表示非法的 breakcontinue。这两个词法单元只会作用于循环,如果没有写在循环里,转译时就会发现并且报错。

    if (true) { break; }
    
    1

    会报错 Unsyntactic break

    if (true) { continue; }
    
    1

    会报错 Unsyntactic continue

  • Errors.ForOfLet

    The left-hand side of a for-of loop may not start with 'let'.
    
    1

    TODO

  • Errors.ForOfAsync

    The left-hand side of a for-of loop may not be 'async'.
    
    1

    TODO

  • 非法的 return

    Errors.IllegalReturn

    'return' outside of function.
    
    1

    转译时,函数体外出现了 return 会抛出此错误。

    比如:

    return
    
    1

    但如果这样配置,就允许函数体外出现 return:

    babel.transformSync(code, {
        parserOpts: {
            allowReturnOutsideFunction: true
        }
    })
    
    1
    2
    3
    4
    5
  • switch 语句中是否有多余的 default

    Errors.MultipleDefaultsInSwitch

    Multiple default clauses.
    
    1
    switch(name) {
        case 'Jack':
            break;
        default:
            break;
        default:
            break;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
  • throw 不允许出现换行符

    Errors.NewlineAfterThrow

    Illegal newline after throw.
    
    1
    throw
    'error'
    
    1
    2
  • Errors.NoCatchOrFinally

    Missing catch or finally clause.
    
    1
    try {
        console.log('Hi')
    }
    
    1
    2
    3
  • Errors.StrictWith

    'with' in strict mode.
    
    1
    'use strict';
    with
    
    1
    2
  • Errors.LabelRedeclaration

    Label '%0' is already declared.
    
    1

    TODO

  • Errors.ForInOfLoopInitializer

    '%0' loop variable declaration may not have an initializer.
    
    1
    let iterable = [10, 20, 30];
    
    for (let value = 50 of iterable) {
        console.log(value);
    }
    
    1
    2
    3
    4
    5

    转译时会抛出错误:

    'for-of' loop variable declaration may not have an initializer. (3:5)
    
    1 | let iterable = [10, 20, 30];
    2 |
    > 3 | for (let value = 50 of iterable) {
        |      ^
    4 |   console.log(value);
    5 | }
    
    1
    2
    3
    4
    5
    6
    7
    8
  • Errors.InvalidLhs

    Invalid left-hand side in %0.
    
    1
    1 = 1
    
    1
    SyntaxError: unknown: Invalid left-hand side in assignment expression. (1:0)
    
    > 1 | 1 = 1
        | ^
    
    1
    2
    3
    4
  • Errors.DeclarationMissingInitializer

    '%0' require an initialization value.
    
    1
    const x;
    
    1
    SyntaxError: unknown: 'Const declarations' require an initialization value. (1:7)
    
    > 1 | const x;
        | 
    
    1
    2
    3
    4
  • Errors.GeneratorInSingleStatementContext

    Generators can only be declared at the top level or inside a block.
    
    1

    已知,在非严格模式下,这样的写法是合法的:

    if (true) function run() {}
    
    1

    但 Generator 函数例外,这样的写法是非法的:

    if (true) function* run() {}
    
    1
  • Errors.DecoratorSemicolon

    Decorators must not be followed by a semicolon.
    
    1
    class XXX {
        @on('foo');
        [Symbol()](e) {
            //
        }
    }
    
    1
    2
    3
    4
    5
    6
    // 这是伪代码
    babel.transformSync(code, {
        plugins: [
            'babel-plugin-proposal-decorators', { decoratorsBeforeExport: true }
        ]
    })
    
    1
    2
    3
    4
    5
    6
    SyntaxError: unknown: Decorators must not be followed by a semicolon. (2:15)
    
      1 | class Test {
    > 2 |     @on('foo');
        |                ^
      3 |     [Symbol()](e) {
      4 |         //
      5 |     }
    
    1
    2
    3
    4
    5
    6
    7
    8
  • Errors.TrailingDecorator

    Decorators must be attached to a class element.
    
    1
    @on name ='Jack';
    
    1
    SyntaxError: unknown: Leading decorators must be attached to a class declaration. (2:7)
    
    1 |
    > 2 | @track greeting ='World';
        | 
    
    1
    2
    3
    4
    5
  • Errors.ConstructorIsGenerator

    Constructor can't be a generator.
    
    1
    class Test {
        * constructor() {}
    }
    
    1
    2
    3
    SyntaxError: unknown: Constructor can't be a generator. (2:6)
    
    1 | class Test {
    > 2 |     * constructor() {}
        |       ^
    3 | }
    
    1
    2
    3
    4
    5
    6
  • Errors.DuplicateConstructor

    Duplicate constructor in the same class.
    
    1
    class Test {
        constructor() {}
        constructor() {}
    }
    
    1
    2
    3
    4
    SyntaxError: unknown: Duplicate constructor in the same class. (3:4)
    
      1 | class Test {
      2 |     constructor() {}
    > 3 |     constructor() {}
        |     ^
      4 | }
    
    1
    2
    3
    4
    5
    6
    7
  • Errors.OverrideOnConstructor

    'override' modifier cannot appear on a constructor declaration.
    
    1

    TODO

  • Errors.ConstructorIsAsync

    Constructor can't be an async function.
    
    1
    class Test {
        async constructor() {}
    }
    
    1
    2
    3
    SyntaxError: unknown: Constructor can't be an async function. (2:10)
    
      1 | class Test {
    > 2 |     async constructor() {}
        |           ^
      3 | }
    
    1
    2
    3
    4
    5
    6
  • Errors.ConstructorIsAccessor

    Class constructor may not be an accessor.
    
    1

    TODO

  • Errors.StaticPrototype

    Classes may not have static property named prototype.
    
    1
    class X {
        static prototype;
    }
    
    1
    2
    3
    SyntaxError: unknown: Classes may not have static property named prototype. (2:11)
    
      1 | class X {
    > 2 |     static prototype;
        |            ^
      3 | }
    
    1
    2
    3
    4
    5
    6
  • Errors.ConstructorClassPrivateField

    Classes may not have a private field named '#constructor'.
    
    1
    class Test {
        #constructor() {}
    }
    
    1
    2
    3
    SyntaxError: unknown: Classes may not have a private field named '#constructor'. (2:4)
    
      1 | class Test {
    > 2 |     #constructor() {}
        |     ^
      3 | }
    
    1
    2
    3
    4
    5
    6
  • Errors.DecoratorStaticBlock

    Decorators can't be used with a static block.
    
    1
    class X {
        constructor() {
    
        }
    
        @decorator()
        static {
    
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    SyntaxError: unknown: Decorators can't be used with a static block. (6:4)
    
      4 |     }
      5 |
    > 6 |     @decorator()
        |     ^
      7 |     static {
      8 |
      9 |     }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
  • Errors.ConstructorClassField

    Classes may not have a field named 'constructor'.
    
    1
    class X {
        constructor = function () {
    
        }
    }
    
    1
    2
    3
    4
    5
    SyntaxError: unknown: Classes may not have a field named 'constructor'. (2:4)
    
      1 | class X {
    > 2 |     constructor = function () {
        |     ^
      3 |
      4 |     }
      5 | }
    
    1
    2
    3
    4
    5
    6
    7
    8
  • Errors.DecoratorBeforeExport

    Decorators must be placed *before* the 'export' keyword. You can set the 'decoratorsBeforeExport' option to false to use the 'export @decorator class {}' syntax.
    
    1
    export @decorator class {}
    
    1
    > 1 | export @decorator class {}
        |        ^
      2 |
    
    1
    2
    3
  • Errors.UnsupportedDefaultExport

    Only expressions, functions or classes are allowed as the `default` export.
    
    1
    export default const x = 1
    
    1
    SyntaxError: unknown: Only expressions, functions or classes are allowed as the `default` export. (1:15)
    
    > 1 | export default const x = 1
        |                ^
      2 |
    
    1
    2
    3
    4
    5
  • Errors.ExportDefaultFromAsIdentifier

    'from' is not allowed as an identifier after 'export default'.
    
    1
    const from = 1
    export default from;
    
    1
    2
    SyntaxError: unknown: 'from' is not allowed as an identifier after 'export default'. (2:15)
    
      1 | const from = 1
    > 2 | export default from;
        |                ^
    
    1
    2
    3
    4
    5
  • Errors.ExportBindingIsString

    A string literal cannot be used as an exported binding without `from`.\n- Did you mean `export { '%0' as '%1' } from 'some-module'`?
    
    1

    TODO

  • Errors.UnsupportedDecoratorExport

    A decorated export must export a class declaration.
    
    1

    TODO

  • Errors.DuplicateDefaultExport

    Only one default export allowed per module.
    
    1
    export default x
    export default y
    
    1
    2
    SyntaxError: unknown: Only one default export allowed per module. (2:0)
    
      1 | export default x
    > 2 | export default y
        | 
    
    1
    2
    3
    4
    5
  • Errors.DuplicateExport

    `%0` has already been exported. Exported identifiers must be unique.
    
    1
    export { x }
    export { x }
    
    1
    2
      1 | export { x }
    > 2 | export { x }
        |          ^
    
    1
    2
    3
  • Errors.ModuleExportNameHasLoneSurrogate

    An export name cannot include a lone surrogate, found '\\u%0'.
    
    1

    TODO

  • Errors.ModuleAttributesWithDuplicateKeys

    Duplicate key "%0" is not allowed in module attributes.
    
    1

    TODO

  • Errors.ModuleAttributeDifferentFromType

    The only accepted module attribute is `type`.
    
    1

    TODO

  • Errors.DestructureNamedImport

    ES2015 named imports do not destructure. Use another statement for destructuring after the import.
    
    1

    TODO

  • Errors.ImportBindingIsString

    A string literal cannot be used as an imported binding.\n- Did you mean `import { "%0" as foo }`?
    
    1

    TODO

  • SourceTypeModuleErrors.ImportOutsideModule

    'import' and 'export' may appear only with 'sourceType: "module"'
    
    1
    export { x }
    
    1
    SyntaxError: unknown: 'import' and 'export' may appear only with 'sourceType: "module"' (1:0)
    Consider renaming the file to '.mjs', or setting sourceType:module or sourceType:unambiguous in your Babel config for this file.
    
    > 1 | export { x }
        | ^
    
    1
    2
    3
    4
    5
# 表达式类错误(expression.js
  • SourceTypeModuleErrors.ImportMetaOutsideModule

    import.meta may appear only with 'sourceType: "module"'
    
    1
    const code = 'import.meta'
    
     babel.transformSync('import.meta', {
         parserOpts: {
             sourceType: 'script'
         }
     })
    
    1
    2
    3
    4
    5
    6
    7
    SyntaxError: unknown: import.meta may appear only with 'sourceType: "module"' (1:0)
    Consider renaming the file to '.mjs', or setting sourceType:module or sourceType:unambiguous in your Babel config for this file.
    
    > 1 | import.meta
        | ^
    
    1
    2
    3
    4
    5
  • Errors.RecordNoProto

    '__proto__' is not allowed in Record expressions.
    
    1

    TODO

  • Errors.DuplicateProto

    Redefinition of __proto__ property.
    
    1

    TODO

  • Errors.UnexpectedAwaitAfterPipelineBody

    Unexpected "await" after pipeline body; await must have parentheses in minimal proposal.
    
    1

    TODO

  • Errors.MixingCoalesceWithLogical

    Nullish coalescing operator(??) requires parens when mixing with logical operators.
    
    1

    TODO

  • Errors.UnexpectedTokenUnaryExponentiation

    Illegal expression. Wrap left hand side or entire exponentiation in parentheses.
    
    1
  • Errors.StrictDelete

    Deleting local variable in strict mode.
    
    1
  • Errors.DeletePrivateField

    Deleting a private field is not allowed.
    
    1
  • Errors.SuperPrivateField

    Private fields can't be accessed on super.
    
    1
  • Errors.OptionalChainingNoTemplate

    https://github.com/tc39/proposal-optional-chaining/issues/54

    Tagged Template Literals are not allowed in optionalChain.
    
    1
  • Errors.ImportCallArity

    'import()' requires exactly %0.
    
    1
    import()
    
    1
    SyntaxError: unknown: `import()` requires exactly one argument. (1:0)
    
    > 1 | import()
        | ^
    
    1
    2
    3
    4
  • Errors.ImportCallSpreadArgument

    '...' is not allowed in 'import()'.
    
    1
    import(...x)
    
    1
    SyntaxError: unknown: `...` is not allowed in `import()`. (1:7)
    
    > 1 | import(...x)
        |        ^
      2 |
    
    1
    2
    3
    4
    5
  • Errors.ImportCallArgumentTrailingComma

    Trailing comma is disallowed inside import(...) arguments.
    
    1
    import('./x.js', )
    
    1
    SyntaxError: unknown: Trailing comma is disallowed inside import(...) arguments. (1:15)
    
    > 1 | import('./x.js', )
        |                ^
    
    1
    2
    3
    4
  • Errors.UnsupportedImport

    'import' can only be used in 'import()' or 'import.meta'.
    
    1
  • Errors.UnsupportedBind

    Binding should be performed on object property.
    
    1
  • Errors.PrivateInExpectedIn

    Private names are only allowed in property accesses (`obj.#%0`) or in `in` expressions (`#%0 in obj`).
    
    1
  • Errors.PrimaryTopicRequiresSmartPipeline

    Primary Topic Reference found but pipelineOperator not passed 'smart' for 'proposal' option.
    
    1
  • Errors.PrimaryTopicNotAllowed

    Topic reference was used in a lexical context without topic binding.
    
    1
  • Errors.LineTerminatorBeforeArrow

    No line break is allowed before '=>'.
    
    1
  • Errors.UnsupportedSuper

    'super' can only be used with function calls (i.e. super()) or in property accesses (i.e. super.prop or super[prop]).
    
    1

    示例1:

    super()
    
    1
    SyntaxError: unknown: `super()` is only valid inside a class constructor of a subclass. Maybe a typo in the method name ('constructor') or not extending another class? (1:0)
    
    > 1 | super()
        | ^
      2 |
    
    1
    2
    3
    4
    5

    示例2:

    class Run {
        constructor() {
            {
                super()
            }
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    SyntaxError: unknown: `super()` is only valid inside a class constructor of a subclass. Maybe a typo in the method name ('constructor') or not extending another class? (4:12)
    
      2 |     constructor() {
      3 |         {
    > 4 |             super()
        |             ^
      5 |         }
      6 |     }
      7 | }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
  • Errors.UnexpectedPrivateField

    Private names can only be used as the name of a class element 
    (i.e. "class C { #p = 42; #m() {} }") 
    or 
    a property of member expression (i.e. "this.#p").
    
    1
    2
    3
    4
    const obj = {
        #name: 'Jack'
    }
    
    1
    2
    3
      1 | const obj = {
    > 2 |     #name: 'Jack'
        |      ^
      3 | }
    
    1
    2
    3
    4
  • Errors.UnsupportedMetaProperty

    The only valid meta property for %0 is %0.%1.
    
    1
    import.name
    
    1
    SyntaxError: unknown: The only valid meta property for import is import.meta. (1:7)
    
    > 1 | import.name
        |        ^
    
    1
    2
    3
    4
  • Errors.UnexpectedNewTarget

    "new.target" can only be used in functions or class properties.
    
    1
    new.target
    
    1
    SyntaxError: unknown: `new.target` can only be used in functions or class properties. (1:0)
    
    > 1 | new.target
        | ^
    
    1
    2
    3
    4
  • Errors.ImportCallNotNewExpression

    Cannot use new with import(...).
    
    1
    new import(x)
    
    1
    SyntaxError: unknown: Cannot use new with import(...). (1:4)
    
    > 1 | new import(x)
        |     ^
    
    1
    2
    3
    4
  • Errors.OptionalChainingNoNew

    Constructors in/after an Optional Chain are not allowed.
    
    1
    const person = {
        name: class {
    
        }
    }
    
    const man = new person?.name()
    
    1
    2
    3
    4
    5
    6
    7
    SyntaxError: unknown: Constructors in/after an Optional Chain are not allowed. (7:28)
    
      5 | }
      6 |
    > 7 | const man = new person?.name()
        |  
    
    1
    2
    3
    4
    5
    6
  • Errors.InvalidEscapeSequenceTemplate

    Invalid escape sequence in template.
    
    1
  • Errors.InvalidRecordProperty

    Only properties and spread elements are allowed in record definitions.
    
    1
  • Errors.UnsupportedPropertyDecorator

    Decorators cannot be used to decorate object literal properties.
    
    1
  • Errors.AccessorIsGenerator

    A %0ter cannot be a generator.
    
    1
  • Errors.BadSetterRestParameter

    A 'set' accesor function argument must not be a rest parameter.
    
    1
  • Errors.IllegalLanguageModeDirective

    Illegal 'use strict' directive in function with non-simple parameter list.
    
    1
  • Errors.UnexpectedToken

    Unexpected token '%0'.
    
    1
  • Errors.UnexpectedArgumentPlaceholder

    Unexpected argument placeholder.
    
    1
  • Errors.YieldBindingIdentifier

    Can not use 'yield' as identifier inside a generator.
    
    1
    const run = function* () {
        const yield = 1;
    }
    
    1
    2
    3
      1 | const run = function* () {
    > 2 |     const yield = 1;
        |           ^
      3 | }
    
    1
    2
    3
    4
  • Errors.AwaitBindingIdentifier

    Can not use 'await' as identifier inside an async function.
    
    1
    const run = async () => {
        const await = 1;
    }
    
    1
    2
    3
      2 | const run = async () => {
    > 3 |     const await = 1;
        |           ^
      4 | }
    
    1
    2
    3
    4
  • Errors.AwaitBindingIdentifierInStaticBlock

    Can not use 'await' as identifier inside a static block.
    
    1
  • Errors.ArgumentsInClass

    'arguments' is only allowed in functions and class methods.
    
    1
  • Errors.UnexpectedKeyword

    Unexpected keyword '%0'.
    
    1
  • Errors.UnexpectedReservedWord

    Unexpected reserved word '%0'.
    
    1
  • Errors.ObsoleteAwaitStar

    'await*' has been removed from the async functions proposal. Use Promise.all() instead.
    
    1
  • Errors.PipelineHeadSequenceExpression

    Pipeline head should not be a comma-separated sequence expression.
    
    1
  • Errors.PipelineBodySequenceExpression

    Pipeline body may not be a comma-separated sequence expression.
    
    1
  • Errors.PipelineTopicUnused

    Pipeline is in topic style but does not use topic reference.
    
    1
# 左值类错误(lval.js
  • Errors.InvalidParenthesizedAssignment

    Invalid parenthesized assignment pattern.
    
    1
    const add = (a, b) = { return a + b; };
    
    1
    SyntaxError: unknown: Invalid parenthesized assignment pattern. (1:13)
    
    > 1 | const add = (a, b) = { return a + b; };
        |              ^
    
    1
    2
    3
    4
  • Errors.MissingEqInAssignment

    Only '=' operator can be used for specifying default value.
    
    1
  • Errors.UnsupportedParameterDecorator

    Decorators cannot be used to decorate parameters.
    
    1
    class MyClass {
        myFunction(@MyDecorator myArg){
    
        }
    }
    
    1
    2
    3
    4
    5
    SyntaxError: unknown: Decorators cannot be used to decorate parameters. (2:15)
    
      1 | class MyClass {
    > 2 |     myFunction(@MyDecorator myArg){
        |                ^
      3 |         
      4 |     }
      5 | }
    
    1
    2
    3
    4
    5
    6
    7
    8
  • Errors.ParamDupe

    Argument name clash.
    
    1
    function run(req, req) {
    
    }
    
    1
    2
    3
    SyntaxError: unknown: Argument name clash. (1:18)
    
    > 1 | function run(req, req) {
        |                   ^
      2 |     
      3 | }
      4 |
    
    1
    2
    3
    4
    5
    6
    7
  • Errors.LetInLexicalBinding

    'let' is not allowed to be used as a name in 'let' or 'const' declarations.
    
    1
    let let = 1
    
    1
    SyntaxError: unknown: 'let' is not allowed to be used as a name in 'let' or 'const' declarations. (1:4)
    
    > 1 | let let = 1
        |     ^
    
    1
    2
    3
    4
  • Errors.InvalidPropertyBindingPattern

    Binding member expression.
    
    1
  • Errors.InvalidLhs

    Invalid left-hand side in %0.
    
    1
    if( x == 1 || y = 2) {
    
    }
    
    1
    2
    3
    SyntaxError: unknown: Invalid left-hand side in assignment expression. (1:4)
    
    > 1 | if( x == 1 || y = 2) {
        |     ^
      2 |     
      3 | }
      4 |
    
    1
    2
    3
    4
    5
    6
    7
  • Errors.InvalidLhsBinding

    Binding invalid left-hand side in %0.
    
    1
    arr.sort((a - b) => {
        return a - b
    })
    
    1
    2
    3
    SyntaxError: unknown: Binding invalid left-hand side in function parameter list. (1:12)
    
    > 1 | data1.sort((a - b) => {
        |             ^
      2 |     return a.count - b.count
      3 | })
      4 |
    
    1
    2
    3
    4
    5
    6
    7
  • Errors.InvalidRestAssignmentPattern

    Invalid rest operator's argument.
    
    1
  • Errors.ElementAfterRest

    Rest element must be last element.
    
    1
    const run = ({ ...param1, param2 }) => {}
    
    1
    SyntaxError: unknown: Rest element must be last element. (1:15)
    
    > 1 | const run = ({ ...param1, param2 }) => {}
        |                ^
    
    1
    2
    3
    4
  • Errors.RestTrailingComma

    Unexpected trailing comma after rest element.
    
    1
    const [ a, b, ...c, ] = [1, 2, 3];
    
    1
    SyntaxError: unknown: Unexpected trailing comma after rest element. (1:18)
    
    > 1 | const [ a, b, ...c, ] = [1, 2, 3];
        |                   ^
    
    1
    2
    3
    4
# 作用域类错误
  • class-scope.js

    • Errors.InvalidPrivateFieldResolution

      Private name #%0 is not defined.
      
      1
    • Errors.PrivateNameRedeclaration

      Duplicate private name #%0.
      
      1
  • scope.js

    • Errors.VarRedeclaration

      Identifier '%0' has already been declared.
      
      1
      const x = 1;
      const x = 2;
      
      1
      2
      SyntaxError: unknown: Identifier 'x' has already been declared. (2:6)
      
      1 | const x = 1;
      > 2 | const x = 2
          |       ^
      
      1
      2
      3
      4
      5
# jsx 类错误
  • ErrorCodes.SyntaxError

    BABEL_PARSER_SYNTAX_ERROR
    
    1
  • Errors.UnterminatedString

    Unterminated string constant.
    
    1
    function getName() {
        return 'Jack"
    }
    
    1
    2
    3
    SyntaxError: unknown: Unterminated string constant. (2:11)
    
      1 | function getName() {
    > 2 |     return 'Jack"
        |            ^
      3 | }
      4 |
    
    1
    2
    3
    4
    5
    6
    7
  • JsxErrors.UnterminatedJsxContent

    Unterminated JSX contents.
    
    1
    function getComponent() {
        return (  
            <div>
        )
    }
    
    1
    2
    3
    4
    5
    SyntaxError: unknown: Unterminated JSX contents. (3:13)
    
      1 | function getComponent() {
      2 |     return (  
    > 3 |         <div>
        |              ^
      4 |     )
      5 | }
      6 |
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
  • JsxErrors.AttributeIsEmpty

    JSX attributes must only be assigned a non-empty expression.
    
    1
    function getComponent() {
        return (  
            <div title={}></div>
        )
    }
    
    1
    2
    3
    4
    5
    SyntaxError: unknown: JSX attributes must only be assigned a non-empty expression. (3:19)
    
      1 | function getComponent() {
      2 |     return (  
    > 3 |         <div title={}></div>
        |                    ^
      4 |     )
      5 | }
      6 |
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
  • JsxErrors.UnsupportedJsxValue

    JSX value should be either an expression or a quoted JSX text.
    
    1
    function getComponent() {
        return (  
            <div title=true></div>
        )
    }
    
    1
    2
    3
    4
    5
    SyntaxError: unknown: JSX value should be either an expression or a quoted JSX text. (3:19)
    
      1 | function getComponent() {
      2 |     return (  
    > 3 |         <div title=true></div>
        |                    ^
      4 |     )
      5 | }
      6 |
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
  • JsxErrors.UnexpectedSequenceExpression

    Sequence expressions cannot be directly nested inside JSX. Did you mean to wrap it in parentheses (...)?
    
    1
  • JsxErrors.MissingClosingTagFragment

    Expected corresponding JSX closing tag for <>.
    
    1
  • JsxErrors.UnwrappedAdjacentJSXElements

    Adjacent JSX elements must be wrapped in an enclosing tag. Did you want a JSX fragment <>...</>?
    
    1
    function getComponent() {
        return (  
            <Comp1 />
            <Comp2 />
        )
    }
    
    1
    2
    3
    4
    5
    6
    SyntaxError: unknown: Adjacent JSX elements must be wrapped in an enclosing tag. Did you want a JSX fragment <>...</>? (4:8)
    
      2 |     return (  
      3 |         <Comp1 />
    > 4 |         <Comp2 />
        |         ^
      5 |     )
      6 | }
      7 |
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

# 特定语法开关

Babel 并不会开启对所有语法特性的转译,这也是出于性能的考虑。

在源码中,和"语法开关"相关的代码主要是文件 babel/packages/babel-parser/src/parser/util.js 定义的方法:

  • expectPlugin(pluginName): 检测某个插件是否被配置,如果没有会抛出错误
  • getPluginOption(): 获取插件的配置项

以下特性默认不会开启,关于每个特性的详细介绍,可查看章节"Babel 内置插件"了解,下表中插件名称均以源码文件命名为准。

  • pipelineOperator

    语法插件: babel-plugin-syntax-pipeline-operator

    转换插件: babel-plugin-proposal-pipeline-operator

    开启对 pipelineOperator 语法的支持,也就是 |> 写法。

  • throwExpressions

    语法插件: babel-plugin-syntax-throw-expressions

    转换插件: babel-plugin-proposal-throw-expressions

    开启对 throwExpressions 的语法支持。

    举例:

    function save(filename = throw new TypeError("Argument required")) {}
    
    1
  • importAssertions

    语法插件: babel-plugin-syntax-import-assertions

    开启对 Import assertions 语法的支持。

    该语法主要是为了更清晰地描述模块的一些特性信息,比如类型。

    举个例子,引入 ./config.json 文件时,声明其内容类型为 json,有几种写法:

    • 静态 import

      import config from './config.json' assert { type: 'json' }
      
      1
    • 动态 import

      import('./config.json', { assert: { type: 'json' } })
      
      1
    • export

      export { default as config } from './config.json' assert { type: 'json' }
      
      1
  • privateIn

    语法插件: babel-plugin-syntax-private-property-in-object

    转换插件: babel-plugin-proposal-private-property-in-object

    class Foo {
        #bar = "bar";
    
        test(obj) {
            return #bar in obj;
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
  • doExpressions

    语法插件: babel-plugin-syntax-do-expressions

    转换插件: babel-plugin-proposal-do-expression

    开启对 do 表达式语法的支持。do 表示可以认为是三元表达式的变种,在较复杂的情况下加,用 do 表达式更简洁。

    let x = 100;
    let y = 20;
    
    let a = do {
        if(x > 10) {
            if(y > 20) {
                'big x, big y';
            } else {
                'big x, small y';
            }
        } else {
            if(y > 10) {
                'small x, big y';
            } else {
                'small x, small y';
            }
        }
    };
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
  • functionSent

    语法插件: babel-plugin-syntax-function-sent

    转换插件: babel-plugin-proposal-function-sent

    开启对 functionSent 语法的支持,即开启对 function.sent 的语法支持。

    function* generator() {
        console.log("Sent", function.sent);
        console.log("Yield", yield);
    }
    
    const iterator = generator();
    iterator.next(1); // Logs "Sent 1"
    iterator.next(2); // Logs "Yield 2"
    
    1
    2
    3
    4
    5
    6
    7
    8
  • recordAndTuple

    语法插件: babel-plugin-syntax-record-and-tuple

    开启对 recordAndTuple 语法的支持,对应的是 proposal-record-tuple 提案。

  • partialApplication

    语法插件: babel-plugin-syntax-partial-application

    转换插件: babel-plugin-proposal-partial-application

    f(x, ?)           // partial application from left
    f(?, x)           // partial application from right
    f(?, x, ?)        // partial application for any arg
    
    1
    2
    3
  • moduleBlocks

    语法插件: babel-plugin-syntax-module-blocks

    开启对 moduleBlocks 语法的支持。

    moduleBlocks 语法简介如下(案例来自tc39):

    let moduleBlock = module {
        export let y = 1;
    };
    let moduleExports = await import(moduleBlock);
    assert(moduleExports.y === 1);
    
    assert(await import(moduleBlock) === moduleExports);  // cached in the module map
    
    1
    2
    3
    4
    5
    6
    7

    这样,提供了非文件的方式引入模块。

    moduleBlocks 只能被 import() 引入,而非 import 表达式。

  • classStaticBlock

    语法插件: babel-plugin-syntax-class-static-block

    转换插件: babel-plugin-proposal-class-static-block

    转换class类中的静态block。

    下例来自Babel官网:

    下例的转换需要在该插件前引入插件 babel-plugin-proposal-class-properties,也就是支持对 class 属性的语法支持和代码转换。

    class C {
        static #x = 42;
        static y;
        static {
            try {
                this.y = doSomethingWith(this.#x);
            } catch {
                this.y = "unknown";
            }
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
  • classPrivateProperties

    语法插件: babel-plugin-syntax-class-properties

    转换插件: babel-plugin-proposal-class-properties

    开启对类的属性(classProperties)、私有属性(classPrivateProperties)、私有方法(classPrivateMethods)的语法支持。

    举例:

    class Man {
        // 属性
        name: 'Jack',
    
        // 私有属性
        private age: 18,
    
        // 私有方法
        private say() {
            console.log('Hi!');
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
  • classPrivateMethods

    语法插件: plugin-syntax-class-properties

    转换插件: babel-plugin-proposal-private-methods

    class Counter extends HTMLElement {
       #xValue = 0;
    
       get #x() {
           return this.#xValue;
       }
       set #x(value) {
           this.#xValue = value;
       }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
  • classProperties

    语法插件: babel-plugin-syntax-class-properties

    转换插件: babel-plugin-proposal-class-properties

    转换 class 中的原型和静态属性/方法。

    下例来自 Babel 官网。

    class Bork {
        // Property initializer syntax
        instanceProperty = "bork";
        boundFunction = () => {
            return this.instanceProperty;
        };
    
        // Static class properties
        static staticProperty = "babelIsCool";
        static staticFunction = function() {
            return Bork.staticProperty;
        };
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
  • exportDefaultFrom

    语法插件: babel-plugin-syntax-export-default-from

    转换插件: babel-plugin-proposal-export-default-from

    export v from "mod";
    
    1
  • moduleStringNames

    语法插件: babel-plugin-syntax-module-string-names

    开启对 moduleStringNames 语法的支持,也就是模块别名的支持。

    export { hi as 'hello' } from './hi.js';
    
    1
  • moduleAttributes

  • decimal

    语法插件: babel-plugin-syntax-demical

# 总结

TODO

Last update: October 7, 2022 19:03
Contributors: hoperyy