const $ = require('../index'); const code = ` `; test('replace vue', () => { const output = $(code, { parseOptions: { language: 'vue' } }) .find('') .replace( `<$_$ :key="num" $$$1>$$$2`, `<$_$ key="num:" $$$1>$$$2` ) .root() .find('') .replace(`var a = 1;`, `var aaaaaaa = 1;`) .root() .generate(); const res = output.match('key="num:"') && output.match('aaaaaaa'); expect(res).toBeTruthy(); }); const keyCodeDemo = ` `; // 全量的keyCode对照表,基于篇幅这里只列出3个 // https://developer.mozilla.org/zh-CN/docs/Web/API/KeyboardEvent/keyCode const keyCodeMap = { 46: 'delete', 32: 'space', 112: 'f1' }; test('replace vue', () => { const ast = $(keyCodeDemo, { parseOptions: { language: 'vue' } }); const scriptAst = ast.find(''); // 匹配取出自定义的keyCode,Node数组 const customKeyCodeList = scriptAst.find(`Vue.config.keyCodes = {$_$}`) .match[0]; //加上自定义keyCode构造汇总所有的keyCodeMap,待会替换template内容的时候需要使用 for (let i = 0; i < customKeyCodeList.length; i = i + 2) { Object.assign(keyCodeMap, { [customKeyCodeList[i].value]: keyCodeMap[customKeyCodeList[i + 1].value] }); //结果:{46: 'delete',32: 'space',112: 'f1', customSpace: 'space', customDelete: 'delete'} } scriptAst .find(`Vue.config.keyCodes = $_$`) .remove() const template = ast.find('') .find(['<$_$>', '<$_$ />']) .each((node) => { //如果节点含有属性,则遍历它的属性 if (Array.isArray(node.attr('content.attributes'))) { node.attr('content.attributes').forEach((attr) => { //使用上文构造出来的汇总keyCodeMap,替换匹配到的属性名 如@keyup.32 -> @keyup.space for (let keyItem in keyCodeMap) { if (attr.key.content.endsWith(`.${keyItem}`)) { attr.key.content = attr.key.content.replace( `.${keyItem}`, `.${keyCodeMap[keyItem]}` ); } } }); } }) .root() .generate(); const res = template.match(/keyup.space\="keys\('keycode 32 space'\)"/g).length == 2 && !template.match(`Vue.config.keyCodes`); expect(res).toBeTruthy(); }); const asyncDemo = ` ` test('replace ref', () => { // 先处理template,针对带有v-for且ref属性的标签,把ref属性名改为:ref,属性值改为函数调用getRefSetter() let ast = $(asyncDemo, { parseOptions: { language: 'vue' } }) let templateRes = ast .find('') .replace(`<$_$ v-for="$_$1" ref="$_$2" $$$1>$$$2`, `<$_$ v-for="$_$1" :ref="getRefSetter('$_$2')" $$$1>$$$2`) // 处理script,在method里加入getRefSetter函数定义 let scriptRes = ast .find('') .replace(`export default { $$$1, methods: { $$$2 } }`, ` export default { $$$1, methods: { $$$2, getRefSetter(refKey) { return (ref) => { !this.$arrRefs && (this.$arrRefs = {}); !this.$arrRefs[refKey] && (this.$arrRefs.arr = []); ref && this.$arrRefs[refKey].push(ref); }; } } }`) // 判断如果原本没有methods属性,就连method一起插入 if (!scriptRes.has(`methods: {}`)) { scriptRes.replace(`export default { $$$1 }`, `export default { methods: { getRefSetter(refKey) { return (ref) => { !this.$arrRefs && (this.$arrRefs = {}); !this.$arrRefs[refKey] && (this.$arrRefs.arr = []); ref && this.$arrRefs[refKey].push(ref); }; } }, $$$1 }`) } // 在判断原来有没有beforeUpdate if (scriptRes.has('beforeUpdate')) { scriptRes.find(`beforeUpdate() {}`) .append('body', $(`this.$arrRefs && (this.$arrRefs.arr = []);`, { isProgram: false }).node) } else { scriptRes.find(`export default {}`) .attr('declaration.properties').push($(`beforeUpdate() { this.$arrRefs && (this.$arrRefs.arr = []); }`, { isProgram: false }).node) } /** * open-delay changed to show-after * close-delay changed to hide-after * hide-after changed to auto-close **/ expect(scriptRes.generate()).toBeTruthy(); }); const popoverDemo = ` ` test('replace ref', () => { const res = $(popoverDemo, { parseOptions: { language: 'vue' }}) .find('') .replace(`$$$2`, `$$$2`) .replace(`$$$2`, `$$$2`) .replace(`$$$2`, `$$$2`) .root() .generate() expect(res.match(`show-after`)).toBeTruthy(); }); const asyncDemo1 = ` ` test('replace ref', () => { // 先处理template,针对带有v-for且ref属性的标签,把ref属性名改为:ref,属性值改为函数调用getRefSetter() function tesssst(c) { let ast = $(c, { parseOptions: { language: 'vue' } }); let templateRes = ast.find('') .replace(`<$_$ v-for="$_$1" ref="$_$2" $$$1>$$$2`, `<$_$ v-for="$_$1" :ref="getRefSetter('$_$2')" $$$1>$$$2`) .root() const tAttr = templateRes.attr('template.attrs'); delete tAttr.functional templateRes = templateRes .generate(); // gennerate会返回完整的sfc console.log(templateRes) } tesssst(asyncDemo1) tesssst(asyncDemo1) tesssst(asyncDemo1) }) test('import', () => { const res = $(` `, { parseOptions: { language: 'vue' }}) .find('') .generate() expect(res.match(`AsyncCompOption`)).toBeTruthy(); }) test('import', () => { const res = $(` `, { parseOptions: { language: 'vue' }}) .find('') .generate() expect(res.match(`Vue2-directive-highlight`)).toBeTruthy(); }) test('test no template', () => { const res = $(` `, { parseOptions: { language: 'vue' }}) .generate() expect(res.match(` `, { parseOptions: { language: 'vue' }}) .find('') .replace('var a = 1', 'var b = 1') .root() .find('') .replace('var a = 1', 'var c = 1') .root() .generate() expect(res.match(`var b = 1`) && res.match(`var c = 1`)).toBeTruthy(); }) test('test tag', () => { const res = $(` `, { parseOptions: { language: 'vue' }}) .find('') .has('<$_$ v-for="$_$1" ref="$_$2" $$$1>$$$2') expect(!res).toBeTruthy(); }) test('test tag attr buttons', () => { const res = $(` `, { parseOptions: { language: 'vue' }}) .find('') .find('<$_$1 $_$2="$_$3">') .each(tag => { tag.match[3].forEach(m => { if (!m.node.content.match('buttons')) return; // 不包含buttons就return m.node.content = // 给属性值赋新值 $(m.node.content).find('buttons') // 将属性值处理为js表达式 .each(item => { if (item.parent().node.type == 'MemberExpression' && item.parent().node.property.name == 'buttons') { // 如果buttons是被调用方,不改变 return; } // 改变buttons变量为abc item.attr('name', 'abc') }) .root() .generate() }) }) .generate() expect(res.match(/abc/g).length == 3).toBeTruthy(); }) test('test tag', () => { const ast = $(` `, { parseOptions: { language: 'vue', sourceType: 'module' } }) const script = ast.find('') script.replace('const a = $_$', 'const a = 2').generate() const res = ast.generate() expect(res.match('import.meta.env.VITE_APP_BASE_URL')).toBeTruthy() }) test('test vue parseoptions', () => { const ast = $(` `, { parseOptions: { language: 'vue', sourceType: 'module' } }) const script = ast .find('').root() .find('') const res = script.generate() expect(res.match('import.meta.env.VITE_APP_BASE_URL')).toBeTruthy() }) test('test vue parseoptions', () => { const ast = $( ``, { parseOptions: { language: 'vue' } } ) const res = ast.generate() expect(res.match('&&')).toBeTruthy() })