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
328 lines (315 loc) • 10.6 kB
JavaScript
import { TOKEN_TYPES, TOKEN_KEYS } from 'shared/constants';
import {
idleConverter,
identifierConverter,
functionConverter,
isNodeContainsFunc,
returnConverter,
variableDeclaratorConverter,
assignmentExpressionConverter,
callExpressionConverter,
loopConverter,
continueConverter,
conditionalConverter,
catchConverter,
tryConverter,
switchStatementConverter,
caseConverter,
breakConverter,
withStatementConverter,
programConverter,
throwStatementConverter,
debuggerConverter,
objectExpressionConverter,
objectPropertyConverter
} from './converters/core';
import {
importDeclarationConverter,
exportNamedDeclarationConverter,
exportDefaultDeclarationConverter,
classDeclarationConverter,
objectPatternConverter,
arrayPatternConverter
} from './converters/Harmony';
const singleTypeFilter = path => {
const statementParent = path.getStatementParent(),
parent = path.parent || {};
if ([TOKEN_KEYS.CONSEQUENT, TOKEN_KEYS.ALTERNATE].includes(path.key)) {
return false;
}
return (
['params'].includes(path.listKey) ||
(statementParent.isReturnStatement() && path.key !== 'body') ||
((statementParent.isLoop() ||
statementParent.isConditional() ||
parent.type === TOKEN_TYPES.CONDITIONAL_EXPRESSION) &&
['test', 'left', 'right'].includes(path.parentKey)) ||
([
TOKEN_TYPES.RETURN,
TOKEN_TYPES.CALL_EXPRESSION,
TOKEN_TYPES.BINARY_EXPRESSION,
TOKEN_TYPES.UPDATE_EXPRESSION,
TOKEN_TYPES.ASSIGNMENT_EXPRESSION,
TOKEN_TYPES.LOGICAL_EXPRESSION,
TOKEN_TYPES.VARIABLE_DECLARATOR,
TOKEN_TYPES.MEMBER_EXPRESSION,
TOKEN_TYPES.NEW_EXPRESSION,
TOKEN_TYPES.FUNCTION_DECLARATION,
TOKEN_TYPES.FUNCTION_EXPRESSION,
TOKEN_TYPES.ARROW_FUNCTION_EXPRESSION,
TOKEN_TYPES.FUNCTION,
TOKEN_TYPES.OBJECT_PROPERTY,
TOKEN_TYPES.ASSIGNMENT_PATTERN,
TOKEN_TYPES.REST_PROPERTY,
TOKEN_TYPES.SPREAD_ELEMENT,
TOKEN_TYPES.ARRAY_EXPRESSION,
TOKEN_TYPES.UNARY_EXPRESSION,
TOKEN_TYPES.IMPORT_DEFAULT_SPECIFIER,
TOKEN_TYPES.IMPORT_SPECIFIER,
TOKEN_TYPES.IMPORT_DECLARATION,
TOKEN_TYPES.EXPORT_DEFAULT_DECLARATION,
TOKEN_TYPES.EXPORT_NAMED_DECLARATION,
TOKEN_TYPES.CLASS_DECLARATION,
TOKEN_TYPES.CLASS_METHOD,
TOKEN_TYPES.SWITCH_STATEMENT,
TOKEN_TYPES.SWITCH_CASE
].includes(parent.type) &&
(!parent.body || parent.body.type !== path.node.type))
);
};
export const DefinitionsMap = {
[TOKEN_TYPES.FUNCTION]: {
type: TOKEN_TYPES.FUNCTION,
getName: functionConverter,
body: true
},
[TOKEN_TYPES.RETURN]: {
type: TOKEN_TYPES.RETURN,
getName: returnConverter,
body: true
},
[TOKEN_TYPES.VARIABLE_DECLARATOR]: {
type: TOKEN_TYPES.VARIABLE_DECLARATOR,
body: true,
getName: variableDeclaratorConverter,
ignore: path => {
const statementParent = path.getStatementParent();
return (
!path.node.init || isNodeContainsFunc(path.node.init) || statementParent.isLoop()
);
}
},
[TOKEN_TYPES.ASSIGNMENT_EXPRESSION]: {
type: TOKEN_TYPES.ASSIGNMENT_EXPRESSION,
body: true,
getName: assignmentExpressionConverter,
ignore: path => {
const statementParent = path.getStatementParent();
return (
statementParent.isVariableDeclaration() ||
path.parent.type === TOKEN_TYPES.LOGICAL_EXPRESSION ||
(statementParent.isConditional() && path.key === TOKEN_KEYS.TEST) ||
isNodeContainsFunc(path.node.right)
);
}
},
[TOKEN_TYPES.CALL_EXPRESSION]: {
type: TOKEN_TYPES.CALL_EXPRESSION,
body: false,
reversed: true,
getName: callExpressionConverter,
ignore: path => {
const statementParent = path.getStatementParent(),
parent = path.parent || {};
if (parent.type === TOKEN_TYPES.ARROW_FUNCTION_EXPRESSION) return false;
return (
statementParent.isVariableDeclaration() ||
[
TOKEN_TYPES.RETURN,
TOKEN_TYPES.CALL_EXPRESSION,
TOKEN_TYPES.NEW_EXPRESSION,
TOKEN_TYPES.UNARY_EXPRESSION,
TOKEN_TYPES.BINARY_EXPRESSION
].includes(parent.type) ||
(statementParent.isConditional() &&
parent.test &&
parent.test.type === TOKEN_TYPES.CALL_EXPRESSION) ||
path.parent.type === TOKEN_TYPES.ASSIGNMENT_EXPRESSION //TODO: BUG, fix line: list = list.filter(i => i % 2)
);
}
},
[TOKEN_TYPES.UPDATE_EXPRESSION]: {
type: TOKEN_TYPES.UPDATE_EXPRESSION,
getName: idleConverter,
ignore: path => path.getStatementParent().isVariableDeclaration()
},
[TOKEN_TYPES.NEW_EXPRESSION]: {
type: TOKEN_TYPES.NEW_EXPRESSION,
getName: idleConverter,
ignore: path =>
path.getStatementParent().isVariableDeclaration() ||
path.parent.type === TOKEN_TYPES.ASSIGNMENT_EXPRESSION ||
path.parent.type === TOKEN_TYPES.THROW_STATEMENT
},
[TOKEN_TYPES.LOOP]: {
type: TOKEN_TYPES.LOOP,
getName: loopConverter,
body: true
},
[TOKEN_TYPES.CONTINUE]: {
type: TOKEN_TYPES.CONTINUE,
getName: continueConverter,
body: true
},
[TOKEN_TYPES.CONDITIONAL]: {
type: TOKEN_TYPES.CONDITIONAL,
getName: conditionalConverter,
body: true
},
[TOKEN_TYPES.SWITCH_STATEMENT]: {
type: TOKEN_TYPES.SWITCH_STATEMENT,
getName: switchStatementConverter,
body: true
},
[TOKEN_TYPES.SWITCH_CASE]: {
type: TOKEN_TYPES.SWITCH_CASE,
getName: caseConverter,
body: true
},
[TOKEN_TYPES.BREAK]: {
type: TOKEN_TYPES.BREAK,
getName: breakConverter,
body: true
},
[TOKEN_TYPES.TRY_STATEMENT]: {
type: TOKEN_TYPES.TRY_STATEMENT,
getName: tryConverter,
body: true
},
[TOKEN_TYPES.CATCH_CLAUSE]: {
type: TOKEN_TYPES.CATCH_CLAUSE,
getName: catchConverter,
body: true
},
[TOKEN_TYPES.WITH_STATEMENT]: {
type: TOKEN_TYPES.WITH_STATEMENT, //TODO: visual
getName: withStatementConverter,
body: true
},
[TOKEN_TYPES.PROGRAM]: {
type: TOKEN_TYPES.PROGRAM, //TODO: visual
getName: programConverter,
body: true
},
[TOKEN_TYPES.THROW_STATEMENT]: {
type: TOKEN_TYPES.THROW_STATEMENT,
getName: throwStatementConverter,
body: true
},
[TOKEN_TYPES.DEBUGGER_STATEMENT]: {
type: TOKEN_TYPES.DEBUGGER_STATEMENT,
getName: debuggerConverter,
body: true
},
[TOKEN_TYPES.BINARY_EXPRESSION]: {
type: TOKEN_TYPES.BINARY_EXPRESSION,
getName: idleConverter,
ignore: singleTypeFilter
},
[TOKEN_TYPES.IDENTIFIER]: {
type: TOKEN_TYPES.IDENTIFIER,
getName: identifierConverter,
ignore: singleTypeFilter
},
[TOKEN_TYPES.STRING_LITERAL]: {
type: TOKEN_TYPES.STRING_LITERAL,
getName: idleConverter,
ignore: singleTypeFilter
},
[TOKEN_TYPES.NUMERIC_LITERAL]: {
type: TOKEN_TYPES.NUMERIC_LITERAL,
getName: idleConverter,
ignore: singleTypeFilter
},
[TOKEN_TYPES.OBJECT_EXPRESSION]: {
type: TOKEN_TYPES.OBJECT_EXPRESSION,
getName: objectExpressionConverter,
ignore: path => {
const node = path.node;
if (node.properties && !node.properties.length) {
return true;
}
return [
TOKEN_TYPES.OBJECT_PROPERTY,
TOKEN_TYPES.ASSIGNMENT_EXPRESSION,
TOKEN_TYPES.VARIABLE_DECLARATOR
].includes(path.parent.type);
},
body: true
},
[TOKEN_TYPES.OBJECT_PROPERTY]: {
type: TOKEN_TYPES.OBJECT_PROPERTY,
getName: objectPropertyConverter,
ignore: path => {
const parentPath = path.parentPath;
return ['params', 'left'].includes(parentPath.parentKey);
},
body: true
},
//ES Harmony features
[TOKEN_TYPES.IMPORT_DECLARATION]: {
type: TOKEN_TYPES.IMPORT_DECLARATION,
getName: importDeclarationConverter,
body: true
},
[TOKEN_TYPES.IMPORT_DEFAULT_SPECIFIER]: {
type: TOKEN_TYPES.IMPORT_DEFAULT_SPECIFIER,
getName: idleConverter
},
[TOKEN_TYPES.IMPORT_SPECIFIER]: {
type: TOKEN_TYPES.IMPORT_SPECIFIER,
getName: idleConverter
},
[TOKEN_TYPES.EXPORT_DEFAULT_DECLARATION]: {
type: TOKEN_TYPES.EXPORT_DEFAULT_DECLARATION,
getName: exportDefaultDeclarationConverter,
body: true
},
[TOKEN_TYPES.EXPORT_NAMED_DECLARATION]: {
type: TOKEN_TYPES.EXPORT_NAMED_DECLARATION,
getName: exportNamedDeclarationConverter,
body: true
},
[TOKEN_TYPES.CLASS_DECLARATION]: {
type: TOKEN_TYPES.CLASS_DECLARATION,
getName: classDeclarationConverter,
body: true
},
[TOKEN_TYPES.OBJECT_PATTERN]: {
type: TOKEN_TYPES.OBJECT_PATTERN,
getName: objectPatternConverter,
ignore: path => {
return (
path.listKey === 'params' ||
[TOKEN_TYPES.VARIABLE_DECLARATOR, TOKEN_TYPES.ASSIGNMENT_PATTERN].includes(
path.parent.type
)
);
},
body: true
},
[TOKEN_TYPES.ARRAY_PATTERN]: {
type: TOKEN_TYPES.ARRAY_PATTERN,
getName: arrayPatternConverter,
ignore: path => {
return (
path.listKey === 'params' ||
[TOKEN_TYPES.VARIABLE_DECLARATOR, TOKEN_TYPES.ASSIGNMENT_PATTERN].includes(
path.parent.type
)
);
},
body: true
}
};
export const DefinitionsList = Object.keys(DefinitionsMap).map(key => DefinitionsMap[key]);