Mirror: The magical sticky regex-based parser generator 🧙

Share hoisted bindings in Babel transform output

Changed files
+80 -5
src
+43
src/babel/__snapshots__/plugin.test.js.snap
···
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`works together with @babel/plugin-transform-modules-commonjs 1`] = `
"\\"use strict\\";
···
// Jest Snapshot v1, https://goo.gl/fbAQLP
+
exports[`deduplicates hoisted expressions 1`] = `
+
"import { match, __private } from \\"reghex\\";
+
const re = /1/;
+
+
var _re_expression = __private.pattern(re);
+
+
const a = function (state) {
+
var y1 = state.y,
+
x1 = state.x;
+
var node = [];
+
var x;
+
+
if (x = __private.exec(state, _re_expression)) {
+
node.push(x);
+
} else {
+
state.y = y1;
+
state.x = x1;
+
return;
+
}
+
+
node.tag = 'a';
+
return node;
+
};
+
+
const b = function (state) {
+
var y1 = state.y,
+
x1 = state.x;
+
var node = [];
+
var x;
+
+
if (x = __private.exec(state, _re_expression)) {
+
node.push(x);
+
} else {
+
state.y = y1;
+
state.x = x1;
+
return;
+
}
+
+
node.tag = 'b';
+
return node;
+
};"
+
`;
+
exports[`works together with @babel/plugin-transform-modules-commonjs 1`] = `
"\\"use strict\\";
+21
src/babel/plugin.test.js
···
).toMatchSnapshot();
});
it('works with local recursion', () => {
// NOTE: A different default name is allowed
const code = `
···
).toMatchSnapshot();
});
+
it('deduplicates hoisted expressions', () => {
+
const code = `
+
import { match } from 'reghex/macro';
+
+
const re = /1/;
+
+
const a = match('a')\`
+
\${re}
+
\`;
+
+
const b = match('b')\`
+
\${re}
+
\`;
+
`;
+
+
expect(
+
transform(code, { babelrc: false, presets: [], plugins: [reghexPlugin] })
+
.code
+
).toMatchSnapshot();
+
});
+
it('works with local recursion', () => {
// NOTE: A different default name is allowed
const code = `
+16 -5
src/babel/transform.js
···
let _matchId = t.identifier('match');
let _privateId = t.identifier(_private);
const privateMethod = (name) =>
t.memberExpression(t.identifier(_privateId.name), t.identifier(name));
···
expression = expression.body.body[0].argument;
}
-
if (
t.isIdentifier(expression) &&
-
path.scope.hasBinding(expression.name)
-
) {
const binding = path.scope.getBinding(expression.name);
if (t.isVariableDeclarator(binding.path.node)) {
const matchPath = binding.path.get('init');
-
if (this.isMatch(matchPath)) return expression;
}
}
const id = path.scope.generateUidIdentifier(
-
`${matchName}_expression`
);
variableDeclarators.push(
···
)
);
return id;
}
);
···
let _matchId = t.identifier('match');
let _privateId = t.identifier(_private);
+
const _hoistedExpressions = new Map();
+
const privateMethod = (name) =>
t.memberExpression(t.identifier(_privateId.name), t.identifier(name));
···
expression = expression.body.body[0].argument;
}
+
const isBindingExpression =
t.isIdentifier(expression) &&
+
path.scope.hasBinding(expression.name);
+
if (isBindingExpression) {
const binding = path.scope.getBinding(expression.name);
if (t.isVariableDeclarator(binding.path.node)) {
const matchPath = binding.path.get('init');
+
if (this.isMatch(matchPath)) {
+
return expression;
+
} else if (_hoistedExpressions.has(expression.name)) {
+
return t.identifier(_hoistedExpressions.get(expression.name));
+
}
}
}
const id = path.scope.generateUidIdentifier(
+
isBindingExpression
+
? `${expression.name}_expression`
+
: `${matchName}_expression`
);
variableDeclarators.push(
···
)
);
+
if (t.isIdentifier(expression)) {
+
_hoistedExpressions.set(expression.name, id.name);
+
}
return id;
}
);