Skip to content

Commit

Permalink
added .debug() to inject debugger statements in functions
Browse files Browse the repository at this point in the history
  • Loading branch information
jsoverson committed Jun 24, 2020
1 parent 2e096ee commit 5bc7eee
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 96 deletions.
26 changes: 26 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ import {
ObjectAssignmentTarget,
Statement,
Expression,
DebuggerStatement,
FunctionBody,
ReturnStatement
} from 'shift-ast';
import { parseScript } from 'shift-parser';
import shiftScope, { Scope, ScopeLookup, Variable, Reference, Declaration } from 'shift-scope';
Expand All @@ -41,6 +44,7 @@ import {
} from './util';
import deepEqual from 'fast-deep-equal';
import { waterfallMap } from './waterfall';
import { threadId } from 'worker_threads';

const debug = DEBUG('shift-refactor');

Expand Down Expand Up @@ -269,6 +273,28 @@ export class RefactorSession {
return nodes.map((node:Node) => this._parentMap.get(node));
}

debug(selector: SelectorOrNode) {
const nodes = findNodes(this.ast, selector);
nodes.forEach(node => {
switch (node.type) {
case 'FunctionExpression':
case 'FunctionDeclaration':
case 'Method':
this.insertBefore(node.body.statements, new DebuggerStatement());
break;
case 'ArrowExpression':
if (node.body.type !== 'FunctionBody') {
this.replace(node.body, new FunctionBody({directives:[], statements:[
new DebuggerStatement(),
new ReturnStatement({expression: node.body })
]}))
}
default:
// nothing;
}
});
}

insertBefore(selector: SelectorOrNode, replacer: Replacer) {
return this._insert(selector, replacer, false);
}
Expand Down
2 changes: 1 addition & 1 deletion src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export function isLiteral(
);
}

