Skip to content

Commit

Permalink
Handle eval in dynamic method key [fix]
Browse files Browse the repository at this point in the history
This doesn't actually work yet due to #102.
  • Loading branch information
overlookmotel committed Jul 27, 2021
1 parent 6dccaec commit d5aab0f
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 25 deletions.
54 changes: 29 additions & 25 deletions lib/babel/visitor.js
Original file line number Diff line number Diff line change
Expand Up @@ -547,42 +547,41 @@ function identifierVisitor(identifierPath, state) {

// Find enclosing function
let functionPath = state[PARENT_FUNCTION_PATH];
if (!functionPath) {
// If is top-level var declaration, record it.
if (functionPath) {
// If is method key (e.g. `{ [x]() {} }`), treat as used in parent function
if (functionPath.isMethod() && functionPath.node.computed) {
const methodKeyPath = functionPath.get('key');
if (
identifierPath === methodKeyPath
|| identifierPath.findParent(
path => path === methodKeyPath || path === functionPath
) === methodKeyPath
) {
functionPath = functionPath[PARENT_FUNCTION_PATH];
}
}
} else if (
parentPath.isVariableDeclarator() && identifierPath.key === 'id'
&& parentPath.parentPath.parentPath.isProgram()
) {
// Top-level var declaration - record it.
// This is a hack to work around Babel not seeing vars created by
// `@babel/plugin-transform-modules-commonjs` when binding is searched for later.
// `.scope.getBinding()` returns undefined. So catalog them here and use the list
// to identify their scope when they're encountered later.
// https://stackoverflow.com/questions/63508492/babels-path-unshiftcontainer-does-not-add-newly-created-vars-to-scope
// TODO Find a better way to do this.
if (
parentPath.isVariableDeclarator() && identifierPath.key === 'id'
&& parentPath.parentPath.parentPath.isProgram()
) {
state[TOP_LEVEL_VAR_NAMES].add(name);
return;
}
state[TOP_LEVEL_VAR_NAMES].add(name);
return;
}

// Handle top-level vars and `eval`
if (!functionPath) {
// Shim `eval` in top level scope
if (name === 'eval' && !identifierPath.scope.getBinding('eval')) processEval(identifierPath, state);

return;
}

// If is method key (e.g. `{ [x]() {} }`), treat as used in parent function
if (functionPath.isMethod() && functionPath.node.computed) {
const methodKeyPath = functionPath.get('key');
if (
identifierPath === methodKeyPath
|| identifierPath.findParent(
path => path === methodKeyPath || path === functionPath
) === methodKeyPath
) {
functionPath = functionPath[PARENT_FUNCTION_PATH];
if (!functionPath) return;
}
}

// Skip function's own name
if (parentPath === functionPath && identifierPath.key === 'id') return;

Expand Down Expand Up @@ -1032,7 +1031,12 @@ function processEval(evalPath, state) {
state[PARENT_FUNCTION_PATH] = evalPath;

const idNode = {name: undefined},
idPath = {node: idNode, parentPath: evalPath, scope: evalPath.scope};
idPath = {
node: idNode,
parentPath: evalPath,
scope: evalPath.scope,
findParent: fn => evalPath.findParent(fn)
};
function includeVar(varName) {
// If `eval()` is in strict mode, skip vars which are reserved words in strict mode
// as they cannot be referenced within the eval - would be a syntax error
Expand Down
39 changes: 39 additions & 0 deletions test/eval.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,45 @@ describe('eval', () => {
}
});
});

// These tests don't work due to https://github.com/overlookmotel/livepack/issues/102
// TODO Uncomment these tests once that issue resolved.
// eslint-disable-next-line jest/no-commented-out-tests
/*
describe('defined in method key', () => {
itSerializes('in function', {
in() {
let fn;
const ext = {x: 1}; // eslint-disable-line no-unused-vars
const obj = { // eslint-disable-line no-unused-vars
[fn = eval('() => ext')]() {}
};
return fn;
},
out: '(a=>()=>a)({x:1})',
validate(fn) {
expect(fn).toBeFunction();
expect(fn()).toEqual({x: 1});
}
});
itSerializes('at top level', {
in: () => requireFixture(`
let fn;
const ext = {x: 1};
const obj = {
[fn = eval('() => ext')]() {}
};
module.exports = fn;
`),
out: '(a=>()=>a)({x:1})',
validate(fn) {
expect(fn).toBeFunction();
expect(fn()).toEqual({x: 1});
}
});
});
*/
});

itSerializesEqual('multi-statement eval', {
Expand Down

0 comments on commit d5aab0f

Please sign in to comment.