Mirror: The magical sticky regex-based parser generator 🧙

Share hoisted bindings in Babel transform output

+80 -5
+43
src/babel/__snapshots__/plugin.test.js.snap
··· 1 1 // Jest Snapshot v1, https://goo.gl/fbAQLP 2 2 3 + exports[`deduplicates hoisted expressions 1`] = ` 4 + "import { match, __private } from \\"reghex\\"; 5 + const re = /1/; 6 + 7 + var _re_expression = __private.pattern(re); 8 + 9 + const a = function (state) { 10 + var y1 = state.y, 11 + x1 = state.x; 12 + var node = []; 13 + var x; 14 + 15 + if (x = __private.exec(state, _re_expression)) { 16 + node.push(x); 17 + } else { 18 + state.y = y1; 19 + state.x = x1; 20 + return; 21 + } 22 + 23 + node.tag = 'a'; 24 + return node; 25 + }; 26 + 27 + const b = function (state) { 28 + var y1 = state.y, 29 + x1 = state.x; 30 + var node = []; 31 + var x; 32 + 33 + if (x = __private.exec(state, _re_expression)) { 34 + node.push(x); 35 + } else { 36 + state.y = y1; 37 + state.x = x1; 38 + return; 39 + } 40 + 41 + node.tag = 'b'; 42 + return node; 43 + };" 44 + `; 45 + 3 46 exports[`works together with @babel/plugin-transform-modules-commonjs 1`] = ` 4 47 "\\"use strict\\"; 5 48
+21
src/babel/plugin.test.js
··· 34 34 ).toMatchSnapshot(); 35 35 }); 36 36 37 + it('deduplicates hoisted expressions', () => { 38 + const code = ` 39 + import { match } from 'reghex/macro'; 40 + 41 + const re = /1/; 42 + 43 + const a = match('a')\` 44 + \${re} 45 + \`; 46 + 47 + const b = match('b')\` 48 + \${re} 49 + \`; 50 + `; 51 + 52 + expect( 53 + transform(code, { babelrc: false, presets: [], plugins: [reghexPlugin] }) 54 + .code 55 + ).toMatchSnapshot(); 56 + }); 57 + 37 58 it('works with local recursion', () => { 38 59 // NOTE: A different default name is allowed 39 60 const code = `
+16 -5
src/babel/transform.js
··· 10 10 let _matchId = t.identifier('match'); 11 11 let _privateId = t.identifier(_private); 12 12 13 + const _hoistedExpressions = new Map(); 14 + 13 15 const privateMethod = (name) => 14 16 t.memberExpression(t.identifier(_privateId.name), t.identifier(name)); 15 17 ··· 116 118 expression = expression.body.body[0].argument; 117 119 } 118 120 119 - if ( 121 + const isBindingExpression = 120 122 t.isIdentifier(expression) && 121 - path.scope.hasBinding(expression.name) 122 - ) { 123 + path.scope.hasBinding(expression.name); 124 + if (isBindingExpression) { 123 125 const binding = path.scope.getBinding(expression.name); 124 126 if (t.isVariableDeclarator(binding.path.node)) { 125 127 const matchPath = binding.path.get('init'); 126 - if (this.isMatch(matchPath)) return expression; 128 + if (this.isMatch(matchPath)) { 129 + return expression; 130 + } else if (_hoistedExpressions.has(expression.name)) { 131 + return t.identifier(_hoistedExpressions.get(expression.name)); 132 + } 127 133 } 128 134 } 129 135 130 136 const id = path.scope.generateUidIdentifier( 131 - `${matchName}_expression` 137 + isBindingExpression 138 + ? `${expression.name}_expression` 139 + : `${matchName}_expression` 132 140 ); 133 141 134 142 variableDeclarators.push( ··· 138 146 ) 139 147 ); 140 148 149 + if (t.isIdentifier(expression)) { 150 + _hoistedExpressions.set(expression.name, id.name); 151 + } 141 152 return id; 142 153 } 143 154 );