export function findNodes(ast: Node, input: SelectorOrNode) {
export function findNodes(ast: Node, input: SelectorOrNode): Node[] {
if (isString(input)) return query(ast, input);
else if (isArray(input)) return input;
else if (isShiftNode(input)) return [input];
Expand Down
204 changes: 109 additions & 95 deletions test/exposed-api.test.ts
Original file line number Diff line number Diff line change
@@ -1,109 +1,123 @@
import chai from 'chai';
import { LiteralStringExpression, Node } from 'shift-ast';
import { parseScript as parse } from 'shift-parser';
import { parseScript as parse, parseScript } from 'shift-parser';
import { RefactorSession, $r } from '../src/index';
// import { Scope } from 'shift-scope';

describe('API', function() {
it('should expose $r', () => {
it('$r (beta)', () => {
const $script = $r(`function foo(){}\nfoo();`);
const nodes = $script(`FunctionDeclaration[name.name="foo"]`);
chai.expect(nodes.length).to.equal(1);
});

it('should expose.query()', () => {
const refactor = new RefactorSession(`function foo(){}\nfoo();`);
const nodes = refactor.query(`FunctionDeclaration[name.name="foo"]`);
it('RefactorSession', () => {
const $script = new RefactorSession(`function foo(){}\nfoo();`);
const nodes = $script.query(`FunctionDeclaration[name.name="foo"]`);
chai.expect(nodes.length).to.equal(1);
});
it('should expose.findOne()', () => {
const refactor = new RefactorSession(`function foo(){}\nfunction bar(){}`);
const node = refactor.findOne(`FunctionDeclaration[name.name="foo"]`);
chai.expect(node.name.name).to.equal('foo');
function shouldBreak() {
refactor.findOne(`FunctionDeclaration`);
}
chai.expect(shouldBreak).to.throw();
});
it('should expose.findParents()', () => {
const refactor = new RefactorSession(`function foo(){}\nfunction bar(){}`);
const nodes = refactor.find(`BindingIdentifier`);
chai.expect(nodes.length).to.equal(2);
const parents = refactor.findParents(nodes);
chai.expect(parents.length).to.equal(2);
});
it('should expose.findReferences()', () => {
const refactor = new RefactorSession(`function foo(){}\nfoo();var a = foo();`);
const fn = refactor.findOne(`FunctionDeclaration[name.name="foo"]`);
chai.expect(refactor.findReferences(fn).length).to.equal(2);
});
it('should expose.findDeclarations()', () => {
const refactor = new RefactorSession(`function foo(){}\nfoo();var a = foo();`);
const fn = refactor.findOne(`FunctionDeclaration[name.name="foo"]`);
chai.expect(refactor.findDeclarations(fn).length).to.equal(1);
});
it('should find nodes by sample source', () => {
const $script = $r(`function foo(){}\nfoo(); a = foo(); b = foo(2); b = foo();`);
let nodes: Node[] = $script.findMatchingExpression('b = foo()');
chai.expect(nodes.length).to.equal(1);
//@ts-ignore
chai.expect(nodes[0]).to.deep.equal($script.ast.statements[4].expression);
nodes = $script.findMatchingStatement('b = foo()');
chai.expect(nodes.length).to.equal(1);
//@ts-ignore
chai.expect(nodes[0]).to.deep.equal($script.ast.statements[4]);
});
it('should expose .parse()', () => {
const src = `var a = 2; function foo(){var a = 4}`;
const ast = parse(src);
const r_ast = RefactorSession.parse(src);
chai.expect(r_ast).to.deep.equal(ast);
});
it('should expose .queryFrom()', () => {
let ast = parse(`var a = 2; function foo(){var a = 4}`);
const refactor = new RefactorSession(ast);
const nodes = refactor.query(`FunctionDeclaration[name.name="foo"]`);
const innerNodes = refactor.queryFrom(nodes, `VariableDeclarator[binding.name="a"]`);
chai.expect(innerNodes.length).to.equal(1);
});
it('should expose .print()', () => {
let ast = parse(`var a = 2; function foo(){var a = 4}`);
const refactor = new RefactorSession(ast);
const newSource = refactor.print();
chai.expect(ast).to.deep.equal(parse(newSource));
});
it('.print() should take any ast', () => {
let ast = parse(`var a = 2; function foo(){var a = 4}`);
const refactor = new RefactorSession(ast);
const newSource = refactor.print(new LiteralStringExpression({ value: 'hi' }));
chai.expect(newSource).to.equal('"hi"');
});
it('.closest() should walk up a tree looking for a matching selector', () => {
let ast = parse(`var a = 2; function foo(){var b = 4}`);
const refactor = new RefactorSession(ast);
const innerBinding = refactor.query('BindingIdentifier[name="b"]');
const parentStatement = refactor.closest(innerBinding, 'VariableDeclarationStatement');
chai.expect(parentStatement.length).to.equal(1);
});
it('.lookupVariable() should return variable lookup by Identifier node', () => {
let ast = parse(`var a = 2; function foo(){var b = 4}`);
const refactor = new RefactorSession(ast);
const innerBinding = refactor.query('BindingIdentifier[name="b"]');
const lookup = refactor.lookupVariable(innerBinding);
chai.expect(lookup).to.be.ok;
chai.expect(lookup.declarations.length).to.equal(1);
});
// it('.lookupScope() should return variable scope', () => {
// let ast = parse(`var a = 2; function foo(){var b = 4}`);
// const refactor = new RefactorSession(ast);
// const innerBinding = refactor.query('BindingIdentifier[name="b"]');
// const lookup = refactor.lookupScope(innerBinding) as Scope;
// chai.expect(lookup).to.be.ok;
// chai.expect(lookup.astNode).to.equal(ast.statements[1]);
// });
it('should expose .cleanup()', () => {
let ast = parse(``);
const refactor = new RefactorSession(ast);
chai.expect(() => refactor.cleanup).to.not.throw();
});

describe('RefactorSession', () => {
it('.query()', () => {
const refactor = new RefactorSession(`function foo(){}\nfoo();`);
const nodes = refactor.query(`FunctionDeclaration[name.name="foo"]`);
chai.expect(nodes.length).to.equal(1);
});
it('.debug()', () => {
const refactor = new RefactorSession(`b = _ => foo(); a.x = function(){b();}`);
refactor.debug(`FunctionExpression, ArrowExpression`);
chai.expect(refactor.ast).to.deep.equal(parseScript('b = _ => {debugger; return foo()}; a.x = function(){debugger;b();}'));
});
it('.findOne()', () => {
const refactor = new RefactorSession(`function foo(){}\nfunction bar(){}`);
const node = refactor.findOne(`FunctionDeclaration[name.name="foo"]`);
chai.expect(node.name.name).to.equal('foo');
function shouldBreak() {
refactor.findOne(`FunctionDeclaration`);
}
chai.expect(shouldBreak).to.throw();
});
it('.findParents()', () => {
const refactor = new RefactorSession(`function foo(){}\nfunction bar(){}`);
const nodes = refactor.find(`BindingIdentifier`);
chai.expect(nodes.length).to.equal(2);
const parents = refactor.findParents(nodes);
chai.expect(parents.length).to.equal(2);
});
it('.findReferences()', () => {
const refactor = new RefactorSession(`function foo(){}\nfoo();var a = foo();`);
const fn = refactor.findOne(`FunctionDeclaration[name.name="foo"]`);
chai.expect(refactor.findReferences(fn).length).to.equal(2);
});
it('.findDeclarations()', () => {
const refactor = new RefactorSession(`function foo(){}\nfoo();var a = foo();`);
const fn = refactor.findOne(`FunctionDeclaration[name.name="foo"]`);
chai.expect(refactor.findDeclarations(fn).length).to.equal(1);
});
it('should find nodes by sample source', () => {
const $script = $r(`function foo(){}\nfoo(); a = foo(); b = foo(2); b = foo();`);
let nodes: Node[] = $script.findMatchingExpression('b = foo()');
chai.expect(nodes.length).to.equal(1);
//@ts-ignore
chai.expect(nodes[0]).to.deep.equal($script.ast.statements[4].expression);
nodes = $script.findMatchingStatement('b = foo()');
chai.expect(nodes.length).to.equal(1);
//@ts-ignore
chai.expect(nodes[0]).to.deep.equal($script.ast.statements[4]);
});
it('.parse()', () => {
const src = `var a = 2; function foo(){var a = 4}`;
const ast = parse(src);
const r_ast = RefactorSession.parse(src);
chai.expect(r_ast).to.deep.equal(ast);
});
it('.queryFrom()', () => {
let ast = parse(`var a = 2; function foo(){var a = 4}`);
const refactor = new RefactorSession(ast);
const nodes = refactor.query(`FunctionDeclaration[name.name="foo"]`);
const innerNodes = refactor.queryFrom(nodes, `VariableDeclarator[binding.name="a"]`);
chai.expect(innerNodes.length).to.equal(1);
});
it('.print()', () => {
let ast = parse(`var a = 2; function foo(){var a = 4}`);
const refactor = new RefactorSession(ast);
const newSource = refactor.print();
chai.expect(ast).to.deep.equal(parse(newSource));
});
it('.print() should take any ast', () => {
let ast = parse(`var a = 2; function foo(){var a = 4}`);
const refactor = new RefactorSession(ast);
const newSource = refactor.print(new LiteralStringExpression({ value: 'hi' }));
chai.expect(newSource).to.equal('"hi"');
});
it('.closest() should walk up a tree looking for a matching selector', () => {
let ast = parse(`var a = 2; function foo(){var b = 4}`);
const refactor = new RefactorSession(ast);
const innerBinding = refactor.query('BindingIdentifier[name="b"]');
const parentStatement = refactor.closest(innerBinding, 'VariableDeclarationStatement');
chai.expect(parentStatement.length).to.equal(1);
});
it('.lookupVariable() should return variable lookup by Identifier node', () => {
let ast = parse(`var a = 2; function foo(){var b = 4}`);
const refactor = new RefactorSession(ast);
const innerBinding = refactor.query('BindingIdentifier[name="b"]');
const lookup = refactor.lookupVariable(innerBinding);
chai.expect(lookup).to.be.ok;
chai.expect(lookup.declarations.length).to.equal(1);
});
// it('.lookupScope() should return variable scope', () => {
// let ast = parse(`var a = 2; function foo(){var b = 4}`);
// const refactor = new RefactorSession(ast);
// const innerBinding = refactor.query('BindingIdentifier[name="b"]');
// const lookup = refactor.lookupScope(innerBinding) as Scope;
// chai.expect(lookup).to.be.ok;
// chai.expect(lookup.astNode).to.equal(ast.statements[1]);
// });
it('.cleanup()', () => {
let ast = parse(``);
const refactor = new RefactorSession(ast);
chai.expect(() => refactor.cleanup).to.not.throw();
});
})

});

0 comments on commit 5bc7eee

Please sign in to comment.