如何通过正则提取文本中函数的函数体?

如下jsText是带有$.extend函数字样的文本,期望通过ignoreExtend方法去除$.extend函数字样,如何实现ignoreExtend?

let jsText = `
$.extend({}, x.x, {
    xx: {},
    xxx: $.extend({}, x.x, {
        xx: {}
    }),
    xxxx: {
        default: () => ({})
    },
    xxxx: $.extend({}, x.x, {
        xx: {}
    })
})`;

function ignoreExtend (jsText) {

    // TODO
    return jsText;
}

jsText = ignoreExtend(jsText);

console.log(jsText);
/*
{
    xx: {},
    xxx: {
        xx: {}
    },
    xxxx: {
        default: () => ({})
    },
    xxxx: {
        xx: {}
    }
}
*/

已做尝试,但是效果还差一点,括号没能都正确匹配上

function ignoreExtend (jsText) {
    return jsText.replace(/\$\.extend\(\{\},\s*.+,\s*(\{[\s\S]+?\})\)/g, '$1');
}
/*
{
    xx: {},
    xxx: $.extend({}, x.x, {
        xx: {}
    },
    xxxx: {
        default: () => ({})
    },
    xxxx: {
        xx: {}
    }
})
*/
回复
阅读 1.6k
2 个回答

我也补充一种常规思路

// 忽略 $.extend() 先给括号加匹配标记,再跟进标记匹配,最后删除标记
function ignoreExtend (jsText) {
    let bracketNumber = [];
    let bracketPairCount = 0;

    jsText = jsText.replace(/[\{\}]/g, (bracket) => {

        if (bracket == '{') {
            bracketPairCount++;
            bracketNumber.push(bracketPairCount);
            return `{${bracketPairCount}`;
        }

        return `}${bracketNumber.pop()}`;
    });

    while (bracketPairCount > 0) {
        let reg = new RegExp(`\\$\\.extend\\(\\{\\d+\\}\\d+,.+?,\\s*(\\{(${bracketPairCount})[\\s\\S]+?\\}(${bracketPairCount}))\\)`);

        jsText = jsText.replace(reg, '$1');
        bracketPairCount--;
    };

    jsText = jsText.replace(/([\{\}])\d+/g, '$1');

    return jsText;
}

就目前根据你提供的demo
通过你的解法 可以通过贪婪匹配 循环多次替换解决。

但如果extend的使用复杂且无规律 则无法使用正则完成此功能。
正则表达式等价于3型文法,只具有相当于有限自动机的表达能力,而匹配不确定数量的括号对至少需要一个栈,需要用相当于下推自动机的2型文法。
这种函数重构的操作建议使用AST转换后改造。

提供一种ast装换的方案。

const j = require('jscodeshift')
const source = `$.extend({}, x.x, {
    xx: {},
    xxx: $.extend({}, x.x, {
        xx: {}
    }),
    xxxx: {
        default: () => ({})
    },
    xxxx: $.extend({}, x.x, {
        xx: {}
    })
})`
const res = j(source)
    .find(j.MemberExpression).forEach(path => {
        let pathTemp = '';
        const length = j(path).find(j.Identifier).map(n => {
            pathTemp += `.${n.value.name}`
        })
        if (pathTemp === '.$.extend') {
            j(path).replaceWith();
            const [a, b, ...rest] = path.parentPath.value.arguments
            path.parentPath.value.arguments = rest

        }
    })
    .toSource();
console.log(res)

转换的结果会有括号,但是这种括号我觉得是合理的。因为()=>{}和()=>({})是两种语义,应该期望是返回{},不保留括号存在导致业务异常的风险。
image.png

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