Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

写一个对象判空的babel插件 #27

Open
AnnVoV opened this issue Feb 23, 2019 · 0 comments
Open

写一个对象判空的babel插件 #27

AnnVoV opened this issue Feb 23, 2019 · 0 comments
Labels

Comments

@AnnVoV
Copy link
Owner

AnnVoV commented Feb 23, 2019

前言

上一篇写了通过写babel插件来入门ast, 在组内分享的时候,有同学说是不是可以写一个判空的插件呢?
a.b => a && a.b呢?我觉得是的,这完全可以做呀!然后就尝试着在分享后写了一个。

在线示例:

https://astexplorer.net/#/gist/a54996b88a3fd57b65dbd9f2a14b4c91/fd5dbe0ea9ca616226980861f9d81e0984568753

转换前:

var change = babelCheckEmpty(a.b.c.d);
var notChange = d && d.e;

转换后

var change = a && (a.b && a.b.c && a.b.c.d);
var notChange = d && d.e;

我只会去转换带有babelCheckEmpty 包裹的方法,这样避免影响其他方法的调用, 代码我贴在下面

module.exports = function (babel) {
    const {types: t} = babel;
    let isEnter = false;
    let objectName = '';
    let stack = [];

    function getTemplateAst(tpl, opts = {}) {
        let ast = babel.template(tpl, opts)({});
        if (Array.isArray(ast)) {
            return ast;
        } else {
            return [ast];
        }
    }

    function generateExpressionStack(stack = []) {
        if (stack.length === 0) {
            return [];
        }
        let result = [];
        stack.reduce((sum, current) => {
            const nowStr = sum.concat('.').concat(current);
            result.push(nowStr);
            return nowStr;
        });
        return result;
    }

    return {
        name: "ast-transform", // not required
        visitor: {
            CallExpression: {
                enter(path) {
                    if (path.node.callee.name !== 'babelCheckEmpty') {
                        return;
                    }
                    isEnter = true;
                },
                exit(path) {
                    if (path.node.callee.name !== 'babelCheckEmpty') {
                        return;
                    }
                    isEnter = false;
                    const resultArr = generateExpressionStack(stack);
                    const arr = getTemplateAst(resultArr.join('&&')) || [];
                    if (arr.length === 0 || stack.length === 0) {
                        return;
                    }
                    path.replaceWith(
                        t.logicalExpression('&&', t.identifier(stack[0]), arr[0].expression),
                    );
                    stack = [];
                },
            },
            MemberExpression(path) {
                if (!isEnter || !path.node) return;
                objectName = path.node.object.name;
                if (t.isIdentifier(path.node.property)) {
                    stack.unshift(path.node.property.name);
                }
                if (objectName) {
                    stack.unshift(objectName);
                }
            },
        },
    };
};

由于直接用逻辑运算自己去构造一个 a.b && a.b.c && a.b.c.d 的ast过于复杂,其实是我不知道到底要怎么去写,所以我就使用了一个上一篇内容没有讲到的一个方法,直接利用babel.template 的API 根据传入的内容直接生成ast

let ast = babel.template(tpl, opts)({});

放入工程里的截图


参考资料:
1.Babel 从入门到插件开发
https://juejin.im/entry/5912ba62a22b9d005819cff7
2.ast 入门(从写babel插件来深入了解ast
#26

@AnnVoV AnnVoV added the Babel label Feb 23, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant