js2flowchart
Version:
> Why? While I've been working on [Under-the-hood-ReactJS](https://github.com/Bogdan-Lyashenko/Under-the-hood-ReactJS) I spent enormous amount of time on creating schemes. Each change in code or flowchart affects all entire scheme instantly, forcing you t
234 lines (227 loc) • 9.49 kB
JavaScript
import generate from '@babel/generator';
import { TOKEN_TYPES, CLASS_FUNCTION_KINDS } from 'shared/constants';
export var idleConverter = function idleConverter(path) {
return generate(path.node).code;
};
export var identifierConverter = function identifierConverter(path) {
if (path.parent.type === TOKEN_TYPES.SPREAD_PROPERTY) {
return '...' + idleConverter(path);
}
return idleConverter(path);
};
/* function */
export var functionConverter = function functionConverter(path) {
var node = path.node,
paramsCode = getFunctionParametersCode(node.params);
var name = '';
if (node.id) {
name = getAnonymousFunctionName(path) + 'function ' + node.id.name + paramsCode;
} else if (node.type === TOKEN_TYPES.ARROW_FUNCTION_EXPRESSION) {
name = getAnonymousFunctionName(path) + paramsCode + ' =>';
} else if (node.type === TOKEN_TYPES.CLASS_METHOD || node.type === TOKEN_TYPES.OBJECT_METHOD) {
name = node.kind === CLASS_FUNCTION_KINDS.CONSTRUCTOR ? 'constructor' + paramsCode : node.key.name + paramsCode;
} else {
name = getAnonymousFunctionName(path) + 'function' + paramsCode;
}
return {
name: name,
pathParentType: path.parent.type
};
};
export var getAnonymousFunctionName = function getAnonymousFunctionName(path) {
var parent = path.parent;
if (!parent || parent.type !== TOKEN_TYPES.VARIABLE_DECLARATOR && parent.type !== TOKEN_TYPES.ASSIGNMENT_EXPRESSION && parent.type !== TOKEN_TYPES.OBJECT_PROPERTY) {
return '';
}
if (parent.left) {
return generate(parent.left).code + ' = ';
}
var parentId = parent.id;
return parentId ? parentId.name + ' = ' : '';
};
export var getFunctionParametersCode = function getFunctionParametersCode(params) {
return "(".concat(params.map(function (p) {
if (p.name) {
return p.name;
}
return generate(p).code;
}).join(', '), ")");
};
export var returnConverter = function returnConverter(path) {
var node = path.node;
if (node.argument && ([TOKEN_TYPES.CONDITIONAL_EXPRESSION, TOKEN_TYPES.OBJECT_EXPRESSION].includes(node.argument.type) || isFunctionType(node.argument.type))) {
return 'return';
}
return path.node.argument ? "return ".concat(generate(path.node.argument).code) : 'return';
};
/* end function */
/* loop */
export var loopConverter = function loopConverter(_ref) {
var node = _ref.node;
if (node.test) {
return generate(node.test).code;
}
if (node.left && node.right) {
var innerPart = node.type === TOKEN_TYPES.FOR_OF_STATEMENT ? 'of' : 'in';
var leftPart = node.left.type === TOKEN_TYPES.VARIABLE_DECLARATION ? getVariableDeclarations(node.left.declarations) : generate(node.left).code;
return "".concat(leftPart, " ").concat(innerPart, " ").concat(generate(node.right).code);
}
};
export var continueConverter = function continueConverter(path) {
return path.node.label ? "continue ".concat(generate(path.node.label).code) : 'continue';
};
/* end loop */
export var conditionalConverter = function conditionalConverter(path) {
return "(".concat(generate(path.node.test).code, ")");
};
/* try-catch */
export var tryConverter = function tryConverter(path) {
return "try";
};
export var catchConverter = function catchConverter(path) {
return path.node.param ? "catch (".concat(generate(path.node.param).code, ")") : '*catchConverter*';
};
export var finallyConverter = function finallyConverter(path) {
//TODO: fix `finally`, not implemented yet because it presents only as a part of parent,
//there is no `finally` visitor as it exist for `catch`
//seems like to do that each try-catch block should be handled in a different way
return '*finallyConverter*';
};
/* end try-catch */
/* switch-case */
export var switchStatementConverter = function switchStatementConverter(path) {
return "switch (".concat(generate(path.node.discriminant).code, ")");
};
export var caseConverter = function caseConverter(path) {
return path.node.test ? "case ".concat(generate(path.node.test).code, ":") : 'default:';
};
export var breakConverter = function breakConverter(path) {
return path.node.label ? "break ".concat(generate(path.node.label).code, ":") : 'break';
};
/* end switch - case */
export var withStatementConverter = function withStatementConverter(path) {
return "with (".concat(generate(path.node.object).code, ")");
};
export var programConverter = function programConverter(path) {
return "".concat(path.node.type, ": source ").concat(path.node.sourceType);
};
export var throwStatementConverter = function throwStatementConverter(path) {
return "throw ".concat(generate(path.node.argument).code);
};
export var debuggerConverter = function debuggerConverter(path) {
return "debugger";
};
export var getVariableDeclarations = function getVariableDeclarations(variables) {
return variables.map(function (v) {
return variableDeclaratorConverter({
node: v
});
}).join(', ');
};
export var variableDeclaratorConverter = function variableDeclaratorConverter(path) {
var node = path.node,
parentKind = path.parent && path.parent.kind || '';
if (node.init && (isNodeContainsFunc(node.init) || node.init.type === TOKEN_TYPES.CONDITIONAL_EXPRESSION)) {
return "".concat(parentKind, " ").concat(node.id.name, " = ");
}
var variableName = '';
if (node.id.type === TOKEN_TYPES.OBJECT_PATTERN) {
variableName = '{...}';
} else if (node.id.type === TOKEN_TYPES.ARRAY_PATTERN) {
variableName = '[...]';
} else {
variableName = node.id.name;
}
if (node.init && [TOKEN_TYPES.CALL_EXPRESSION, TOKEN_TYPES.NEW_EXPRESSION].includes(node.init.type)) {
return "".concat(parentKind, " ").concat(variableName, " = ") + callExpressionConverter({
node: node.init
});
}
if (node.init && node.init.type === TOKEN_TYPES.OBJECT_EXPRESSION) {
return "".concat(parentKind, " ").concat(variableName, " = ").concat(objectExpressionConverter());
}
if (node.id && node.id.type === TOKEN_TYPES.OBJECT_PATTERN) {
return "".concat(parentKind, " {...} = ").concat(node.init.name);
}
if (node.id && node.id.type === TOKEN_TYPES.ARRAY_PATTERN) {
return "".concat(parentKind, " [...] = ").concat(node.init.name);
}
return parentKind + ' ' + generate(node).code;
};
export var assignmentExpressionConverter = function assignmentExpressionConverter(_ref2) {
var node = _ref2.node;
if (isNodeContainsFunc(node.right) || node.right.type === TOKEN_TYPES.CONDITIONAL_EXPRESSION) {
return "".concat(getLeftAssignmentName(node.left), " ").concat(node.operator, " ");
}
if (node.right.type === TOKEN_TYPES.OBJECT_EXPRESSION) {
return "".concat(getLeftAssignmentName(node.left), " ").concat(node.operator, " ").concat(objectExpressionConverter());
}
if ([TOKEN_TYPES.CALL_EXPRESSION, TOKEN_TYPES.NEW_EXPRESSION].includes(node.right.type)) {
return "".concat(getLeftAssignmentName(node.left), " ").concat(node.operator, " ").concat(callExpressionConverter({
node: node.right
}));
}
return generate(node).code;
};
var getLeftAssignmentName = function getLeftAssignmentName(node) {
if (node.name) {
return node.name;
}
return generate(node).code;
};
export var callExpressionConverter = function callExpressionConverter(_ref3) {
var node = _ref3.node;
var argumentsCode = '';
if (node.arguments && node.arguments.length) {
argumentsCode = node.arguments.map(getArgumentName).join(', ');
}
var callee = node.callee;
if (callee.type === TOKEN_TYPES.MEMBER_EXPRESSION && callee.object.type === TOKEN_TYPES.CALL_EXPRESSION) {
return {
name: ".".concat(callee.property.name, "(").concat(argumentsCode, ")"),
chain: true
};
} else if (argumentsCode) {
return "".concat(generate(node.callee).code, "(").concat(argumentsCode, ")");
}
return generate(node).code;
};
var getArgumentName = function getArgumentName(argument) {
if (isNodeContainsFunc(argument)) return '*()';
if (argument.type === TOKEN_TYPES.OBJECT_EXPRESSION) return objectExpressionConverter();
if (argument.name) return argument.name;
if (argument.value) return argument.type === TOKEN_TYPES.STRING_LITERAL ? "'".concat(argument.value, "'") : argument.value;
return generate(argument).code;
};
export var objectExpressionConverter = function objectExpressionConverter(path) {
var name = '{*}';
if (path) return {
name: name,
pathParentType: path.parent.type
};
return name;
};
export var objectPropertyConverter = function objectPropertyConverter(path) {
var node = path.node;
if (node.value && isFunctionType(node.value.type)) {
return node.key.name + ': ';
}
if (node.value && node.value.type === TOKEN_TYPES.OBJECT_EXPRESSION) {
return node.key.name + ': ' + objectExpressionConverter();
}
return generate(node).code;
};
var getFirstCallee = function getFirstCallee(callee) {
if (!callee) return callee;
if (callee.type === TOKEN_TYPES.MEMBER_EXPRESSION && callee.object.type === TOKEN_TYPES.CALL_EXPRESSION) {
return getFirstCallee(callee.object);
}
return callee;
};
export var isFunctionType = function isFunctionType(type) {
return [TOKEN_TYPES.FUNCTION_EXPRESSION, TOKEN_TYPES.FUNCTION, TOKEN_TYPES.ARROW_FUNCTION_EXPRESSION, TOKEN_TYPES.FUNCTION_DECLARATION].includes(type);
};
export var isNodeContainsFunc = function isNodeContainsFunc(node) {
var functions = [TOKEN_TYPES.ARROW_FUNCTION_EXPRESSION, TOKEN_TYPES.FUNCTION_EXPRESSION];
return node && functions.indexOf(node.type) !== -1;
};