最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Babel的奇妙冒险 @babel/plugin-transform-member-expression-literals

    正文概述 掘金(NickNa)   2020-11-23   679

    前置知识

    • AST spec
    • Babel 插件开发手册

    源码解析

    源代码

    import { declare } from "@babel/helper-plugin-utils";
    import { types as t } from "@babel/core";
    
    export default declare(api => {
      api.assertVersion(7);
    
      return {
        name: "transform-member-expression-literals",
    
        visitor: {
          MemberExpression: {
            exit({ node }) {
              const prop = node.property;
              if (
                !node.computed &&
                t.isIdentifier(prop) &&
                !t.isValidES3Identifier(prop.name)
              ) {
                // foo.default -> foo["default"]
                node.property = t.stringLiteral(prop.name);
                node.computed = true;
              }
            },
          },
        },
      };
    });
    

    用途

    官方说明

    看 UT,对象的key如果是关键字,会从.形式转为[]形式

    // input.js
    test.catch;
    test.catch.foo;
    test["catch"];
    test["catch"].foo;
    
    // output.js
    test["catch"];
    test["catch"].foo;
    test["catch"];
    test["catch"].foo;
    

    解析

    通过Babel 插件开发手册,可知插件中的访问器处理 MemberExpression,AST spec中 MemberExpression 说明如下:

    interface MemberExpression <: Expression, Pattern {
      type: "MemberExpression";
      object: Expression | Super;
      property: Expression | PrivateName;
      computed: boolean;
    }
    

    A member expression. If computed is true, the node corresponds to a computed (a[b]) member expression and property is an Expression. If computed is false, the node corresponds to a static (a.b) member expression and property is an Identifier or a PrivateName.

    意思是:成员表达式。如果 computed 为 true,则节点对应于计算的(a[b])成员表达式,而 property 是表达式。如果 computed 为 false,则节点对应于静态(a.b)成员表达式,并且属性是标识符或 PrivateName。

    exit 方法的参数是一个响应式的 Path 对象, 包含 parent, node 等属性。path.node.property得到一个AST节点的属性值

    t.isIdentifier(prop) 相当于 node.property.type === 'identifier'

    isValidES3Identifier 主要验证下 key 合法性,部分代码如下:

    /**
     * Check if the input `name` is a valid identifier name
     * and isn't a reserved word.
     */
    export default function isValidIdentifier(
      name: string,
      reserved: boolean = true,
    ): boolean {
      if (typeof name !== "string") return false;
    
      if (reserved) {
        if (isKeyword(name) || isStrictReservedWord(name)) {
          return false;
        } else if (name === "await") {
          // invalid in module, valid in script; better be safe (see #4952)
          return false;
        }
      }
    
      return isIdentifierName(name);
    }
    
    const RESERVED_WORDS_ES3_ONLY: Set<string> = new Set([
      "abstract",
      "boolean",
      "byte",
      "char",
      "double",
      "enum",
      "final",
      "float",
      "goto",
      "implements",
      "int",
      "interface",
      "long",
      "native",
      "package",
      "private",
      "protected",
      "public",
      "short",
      "static",
      "synchronized",
      "throws",
      "transient",
      "volatile",
    ]);
    
    /**
     * Check if the input `name` is a valid identifier name according to the ES3 specification.
     *
     * Additional ES3 reserved words are
     */
    export default function isValidES3Identifier(name: string): boolean {
      return isValidIdentifier(name) && !RESERVED_WORDS_ES3_ONLY.has(name);
    }
    

    node.property = t.stringLiteral(prop.name); 作用是把 PrivateName 改为 Expression。

    interface PrivateName <: Node {
      type: "PrivateName";
      id: Identifier;
    }
    
    interface Literal <: Expression { }
    
    interface StringLiteral <: Literal {
      type: "StringLiteral";
      value: string;
    }
    

    AST树对比(去掉了解析的位置信息)

    // test.catch.foo;
    {
      "type": "Program",
      "body": [
        {
          "type": "ExpressionStatement",
          "expression": {
            "type": "MemberExpression",
            "object": {
              "type": "MemberExpression",
              "object": {
                "type": "Identifier",
                "name": "test"
              },
              "property": {
                "type": "Identifier",
                "name": "catch"
              },
              "computed": false,
              "optional": false
            },
            "property": {
              "type": "Identifier",
              "name": "foo"
            },
            "computed": false,
            "optional": false
          }
        }
      ],
      "sourceType": "module"
    }
    
    // test["catch"].foo
    {
      "type": "Program",
      "body": [
        {
          "type": "ExpressionStatement",
          "expression": {
            "type": "MemberExpression",
            "object": {
              "type": "MemberExpression",
              "object": {
                "type": "Identifier",
                "name": "test"
              },
              "property": {
                "type": "Literal",
                "start": 5,
                "end": 12,
                "value": "catch",
                "raw": "\"catch\""
              },
              "computed": true,
              "optional": false
            },
            "property": {
              "type": "Identifier",
              "name": "foo"
            },
            "computed": false,
            "optional": false
          }
        }
      ],
      "sourceType": "module"
    }
    

    变动部分

    // test.catch.foo;
    "property": {
      "type": "Identifier",
      "name": "catch"
    },
    "computed": false,
    
    // test["catch"].foo
    "property": {
      "type": "Literal",
      "value": "catch",
      "raw": "\"catch\""
    },
    "computed": true,
    

    起源地下载网 » Babel的奇妙冒险 @babel/plugin-transform-member-expression-literals

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元